Thread (5 messages) 5 messages, 3 authors, 18d ago
COLD18d

Revision v1 of 2 in this series.

Revisions (2)
  1. v1 current
  2. v2 [diff vs current]

[PATCH BlueZ 2/2] tools: test-runner: use virtiofsd for filesystem passthrough

From: Pauli Virtanen <hidden>
Date: 2026-09-10 16:48:29
Subsystem: the rest · Maintainer: Linus Torvalds

The 9p mount passthrough I/O is fairly CPU hungry and slow. This matters
for e.g. running Python in test-runner, since it has to read some amount
of files for startup.

Add support for virtio-fs, which is significantly less CPU intensive.

Enable it by default if /usr/libexec/virtiofsd is available, fall back
to 9p if virtiofsd not usable.

Before:
$ time make check-functional
real	2m9,736s
user	1m29,570s
sys	0m32,715s

After:
$ time make check-functional
real	1m35,703s
user	0m28,938s
sys	0m7,453s
---
 doc/test-runner.rst |   3 +
 tools/test-runner.c | 210 +++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 199 insertions(+), 14 deletions(-)
diff --git a/doc/test-runner.rst b/doc/test-runner.rst
index 6787507c3..ac8f22c19 100644
--- a/doc/test-runner.rst
+++ b/doc/test-runner.rst
@@ -26,6 +26,7 @@ OPTIONS
 :-P/--pcie=<qemu_args>: Provide PCIe device
 :-q/--qemu=<path>: QEMU binary
 :-k/--kernel=<image>: Kernel image (bzImage)
+:-F/--virtiofs=<virtiofsd / no>: Enable/disable use of virtio-fs.
 :-h/--help: Show help options
 
 Kernel
@@ -47,6 +48,8 @@ option (like the Bluetooth subsystem) can be enabled on top of this.
 	CONFIG_VIRTIO=y
 	CONFIG_VIRTIO_PCI=y
 	CONFIG_VIRTIO_CONSOLE=y
+	CONFIG_VIRTIO_FS=y
+	CONFIG_FUSE_FS=y
 
 	CONFIG_NET=y
 	CONFIG_INET=y
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 63fedece0..d6589f7a9 100644
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -64,6 +64,7 @@ static char *usb_dev;
 static char *pcie_dev;
 static char *extra_opts[EXTRA_OPT_MAX];
 static int num_extra_opts;
