[RFC PATCH 15/15] iommufd/selftest: Add test to verify iommufd preservation

Samiullah Khawaja posted 15 patches 3 days, 2 hours ago
[RFC PATCH 15/15] iommufd/selftest: Add test to verify iommufd preservation
Posted by Samiullah Khawaja 3 days, 2 hours ago
Test iommufd preservation by setting up an iommufd and preserve it
across live update. Test takes VFIO cdev path of a device bound to
vfio-pci driver and binds it to an iommufd being preserved.

Note that the helper functions setup_cdev, open_iommufd, and
setup_iommufd will be replaced with VFIO selftest library. Similarly the
helper function defined to open and interface with Live Update
Orchestrator device will be replaced with a common helper library.

Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
 tools/testing/selftests/iommu/Makefile        |   1 +
 .../selftests/iommu/iommufd_liveupdate.c      | 196 ++++++++++++++++++
 2 files changed, 197 insertions(+)
 create mode 100644 tools/testing/selftests/iommu/iommufd_liveupdate.c

diff --git a/tools/testing/selftests/iommu/Makefile b/tools/testing/selftests/iommu/Makefile
index 84abeb2f0949..42c962c5e612 100644
--- a/tools/testing/selftests/iommu/Makefile
+++ b/tools/testing/selftests/iommu/Makefile
@@ -6,5 +6,6 @@ LDLIBS += -lcap
 TEST_GEN_PROGS :=
 TEST_GEN_PROGS += iommufd
 TEST_GEN_PROGS += iommufd_fail_nth
+TEST_GEN_PROGS += iommufd_liveupdate
 
 include ../lib.mk
