linux 3.13版本nvme驱动阅读记录三

这里记录static int nvme_dev_add(struct nvme_dev *dev)函数。

在调用完这个函数以后,就可以在dev目录下看到响应的设备了。

static int nvme_dev_add(struct nvme_dev *dev)
{
	int res;
	unsigned nn, i;
	struct nvme_ns *ns;
	struct nvme_id_ctrl *ctrl;
	struct nvme_id_ns *id_ns;
	void *mem;
	dma_addr_t dma_addr;
	/*
		该字段表示控制器支持的最小hostmemory页面大小。最小内存页大小是(2 ^ (12 + MPSMIN))。
		主机不能配置内存页大小inCC。小于此值的MPS。
	*/
	int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
	mem = dma_alloc_coherent(&dev->pci_dev->dev, 8192, &dma_addr, GFP_KERNEL);
	if (!mem)
		return -ENOMEM;

	res = nvme_identify(dev, 0, 1, dma_addr);//cns为1是struct nvme_id_ctrl结构
	if (res) {
		res = -EIO;
		goto out;
	}
	ctrl = mem;
	nn = le32_to_cpup(&ctrl->nn); //Number of namespaces
	dev->oncs = le16_to_cpup(&ctrl->oncs);//option nvm command support
	//下面这几个后面可以与scsi命令转nvme,或者上层获取nvme相关的版本信息
	memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn));//serial number
	memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn)); //model number
	memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));//firmware revision
	/*
		2^n,以MPSMIN为单位, 最大数据传输长度,0表示没有限制
		比如mdts是1,以2^n表示值是2(即2 * 4096/),假设mpsmin是0,即支持的最小内存页为4096,
		那么max_hw_sectors = 1 << (1 + 12 - 9) = 1 << 4 = 16
		16 * 512(扇区的大小) = 2 * 4096 刚好是对应的。
	*/
	if (ctrl->mdts)
		dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9);
	/*
		这个不知道描述的是啥?
	*/
	if ((dev->pci_dev->vendor == PCI_VENDOR_ID_INTEL) && (dev->pci_dev->device == 0x0953) && ctrl->vs[3])
		dev->stripe_size = 1 << (ctrl->vs[3] + shift);

	id_ns = mem;
	for (i = 1; i <= nn; i++) { //number of namespaces
		res = nvme_identify(dev, i, 0, dma_addr); //cns为0是struct nvme_id_ns结构
		if (res)
			continue;
		/*
			Namespace Capacity (NCAP):
			该字段表示在任何时间点命名空间中可以分配的最大逻辑块数量。
			逻辑块的数量取决于格式化的LBA大小。在格式化命名空间之前,此字段是未定义的。
			该字段用于精简配置,报告一个小于等于Namespace Size的值。备用LBAs不作为该字段的一部分报告。
			当使用Write或Write uncorrectable命令写入逻辑块时,会分配逻辑块。
			可以使用数据集管理、清理或写零命令释放逻辑块
		*/
		if (id_ns->ncap == 0)
			continue;
		res = nvme_get_features(dev, NVME_FEAT_LBA_RANGE, i, dma_addr + 4096, NULL);
		if (res)
			memset(mem + 4096, 0, 4096);
		ns = nvme_alloc_ns(dev, i, mem, mem + 4096);
		if (ns)
			list_add_tail(&ns->list, &dev->namespaces);//namespace 用一个链表表示
	}
	//根据namespace来增加disk,函数调用完毕,就可以在dev目录下看到相应的设备节点了
	list_for_each_entry(ns, &dev->namespaces, list)
		add_disk(ns->disk); //这个函数相信大家都比较熟悉了
	res = 0;
out:
	dma_free_coherent(&dev->pci_dev->dev, 8192, mem, dma_addr);
	return res;
}

nvme_alloc_ns

static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid, 
		struct nvme_id_ns *id, struct nvme_lba_range_type *rt)
{
	struct nvme_ns *ns;
	struct gendisk *disk;
	int lbaf;

	if (rt->attributes & NVME_LBART_ATTRIB_HIDE)//值是0或者1,其它是非法值
		return NULL;

