[PATCH v1 1/1] vfio: fix device lookup with no-IOMMU mode enabled

Anatoly Burakov anatoly.burakov at intel.com
Thu Oct 15 17:43:53 CEST 2026


Currently, when the `enable_unsafe_noiommu_mode` VFIO module parameter is
set, VFIO selects PA IOVA mode and then only looks for no-IOMMU device
nodes: `/dev/vfio/noiommu-N` in group mode, and `noiommu-vfioN` in cdev
mode.

However, the parameter does not make every VFIO device a no-IOMMU
device. The kernel only creates a no-IOMMU group for a device that has no
IOMMU group of its own; devices behind a real IOMMU still get a regular
`/dev/vfio/N` group and `vfioN` cdev. On such systems, devices bound
to `vfio-pci` are therefore reported as not managed by VFIO and fail
to probe.

Fix it by trying the regular node name first and falling back to the
no-IOMMU one, in both group and cdev modes.

Fixes: 6cb38a7f56e0 ("vfio: introduce VFIO mode")
Fixes: d129414711aa ("vfio: introduce cdev mode")

Signed-off-by: Anatoly Burakov <anatoly.burakov at intel.com>
---
 lib/eal/linux/eal_vfio_cdev.c  | 65 +++++++++++++++++-----------------
 lib/eal/linux/eal_vfio_group.c | 19 +++++-----
 2 files changed, 44 insertions(+), 40 deletions(-)

diff --git a/lib/eal/linux/eal_vfio_cdev.c b/lib/eal/linux/eal_vfio_cdev.c
index ab4d3f9f44..4a955248ae 100644
--- a/lib/eal/linux/eal_vfio_cdev.c
+++ b/lib/eal/linux/eal_vfio_cdev.c
@@ -23,8 +23,29 @@
 #include "eal_internal_cfg.h"
 
 #define DEV_VFIO_IOMMUFD_PATH "/dev/iommu"
