NVME Driver分析之nvme_submit_sync_cmd

前面在分支创建 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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值