当前位置: 首页 > news >正文

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驱动阅读记录一

内核版本较低的nvme驱动代码不多&#xff0c;而且使用的是单队列的架构&#xff0c;阅读起来会轻松一点。 这个版本涉及到的nvme驱动源码文件一共就4个&#xff0c;两个nvme.h文件&#xff0c;分别在include/linux ,include/uapi/linux目录下&#xff0c;nvme-core.c是主要源码…...

掌握RESTful API:规范与设计详解

前言 RAML (RESTful API Modeling Language) 和 OAS (OpenAPI Specification) 都是用于描述和定义 RESTful API 的规范。它们分别提供了不同的功能和优势。 RAML&#xff08;RESTful API Modeling Language&#xff09;&#xff1a; RAML简介 RAML&#xff08;RESTful API M…...

「更新」Topaz Video AI v4.0.3中文版

Topaz Video AI是一款功能强大的视频处理软件&#xff0c;它利用人工智能技术对视频进行智能分析和优化&#xff0c;旨在为用户提供高效、智能的视频编辑和增强功能。 首先&#xff0c;Topaz Video AI具备强大的视频修复功能。它可以自动识别并修复视频中的各种问题&#xff0…...

OpenAI最新官方GPT最佳实践指南,一文讲清ChatGPT的Prompt玩法

原文&#xff1a;Sina Visitor System OpenAI的官网发表万字GPT最佳实践指南&#xff0c;讲清Prompt提示词的原则和策略&#xff0c;这里是总结和全文翻译 原创图像&#xff0c;AI辅助生成 OpenAI的官网上刚刚发表一篇万字的GPT最佳实践指南&#xff0c;这份指南把写好Promp…...

树结构及其算法-用链表来实现二叉树

目录 树结构及其算法-用链表来实现二叉树 C代码 树结构及其算法-用链表来实现二叉树 以链表实现二叉树就是使用链表来存储二叉树&#xff0c;也就是运用动态分配内存和指针的方式来建立二叉树。 使用链表来表示二叉树的好处是节点的增加与删除操作相当容易&#xff0c;缺点…...

openwrt(三):在hostapd获取已关联的STA的MAC地址

在深度开发openwrt的hostapd模块的时候&#xff0c;有些功能的实现需要获取已关联的sta的mac地址&#xff0c;下面是相关的方法描述&#xff1a; 第一、在sta_info.c文件中&#xff0c;可以找到一个名为struct hostap_sta_info * hostapd_get_sta(struct hostapd_data *hapd, …...

为何袁世凯要把“元宵节”改为“上元节”?

网民把春节除夕日排除在法定假期之外的相关热议&#xff0c;在微博评论区部分已被关闭。官方学者的解释是&#xff1a;“回归传统。” 这就令人难免要回顾历史&#xff0c;并发觉只有在袁世凯称帝之后&#xff0c;才有过取消“元宵节”改为“上元节”的笑话&#xff0c;因为“元…...

python将图片序列保存成gif

这里用到的模块是imageio。用imageio.mimsave即可将图片序列保存成gif动态图。以下是本人编写的小实验&#xff1a; import cv2 import imageiopaths ["./images/0001.png", "./images/0002.png", "./images/0003.png", ...] frames [] for i…...

UE4用C++修改蓝图对象的属性值

if (auto Property FindFProperty<FStrProperty>(Class, "A")){Object->Modify();Property->SetPropertyValue_InContainer(Object.GetClass(), "OK");}...

供应商等级:一级、二级和三级供应商之间有什么区别

作为一名专业采购人员&#xff0c;你知道拥有一个可靠且具有成本效益的供应链有多么重要。确保供应链顺利运行的方法之一就是利用供应商分级。 什么是供应商分级&#xff1f; 供应商分级是根据供应商的绩效和对企业的重要性&#xff0c;对其进行分类的做法。 因此&#xff0c…...

