Add a selftest to exercise preserving a vfio-pci device in noiommu mode across a Live Update. For now the test is simple and verifies that the device file can be preserved, retrieved, and bound to iommufd in the new kernel. Subsequent commits extend this test to verify that the device cannot be opened through VFIO during the Live Update and that DMA continues across it. This test is added to TEST_GEN_PROGS_EXTENDED since it must be run manually along with a kexec. The kernel must be booted with vfio.enable_unsafe_noiommu_mode=1 and the target device must be bound to vfio-pci in both kernels. In the new kernel, the preserved device must be bound to vfio-pci without first being bound to or unbound from any other driver. This can be done during boot, e.g. with vfio-pci.ids=: on the kernel command line, or manually before running stage 2: $ echo vfio-pci > /sys/bus/pci/devices/0000:00:04.0/driver_override $ echo 0000:00:04.0 > /sys/bus/pci/drivers/vfio-pci/bind To run this test manually: $ ./vfio_pci_liveupdate_kexec_test -s 1 0000:00:04.0 $ kexec ... # NOTE: Exact method will be distro-dependent $ ./vfio_pci_liveupdate_kexec_test -s 2 0000:00:04.0 Do not unbind the device from vfio-pci after kexec (e.g. by running setup.sh on a device that is already bound to vfio-pci), as unbinding it would reset it. This test is considered passing if all commands exit with 0. Co-developed-by: David Matlack Signed-off-by: David Matlack Signed-off-by: Vipin Sharma --- tools/testing/selftests/vfio/Makefile | 4 + .../vfio/vfio_pci_liveupdate_kexec_test.c | 102 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile index ece2a3618f2b..7010a243a0b7 100644 --- a/tools/testing/selftests/vfio/Makefile +++ b/tools/testing/selftests/vfio/Makefile @@ -15,6 +15,10 @@ TEST_GEN_PROGS += vfio_pci_driver_test TEST_GEN_PROGS += vfio_pci_sriov_uapi_test TEST_GEN_PROGS += vfio_pci_liveupdate_uapi_test +# This test must be run manually since it requires the user/automation to +# perform a kexec during the test. +TEST_GEN_PROGS_EXTENDED += vfio_pci_liveupdate_kexec_test + TEST_FILES += scripts/cleanup.sh TEST_FILES += scripts/lib.sh TEST_FILES += scripts/run.sh diff --git a/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c new file mode 100644 index 000000000000..4919175c1173 --- /dev/null +++ b/tools/testing/selftests/vfio/vfio_pci_liveupdate_kexec_test.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include + +static const char *device_bdf; + +static char state_session[LIVEUPDATE_SESSION_NAME_LENGTH]; +static char device_session[LIVEUPDATE_SESSION_NAME_LENGTH]; + +enum { + STATE_TOKEN, + DEVICE_TOKEN, +}; + +static void check_noiommu(const char *bdf) +{ + const char *cdev_path = __vfio_pci_get_cdev_path(bdf, true); + int fd; + + if (!cdev_path) + ksft_exit_skip("Device %s is not bound to vfio-pci in noiommu mode\n", + bdf); + + fd = open(cdev_path, O_RDWR); + if (fd < 0) + ksft_exit_skip("open(%s) failed: %s\n", cdev_path, + strerror(errno)); + + close(fd); + free((void *)cdev_path); +} + +static void before_kexec(int luo_fd) +{ + struct vfio_pci_device *device; + struct iommu *iommu; + int session_fd; + int ret; + + check_noiommu(device_bdf); + + iommu = iommu_init(MODE_IOMMUFD_NOIOMMU); + device = vfio_pci_device_init(device_bdf, iommu); + + create_state_file(luo_fd, state_session, STATE_TOKEN, /*next_stage=*/2); + + session_fd = luo_create_session(luo_fd, device_session); + VFIO_ASSERT_GE(session_fd, 0); + + ret = luo_session_preserve_fd(session_fd, device->fd, DEVICE_TOKEN); + VFIO_ASSERT_EQ(ret, 0); + + close(luo_fd); + daemonize_and_wait(); +} + +static void after_kexec(int luo_fd, int state_session_fd) +{ + struct vfio_pci_device *device; + struct iommu *iommu; + int session_fd; + int device_fd; + int stage; + + restore_and_read_stage(state_session_fd, STATE_TOKEN, &stage); + VFIO_ASSERT_EQ(stage, 2); + + session_fd = luo_retrieve_session(luo_fd, device_session); + VFIO_ASSERT_GE(session_fd, 0); + + device_fd = luo_session_retrieve_fd(session_fd, DEVICE_TOKEN); + VFIO_ASSERT_GE(device_fd, 0); + + iommu = iommu_init(MODE_IOMMUFD_NOIOMMU); + + /* + * This will invoke various ioctls on device_fd such as + * VFIO_DEVICE_GET_INFO. So this is a decent sanity test + * that LUO actually handed us back a valid VFIO device + * file and not something else. + */ + device = __vfio_pci_device_init(device_bdf, iommu, device_fd); + + VFIO_ASSERT_EQ(luo_session_finish(session_fd), 0); + + vfio_pci_device_cleanup(device); + iommu_cleanup(iommu); +} + +int main(int argc, char *argv[]) +{ + device_bdf = vfio_selftests_get_bdf(&argc, argv); + + snprintf_assert(device_session, sizeof(device_session), + "vfio-lu-device-%s", device_bdf); + snprintf_assert(state_session, sizeof(state_session), + "vfio-lu-state-%s", device_bdf); + + return luo_test(argc, argv, state_session, before_kexec, after_kexec); +} -- 2.56.0.317.g454f8302e1-goog