NVME Driver分析之nvme_submit_sync_cmd

来源:互联网 发布:nginx 默认403页面 编辑:程序博客网 时间:2024/06/05 02:49

前面在分支创建 I/O Completion Queue 命令时我们提到了,Admin command的提交,使用了nvme_submit_admin_cmd函数,该函数其实是调用了nvme_submit_sync_cmd(dev, 0, cmd, result, ADMIN_TIMEOUT),即提交了一个同步的命令。

static int nvme_submit_sync_cmd(struct nvme_dev *dev, int q_idx,       struct nvme_command *cmd,       u32 *result, unsigned timeout){ int cmdid, ret; struct sync_cmd_info cmdinfo; struct nvme_queue *nvmeq; nvmeq = lock_nvmeq(dev, q_idx); if (!nvmeq)  return -ENODEV; cmdinfo.task = current; cmdinfo.status = -EINTR; cmdid = alloc_cmdid(nvmeq, &cmdinfo, sync_completion, timeout); //给cmd分配一个cmd id,通过在一个bitmap(还记得第一节分析init时提到的cmdid_data吗,它就是这个bitmap)中从头向后找,找到第一个非0的位置,就将这个位置号作为cmd id分配给这个cmd。。在alloc_cmdid的时候还会把completion的回调函数(sync_completion)以及回调函数的(cmdinfo)放入nvmeq->cmdinfo中,当controller处理完命令以后,host在被唤醒或者检测到这个命令处理完以后,就会执行这个回调函数,处理completion queue。  if (cmdid < 0) {  unlock_nvmeq(nvmeq);  return cmdid; } cmd->common.command_id = cmdid; //把获取的cmd id赋值要提交的cmd的command_id set_current_state(TASK_KILLABLE); ret = nvme_submit_cmd(nvmeq, cmd); //把command拷贝到queue中去同时按一下doorbell寄存器门铃,nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride] if (ret) {//如果提交失败  free_cmdid(nvmeq, cmdid, NULL);//把cmdid清理掉  unlock_nvmeq(nvmeq);  set_current_state(TASK_RUNNING);  return ret;//返回error code } unlock_nvmeq(nvmeq); schedule_timeout(timeout);//等待controller执行完,执行一次cpu调度 if (cmdinfo.status == -EINTR)   nvmeq = lock_nvmeq(dev, q_idx);  if (nvmeq) {//如果queue还存在,则说明timeout超时了   nvme_abort_command(nvmeq, cmdid);//aborted掉这个cmd   unlock_nvmeq(nvmeq);  }  return -EINTR; } if (result)  *result = cmdinfo.result; return cmdinfo.status;}
static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd){ unsigned long flags; u16 tail; spin_lock_irqsave(&nvmeq->q_lock, flags); if (nvmeq->q_suspended) { //如果是suspend状态,说明这个queue没有init OK,所以要返回error信息  spin_unlock_irqrestore(&nvmeq->q_lock, flags);  return -EBUSY; } tail = nvmeq->sq_tail; //找到submission queue的队尾 memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd)); //把要提交的命令拷贝到循环队列的队尾中去 if (++tail == nvmeq->q_depth) //如果循环队列到头了,则队尾重新归0  tail = 0; writel(tail, nvmeq->q_db); //按门铃!!! nvmeq->sq_tail = tail; //更新队尾的位置 spin_unlock_irqrestore(&nvmeq->q_lock, flags); return 0;}

static void *free_cmdid(struct nvme_queue *nvmeq, int cmdid,      nvme_completion_fn *fn){ void *ctx; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); if (cmdid >= nvmeq->q_depth || !info[cmdid].fn) {  if (fn)   *fn = special_completion;  return CMD_CTX_INVALID; } if (fn)  *fn = info[cmdid].fn; ctx = info[cmdid].ctx; info[cmdid].fn = special_completion; info[cmdid].ctx = CMD_CTX_COMPLETED; clear_bit(cmdid, nvmeq->cmdid_data); //将该cmdid对应的bitmap位清零 wake_up(&nvmeq->sq_full); //唤醒等待获取cmdid的进程,同时唤醒process_cq的处理进程 return ctx;}



0 0