diff --git a/tools/testing/selftests/iommu/iommufd_liveupdate.c b/tools/testing/selftests/iommu/iommufd_liveupdate.c
new file mode 100644
index 000000000000..1003d0cf2cae
--- /dev/null
+++ b/tools/testing/selftests/iommu/iommufd_liveupdate.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (c) 2025, Google LLC.
+ * Samiullah Khawaja <skhawaja@google.com>
+ */
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#define __EXPORTED_HEADERS__
+#include <linux/liveupdate.h>
+#include <linux/iommufd.h>
+#include <linux/types.h>
+#include <linux/vfio.h>
+
+#include "../kselftest.h"
+
+#define ksft_assert(condition) \
+	do { if (!(condition)) \
+	ksft_exit_fail_msg("Failed: %s at %s %d\n", \
+	#condition, __FILE__, __LINE__); } while (0)
+
+int setup_cdev(const char *vfio_cdev_path)
+{
+	int cdev_fd;
+
+	cdev_fd = open(vfio_cdev_path, O_RDWR);
+	if (cdev_fd < 0)
+		ksft_exit_skip("Failed to open VFIO cdev: %s\n", vfio_cdev_path);
+
+	return cdev_fd;
+}
+
+int open_iommufd(void)
+{
+	int iommufd;
+
+	iommufd = open("/dev/iommu", O_RDWR);
+	if (iommufd < 0)
+		ksft_exit_skip("Failed to open /dev/iommu. IOMMUFD support not enabled.\n");
+
+	return iommufd;
+}
+
+int setup_iommufd(int iommufd, int cdev_fd)
+{
+	int ret;
+
+	struct vfio_device_bind_iommufd bind = {
+		.argsz = sizeof(bind),
+		.flags = 0,
+	};
+	struct iommu_ioas_alloc alloc_data  = {
+		.size = sizeof(alloc_data),
+		.flags = 0,
+	};
+	struct vfio_device_attach_iommufd_pt attach_data = {
+		.argsz = sizeof(attach_data),
+		.flags = 0,
+	};
+
+	bind.iommufd = iommufd;
+	ret = ioctl(cdev_fd, VFIO_DEVICE_BIND_IOMMUFD, &bind);
+	ksft_assert(!ret);
+
+	ret = ioctl(iommufd, IOMMU_IOAS_ALLOC, &alloc_data);
+	ksft_assert(!ret);
+
+	attach_data.pt_id = alloc_data.out_ioas_id;
+	ret = ioctl(cdev_fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data);
+	ksft_assert(!ret);
+
+	return ret;
+}
+
+int open_liveupdate_orchestrator(void)
+{
+	int luo;
+
+	luo = open("/dev/liveupdate", O_RDWR);
+	ksft_assert(luo > 0);
+
+	return luo;
+}
+
+__u32 liveupdate_get_state(int luo)
+{
+	struct liveupdate_ioctl_get_state state;
+	int ret;
+
+	state.size = sizeof(state);
+	ret = ioctl(luo, LIVEUPDATE_IOCTL_GET_STATE, &state);
+	ksft_assert(!ret);
+
+	return state.state;
+}
+
+bool liveupdate_state_normal(int luo)
+{
+	return liveupdate_get_state(luo) == LIVEUPDATE_STATE_NORMAL;
+}
+
+bool liveupdate_state_updated(int luo)
+{
+	return liveupdate_get_state(luo) == LIVEUPDATE_STATE_UPDATED;
+}
+
+int liveupdate_set_event(int luo, enum liveupdate_event ev)
+{
+	struct liveupdate_ioctl_set_event event;
+	int ret;
+
+	event.event = ev;
+	event.size = sizeof(event);
+
+	ret = ioctl(luo, LIVEUPDATE_IOCTL_SET_EVENT, &event);
+	ksft_assert(!ret);
+
+	return ret;
+}
+
+int liveupdate_preserve_iommufd(int luo, int iommufd, int token)
+{
+	struct liveupdate_ioctl_fd_preserve preserve;
+	int ret;
+
+	preserve.fd = iommufd;
+	preserve.token = token;
+	preserve.size = sizeof(preserve);
+
+	ret = ioctl(luo, LIVEUPDATE_IOCTL_FD_PRESERVE, &preserve);
+	ksft_assert(!ret);
+
+	return ret;
+}
+
+int liveupdate_restore_iommufd(int luo, int token)
+{
+	struct liveupdate_ioctl_fd_restore restore;
+	int ret;
+
+	restore.token = token;
+	restore.size = sizeof(restore);
+
+	ret = ioctl(luo, LIVEUPDATE_IOCTL_FD_RESTORE, &restore);
+	ksft_assert(!ret);
+	ksft_assert(restore.fd > 0);
+
+	return restore.fd;
+}
+
+int main(int argc, char *argv[])
+{
+	int iommufd, cdev_fd, luo, ret;
+	const int token = 0x123456;
+
+	if (argc < 2) {
+		printf("Usage: ./iommufd_liveupdate <vfio_cdev_path>\n");
+		return 1;
+	}
+
+	cdev_fd = setup_cdev(argv[1]);
+
+	luo = open_liveupdate_orchestrator();
+	ksft_assert(luo > 0);
+
+	if (liveupdate_state_normal(luo))
+		iommufd = open_iommufd();
+	else if (liveupdate_state_updated(luo))
+		iommufd = liveupdate_restore_iommufd(luo, token);
+	else
+		ksft_exit_fail_msg("Test can only run when LUO state is normal or updated");
+
+	ret = setup_iommufd(iommufd, cdev_fd);
+	ksft_assert(!ret);
+
+	if (liveupdate_state_normal(luo)) {
+		ret = liveupdate_preserve_iommufd(luo, iommufd, token);
+		ksft_assert(!ret);
+
+		ret = liveupdate_set_event(luo, LIVEUPDATE_PREPARE);
+		ksft_assert(!ret);
+
+		while (1)
+			sleep(5);
+	} else {
+		ret = liveupdate_set_event(luo, LIVEUPDATE_FINISH);
+		ksft_assert(!ret);
+	}
+
+	return 0;
+}
+
-- 
2.51.0.536.g15c5d4f767-goog