概述
本文基于SPDK v23.1版本的hello_world
示例来说明SPDK的nvme命令处理流程,代码架构如下:
example\nvme\hello_world.c
int main(int argc, char **argv)
{
spdk_env_opts_init(&opts);
rc = parse_args(argc, argv, &opts); // 参数解析
opts.name = "hello_world";
if (spdk_env_init(&opts) < 0) { // spdk环境初始化,最终调用的是dpdk的环境初始化
}
// 扫描设备,并将驱动和设备绑定,调用用户提供的回调`probe_cb`和`attach_cb`
rc = spdk_nvme_probe(&g_trid, NULL, probe_cb, attach_cb, NULL);
hello_world(); // IO qpair创建、nvme的读写
cleanup(); // 资源释放
return rc;
}
标准的NVMe处理涉及到NVMe子系统、HOST CPU、HOST 内存三方面,下图展示这三者之间的关系:
- NVMe子系统作为PCIe总线的Endpoint存在,可以直接与RC连接,也可以通过一个Switch连接到PCIe总线;
- NVMe命令存放在HOST内存的SQ中,命令处理完NVMe子系统会生成一个完成命令并放到HOST内存的CQ中;
-
NVMe协议对于SQ/CQ的个数并没有要求一一对应,但是SPDK中是一一对应的都放在一个qpair中
nvme子系统.png
初始化SPDK环境
接口:int spdk_env_init(const struct spdk_env_opts *opts)
这里需要的参数opts
可以通过接口spdk_env_opts_init(&opts)
来设置,以及通过当前程序提供的参数来修改parse_args(argc, argv, &opts)
默认的opts
参数配置如下:
[ DPDK EAL parameters: hello_world --no-shconf -c 0x1 --huge-unlink --log-level=lib.eal:6 --log-level=lib.cryptodev:5 --log-level=user1:6 --iova-mode=pa --base-virtaddr=0x200000000000 --match-allocations --file-prefix=spdk_pid76450 ]
最终调用DPDK的接口rte_eal_init
来完成SPDK环境的初始化,为后面的操作做好准备工作
设备查找
设备的注册
SPDK对设备的管理类似Linux的设备驱动模型:包含bus
、device
、driver
三个部分。如下:
/**
* Structure describing the PCI bus
*/
struct rte_pci_bus {
struct rte_bus bus; /**< Inherit