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

这篇具有很好参考价值的文章主要介绍了linux 3.13版本nvme驱动阅读记录一。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

内核版本较低的nvme驱动代码不多,而且使用的是单队列的架构,阅读起来会轻松一点。

这个版本涉及到的nvme驱动源码文件一共就4个,两个nvme.h文件,分别在include/linux ,include/uapi/linux目录下,nvme-core.c是主要源码文件,还有nvme-scsi.c,这个文件是scsi协议转nvme协议的。

先从模块入口函数开始吧。

static int __init nvme_init(void)
{
	int result;

	nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");//创建内核线程,并开始运行
	if (IS_ERR(nvme_thread))
		return PTR_ERR(nvme_thread);

	result = register_blkdev(nvme_major, "nvme");//块设备驱动注册,得到主设备号
	if (result < 0)
		goto kill_kthread;
	else if (result > 0)
		nvme_major = result;//返回的主设备号

	result = pci_register_driver(&nvme_driver);//注册pci驱动
	if (result)
		goto unregister_blkdev;
	return 0;
unregister_blkdev:
	unregister_blkdev(nvme_major, "nvme");
kill_kthread:
	kthread_stop(nvme_thread);//停止内核线程的运行
	return result;
}

static void __exit nvme_exit(void)
{
	pci_unregister_driver(&nvme_driver);
	unregister_blkdev(nvme_major, "nvme");
	kthread_stop(nvme_thread);//nvme_thread内核线程停止运行
}

MODULE_AUTHOR("Matthew Wilcox <willy@linux.intel.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.8");
module_init(nvme_init);
module_exit(nvme_exit);

模块入口函数主要干了三件事。
1:创建了一个内核线程,主要作用是处理不满足prp条件时,队列函数提交的bio,这个后面在详细说。
2:注册了一个块设备驱动,作用是得到了一个主设备号,后面是需要这个设备号的。
3:注册了pci驱动,作用大家都知道的。

模块退出函数与模块入口函数做的事情刚好相反,也没什么说的。

接着看probe函数:

static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	int result = -ENOMEM;
	struct nvme_dev *dev;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;
	/*
		后续保存中断向量的信息
	*/
	dev->entry = kcalloc(num_possible_cpus(), sizeof(*dev->entry), GFP_KERNEL);
	if (!dev->entry)
		goto free;

	//queue[0] queue[1]存放的是指针
	dev->queues = kcalloc(num_possible_cpus() + 1, sizeof(void *), GFP_KERNEL); //sizeof(void *) 8 or 4
	if (!dev->queues)
		goto free;

	INIT_LIST_HEAD(&dev->namespaces);//初始化链表头,后续将nvme的namespaces以链表的形式链接起来
	dev->pci_dev = pdev;

	result = nvme_set_instance(dev);//获取一个instance值,用于后面的磁盘名称命名
	if (result)
		goto free;

	result = nvme_setup_prp_pools(dev);//prp地址的申请,即prp list,后续用来保存bio转换出来的dma地址
	if (result)
		goto release;

	result = nvme_dev_start(dev);//admin queue 和 io queu的配置
	if (result) {
		if (result == -EBUSY)
			goto create_cdev;
		goto release_pools;
	}

	result = nvme_dev_add(dev); //和内核块设备提供的函数操作有关
	if (result)
		goto shutdown;
create_cdev:
	//利用miscdev结构体提供一些字符设备的操作(回调函数),用户空间可以下发一些nvme的命令等
	scnprintf(dev->name, sizeof(dev->name), "nvme%d", dev->instance);
	dev->miscdev.minor = MISC_DYNAMIC_MINOR;
	dev->miscdev.parent = &pdev->dev;
	dev->miscdev.name = dev->name;
	dev->miscdev.fops = &nvme_dev_fops;
	result = misc_register(&dev->miscdev);
	if (result)
		goto remove;

	kref_init(&dev->kref);//设备引用计数初始化,值为1
	return 0;
remove:
	nvme_dev_remove(dev);
shutdown:
	nvme_dev_shutdown(dev);
