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

2023-11-02 21:36

本文主要是介绍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 4if (!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);//设备引用计数初始化,值为1return 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 128kif (!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函数就不用多说了,模块退出时资源的释放。

先聊这么多。

这篇关于linux 3.13版本nvme驱动阅读记录一的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/333469

相关文章

Linux中修改Apache HTTP Server(httpd)默认端口的完整指南

《Linux中修改ApacheHTTPServer(httpd)默认端口的完整指南》ApacheHTTPServer(简称httpd)是Linux系统中最常用的Web服务器之一,本文将详细介绍如何... 目录一、修改 httpd 默认端口的步骤1. 查找 httpd 配置文件路径2. 编辑配置文件3. 保存

Linux使用scp进行远程目录文件复制的详细步骤和示例

《Linux使用scp进行远程目录文件复制的详细步骤和示例》在Linux系统中,scp(安全复制协议)是一个使用SSH(安全外壳协议)进行文件和目录安全传输的命令,它允许在远程主机之间复制文件和目录,... 目录1. 什么是scp?2. 语法3. 示例示例 1: 复制本地目录到远程主机示例 2: 复制远程主

conda安装GPU版pytorch默认却是cpu版本

《conda安装GPU版pytorch默认却是cpu版本》本文主要介绍了遇到Conda安装PyTorchGPU版本却默认安装CPU的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、问题描述二、网上解决方案罗列【此节为反面方案罗列!!!】三、发现的根本原因[独家]3.1 p

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

Redis指南及6.2.x版本安装过程

《Redis指南及6.2.x版本安装过程》Redis是完全开源免费的,遵守BSD协议,是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSIC语言编写、支持网络、... 目录概述Redis特点Redis应用场景缓存缓存分布式会话分布式锁社交网络最新列表Redis各版本介绍旧

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代

apache的commons-pool2原理与使用实践记录

《apache的commons-pool2原理与使用实践记录》ApacheCommonsPool2是一个高效的对象池化框架,通过复用昂贵资源(如数据库连接、线程、网络连接)优化系统性能,这篇文章主... 目录一、核心原理与组件二、使用步骤详解(以数据库连接池为例)三、高级配置与优化四、典型应用场景五、注意事

Linux CPU飙升排查五步法解读

《LinuxCPU飙升排查五步法解读》:本文主要介绍LinuxCPU飙升排查五步法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录排查思路-五步法1. top命令定位应用进程pid2.php top-Hp[pid]定位应用进程对应的线程tid3. printf"%

Linux下安装Anaconda3全过程

《Linux下安装Anaconda3全过程》:本文主要介绍Linux下安装Anaconda3全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录简介环境下载安装一、找到下载好的文件名为Anaconda3-2018.12-linux-x86_64的安装包二、或者通

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo