nvme驱动_Linux 4.4.220 NVMe驱动及其BIOLayer数据流分析

    公众号之前的文章“Linux 4.4.220 PCI总线驱动分析”对PCIe做了详细分析,这篇文章挑选NVMe驱动,来分析其BIO层的数据流。之所以挑选NVMe是因为和XHCI以及AHCI相比,NVMe是最新的HCI,没有历史包袱,设计的非常简洁,驱动代码更加容易理解。

本文将分成三部分,第一部分分析drivers/NVMe/host/pci.c文件内的nvme_driver,包含NVMe设备探测,移除,关闭等操作,第二部分简单介绍NVMe相关的Open Channel SSD驱动,第三部分分析NVMeBIO层数据流。PART1和PART2已经在文章“Linux 4.4.220 NVMe驱动和BIOLayer分析(上)”(已经合并到此文,所以删除原文了)中介绍过,但写PART3的过程中对前两部分有些修改,所以这篇文章仍然包括了前两部分。

PART1

研究Linux NVMe驱动之前,先简单介绍下NVMe协议。

NVMe也叫NVM HCI,从名字可以看出它是一个host controller interface,协议对其介绍为:NVMExpress is a scalable host controller interface designed to address the needs of Enterprise and Client systems that utilize PCI Express based solid state drives。可以看出它专为基于PCIe的固态硬盘而设计。硬盘相关的HCI,公众号文章之前已经介绍了AHCI,NVMe在软件设计上和AHCI有类似之处,前者管理Admin/IO Submission用于存放IO命令和Data buffer信息,Admin/IO Completion Queue用于存放命令执行状态,以及doorbell用于触发命令执行,后者管理CommandList和RFIS以及CommandIssue都能和NVMe对应上。

但二者对应的外设差别非常大,主要体现在两点,1,AHCI和外设之间传输数据使用SATA协议,NVMe管理的SSD设备则使用PCIe协议。2,AHCI的对端外设计算机系统(一般是ARM SOC)上,使用的是AXI总线,而AHCI在X86系统里是一个PCIe类型的Function,X86下发的命令是通过PCIe总线传输,因此在AHCI和X86之间有一个设备,专门用于AXI总线和PCIe总线之间转换。而NVMe则不需要这个设备,因为SSD的SOC也使用了PCIe总线。因此二者的数据传输速率与软件协议栈优劣和物理层传输速率都相关,NVMe在这两方面均优于AHCI。

NVMe的框架和几个关键数据结构如下图所示。

55c1e06727ac2cb4325b3f565c849a63.png

1549a628e9cb2d271d5713356601b888.png

8199e52dfca35f110b1746f4b987686f.png

9aadb43363f074bac00a71b23147ede1.png

NVM command和Admin command都必须包含以下字段:Command Dword0,Namespace Identifier,Metadata Pointer,PRPEntry 1,PRPEntry 2,SGLEntry 1,MetadataSGL Segment Pointer。DW10-DW15根据不同的命令,使用方式不同,比如Identify命令就只使用了DW10。DW0数据结构如图所示

90bfe422951dbad2669c6d2eaa0b8d99.png

52a518cc0ae9c3fc7d1ccfad1a49958f.png

Completion queue的数据结构如图所示

517676d3f6da54de622b66e07befad85.png

c923b34b7f890e4a0879994dd21f0931.png

现在来看NVMe驱动开始的地方--nvme_init,如图所示

7c62983689658b21bf48078e8c7085b8.png

1,首先调用register_blkdev注册一个名字叫nvme的块设备,然后调用__register_chrdev注册一个名字叫nvme的字符设备,并为其注册nvme_dev_fops数据结构,如下图所示

d849edf4871cb3ef4f31f9b552ae35b1.png

其中最重要的成员是nvme_dev_ioctl,如下图所示

ec1a8b98e2e84a02b58ac2e64710c63c.png

这个数据结构还有一个兄弟叫nvme_iotcl,作为对比,其数据结构如图所示

d1e0cf6920f54831d65f798248804492.png

bab7f90ed4ff83daa7abef6a680445ea.png

可以看出nvme_ioctl和nvme_dev_ioctl都包含了NVMe_IOCTL_SUBMIT_IO和NVMe_IOCTL_IO_CMD这两条case,它们调用nvme_user_cmd时传入的参数是nvme_passthru_cmd数据格式,即pass_thru命令。NVMe_IOCTL_SUBMIT_IO则是nvme_ioctl独有的,该case下,调用nvme_submit_io时传入参数则是nvme_user_io数据格式。块设备可以接收两种形式的命令,而字符设备只能解析第一种。

不论是nvme_user_cmd还是nvme_submit_io,他们调用的主要路径都是__nvme_submit_sync_cmd->blk_execute_rq->blk_execute_rq_nowait->blk_mq_insert_request->q->mq_ops->map_queue(nvme_mq_admin_ops将其注册为blk_mq_map_queue)->__blk_mq_insert_request->blk_mq_run_hw_queue->__blk_mq_run_hw_queue->q->mq_ops->queue_rq(nvme_mq_admin_ops将其注册为nvme_queue_rq)->nvme_submit_iod,最终回到NVMe驱动层将真正的IO命令打到SubmissionQueue中。调用过程根据是否是pass_thru命令略有不同。

2,调用nvme_init注册数据结构nvme _driver,如图所示,其注册过程调用路径为:module_init->nvme_init->pci_register_driver(&nvme_driver),

3de3a080d1260a673b252139a4b49116.png

8bb34611b2c0eec320bf644551288998.png

上图中PCI_CLASS_STORAGE_EXPRESS= 0x010802,NVMe Spec定义的classcode正是010802h,如图所示。

0cc23462040ffedd2c124571c630ff4f.png

nvme_init是传给module_init的,而module_init=__initcall(x)=device_initcall,所以nvme_init会在内核调用do_initcalls()时被执行。

a36b395572dd096e0769325a1a54e947.png

在device_initcall阶段,pci总线的初始化已经完成,pci总线上的所有设备都登记在册,当NVMe驱动注册时,会触发driver_attach->bus_for_each_dev->__driver_attach->driver_match_device,来查看新注册的驱动的id_table信息是否与已有的设备匹配,其classcode是010802h,此时启动__driver_attach->driver_probe_device->really_probe->drv->probe过程,调用nvme_driver的成员函数nvme_probe。

3, nvme_probe函数主体如下图所示

b951b3c814573bd2d35a58d9a1eae760.png

5d7beb7c95021d560032cd1ed23851a4.png

第一条nvme相关的动作是对dev->queues的初始化,下图是nvm

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值