]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
vfio: selftests: Add tests to validate SR-IOV UAPI
authorRaghavendra Rao Ananta <rananta@google.com>
Tue, 5 May 2026 21:28:38 +0000 (21:28 +0000)
committerAlex Williamson <alex@shazbot.org>
Wed, 20 May 2026 17:54:10 +0000 (11:54 -0600)
Add a selftest, vfio_pci_sriov_uapi_test.c, to validate the
SR-IOV UAPI, including the following cases, iterating over
all the IOMMU modes currently supported:
 - Setting correct/incorrect/NULL tokens during device init.
 - Close the PF device immediately after setting the token.
 - Change/override the PF's token after device init.

Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Reviewed-by: Vipin Sharma <vipinsh@google.com>
Reviewed-by: David Matlack <dmatlack@google.com>
Tested-by: David Matlack <dmatlack@google.com>
Link: https://lore.kernel.org/r/20260505212838.1698034-9-rananta@google.com
Signed-off-by: Alex Williamson <alex@shazbot.org>
tools/testing/selftests/vfio/Makefile
tools/testing/selftests/vfio/vfio_pci_sriov_uapi_test.c [new file with mode: 0644]

index da2dae1099e454bbce1e64a97b8003ab9d30fd96..e6e8cb52ab0301ddd306c9c0da785403d57ba045 100644 (file)
@@ -12,6 +12,7 @@ TEST_GEN_PROGS += vfio_iommufd_setup_test
 TEST_GEN_PROGS += vfio_pci_device_test
 TEST_GEN_PROGS += vfio_pci_device_init_perf_test
 TEST_GEN_PROGS += vfio_pci_driver_test
+TEST_GEN_PROGS += vfio_pci_sriov_uapi_test
 
 TEST_FILES += scripts/cleanup.sh
 TEST_FILES += scripts/lib.sh
