Async. I/O on GPU memory via io_uring_dmabuf#
Warning
This needs a kernel that is not yet released. See Kernel.
The io_uring_dmabuf backend lets io_uring read and write GPU memory on a
regular NVMe block device. A CUDA or HIP allocation is exported as a dma-buf
and registered with io_uring as a fixed buffer. The NVMe controller then
transfers directly to and from the GPU over PCIe peer-to-peer.
Unlike uPCIe CUDA, this uses the kernel NVMe driver, so the
device is e.g. /dev/nvme0n1.
Usage#
Select the backend with opts.be or --be:
xnvmeperf run /dev/nvme0n1 --be io_uring_dmabuf --direct \
--iopattern randread --iosize 4096 --qdepth 32 --runtime 10 --cpumask 0x1
Register GPU memory with xnvme_buf_register() and release it with
xnvme_buf_unregister().
Buffers from xnvme_buf_alloc() are in host memory, IO on them is regular
io_uring IO.
cudaMalloc(&buf, nbytes);
xnvme_buf_register(dev, buf, nbytes);
xnvme_buf_register() registers the allocation with the ring of every queue of
the device, and a new queue registers the allocations already registered. IO on
a registered buffer is submitted as IORING_OP_READ_FIXED / IORING_OP_WRITE_FIXED.
Limitations#
Target |
A raw NVMe block device opened with |
Polling |
Not supported, |
Vectored |
Not supported ( |
Size |
A |
Count |
At most 256 mapped allocations per device |
Threads |
|
Kernel#
The io_uring and nvme-pci support is applied to the io_uring tree, but
not yet in a released kernel:
The backend is built with liburing and CUDA or HIP. No liburing release
has the UAPI of the series yet, so xNVMe defines the part it uses itself. On a
kernel without the series, opening the device fails with -ENOTSUP. The
xnvme-devenv guest runs a kernel with
the series, and the CI runs the backend there, on an emulated AMD GPU.
IOMMU#
The dma-buf exported by CUDA and HIP holds physical addresses. When an IOMMU
translates for the NVMe device, xnvme_buf_register() maps those addresses into
the IOMMU domain of the device at IOVA == physical address.
This needs the dmabuf-import and iommu-map-pa kernel modules, see
Kernel Module and Enforcing IOMMU.
Without them xnvme_buf_register() fails. Alternatively, boot with iommu=pt,
amd_iommu=off or intel_iommu=off. The IOMMU group is read from the
controller of the namespace; where it cannot be found, as for the head of a
native-multipath namespace, while IOMMU groups exist, xnvme_buf_register()
fails with -ENODEV rather than map without the IOMMU.
Note
The dma-buf must be physically contiguous. See
Enforcing IOMMU for caveats of iommu-map-pa.