+static const char *virtiofsd = "/usr/libexec/virtiofsd";
 
 static const char *qemu_table[] = {
 	"qemu-system-x86_64",
@@ -235,9 +236,6 @@ static char *const qemu_argv[] = {
 	"-m", "256M",
 	"-net", "none",
 	"-no-reboot",
-	"-fsdev", "local,id=fsdev-root,path=/,readonly=on,security_model=none,"
-	"multidevs=remap",
-	"-device", "virtio-9p-pci,fsdev=fsdev-root,mount_tag=/dev/root",
 	"-chardev", "stdio,id=con,mux=on",
 	"-serial", "chardev:con",
 	"-device", "virtio-serial",
@@ -495,6 +493,98 @@ static void pcie_unbind_vfio(void)
 	pcie_probe(pcie_bdf);
 }
 
+static bool check_virtiofsd(void)
+{
+	if (!virtiofsd)
+		return false;
+
+	if (access(virtiofsd, X_OK)) {
+		fprintf(stderr, "%s not available", virtiofsd);
+		return false;
+	}
+
+	return true;
+}
+
+static pid_t start_virtiofsd(const char *tmpdir)
+{
+	pid_t pid;
+	char path[PATH_MAX];
+	struct stat st;
+
+	printf("Using virtiofsd %s\n", virtiofsd);
+
+	snprintf(path, sizeof(path), "%s/virtiofs", tmpdir);
+
+	pid = fork();
+	if (pid < 0)
+		return pid;
+
+	if (pid == 0) {
+		char *envp[1];
+		const char *cmd[] = {
+			virtiofsd,
+			"--socket-path", path,
+			"--shared-dir", "/",
+			"--readonly",
+			"--tag", "/dev/root",
+			/* Drop unnecessary capabilities, if run as root */
+			"--modcaps=-chown:-dac_override:-fowner:-fsetid:"
+					"-setgid:-setuid:-mknod:-setfcap",
+			/*
+			 * Disabling namespace sandbox is needed to allow the
+			 * guest to mount other virtio/9p filesystems.
+			 */
+			"--sandbox", "none",
+			NULL
+		};
+
+		envp[0] = NULL;
+		execve(cmd[0], (char **)cmd, envp);
+		exit(EXIT_SUCCESS);
+	}
+
+	while (1) {
+		int status;
+		pid_t ret;
+
+		if (!stat(path, &st))
+			break;
+
+		ret = waitpid(pid, &status, WNOHANG);
+		if (ret < 0 && errno == -EINTR)
+			continue;
+		else if (ret)
+			return -EIO;
+
+		sleep(1);
+	}
+
+	return pid;
+}
+
+static void cleanup_virtiofsd(pid_t pid, const char *tmpdir)
+{
+	char path[PATH_MAX];
+	int status;
+
+	if (pid > 0) {
+		kill(pid, SIGTERM);
+		while (waitpid(pid, &status, 0) < 0) {
+			if (errno != EINTR)
+				break;
+		}
+	}
+
+	if (tmpdir[0]) {
+		snprintf(path, sizeof(path), "%s/virtiofs", tmpdir);
+		unlink(path);
+		snprintf(path, sizeof(path), "%s/virtiofs.pid", tmpdir);
+		unlink(path);
+		rmdir(tmpdir);
+	}
+}
+
 static pid_t qemu_pid;
 
 /* Forwards the signal to QEMU so it can shutdown, the host driver is then
@@ -509,10 +599,13 @@ static void qemu_signal(int sig)
 static int start_qemu(void)
 {
 	char cwd[PATH_MAX/2], initcmd[PATH_MAX], testargs[PATH_MAX];
+	const char *fscmdline;
 	char cmdline[CMDLINE_MAX];
+	char tmpdir[PATH_MAX] = {0};
 	char **argv;
 	int i, pos, status = 0;
 	pid_t pid;
+	pid_t virtiofsd_pid = -1;
 
 	check_virtualization();
 
@@ -539,15 +632,20 @@ static int start_qemu(void)
 		pos += n;
 	}
 
+	if (!virtiofsd)
+		fscmdline = "rootfstype=9p "
+				"rootflags=trans=virtio,version=9p2000.u";
+	else
+		fscmdline = "rootfstype=virtiofs root=/dev/root";
+
 	snprintf(cmdline, sizeof(cmdline),
 				"console=hvc0 earlyprintk=serial "
-				"no_hash_pointers=1 rootfstype=9p "
-				"rootflags=trans=virtio,version=9p2000.u "
-				"%s quiet ro init=%s "
+				"no_hash_pointers=1 %s %s quiet ro init=%s "
 				"TESTHOME=%s TESTDBUS=%u TESTDAEMON=%u "
 				"TESTDBUSSESSION=%u XDG_RUNTIME_DIR=/run/user/0 "
 				"TESTMONITOR=%u TESTEMULATOR=%u TESTDEVS=%d "
 				"TESTAUTO=%u TESTAUDIO='%s' TESTARGS=\'%s\'",
+				fscmdline,
 				/* PCIe passthrough requires ACPI and APIC for
 				 * device enumeration and MSI interrupts.
 				 */
@@ -559,6 +657,7 @@ static int start_qemu(void)
 				testargs);
 
 	argv = alloca(sizeof(qemu_argv) +
+			sizeof(char *) * 8 +
 			(sizeof(char *) * (8 + (num_devs * 4))) +
 			(sizeof(char *) * (usb_dev ? 4 : 0)) +
 			(sizeof(char *) * (pcie_dev ? 2 : 0)) +
@@ -567,6 +666,60 @@ static int start_qemu(void)
 
 	pos = (sizeof(qemu_argv) / sizeof(char *)) - 1;
 
+	if (!virtiofsd) {
+		argv[pos++] = "-fsdev";
+		argv[pos++] = "local,id=fsdev-root,path=/,readonly=on,"
+				"security_model=none,multidevs=remap";
+		argv[pos++] = "-device";
+		argv[pos++] = "virtio-9p-pci,fsdev=fsdev-root,"
+				"mount_tag=/dev/root";
+	} else {
+		char *chrdev;
+		const char *tmp_base;
+		const char *mem = "256M";
+		char *memdev;
+
+		tmp_base = getenv("TMPDIR");
+		if (!tmp_base)
+			tmp_base = "/tmp";
+
+		snprintf(tmpdir, sizeof(tmpdir), "%s/bluez-test-runner.XXXXXX",
+								tmp_base);
+		if (!mkdtemp(tmpdir)) {
+			perror("mkdtemp failed");
+			return EXIT_FAILURE;
+		}
+
+		chrdev = alloca(48 + strlen(tmpdir));
+		sprintf(chrdev, "socket,id=virtiofs0,path=%s/virtiofs", tmpdir);
+
+		argv[pos++] = "-chardev";
+		argv[pos++] = chrdev;
+		argv[pos++] = "-device";
+		argv[pos++] = "vhost-user-fs-pci,queue-size=1024,"
+					"chardev=virtiofs0,tag=/dev/root";
+
+		/* Find out memory size */
+		for (i = 0; i < pos; ++i) {
+			if (strcmp(argv[i], "-m") == 0 && i + 1 < pos)
+				mem = argv[i+1];
+		}
+		for (i = 0; i < num_extra_opts; ++i) {
+			if (strcmp(extra_opts[i], "-m") == 0 &&
+							i + 1 < num_extra_opts)
+				mem = extra_opts[i+1];
+		}
+
+		memdev = alloca(48 + strlen(mem));
+		sprintf(memdev, "memory-backend-memfd,id=mem0,size=%s,share=on",
+									mem);
+
+		argv[pos++] = "-object";
+		argv[pos++] = memdev;
+		argv[pos++] = "-numa";
+		argv[pos++] = "node,memdev=mem0";
+	}
+
 	/* Make sure qemu_binary is not null */
 	if (!qemu_binary) {
 		fprintf(stderr, "No QEMU binary is set\n");
@@ -616,21 +769,29 @@ static int start_qemu(void)
 
 	argv[pos] = NULL;
 
-	if (!pcie_dev) {
+	/* Exec directly if no setup/cleanup needed */
+	if (!pcie_dev && !virtiofsd) {
 		execve(argv[0], argv, qemu_envp);
 		return EXIT_FAILURE;
 	}
 
-	/* With a device passed through the host driver has to be restored
-	 * once the guest is done with it, so QEMU cannot simply replace this
-	 * process here.
-	 */
-	pcie_bind_vfio();
+	if (virtiofsd) {
+		virtiofsd_pid = start_virtiofsd(tmpdir);
+		if (virtiofsd_pid < 0) {
+			perror("Failed to start virtiofsd");
+			cleanup_virtiofsd(virtiofsd_pid, tmpdir);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (pcie_dev)
+		pcie_bind_vfio();
 
 	pid = fork();
 	if (pid < 0) {
 		perror("Failed to fork new process");
 		pcie_unbind_vfio();
+		cleanup_virtiofsd(virtiofsd_pid, tmpdir);
 		return EXIT_FAILURE;
 	}
 
@@ -655,7 +816,11 @@ static int start_qemu(void)
 
 	qemu_pid = -1;
 
-	pcie_unbind_vfio();
+	if (pcie_dev)
+		pcie_unbind_vfio();
+
+	if (virtiofsd)
+		cleanup_virtiofsd(virtiofsd_pid, tmpdir);
 
 	return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
 }
@@ -1524,6 +1689,7 @@ static void usage(void)
 		"\t-q, --qemu <path>      QEMU binary\n"
 		"\t-H, --qemu-host-cpu    Use host CPU (requires KVM support)\n"
 		"\t-k, --kernel <image>   Kernel bzImage or source tree path\n"
+		"\t-F, --virtiofs[=no / virtiofsd] Virtiofsd disable / path\n"
 		"\t-o, --option <opt>     Additional argument passed to QEMU\n"
 		"\t-h, --help             Show help options\n");
 }
@@ -1544,6 +1710,7 @@ static const struct option main_options[] = {
 	{ "usb",     required_argument, NULL, 'U' },
 	{ "pcie",    required_argument, NULL, 'P' },
 	{ "option",  required_argument, NULL, 'o' },
+	{ "virtiofs", optional_argument, NULL, 'F' },
 	{ "version", no_argument,       NULL, 'v' },
 	{ "help",    no_argument,       NULL, 'h' },
 	{ }
@@ -1552,6 +1719,7 @@ static const struct option main_options[] = {
 int main(int argc, char *argv[])
 {
 	char kernel_path[PATH_MAX];
+	bool virtiofs_auto = true;
 
 	if (getpid() == 1 && getppid() == 0) {
 		prepare_sandbox();
@@ -1565,7 +1733,7 @@ int main(int argc, char *argv[])
 	for (;;) {
 		int opt;
 
-		opt = getopt_long(argc, argv, "au::bdsl::mq:Hk:A::U:P:o:vh",
+		opt = getopt_long(argc, argv, "au::bdsl::mq:Hk:A::U:P:o:F::vh",
 						main_options, NULL);
 		if (opt < 0)
 			break;
@@ -1620,6 +1788,15 @@ int main(int argc, char *argv[])
 			}
 			extra_opts[num_extra_opts++] = optarg;
 			break;
+		case 'F':
+			virtiofs_auto = false;
+			if (optarg) {
+				if (strcmp(optarg, "no") == 0)
+					virtiofsd = NULL;
+				else
+					virtiofsd = optarg;
+			}
+			break;
 		case 'v':
 			printf("%s\n", VERSION);
 			return EXIT_SUCCESS;
@@ -1631,6 +1808,11 @@ int main(int argc, char *argv[])
 		}
 	}
 
+	if (virtiofs_auto && !check_virtiofsd()) {
+		fprintf(stderr, ": virtiofs disabled\n");
+		virtiofsd = NULL;
+	}
+
 	if (run_auto) {
 		if (argc - optind > 0) {
 			fprintf(stderr, "Invalid command line parameters\n");
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help