BF2中的snap-direct功能是提供了一个zero copy的机制,支持将host侧的数据不经过soc的缓存,硬件直接从host上DMA数据下来,或者DMA数据到host内存。下面以在BF2中创建了一个virtio-blk device的设备为例,介绍下存储卸载的工作流程。
1. 控制面流程
BF2中提供了emulation manager功能,libsnap中提供了创建、修改、销毁emulation object的API接口,可以使用如下命令,实现创建和使用virtio-blk device。
1)snap_rpc.py emulation_managers_list
IB device具有emulation manager的能力,不同的IB device具备的能力集是不一样的,该命令可以查询IB device的emulation能力。
2)snap_rpc.py emulation_device_attach mlx5_0 virtio_blk
每个emulation manager具备emulated PCI function的能力,用户可以动态地添加这些可卸载的PCI设备,这个过程称为热插拔PCIe function,该命令可以模拟一个virtio_blk类型的pcie设备,host侧可以立马感知到该PCIe设备。该命令执行完毕之后,可以看到该PCIe设备的BDF号和pci_index。
3)snap_rpc.py emulation_device_detach mlx5_0 virtio_blk [-d 64:00.0/ -i 0]
从系统中卸载mlx5_0下模拟的virtio_blk类型的PCIe设备,可以指定BDF或者是PCIe的index(PF号)。snap_rpc.py emulation_functions_list查询系统中所有function,包括静态和热插拔的function。
4)snap_rpc.py bdev_null_create Snap0
创建spdk bdev,bdev的name是Snap0。
5)snap_rpc.py controller_virtio_blk_create mlx5_0 [-d 64:00.0/ --pf_id 0] --bdev_type spdk --bdev Snap0
- 该命令是基于已创建的virtio_blk device,创建virtio_blk controller。这个controller会和Snap0关联起来,此时host上就可以看到一个virtio_blk的设备,写到virtio_blk设备中的数据就可以通过virtio_blk controller被spdk收到。
- 当virtio_blk device呈现给host之后,必须要对应有virtio_blk的controller,否则host侧的驱动会hung住直至virtio_blk controller成功打开。
- 当创建virtio_blk controller时,后端的block device必须已经存在。controller 下的bdev不支持动态修改,只有当controller suspend状态下,才可以进行后端管理操作
- 可以控制virtio_blk controller暂停工作,暂停期间,会停止对host侧的驱动接收IO请求,只会处理flight IO。
6)snap_rpc.py controller_virtio_blk_bdev_attach [ctrl_name] spdk [bdev_name]
关联一个新的block device到指定的controller上,对应的也有detach操作。
7)snap_rpc.py controller_virtio_blk_bdev_list [ctrl_name]
可以查询controller下的所有bdev。
在完成virtio_blk device和virtio_blk controller的创建之后,host上的业务就可以通过virtio_blk driver识别出virtio_blk device,并向该device发出读写IO请求。
2. 数据面流程
在启动virtio_blk controller时,根据硬件支持的能力,会创建多个virtio-blk ctrl queue,每个queue对应virtio-blk driver创建的vring。在DPU侧看来,每一个virtio-blk ctrl queue的底层本质上是一对RDMA QPs,包括一个software QP和 一个fireware QP,software QP是给DPU使用的,fireware QP则是给host侧使用,在DPU上通过RDMA READ或者RDMA WRITE的方式向host侧读写数据。
当virtio-blk driver投递一个读写IO请求到vring中后,会调用vp_notify通知后端驱动,由于virtio blk device是DPU模拟的,也就是通知给了硬件,硬件就可以从vring 拿到驱动下发的IO请求命令,其实也就是vring_desc,硬件会封装成一个cmd消息,通过post send到fireware QP,DPU就可以通过software QP接收到vring_desc的具体信息。DPU根据vring_desc的信息,通过RDMA READ拿到具体的命令信息,并区分出来是读IO请求还是写IO请求。同时在DPU上可以配置是否开启zcopy机制。DPU在处理IO请求时,会根据是否是zcopy,区分处理。
1)non-zcopy,调用spdk的接口分配一片内存,用于缓存读写的IO,如果是写请求,会先通过RDMA READ从host侧读取数据到已申请的内存上,然后调用spdk的接口发送出去;如果是读请求,然后通过spdk向远端磁盘发出读IO请求,当IO已经读取到指定的内存后,通过RDMA WRITE的方式将IO写到host侧;
2)zcopy:如果是写请求,会直接将host的内存信息,以iov的格式交给spdk,此时spdk必须使用RDMA协议作为传输层。将读写IO请求以及iov,转换成sge的格式,post send到QP中,转换sge的过程中,对于host侧的iov,需要生成一个特殊的cross mkey,该cross mkey是一个indirect key,当硬件在处理该WQE时,拿到cross mkey时,会指向硬件中另外一个mpt表,从而得到iov指向的内存是指向host上的内存,会直接从host侧指定的地址上DMA数据到硬件中;如果是读IO请求,也是一样的处理过程,从远端磁盘读取到的数据时,采用RDMA read的方式直接将数据读取到host侧iov指定的内存上,并在RDMA read成功后,再通知到host侧。