存储性能软件加速库(SPDK)

SPDK由英特尔发起,用于加速NVMe SSD作为后端存储的软件加速库。这个软件库的核心是用户态、异步、轮询方式的NVMe驱动。
相比内核的NVMe驱动,SPDK可以大幅降低NVMe command的延迟,提高单CPU核的IOps。
SPDK最早全称为DPDK for storage,DPDK作为SPDK默认的环境库。

目前而言,SPDK并非一个通用的适配解决方案。把内核驱动放到用户态,导致需要在用户态实施一套基于用户态软件驱动的完整I/O栈。文件系统是其中一个重要话题,内核常见的文件系统如ext4,Btrfs不能直接使用。目前SPDK提供了简单的文件系统blobfs/blostore,但不支持可移植操作系统接口,为此使用文件系统的应用需要将其直接迁移到SPDK的用户态“文件系统”上,同时需要做一些代码移植工作,如不使用可移植操作系统接口,而采用类似AIO的异步读/写方式。

目前SPDK比较好的应用场景有以下几种

  • 提供块设备接口的后端存储应用,如iSCSI Target 、NVMe-oF Target
  • 对虚拟机中I/O的加速,主要指Linux下QEMU/KVM作为Hypervisor管理虚拟机的场景,使用vhost交互协议,实现基于共享内存通道的高效vhost用户态Target。加速主要原理是减少了VM中断等事件的数目(如interrupt、VM_EXIT),并且缩短了host OS中的I/O栈。
  • SPDK加速数据库存储引擎,通过实现RocksDB中的抽象文件类,SPDK的blobfs/blobstore目前可以和RocksDB集成,用于加速在NVMe SSD上使用RocksDB引擎。其本质是bypass(旁路)kernel文件系统,完全使用基于SPDK的用户态I/O栈。

用户态驱动

内核驱动模块在内核加载成功后,会被标识为块设备或字符设备,同时定义相关的访问接口,包括管理接口、数据接口等。这些接口直接或间接和文件系统子系统结合,提供给用户态的程序,通过系统调用的方式发起控制和读/写操作。

用户态应用程序和内核驱动的交互离不开用户态和内核态的上下文切换以及系统调用的开销。
用户态驱动的出现就是为了减少软件本身的开销,包括上下文切换以及系统调用等。在用户态,目前可以通过UIO(Userspace I/O)或VFIO(Virtual Function I/O)两种方式对硬件固态硬盘设备进行访问。

(1)UIO

在用户态实现设备驱动,主要需要解决以下两个问题

  • 如何访问设备的内存:Linux通过映射设备的内存到用户态来提供访问 即 mmap()。但这种方法会引入安全性和可靠性的问题。UIO通过限制不相关的物理设备的映射改善了问题。
  • 如何处理设备产生的中断:中断本身需要在内核处理,针对这个限制,需要一个小的内核模块通过最基本的中端服务程序来处理。这个中断服务程序可以只是向操作系统确认中断,或者关闭中断等最基础的操作,剩下的具体操作可以在用户态处理。UIO架构如图,用户态驱动和UIO内核模块通过/dev/uioX设备来实现基本交互,同时通过sysfs来得到相关设备的内存映射、内核驱动等信息。

(2)VFIO

相对于UIO,VFIO更多从安全角度考虑,把设备I/O、中断、DMA暴露到用户空间,从而可以在用户空间完成设备驱动的框架。难点在于如何防止设备写内存耶来发动DMA攻击。

**IOMMU(I/O Memory Management Unit)**的引入对设备进行了限制,设备I/O地址需要经过IOMMU重映射为内存物理地址。操作系统以互斥的方式管理MMU和IOMMU,这样物理设备将不能绕过或污染可配置的内存管理表项

android sp和dp有什么区别_后端

(3)用户态DMA

基于UIO和VFIO,可以实现用户态驱动,把一个硬件设备分配给一个进程,允许该进程来操作和读/写该设备,不需要通过内核驱动产生额外的内存复制,可以直接从用户态发起对设备的DMA。如图,虚线代表设备通过DMA直接访问响应的内存页,实线代表CPU访问内存页的方式。

android sp和dp有什么区别_后端_02


这里必须考虑3个问题。

  • 提供设备可以认知的内存地址(可以是物理地址,也可以是虚拟地址)。
  • 物理内存必须在位(考虑到虚拟内存可能被操作系统交换出去,产生却页)。
  • CPU对内存的更新必须对设备可见。
    其中第1个和第3个问题通过设备直接支持IOMMU,同时和CPU之间实现缓存一致性来解决。
    第2个问题,从用户态进程来看,如何保证在DMA过程中物理内存是在位的。
    Linux主流方法是人工把虚拟地址对应的物理内存Pin在位置上(即不会被换出)。VFIO本质上是通过Pin实现的暴露DMA。

(4)大页(Hugepage)

虚拟地址映射到物理地址的主要工作是TLB(Transition Lookaside Buffers)与MMU一起完成的。虚拟地址寻址时,首先在TLB中查找,如果没有找到,则通过MMU加载的页表基地址进行多次寻表来找到对应的物理地址。如果找不到,则产生缺页,有相应的handler进行处理,填充页表和更新TLB。
通过页表查询而导致经常缺页带来的CPU开销是非常大的,TLB的出现可以很好地解决性能问题。但经常性的缺页是不可避免的,为此可以采用大页的方式。
通过Hugepage分配大页可以提高性能。因为页大小的增加,可以减少缺页异常
大页还有一个优势是预先分配的内存基本上不会被换出,当进行DMA的时候,所对应的虚拟地址永远有相对应的物理页。结合VFIO,可以显著并安全地提升用户态对设备的读/写操作效率。当然,大页也有缺点,比如它需要额外配置,需要应用程序事先预估使用多少内存,大页需要在进程启动之前实现启用和分配好内存