作者 三角板
给SSD Fans原创投稿技术或市场文章,拿>=100元稿费。
以前只知道DPDK,最近听说SPDK,深入研究了一把。
Dpdk依赖UIO技术,见如下网址:
http://www.cnblogs.com/kb342/p/5168197.html
举个例子说明一下UIO技术,做过vxworks的工程师都知道,vxworks没有用户空间和内核空间的概念,所有的任务(进程或者线程)都跑在一个用户空间,调试时候可以直接敲函数读写寄存器;到了linux里面一般都用创建芯片寄存器ioremap空间向对应的proc文件实现对寄存器状态的读写配置。Intel利用其在X86平台(还有pcie)领域的巨大优势,专门写了一个通用的驱动来简化统一实现这个功能(UIO驱动加eal层),这就是UIO技术。猜测也正是因为如此,spdk只能跑在X86架构的服务器平台上。当然这仅仅是个简单的例子,方便大家了解,其实不仅仅是intel,各大公司比如华为都有内部这样一套统一的内核和用户通信调试接口提高效率。
SPDK依赖于dpdk,编译步骤如下:
下载spdk
git clone https://github.com/spdk/spdk.git
下载dpdk
Spdk代码下载后默认有一个dpdk的空文件夹用来存放dpdk源码
在spdk目录下如下操作:
提示用git submodule update –init命令下载dpdk
编译dpdk
进入dpdk目录
输入如下命令
make install T=x86_64-native-linuxapp-gcc DESTDIR=.
需要说明的是dpdk有自身一套很智能的安装和配置接口,这里直接手动操作,具体配置方法后续说明。
4. 安装相关编译工具和依赖库
进入spdk/目录,执行pkgdep.sh脚本,自动下载安装相关依赖文件。
编译spdk
进入spdk目录执行make DPDK_DIR=./dpdk/x86_64-native-linuxapp-gcc
编译通过。
dpdk配置
见如下网址http://blog.csdn.net/fjssharpsword/article/details/50946110
建议用自带的选择配置模式进行如下操作(可以和步骤3一起完成):
安装UIO驱动(Insert IGB UIO module);
Setup hugepage mappings for non-NUMA systems 输入64或128;
Bind Ethernet device to IGB UIO module;
Display current Ethernet device settings;
Run test application(非必要操作)
配置spdk
Spdk目录下执行如下命令s/setup.sh
执行测试程序spdk/example/nvme/目录下相关测试命令
本测试例子是写入一个数据后做回读测试,详见代码。
最后不要忘记在安装调试spdk包的时候插上nvme ssd。
内核代码层次
Nvme相关的api文件存在于libnvme目录下的C文件中,看文件名称就知道各个层次对应的接口函数:
Nvme_ctrl, nvme_ns, nvme_qpair, nvme_transport, nvme_pcie等,nvme_transport其实是nvme_pcie层次的封装,详见
nvme_transport.c文件的宏定义如下如下:
#define TRANSPORT_PCIE(func_name, args) case SPDK_NVME_TRANSPORT_PCIE: return nvme_pcie_ ## func_name args;
#ifdef SPDK_CONFIG_RDMA
#define TRANSPORT_FABRICS_RDMA(func_name, args) case SPDK_NVME_TRANSPORT_RDMA: return nvme_rdma_ ## func_name args;
#define TRANSPORT_RDMA_AVAILABLE true
#else
#define TRANSPORT_FABRICS_RDMA(func_name, args) case SPDK_NVME_TRANSPORT_RDMA: SPDK_UNREACHABLE();
#define TRANSPORT_RDMA_AVAILABLE false
#endif
#define NVME_TRANSPORT_CALL(trtype, func_name, args)
do {
switch (trtype) {
TRANSPORT_PCIE(func_name, args)
TRANSPORT_FABRICS_RDMA(func_name, args)
TRANSPORT_DEFAULT(trtype)
}
SPDK_UNREACHABLE();
} while (0)
Eal层次是工作在底层的统一抽象层次,类似于hal层次,获取设备的寄存器IO地址空间和中断等传递给用户层设备,用mmap实现。
每个request处理完之后的触发的的cpl处理回调函数是从qpair这个结构体中传递过去的,见nvme_pcie_ctrlr_create_io_qpair, nvme_pcie_qpair_construct, nvme_pcie_qpair_process_completions
uio驱动可以参见内核的/driver/uio 目录,直接创建一个字符设备接口作为用户和内核态的信息交换接口,可以直接看uio.c的代码,其他的是针对各种特定设备的uio驱动。
Dpdk的igb_uio驱动代码文件在如下位置:dpdkliblibrte_eallinuxappigb_uio,可以查看源码,具体的实现机制如下网址有详细介绍。
igb_uio ————————> http://blog.csdn.net/weijitao/article/details/52949454
Eal层代码位置spdkdpdkliblibrte_eal 这个里面创建了内核和用户态的信息交换机制,代码比较复杂,不太容易读。
Dpdk ———————-> http://dpdk.org/doc/guides/prog_guide/env_abstraction_layer.html
eal层次代码比较复杂,目前还没有完全理出来。
Eal中的相关thread:eal_intr_thread_main ,eal_thread_loop,hpet_msb_inc等用来完成数据传递和状态同步等。
Eal层次获取PCI信息是靠解析sysfs的节点实现的,获取信息后传递给用户态的driver,见eal_pci.c
简单来说SPDK的实现道理如下:通过UIO这个的驱动接口,仅仅将驱动中共性的一部分放在内核态实现,eal层次通过解析sysfs的相关节点获取设备信息(bar,function等)传递给用户态的设备模型,之后eal层次用mmap建立内核和用户空间的数据传递通道和同步机制,实现了驱动的用户态功能实现。
Spdk提供了多种存储接口(scsi,nvme等)不同层次的操作接口,可以供用户直接调用完成设备识别,控制,数据传输等。