首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

深入理解SPDK读写数据的过程,从应用到NVMe驱动

前文我们从使用层面介绍了基于SPDK读写数据的过程,本文将继续深入从NVMe驱动的实现层面介绍一下数据读写的相关内容。在SPDK的NVMe驱动中,两个基本的读写API分别是spdk_nvme_ns_cmd_read和spdk_nvme_ns_cmd_write。这两个API提供基于缓冲区的数据读写,也就是数据是在一段连续的内存缓冲区中。

除了上述基本的API外,SPDK还有一些扩展功能的API,以写数据的API为例,比如支持SGL的spdk_nvme_ns_cmd_writev,以及spdk_nvme_ns_cmd_writev_with_md和spdk_nvme_ns_cmd_writev_ext等函数。本文以基本API spdk_nvme_ns_cmd_read/write为例进行介绍,其他API大同小异,本文不再赘述。

在介绍函数具体实现之前,我们先了解一下NVMe协议读数据的流程,具体如下图所示。在读数据时,NVMe命令会被写入提交队列(1),然后通过门铃通知NVMe控制器命令就绪(2)。NVMe控制器接到就绪通知后会从提交队列读取命令(3),然后进行命令的处理工作(4),具体包括将数据从控制器拷贝到主机内存等。处理完成后通过完成队列通知主机,本文暂时只介绍前半部分。

了解了具体流程,接下来我们详细介绍一下具体实现。如下图是spdk_nvme_ns_cmd_read的原型,其参数包括目的命名空间、IO队列对、缓冲区、LBA位置等信息。如果大家开发过文件读写的相关程序,相信很容易理解上述参数。

在前文中我们介绍过NVMe协议是通过队列对实现主机与存储设备交互的(具体如下图),因此这里读写数据自然需要指定由哪个队列对来处理该请求。另外,在一个存储设备中,可以有一个或者多个命名空间,所以我们必须指定向哪个命名空间来访问数据。

对于后面的lba和lba_count则是标示数据在命名空间的具体位置以及访问的数据量是多少。最后,cb_fn和cb_arg则是回调函数和其参数。对于存储设备的读写请求不会即刻得到满足,SPDK是通过这个回调函数来通知上次请求完成情况的。

写数据的API与读数据的API参数一致,并没有明显的差异。如下图是读数据的API函数原型,大家可以自行对比一下参数列表,本文不再赘述。

以前文hello world实现为例,写数据时的函数调用如下所示。其中参数都很明确易懂,本文不再赘述,当写数据成功后会调用回调函数write_complete。应用程序通过回调函数进行下一步的处理,本例中是读取数据。

我们回到写数据API的实现,如下图是该API的实现代码,可以看到这里分为两个主要步骤。步骤1是构建一个请求数据结构(_nvme_ns_cmd_rw),这个数据实现了上述命名空间、IO队列对和LBA等参数的封装。这里面比较重要的是增加了一个操作码(OP Code)SPDK_NVME_OPC_WRITE,标示这是一个写数据的请求。完成数据结构的构建后,就会将请求向下层提交(nvme_qpair_submit_request)。

我们接着看一下请求提交的过程。我们忽略函数调用的一些细节,提交过程的函数调用整体如下图所示,最终会通过函数指针的方式调用到transport(不清楚如何翻译,这里保持英文)的实现。我们知道NVMe协议支持多种transport,比如PCIe、RDMA和FC等,SPDK的NVMe驱动也实现了对多种transport的支持,具体方法就是通过函数指针的方式。

以PCIe为例,此时该函数指针为nvme_pcie_qpair_submit_request,本文分两部分介绍一下该函数的具体实现。其中前半部分是tracker的初始化,tracker是用来跟踪请求处理的。在SPDK中,tracker在 队列对(qpair)初始化的时候分配内存,而不是每次有请求的时候。这里tracker通过free列表和outstanding列表来管理,未使用的tracker放在free列表中,提交的tracker放在outstanding列表中。

在函数中,由于请求需要消耗一个tracker,因此该tracker会从free列表移除,并移入到outstanding列表中。而后,该函数将请求数据结构、回调函数和回调函数参数等内容填充到tracker当中。

函数的后半部分则是将tracker提交,其本质是将NVMe命令拷贝到队列对的命令队列当中,然后通知控制器读取数据。在提交tracker之前,该函数会进行一些基本的判断,比如判断是否支持SGL、是否需要双字对齐等。

然后根据支持的类型,通过全局变量g_nvme_pcie_build_req_table中注册的函数指针来进行不同的处理。最后调用nvme_pcie_qpair_submit_tracker提交tracker。

我们继续分析一下提交tracker的实现,这里主要分为两步,首先是将命令拷贝到队列对的提交队列中,然后按门铃通知控制器提交队列有数据,激活其读取数据。

回到读数据的API,可以看到其实现与写数据的API基本一样,唯一区别的地方操作码不同,写数据是SPDK_NVME_OPC_WRITE,这里是SPDK_NVME_OPC_READ。后续流程完全一样,本文不再赘述。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OCWiCRhabxwVVMFR9FWsPEnw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券