uPCIe#
xNVMe provides kernel-bypassing backends implemented using uPCIe, a minimal header-only user space NVMe driver. Unlike SPDK, uPCIe has no reactor, threading model, or application framework — just direct PCIe BAR access and DMA.
Two backend configs are available, differing in where I/O buffers are allocated:
uPCIe (host memory) — DMA buffers in host memory, backed by hugepages.
uPCIe CUDA — DMA buffers in GPU device memory, enabling PCIe peer-to-peer (P2P) transfers directly between the NVMe device and the GPU.
Device Identifiers#
When using user space NVMe drivers, the operating-system kernel NVMe
driver is detached and the device is bound either to uio_pci_generic for the
legacy no-IOMMU path or to vfio-pci for the IOMMU-backed path. Thus, the
device files in /dev/, such as /dev/nvme0n1, are not available. Devices
are instead identified by their PCI id (0000:03:00.0), and namespace
identifier via the --nsid option.
System Configuration#
Driver Attachment#
Use the xnvme-driver script to unbind the kernel NVMe driver and bind
devices to uio_pci_generic or vfio-pci:
xnvme-driver
upcie supports both uio_pci_generic (no-IOMMU path) and vfio-pci
(IOMMU-backed path). upcie-cuda requires the non-VFIO path.
Privileges#
uPCIe requires root for two reasons:
Reading
/proc/self/pagemapto translate virtual to physical addresses for DMA.Accessing VFIO device nodes and writing PCI config space via sysfs to enable Bus Master when needed.
Dual-Backend Operation#
The same PCIe device can be opened simultaneously with both upcie and
upcie-cuda. This lets one handle send I/O through host-memory buffers while
another sends I/O through GPU device-memory buffers, both going to the same NVMe
controller. Opening order does not matter; the controller is torn down only when
the last handle across both backends is closed. Attempting to open the same URI
with an unrelated backend while either uPCIe backend holds it returns -EBUSY.
Before opening both handles, complete the system configuration for each backend: hugepages for uPCIe (host memory) and the additional CUDA and kernel requirements for uPCIe CUDA.
Example#
struct xnvme_opts host_opts = xnvme_opts_default();
struct xnvme_opts cuda_opts = xnvme_opts_default();
host_opts.be = "upcie";
cuda_opts.be = "upcie-cuda";
struct xnvme_dev *host_dev = xnvme_dev_open("0000:03:00.0", &host_opts);
struct xnvme_dev *cuda_dev = xnvme_dev_open("0000:03:00.0", &cuda_opts);