release_pools:
	nvme_free_queues(dev);
	nvme_release_prp_pools(dev);
release:
	nvme_release_instance(dev);
free:
	kfree(dev->queues);
	kfree(dev->entry);
	kfree(dev);
	return result;
}

static void nvme_remove(struct pci_dev *pdev)
{
	struct nvme_dev *dev = pci_get_drvdata(pdev);
	misc_deregister(&dev->miscdev);
	//引用计数为0,调用nvme_free_dev函数
	kref_put(&dev->kref, nvme_free_dev);
}

static DEFINE_PCI_DEVICE_TABLE(nvme_id_table) = {
	{ PCI_DEVICE_CLASS(0x010802, 0xffffff) }, //class满足0x010802就会调用probe函数
	{ 0,}
};
MODULE_DEVICE_TABLE(pci, nvme_id_table);
static struct pci_driver nvme_driver = {
	.name		= "nvme",
	.id_table	= nvme_id_table,
	.probe		= nvme_probe,
	.remove		= nvme_remove,
};

struct nvme_dev结构体定义:

struct nvme_dev {
	struct list_head node;
	struct nvme_queue **queues;
	u32 __iomem *dbs;
	struct pci_dev *pci_dev;
	struct dma_pool *prp_page_pool;
	struct dma_pool *prp_small_pool;
	int instance;
	int queue_count;
	int db_stride;
	u32 ctrl_config;
	struct msix_entry *entry;
	struct nvme_bar __iomem *bar;
	struct list_head namespaces;
	struct kref kref;
	struct miscdevice miscdev;
	char name[12];
	char serial[20];
	char model[40];
	char firmware_rev[8];
	u32 max_hw_sectors;
	u32 stripe_size;
	u16 oncs;
};

总结下probe函数干的事情:
1:struct nvme_dev *dev 申请内存,这个结构体就代表一个nvme设备。
2:dev->entry申请内存,大小是num_possible_cpus() * sizeof(*dev->entry),后面用于保存向量的相关信息,因为nvme是支持多队列的,所以后面可以将队列和特定的中断向量进行绑定,这个后面遇到相关代码再说。至于num_possible_cpus()的值是多少,什么意思,也不用研究那么多,主要知道它是一个数值就行了,比如是4.
3:dev->queues这个是一个二级指针,所以它是保存一级指针的值,也就是保存的是struct nvme_queue的地址,上面代码也进行了注释。
4:namespaces的初始化以及dev->pci_dev = pdev赋值
5:nvme_set_instance函数的调用,里面怎么实现的不用管,主要知道调用完它以后,dev->instance得到一个值就行了,这个值用于磁盘的命名,比如在dev目录下看到的/dev/nvmexxx就和这个值有关系。
5:nvme_setup_prp_pools函数调用,主要就是申请一大块内存,然后用于保存prp的地址。
它的函数实现如下:

static int nvme_setup_prp_pools(struct nvme_dev *dev)//放prp指针
{
	struct device *dmadev = &dev->pci_dev->dev;
	dev->prp_page_pool = dma_pool_create("prp list page", dmadev, PAGE_SIZE, PAGE_SIZE, 0);
	if (!dev->prp_page_pool)
		return -ENOMEM;

	dev->prp_small_pool = dma_pool_create("prp list 256", dmadev, 256, 256, 0); //Optimisation for I/Os between 4k and 128k
	if (!dev->prp_small_pool) {
		dma_pool_destroy(dev->prp_page_pool);
		return -ENOMEM;
	}
	return 0;
}

只需要知道调用结束以后,dev->prp_page_pool和dev->prp_small_pool不是空就行了,后面用到的时候再解释,为什么这里需要两个值。
6:调用nvme_dev_start
7:调用nvme_dev_add
8:利用miscdev做一些用户层可以用ioctl下发一些操作。

其中,6,7,8是重点,后面会逐一分析。

remove函数就不用多说了,模块退出时资源的释放。

先聊这么多。文章来源地址https://www.toymoban.com/news/detail-741073.html

