NVME Driver分析之nvme_dev_add函数分析

 nvme_dev_add @ nvme_probe


①int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;//计算shift,为2的shift次幂
②mem = dma_alloc_coherent(&pdev->dev, 8192, &dma_addr, GFP_KERNEL);//这块空间用于读取identifier controller和identifier namespace
③nvme_identify(dev, 0, 1, dma_addr);//读取controller identifier,参数1表示是controller,0表示namespaces
④ctrl = mem; //根据读取的identifier controller配置各种信息
 nn = le32_to_cpup(&ctrl->nn); //获取namespaces的个数
 dev->oncs = le16_to_cpup(&ctrl->oncs); //获取controller optional nvme command support属性
 dev->abort_limit = ctrl->acl + 1; //abort limit
 dev->vwc = ctrl->vwc; //易失缓冲区是否存在 cache
 memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn)); //序列号
 memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn)); //model number
 memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));//firmware 版本号 
 if (ctrl->mdts) 
  dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9); //这里减9之后正好是512,一个sector的大小
 if ((pdev->vendor == PCI_VENDOR_ID_INTEL) && 
   (pdev->device == 0x0953) && ctrl->vs[3]) 
  dev->stripe_size = 1 << (ctrl->vs[3] + shift);//如果是英特尔的某型号的nvme设备的话,则使用如下stripe
⑤id_ns = mem;
 for (i = 1; i <= nn; i++) { //对于每一块儿namespace执行如下操作
     res = nvme_identify(dev, i, 0, dma_addr);//获取namespace identifier,参数为0表示要获取的是namespace,大小是2048
     nvme_get_features(dev, NVME_FEAT_LBA_RANGE, i,   dma_addr + 4096, NULL);//获取每一块儿namespace的LBA Range,dma_addr的空间总共8192,前4096存放了identifier,所以这里使用后4096的大小
    nvme_alloc_ns(dev, i, mem, mem + 4096);//将每一个nvmespace看成一个磁盘。这里会实现块设备的make request和alloc disk
⑥list_for_each_entry(ns, &dev->namespaces, list)
       add_disk(ns->disk);//为每一块儿namespace执行add_disk操作

 nvme_alloc_ns@nvme_add函数分析


①ns = kzalloc(sizeof(*ns), GFP_KERNEL); //开辟namespace的结构体
 ns->queue = blk_alloc_queue(GFP_KERNEL); //为块设备分配请求队列
 blk_queue_make_request(ns->queue, nvme_make_request); //绑定请求队列和制造请求函数
 ns->dev = dev;
 ns->queue->queuedata = ns;
disk = alloc_disk(0);//执行alloc disk操作
③ns->ns_id = nsid; //对ns结构体进行赋值
 ns->disk = disk;
 lbaf = id->flbas & 0xf;
 ns->lba_shift = id->lbaf[lbaf].ds; //LBA data  size
 ns->ms = le16_to_cpu(id->lbaf[lbaf].ms); //metadata size
 ④blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);//block告诉内核磁盘的尺寸
⑤disk->major = nvme_major; //磁盘的设备号
 disk->first_minor = 0; //
 disk->fops = &nvme_fops;
 disk->private_data = ns;
 disk->queue = ns->queue;
 disk->driverfs_dev = &dev->pci_dev->dev;
 disk->flags = GENHD_FL_EXT_DEVT;
 sprintf(disk->disk_name, "nvme%dn%d", dev->instance, nsid);//磁盘对外显示的diskname
 set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); //磁盘的每个sector size
⑥if (dev->oncs & NVME_CTRL_ONCS_DSM)
      nvme_config_discard(ns);//如果支持data set management namespace执行config discard 操作
至此,nvme_dev_add就讲完了

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`nvme_submit_user_cmd()` 函数NVMe 驱动中用于向 NVMe 设备提交用户命令的函数。该函数的实现如下: ```c int nvme_submit_user_cmd(struct request_queue *q, struct nvme_command *cmd, void __user *ubuf, void __user *meta, unsigned timeout) { ... struct nvme_ns *ns = q->queuedata; ... struct nvme_user_io io = { .opcode = cmd->common.opcode, .flags = cmd->common.flags, .control = cpu_to_le16((timeout ? NVME_IO_FLAGS_PRACT : 0) | NVME_IO_FLAGS_CQ_UPDATE | NVME_IO_FLAGS_SGL_METABUF), .metadata = (__u64)meta, .addr = (__u64)ubuf, .slba = cpu_to_le64(cmd->rw.slba), .nlb = cpu_to_le16(cmd->rw.nblocks), .dsmgmt = cpu_to_le16(cmd->rw.dsmgmt), .reftag = cpu_to_le16(cmd->rw.reftag), .apptag = cpu_to_le16(cmd->rw.apptag), .appmask = cpu_to_le16(cmd->rw.appmask), }; ... ret = nvme_submit_user_cmd_hw(q, ns, &io, &cmd->common, timeout); ... return ret; } ``` 该函数的主要作用是将用户命令转换为 `nvme_user_io` 结构体,并调用 `nvme_submit_user_cmd_hw()` 函数将该命令提交给 NVMe 设备。下面是对该函数的参数及关键代码进行分析: - `q`:请求队列指针,用于指定 NVMe 设备所在的请求队列。 - `cmd`:NVMe 命令结构体指针,包含了要提交的 NVMe 写入命令的相关信息。 - `ubuf`:用户数据缓冲区的指针,该缓冲区包含了要写入存储介质的数据。 - `meta`:元数据缓冲区的指针,该缓冲区用于存储 NVMe 设备返回的写入操作结果。 - `timeout`:命令超时时间,以毫秒为单位。 该函数首先从请求队列中获取 NVMe 命名空间指针 `ns`,然后将用户命令转换为 `nvme_user_io` 结构体,并设置了一些命令的控制标志位。接着,该函数调用 `nvme_submit_user_cmd_hw()` 函数将命令提交给 NVMe 设备。 在 `nvme_submit_user_cmd_hw()` 函数中,NVMe 驱动会将 `nvme_user_io` 结构体中的数据转换为 NVMe 命令数据结构,并将该命令放入命令队列中。然后,NVMe 驱动会等待命令完成,并将命令的执行结果存储到元数据缓冲区中。最后,驱动程序会更新命令队列和完成队列的指针,并返回命令的执行状态。 在 NVMe 驱动中,`nvme_submit_user_cmd()` 函数是将用户命令提交给 NVMe 设备的入口函数,它的实现非常简单,主要是将用户命令转换为 NVMe 命令,并调用硬件相关的函数将命令提交给 NVMe 设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值