软考 系统架构设计师系列知识点之净室软件工程(3)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之净室软件工程&#xff08;2&#xff09; 所属章节&#xff1a; 第5章. 软件工程基础知识 第5节. 净室软件工程 相关试题 1. 以下关于软件开发方法的叙述&#xff0c;错误的是&#xff08;&#xff09;。 A. 对于较为…...

『VUE H5页面 - PDF预览』

使用依赖&#xff1a;vue-pdf实现需求&#xff1a;将 PDF url 地址 转换为本地页面预览 <template><div class"pdf-preview"><NavBar /><div class"container"><VuePdf v-for"i in numPages" :key"i" cla…...

使用lua-resty-request库编写爬虫IP实现数据抓取

目录 一、lua-resty-request库介绍 二、使用lua-resty-request库进行IP数据抓取 1、获取IP地址 2、设置请求 3、处理数据 三、代码实现 四、注意事项 五、总结 本文将深入探讨如何使用lua-resty-request库在爬虫程序中实现IP数据抓取。我们将首先介绍lua-resty-request…...

vue-admin-templete项目配置在手机上预览

参考文档&#xff1a; https://blog.csdn.net/qq_29752857/article/details/109802801想要在手机上预览本地打开的vue-admin-templete项目&#xff0c;首先要确保手机和电脑在同一网段。 参考文档&#xff1a;https://blog.csdn.net/m0_57236802/article/details/1315234471.查…...

服务号升级订阅号的流程

服务号和订阅号有什么区别&#xff1f;服务号转为订阅号有哪些作用&#xff1f;首先我们要知道服务号和订阅号有什么区别。服务号侧重于对用户进行服务&#xff0c;每月可推送4次&#xff0c;每次最多8篇文章&#xff0c;发送的消息直接显示在好友列表中。订阅号更侧重于信息传…...

redhat7.4 安装lnmp操作环境

PacVim安装 https://baijiahao.baidu.com/s?id1601033830453371540&wfrspider&forpc 安装php https://www.cnblogs.com/alliancehacker/p/12255445.html https://blog.csdn.net/weixin_39709920/article/details/104274545 安装mysql https://www.cnblogs.com/laumian…...

Java判断是否有特殊字符串

//特殊字符串过滤private static String REGEX_RULE "[ _~!#$%^&*()|{}:;,\\[\\].<>/?~&#xff01;#&#xffe5;%……&*&#xff08;&#xff09;——|{}【】‘&#xff1b;&#xff1a;\"”“’。&#xff0c;、&#xff1f;]|\n|\r|\t";pri…...

服务器搭建:从零开始创建自己的Spring Boot应用【含登录、注册功能】

当然&#xff0c;你可以先按照IDEA搭建SSM框架【配置类、新手向】完成基础框架的搭建 步骤 1&#xff1a;设计并实现服务器端的用户数据库 在这个示例中&#xff0c;我们将使用MySQL数据库。首先&#xff0c;你需要安装MySQL并创建一个数据库以存储用户信息。以下是一些基本步…...

家用小型洗衣机哪款性价比高?公认好用四款内衣洗衣机推荐

小型的内衣洗衣机由于体积小巧&#xff0c;而且实用&#xff0c;非常适合没有太多空闲时间的上班族以及小型住户的使用。想要挑选到一款能够满足每日清洗需要&#xff0c;同时拥有便携与高效率的小型内衣洗衣机&#xff0c;也许会让你选择得有些烦恼。我们为大家挑选了一些性价…...

Sui zkSend,创建链接可直接发送SUI,快来体验吧

基于zkLogin和Sui的本机密码学构建&#xff0c;zkSend允许用户创建包含特定数量SUI的链接。该链接可以通过任何消息客户端发送&#xff0c;例如电子邮件和直接消息&#xff0c;甚至可以在媒体上转换成QR码。 使用zkSend发送SUI比复制和粘贴钱包地址&#xff0c;然后授权token转…...

