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就讲完了