创建NVMe块设备
对于Linux的块设备来说,其主要的是通过device_add_disk或者add_disk函数(后者是对前者的简单包装)来向操作系统添加一个设备实例。具体原理我们在之前的文章中已经介绍过,本文不再啰嗦了,想了解的同学请自行翻阅一下历史文章。其基本原理就是通过调用该函数,就会创建在/dev目录下看到的类似sdX的块设备。
NVMe本身也是块设备,自然也不会跳出这个大框架。首先从硬件层面上,我们知道任何设备必须通过某个总线与CPU向连接,NVMe则正是通过PCIe总线与CPU相连。
图1 Linux内核的总线架构
当然,目前NVMe除了可以通过PCIe总线与CPU相连外,还可以通过其它通道连接,比如FC或者IB。后者则是一种将NVMe设备从计算节点独立出来的方式,也就是此时NVMe就不再是一个卡设备,而是一个独立机箱的设备。无论何种方式相连接,其本质是一样等。
然后是操作系统软件层面的内容。硬件的连通性是基础,当硬件已经连通后,就可以在Linux内核层面发现设备,并进行初始化了。软件层面的初始化有两种情况,一种是计算机启动的时候,操作系统会扫描总线上的设备,并完成初始化;另外一种情况是设备在系统启动后连接的,此时需要手动触发扫描的过程。
无论是系统启动也好,还是手动触发扫描也好,NVMe发现设备的核心流程是一样的。
图2 设备初始化主流程
与其它块设备类似,NVMe设备初始化完成后会在/dev目录下出现一个文件。NVMe设备会出现一个形如nvmeXnY的设备文件。如图3所示,红色方框中的为一个NVMe块设备。
图3 Linux中的NVMe块设备
核心实现分析
上面我们简要的介绍了初始化的主流程。在上面初始化流程中需要重点关注的是nvme_alloc_ns函数的流程。该函数完成了块设备创建、基本信息填充和块设备注册到内核等工作。如图4是该函数的部分代码片段。这部分片段完成了函数指针的初始化、命令队列初始化和设备名称的初始化等工作。具体关于nvme_alloc_ns函数源代码的逻辑请自行阅读代码,本文不再赘述。
图4 代码初始化
在整个初始化流程中比较关键的是对请求队列(request_queue)中请求处理函数指针(make_request_fn)的初始化及多队列函数集(mq_ops)的初始化。因为,这里的函数正是NVMe区别于SCSI等类型设备数据处理流程的地方。
NVMe设备的IO流程
为了便于理解NVMe的处理流程,我们给出了传统SCSI及NVMe数据处理的对比流程。如图5所示,整个流程是从通用块层的接口(submit_bio)开始的,这个函数大家都非常清楚了。
图5 NVMe数据流程
对于NVMe设备来说,在初始化的时候初始化函数指针make_request_fn为nvme_queue_rq,该函数就是NVMe驱动程序的请求处理接口。该函数最终会将请求写入NVMe中的SQ队列当中,并通知控制器处理请求。
标签:初始化,函数,流程,总线,nvme,driver,linux,NVMe,设备 From: https://blog.csdn.net/lwexin/article/details/143446489