-#define DEV_VFIO_CDEV_FMT "/dev/vfio/devices/vfio%d"
-#define DEV_VFIO_NOIOMMU_CDEV_FMT "/dev/vfio/devices/noiommu-vfio%d"
+#define DEV_VFIO_CDEV_FMT "/dev/vfio/devices/%s%d"
+
+/*
+ * Global no-IOMMU mode does not mean every device is no-IOMMU: the kernel only
+ * creates no-IOMMU devices for devices without an IOMMU group, so cdev names
+ * are tried in this order regardless of IOVA mode.
+ */
+static const char * const cdev_prefixes[] = {"vfio", "noiommu-vfio"};
+
+/* return length of the matching cdev prefix, or 0 if the name has none */
+static size_t
+cdev_match_prefix(const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < RTE_DIM(cdev_prefixes); i++) {
+		size_t len = strlen(cdev_prefixes[i]);
+
+		if (strncmp(name, cdev_prefixes[i], len) == 0)
+			return len;
+	}
+	return 0;
+}
 
 static int vfio_cdev_dma_map(struct vfio_container *cfg);
 static int vfio_cdev_dma_mem_map(struct vfio_container *cfg, uint64_t vaddr,
@@ -208,28 +229,12 @@ int
 vfio_cdev_get_device_num(const char *sysfs_base, const char *dev_addr, int *cdev_dev_num)
 {
 	char linkname[PATH_MAX] = {0};
-	const char *prefix;
 	size_t prefix_len;
 	char *dev_tok, *end;
 	int dev_num;
 	DIR *dir;
 	struct dirent *entry;
 
-	/* different prefixes for different IOMMU modes */
-	switch (vfio_global_cfg.iova_mode) {
-	case DEV_VFIO_IOVA_MODE_VA:
-		prefix = "vfio";
-		prefix_len = strlen(prefix);
-		break;
-	case DEV_VFIO_IOVA_MODE_PA:
-		prefix = "noiommu-vfio";
-		prefix_len = strlen(prefix);
-		break;
-	default:
-		EAL_LOG(ERR, "VFIO IOMMU mode is not initialized");
-		return -1;
-	}
-
 	/* check if vfio-dev directory exists for this device */
 	snprintf(linkname, sizeof(linkname), "%s/%s/vfio-dev", sysfs_base, dev_addr);
 
@@ -241,7 +246,8 @@ vfio_cdev_get_device_num(const char *sysfs_base, const char *dev_addr, int *cdev
 
 	/* find the matching VFIO cdev entry */
 	while ((entry = readdir(dir)) != NULL) {
-		if (strncmp(entry->d_name, prefix, prefix_len) != 0)
+		prefix_len = cdev_match_prefix(entry->d_name);
+		if (prefix_len == 0)
 			continue;
 
 		/* parse device number after the cdev entry prefix */
@@ -281,21 +287,16 @@ static int
 cdev_open_device_fd(int cdev_dev_num)
 {
 	char devname[PATH_MAX] = {0};
-	int dev_fd;
+	int dev_fd = -1;
+	unsigned int i;
 
-	switch (vfio_global_cfg.iova_mode) {
-	case DEV_VFIO_IOVA_MODE_VA:
-		snprintf(devname, sizeof(devname), DEV_VFIO_CDEV_FMT, cdev_dev_num);
-		break;
-	case DEV_VFIO_IOVA_MODE_PA:
-		snprintf(devname, sizeof(devname), DEV_VFIO_NOIOMMU_CDEV_FMT, cdev_dev_num);
-		break;
-	default:
-		EAL_LOG(ERR, "VFIO IOMMU mode is not initialized");
-		return -1;
+	for (i = 0; i < RTE_DIM(cdev_prefixes); i++) {
+		snprintf(devname, sizeof(devname), DEV_VFIO_CDEV_FMT,
+			cdev_prefixes[i], cdev_dev_num);
+		dev_fd = open(devname, O_RDWR);
+		if (dev_fd >= 0 || errno != ENOENT)
+			break;
 	}
-
-	dev_fd = open(devname, O_RDWR);
 	if (dev_fd < 0) {
 		EAL_LOG(ERR, "Cannot open %s: %s", devname, strerror(errno));
 		return -1;
diff --git a/lib/eal/linux/eal_vfio_group.c b/lib/eal/linux/eal_vfio_group.c
index aef3e9f27a..7c879830ac 100644
--- a/lib/eal/linux/eal_vfio_group.c
+++ b/lib/eal/linux/eal_vfio_group.c
@@ -554,17 +554,20 @@ vfio_open_group_sysfs(int iommu_group_num)
 	char filename[PATH_MAX];
 	int fd;
 
-	if (vfio_global_cfg.iova_mode == DEV_VFIO_IOVA_MODE_VA)
-		snprintf(filename, sizeof(filename), DEV_VFIO_GROUP_FMT, iommu_group_num);
-	else if (vfio_global_cfg.iova_mode == DEV_VFIO_IOVA_MODE_PA)
-		snprintf(filename, sizeof(filename), DEV_VFIO_NOIOMMU_GROUP_FMT, iommu_group_num);
-
-	/* reset errno before open to differentiate errors */
-	errno = 0;
+	/*
+	 * Global no-IOMMU mode does not mean every group is no-IOMMU: the
+	 * kernel only creates no-IOMMU groups for devices without an IOMMU
+	 * group, so try the regular group first, then the no-IOMMU one.
+	 */
+	snprintf(filename, sizeof(filename), DEV_VFIO_GROUP_FMT, iommu_group_num);
 	fd = open(filename, O_RDWR);
+	if (fd < 0 && errno == ENOENT) {
+		snprintf(filename, sizeof(filename), DEV_VFIO_NOIOMMU_GROUP_FMT, iommu_group_num);
+		fd = open(filename, O_RDWR);
+	}
 
 	/* we have to differentiate between failed open and non-existence */
-	if (errno == ENOENT)
+	if (fd < 0 && errno == ENOENT)
 		return -ENOENT;
 	return fd;
 }
-- 
2.52.0



More information about the dev mailing list