到了这里,关于linux 3.13版本nvme驱动阅读记录一的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • NVMe驱动注释(持续更新)

    个人主页 :www.jiasun.top 界面比CSDN简洁一点,阅读体验更好,现已将全部博客迁移到个人主页,欢迎关注! 往期文章: NVMe驱动学习记录-1 NVMe驱动学习记录-2 NVMe驱动 请求路径学习记录 整合了之前文章的一些内容 源码地址:https://mirrors.tuna.tsinghua.edu.cn/kernel/v4.x/linux-4.19.90.ta

    2024年02月16日
    浏览(30)
  • 2023年7月13日 星期四 Linux驱动作业

    a.应用程序通过阻塞的io模型来读取number变量的值 b.number是内核驱动中的一个变量 c.number的值随着按键按下而改变(按键中断) 例如number=0 按下按键number=1再次按下按键number=0d.在按下按键的时候需要同时将1ed1的状态取反 t e.驱动中需要编写字符设备驱动f.驱动中需要自动创建设备

    2024年02月16日
    浏览(28)
  • VMware ESXi 8.0 Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)

    发布 ESXi 8.0 集成驱动版,在个人电脑上运行企业级工作负载 请访问原文链接:https://sysin.org/blog/vmware-esxi-8-sysin/,查看最新版。原创作品,转载请保留出处。 作者主页:www.sysin.org 发布 ESXi 8.0 集成驱动版,在个人电脑上运行企业级工作负载,构建开发、测试和学习的最佳平台

    2024年02月05日
    浏览(31)
  • VMware ESXi 7.0 U3l Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)

    ESXi 7 U3 标准版集成 Intel 网卡、USB 网卡 和 NVMe 驱动 请访问原文链接:https://sysin.org/blog/vmware-esxi-7-u3-sysin/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 2023-03-31,发布 ESXi 7.0U3l,**此版本包含 59 个功能修复,2 个安全修复(详见 “更新说明”),属于 “重大

    2023年04月20日
    浏览(41)
  • linux驱动开发入门(学习记录)

    2023.7.6及7.7 、

    2024年02月15日
    浏览(29)
  • VMware ESXi 7.0 U3n macOS Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)

    VMware ESXi 7.0 U3n macOS Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) ESXi 7 U3 标准版集成 Intel 网卡、USB 网卡 和 NVMe 驱动 请访问原文链接:https://sysin.org/blog/vmware-esxi-7-u3-sysin/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 2023-07-07,ESXi 7.0U3n 发布,本站同

    2024年02月11日
    浏览(39)
  • 【嵌入式Linux驱动】驱动开发调试相关的关系记录

    https://www.processon.com/mindmap/64537772b546c76a2f37bd2f

    2024年02月02日
    浏览(40)
  • Linux驱动学习记录 cpu主频

    以imx6ull芯片为例 imx6ull芯片主频是792MHz 查看cpu信息 BogoMIPS这一条,此时 BogoMIPS为 3.00,BogoMIPS是 Linux系统中 衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,BogoMIPS值就越大。 BogoMIPS只是粗略的计算 CPU性能,并不十分准确。 目录/sys/bus/cpu/devices/cpu0/cpufreq 中

    2024年02月09日
    浏览(24)
  • Linux驱动bug及解决方法记录

    记录一下自己驱动开发遇到的bug以及解决方法,如有错误欢迎指正。 1.遇到 insmod: ERROR: could not insert module xxx.ko: File exists ,一般是重复加载的问题,只需要 rmmod 卸载掉,再重新 insmod 加载即可解决。 2.遇到 insmod: ERROR: could not insert module xxx.ko: Operation not permitted ,一般是加载的

    2024年02月13日
    浏览(36)
  • VMware ESXi 8.0U2b macOS Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) (Broadcom VMware 首次重大更新)

    VMware ESXi 8.0U2b macOS Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) (Broadcom VMware 首次重大更新) 发布 ESXi 8.0U2 集成驱动版,在个人电脑上运行企业级工作负载 请访问原文链接:https://sysin.org/blog/vmware-esxi-8-u2-sysin/,查看最新版。原创作品,转载请保留出处。 作者主页:

    2024年04月17日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包