SQL面试

#(1)请写出要查询员工J开头的名字其工号(EMPNO)及部门名称(DEPTNA)的 SQL语句SELECT e.emp,e.name,d.deptna FROM emp e left join dept d on d.deptno e.deptno where e.name like J%#(2)请写出要查询 Kevin 所在部门的部门代号(DEPTNO)及部门名称(DEPTNA)的 SQL 语句SELECT e…...

Elasticsearch(一)---介绍

简介 Elasticsearch是一个基于Lucene的实际的分布式搜索和分析引擎。设计用于云计算中&#xff0c;能够达到近实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速&#xff0c;安装使用方便。基于RESTful接口。 官网地址&#xff1a;Elasticsearch 平台 — 大规模查找…...

Vscode LinuxC++环境配置

C环境配置 文章目录 C环境配置一、Visual Studio Code相关信息二、Python开发环境配置三、C 开发环境配置四、第一个C程序五、附录&#xff1a;vs code 中变量解释 一、Visual Studio Code相关信息 Visual Studio Code 下载地址&#xff1a;https://code.visualstudio.com/dow…...

【tensorboard打开失败】No dashboards are active for the current data set.

这里我再跟视频学的时候&#xff0c;找了很多的指令&#xff0c;说是对应版本不一样&#xff0c;但是发现用了很多指令都可以弹出来跳转的url&#xff0c;那应该就不是输入指令的问题 直到我想把logs里面的文件删掉重新跑的时候&#xff0c;我突然注意到这里有中文字符&#xf…...

客服管理者如何调动客服人员的积极性?

客户是企业的财富&#xff0c;良好的客户服务体验可以有效地促进企业的销售和声誉&#xff0c;因此&#xff0c;客服工作显得尤为重要。而客服人员的积极性直接影响了整个客服部门的质量和效率。如何调动客服人员的积极性&#xff0c;成为了每个客服管理者都需要面对的难题。本…...

Jenkins自动化部署简单配置

下载安装jenkins 安装Jenkins步骤 点击Next的时候会有jdk版本跟Jenkins版本不符合的情况 1. 看下任务管理器内Jenkins服务是否启动&#xff0c;在浏览器里面输入localhost:2023&#xff08;端口号是安装时输入的&#xff09; 2. 根据路径找到放置密码的文件&#xff08;C…...

Linux————内置命令大全

&#xff08;一&#xff09;内置命令 Shell 内置命令&#xff0c;就是由 Bash Shell 自身提供的命令&#xff0c;而不是文件系统中的可执行脚本文件。内置命令的执行速度通常优于外部命令&#xff0c;因为执行外部命令不仅会导致磁盘I/O操作&#xff0c;而且还需要为其fork一个…...

从用户角度出发,如何优化大数据可视化体验|北京蓝蓝UI设计公司

作者&#xff1a;蓝蓝设计-鹤鹤 大数据已经成为人们探索世界的新工具。但是&#xff0c;对于普通用户而言&#xff0c;大数据往往比较抽象和难以理解&#xff0c;因此&#xff0c;大数据可视化作为一种非常有效的工具工具被广泛应用。然而&#xff0c;在实际应用中&#xff0c…...

【vue】封装树形下拉框组件 el-popover+el-tree+el-select

父组件使用 <template><div>{{ array }} 更多属性详见wgyTreeSelect组件<wgyTreeSelectv-model"array":list"list":multiple"true":disabled-ids"[111,113,2]"/></div> </template><script> /*…...

docker安装Kafka,SpringBoot整合Kafka

#拉取镜像 docker pull wurstmeister/zookeeper #运行容器 docker run --restartalways --name zookeeper -p 2181:2181 \ -v /etc/localtime:/etc/localtime -d wurstmeister/zookeeper#拉取镜像 docker pull wurstmeister/kafka#运行容器 docker run --restartalways --name …...