diff --git a/tools/testing/selftests/vfio/vfio_pci_sriov_uapi_test.c b/tools/testing/selftests/vfio/vfio_pci_sriov_uapi_test.c
new file mode 100644 (file)
index 0000000..19d657d
--- /dev/null
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "lib/include/libvfio/assert.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <linux/limits.h>
+
+#include <libvfio.h>
+
+#include "../kselftest_harness.h"
+
+#define UUID_1 "52ac9bff-3a88-4fbd-901a-0d767c3b6c97"
+#define UUID_2 "88594674-90a0-47a9-aea8-9d9b352ac08a"
+
+static const char *pf_bdf;
+static char *vf_bdf;
+
+static pid_t main_pid;
+
+static int container_setup(struct vfio_pci_device *device, const char *bdf,
+                          const char *vf_token)
+{
+       vfio_pci_group_setup(device, bdf);
+       vfio_container_set_iommu(device);
+       __vfio_pci_group_get_device_fd(device, bdf, vf_token);
+
+       /* The device fd will be -1 in case of mismatched tokens */
+       return (device->fd < 0);
+}
+
+static int iommufd_setup(struct vfio_pci_device *device, const char *bdf,
+                        const char *vf_token)
+{
+       vfio_pci_cdev_open(device, bdf);
+       return __vfio_device_bind_iommufd(device->fd,
+                                         device->iommu->iommufd, vf_token);
+}
+
+static int device_init(const char *bdf, struct iommu *iommu,
+                      const char *vf_token, struct vfio_pci_device **out_dev)
+{
+       struct vfio_pci_device *device = vfio_pci_device_alloc(bdf, iommu);
+       int ret;
+
+       if (iommu->mode->container_path)
+               ret = container_setup(device, bdf, vf_token);
+       else
+               ret = iommufd_setup(device, bdf, vf_token);
+
+       *out_dev = device;
+       return ret;
+}
+
+static void device_cleanup(struct vfio_pci_device *device)
+{
+       if (!device)
+               return;
+
+       if (device->fd > 0)
+               VFIO_ASSERT_EQ(close(device->fd), 0);
+
+       if (device->group_fd)
+               VFIO_ASSERT_EQ(close(device->group_fd), 0);
+
+       vfio_pci_device_free(device);
+}
+
+FIXTURE(vfio_pci_sriov_uapi_test) {
+       struct vfio_pci_device *pf;
+       struct vfio_pci_device *vf;
+       struct iommu *iommu;
+       char *pf_token;
+};
+
+FIXTURE_VARIANT(vfio_pci_sriov_uapi_test) {
+       const char *iommu_mode;
+       char *vf_token;
+};
+
+#define FIXTURE_VARIANT_ADD_IOMMU_MODE(_iommu_mode, _name, _vf_token)          \
+FIXTURE_VARIANT_ADD(vfio_pci_sriov_uapi_test, _iommu_mode ## _ ## _name) {     \
+       .iommu_mode = #_iommu_mode,                                             \
+       .vf_token = (_vf_token),                                                \
+}
+
+FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(same_uuid, UUID_1);
+FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(diff_uuid, UUID_2);
+FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(null_uuid, NULL);
+
+FIXTURE_SETUP(vfio_pci_sriov_uapi_test)
+{
+       self->iommu = iommu_init(variant->iommu_mode);
+
+       self->pf_token = UUID_1;
+       ASSERT_EQ(device_init(pf_bdf, self->iommu, self->pf_token, &self->pf), 0);
+}
+
+FIXTURE_TEARDOWN(vfio_pci_sriov_uapi_test)
+{
+       device_cleanup(self->vf);
+       device_cleanup(self->pf);
+       iommu_cleanup(self->iommu);
+}
+
+/*
+ * This asserts if the VF device is successfully created if its token matches
+ * with the token used to create/override the PF or fails during a mismatch.
+ */
+#define ASSERT_COND_VF_CREATION(_ret) do {                                     \
+       if (!variant->vf_token || strcmp(self->pf_token, variant->vf_token)) {  \
+               ASSERT_NE((_ret), 0);                                           \
+       } else {                                                                \
+               ASSERT_EQ((_ret), 0);                                           \
+       }                                                                       \
+} while (0)
+
+/*
+ * Validate if the UAPI handles correctly and incorrectly set token on the VF.
+ */
+TEST_F(vfio_pci_sriov_uapi_test, init_token_match)
+{
+       int ret;
+
+       ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
+       ASSERT_COND_VF_CREATION(ret);
+}
+
+/*
+ * After closing the PF, validate if the VF access still needs the right token.
+ */
+TEST_F(vfio_pci_sriov_uapi_test, pf_early_close)
+{
+       int ret;
+
+       device_cleanup(self->pf);
+
+       /* Clean the 'pf' to avoid calling device_cleanup() again. */
+       self->pf = NULL;
+
+       ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
+       ASSERT_COND_VF_CREATION(ret);
+}
+
+/*
+ * After PF device init, override the existing token and validate if the newly
+ * set token is the one that's active.
+ */
+TEST_F(vfio_pci_sriov_uapi_test, override_token)
+{
+       int ret;
+
+       self->pf_token = UUID_2;
+       vfio_device_set_vf_token(self->pf->fd, self->pf_token);
+
+       ret = device_init(vf_bdf, self->iommu, variant->vf_token, &self->vf);
+       ASSERT_COND_VF_CREATION(ret);
+}
+
+static void vf_teardown(void)
+{
+       /*
+        * The child processes, created by TEST_F()s, inherits this atexit()
+        * handler. Hence, check and destroy the VF only when the main/parent
+        * process exits.
+        */
+       if (getpid() != main_pid)
+               return;
+
+       free(vf_bdf);
+       sysfs_sriov_numvfs_set(pf_bdf, 0);
+}
+
+static void vf_setup(void)
+{
+       char *vf_driver;
+       int nr_vfs;
+
+       nr_vfs = sysfs_sriov_totalvfs_get(pf_bdf);
+       if (nr_vfs <= 0)
+               ksft_exit_skip("SR-IOV may not be supported by the PF: %s\n", pf_bdf);
+
+       nr_vfs = sysfs_sriov_numvfs_get(pf_bdf);
+       if (nr_vfs != 0)
+               ksft_exit_skip("SR-IOV already configured for the PF: %s\n", pf_bdf);
+
+       /* Create only one VF for testing */
+       sysfs_sriov_numvfs_set(pf_bdf, 1);
+
+       /*
+        * Setup an exit handler to destroy the VF in case of failures
+        * during further setup at the end of the test run.
+        */
+       main_pid = getpid();
+       VFIO_ASSERT_EQ(atexit(vf_teardown), 0);
+
+       vf_bdf = sysfs_sriov_vf_bdf_get(pf_bdf, 0);
+
+       /*
+        * The VF inherits the driver from the PF.
+        * Ensure this is 'vfio-pci' before proceeding.
+        */
+       vf_driver = sysfs_driver_get(vf_bdf);
+       VFIO_ASSERT_NE(vf_driver, NULL);
+       VFIO_ASSERT_EQ(strcmp(vf_driver, "vfio-pci"), 0);
+       free(vf_driver);
+
+       printf("Created 1 VF (%s) under the PF: %s\n", vf_bdf, pf_bdf);
+}
+
+int main(int argc, char *argv[])
+{
+       pf_bdf = vfio_selftests_get_bdf(&argc, argv);
+       vf_setup();
+
+       return test_harness_run(argc, argv);
+}