	ns = kzalloc(sizeof(*ns), GFP_KERNEL);
	if (!ns)
		return NULL;
	//申请队列结构体
	ns->queue = blk_alloc_queue(GFP_KERNEL);
	if (!ns->queue)
		goto out_free_ns;
	ns->queue->queue_flags = QUEUE_FLAG_DEFAULT;
	queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, ns->queue);//不需要上层进行io合并操作
	queue_flag_set_unlocked(QUEUE_FLAG_NONROT, ns->queue);//随机访问设备
	//nvme_make_request发起命令的回调函数
	blk_queue_make_request(ns->queue, nvme_make_request);
	ns->dev = dev;
	ns->queue->queuedata = ns;

	disk = alloc_disk(NVME_MINORS); //申请struct gendisk结构
	if (!disk)
		goto out_free_queue;
	ns->ns_id = nsid; //namespace id
	ns->disk = disk;
	lbaf = id->flbas & 0xf;//取0-3bit
	ns->lba_shift = id->lbaf[lbaf].ds; //得到LBA的大小,逻辑块的大小,2^n表示
	ns->ms = le16_to_cpu(id->lbaf[lbaf].ms);
	//设置队列的逻辑块大小,以字节位单位
	blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
	/*
		将控制器的限制告诉内核(单次io的大小限制)
	*/
	if (dev->max_hw_sectors)
		blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);//max_segment_size

	disk->major = nvme_major;
	disk->minors = NVME_MINORS;
	disk->first_minor = NVME_MINORS * nvme_get_ns_idx();
	disk->fops = &nvme_fops;//块设备操作
	disk->private_data = ns;
	disk->queue = ns->queue;//块设备相关的队列
	disk->driverfs_dev = &dev->pci_dev->dev;
	//磁盘名称,这里利用到了instance和nsid的值
	sprintf(disk->disk_name, "nvme%dn%d", dev->instance, nsid);
	/*
		设置磁盘容量,以扇区为单位
		Namespace Size (NSZE):
		该字段表示logicalblocks中命名空间的总大小。
		大小为n的命名空间由LBA 0到(n - 1)个LBA组成,逻辑块的数量根据格式化后的LBA大小确定。
		在格式化命名空间之前,此字段是未定义的。
	*/
	set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
	/*
	位3如果设置为“1”,则控制器支持写零命令。如果设置为“0”,则该控制器不支持写零命令。
	位2如果设置为“1”,则控制器支持数据集管理命令。如果清除为“0”,则控制器不支持数据集管理命令。
	第1位如果设置为“1”,则控制器支持Write Uncorrectable命令。如果清除为“0”,则该控制器不支持Write Uncorrectable命令。
	位0如果设置为“1”,则控制器支持比较命令。如果清除为“0”,则控制器不支持比较命令。
	*/
	if (dev->oncs & NVME_CTRL_ONCS_DSM) //0100
		nvme_config_discard(ns);
	return ns;
out_free_queue:
	blk_cleanup_queue(ns->queue);
out_free_ns:
	kfree(ns);
	return NULL;
}

这里看到了块设备编程当中比较主要的函数,比如:

blk_queue_make_request
blk_cleanup_queue
set_capacity
alloc_disk
add_disk

nvme_fops

static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
	struct nvme_ns *ns = bdev->bd_disk->private_data;

	switch (cmd) {
	case NVME_IOCTL_ID:
		force_successful_syscall_return();
		return ns->ns_id;
	case NVME_IOCTL_ADMIN_CMD: //admin命令
		return nvme_user_admin_cmd(ns->dev, (void __user *)arg);
	case NVME_IOCTL_SUBMIT_IO: //io命令
		return nvme_submit_io(ns, (void __user *)arg);
	case SG_GET_VERSION_NUM:
		return nvme_sg_get_version_num((void __user *)arg);
	case SG_IO: //scsi-nvme
		return nvme_sg_io(ns, (void __user *)arg);
	default:
		return -ENOTTY;
	}
}

static const struct block_device_operations nvme_fops = {
	.owner = THIS_MODULE,
	.ioctl = nvme_ioctl,
	.compat_ioctl = nvme_ioctl,
};

你可能感兴趣的:(#,nvme,linux)