• 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

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

    blk_queue_make_request
    blk_cleanup_queue
    set_capacity
    alloc_disk
    add_disk
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    java+selenium获取动态下拉列表元素
    【论文笔记】UniPAD: A Universal Pre-training Paradigm for Autonomous Driving
    Ubuntu中安装clion并把clion添加到桌面快捷方式
    2022牛客多校十 E-Reviewer Assignment(最小费用流)
    java面试笔试题
    Java msyql批量插入 十万条数据
    Nginx反向代理和负载均衡
    第六章:利用dumi搭建组件文档【前端工程化入门-----从零实现一个react+ts+vite+tailwindcss组件库】
    git笔记
    测试部门来了个00后卷王之王,老油条感叹真干不过,但是...
  • 原文地址:https://blog.csdn.net/qq_38158479/article/details/134293418