diff mbox series

[2/5] usermode_driver_mgmt: Introduce management of user mode drivers

Message ID 20230317145240.363908-3-roberto.sassu@huaweicloud.com (mailing list archive)
State New
Headers show
Series usermode_driver: Add management library and API | expand

Commit Message

Roberto Sassu March 17, 2023, 2:52 p.m. UTC
From: Roberto Sassu <roberto.sassu@huawei.com>

Reuse the bpfilter code, with some adjustments to make the code more
generic, and usable for other user mode drivers.

:: struct umd_mgmt <=== struct bpfilter_umh_ops ::
Replace the start method with post_start, to allow for some customization
of the start procedure. All start procedures have in common the call to
fork_usermode_driver().

Remove the sockopt method, as it is use case-specific. Instead, use one of
the following two alternatives: generate the message from the manager of
the driver (e.g. sockopt), by exporting the message format definition; or,
define a new structure embedding the umd_mgmt and the custom method, which
can be registered from the kernel module through the post_start method.

Add kmod and kmod_loaded. Kmod is name of the kernel module that
umd_mgmt_send_recv() (from bpfilter_mbox_request()) attempts to load if
kmod_loaded is false (the driver is not yet started). Kmod_loaded is added
to replace the sockopt method test, and ensure that the driver is ready for
use.

:: start_umh() <=== start_umh() ::
Remove the connection test, and call the post_start method in umd_mgmt, if
set, which could point to that test.

:: shutdown_umh() <=== shutdown_umh() ::
Same code.

:: umd_mgmt_send_recv() <=== bpfilter_mbox_request() ::
Replace the bpfilter_mbox_request() parameters with the parameters of
umd_send_recv(), except for the first which is a umd_mgmt structure instead
of umd_info. Also, call umd_send_recv() instead of the sockopt method, and
shutdown the driver if there is an error.

:: umd_mgmt_load() <=== load_umh() ::
Same code except for the registration of the start and sockopt methods,
replaced with setting kmod_loaded to true (not depending on CONFIG_INET),
as mentioned in the explanation of umd_mgmt.

:: umd_mgmt_unload() <=== fini_umh() ::
Same code except for the deregistration of the start and sockopt methods,
replaced with setting kmod_loaded to false (not depending on CONFIG_INET).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 MAINTAINERS                          |   7 ++
 include/linux/usermode_driver_mgmt.h |  35 +++++++
 kernel/Makefile                      |   2 +-
 kernel/usermode_driver_mgmt.c        | 137 +++++++++++++++++++++++++++
 4 files changed, 180 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/usermode_driver_mgmt.h
 create mode 100644 kernel/usermode_driver_mgmt.c
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index a3b14ec3383..7b27435fd20 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11245,6 +11245,13 @@  S:	Maintained
 F:	include/linux/umh.h
 F:	kernel/umh.c
 
+KERNEL USERMODE DRIVER MANAGEMENT
+M:	Roberto Sassu <roberto.sassu@huawei.com>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+F:	include/linux/usermode_driver_mgmt.h
+F:	kernel/usermode_driver_mgmt.c
+
 KERNEL VIRTUAL MACHINE (KVM)
 M:	Paolo Bonzini <pbonzini@redhat.com>
 L:	kvm@vger.kernel.org
diff --git a/include/linux/usermode_driver_mgmt.h b/include/linux/usermode_driver_mgmt.h
new file mode 100644
index 00000000000..3f9fc783a09
--- /dev/null
+++ b/include/linux/usermode_driver_mgmt.h
@@ -0,0 +1,35 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * User mode driver management API.
+ */
+#ifndef __LINUX_USERMODE_DRIVER_MGMT_H__
+#define __LINUX_USERMODE_DRIVER_MGMT_H__
+
+#include <linux/usermode_driver.h>
+
+/**
+ * struct umd_mgmt - user mode driver management structure
+ * @info: user mode driver information
+ * @lock: lock to serialize requests to the UMD Handler
+ * @post_start: function with additional operations after UMD Handler is started
+ * @kmod: kernel module acting as UMD Loader, to start the UMD Handler
+ * @kmod_loaded: whether @kmod is loaded or not
+ *
+ * Information necessary to manage the UMD during its lifecycle.
+ */
+struct umd_mgmt {
+	struct umd_info info;
+	struct mutex lock;
+	int (*post_start)(struct umd_mgmt *mgmt);
+	const char *kmod;
+	bool kmod_loaded;
+};
+
+int umd_mgmt_send_recv(struct umd_mgmt *mgmt, void *in, size_t in_len,
+		       void *out, size_t out_len);
+int umd_mgmt_load(struct umd_mgmt *mgmt, char *start, char *end);
+void umd_mgmt_unload(struct umd_mgmt *mgmt);
+
+#endif /* __LINUX_USERMODE_DRIVER_MGMT_H__ */
diff --git a/kernel/Makefile b/kernel/Makefile
index 10ef068f598..ee47f7c2023 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -12,7 +12,7 @@  obj-y     = fork.o exec_domain.o panic.o \
 	    notifier.o ksysfs.o cred.o reboot.o \
 	    async.o range.o smpboot.o ucount.o regset.o
 
-obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
+obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o usermode_driver_mgmt.o
 obj-$(CONFIG_MODULES) += kmod.o
 obj-$(CONFIG_MULTIUSER) += groups.o
 
diff --git a/kernel/usermode_driver_mgmt.c b/kernel/usermode_driver_mgmt.c
new file mode 100644
index 00000000000..4fb06b37f62
--- /dev/null
+++ b/kernel/usermode_driver_mgmt.c
@@ -0,0 +1,137 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * User mode driver management library.
+ */
+#include <linux/kmod.h>
+#include <linux/fs.h>
+#include <linux/usermode_driver_mgmt.h>
+
+static void shutdown_umh(struct umd_mgmt *mgmt)
+{
+	struct umd_info *info = &mgmt->info;
+	struct pid *tgid = info->tgid;
+
+	if (tgid) {
+		kill_pid(tgid, SIGKILL, 1);
+		wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
+		umd_cleanup_helper(info);
+	}
+}
+
+static int start_umh(struct umd_mgmt *mgmt)
+{
+	int err;
+
+	/* fork usermode process */
+	err = fork_usermode_driver(&mgmt->info);
+	if (err)
+		return err;
+	pr_info("Loaded %s pid %d\n", mgmt->info.driver_name,
+		pid_nr(mgmt->info.tgid));
+
+	if (mgmt->post_start) {
+		err = mgmt->post_start(mgmt);
+		if (err)
+			shutdown_umh(mgmt);
+	}
+
+	return err;
+}
+
+/**
+ * umd_mgmt_send_recv - Communicate with the UMD Handler and start it.
+ * @mgmt: user mode driver management structure
+ * @in: request message
+ * @in_len: size of @in
+ * @out: reply message
+ * @out_len: size of @out
+ *
+ * Send a message to the UMD Handler through the created pipe and read the
+ * reply. If the UMD Handler is not available, invoke the UMD Loader to
+ * instantiate it. If the UMD Handler exited, restart it.
+ *
+ * Return: Zero on success, a negative value otherwise.
+ */
+int umd_mgmt_send_recv(struct umd_mgmt *mgmt, void *in, size_t in_len,
+		       void *out, size_t out_len)
+{
+	int err;
+
+	mutex_lock(&mgmt->lock);
+	if (!mgmt->kmod_loaded) {
+		mutex_unlock(&mgmt->lock);
+		request_module(mgmt->kmod);
+		mutex_lock(&mgmt->lock);
+
+		if (!mgmt->kmod_loaded) {
+			err = -ENOPROTOOPT;
+			goto out;
+		}
+	}
+	if (mgmt->info.tgid &&
+	    thread_group_exited(mgmt->info.tgid))
+		umd_cleanup_helper(&mgmt->info);
+
+	if (!mgmt->info.tgid) {
+		err = start_umh(mgmt);
+		if (err)
+			goto out;
+	}
+	err = umd_send_recv(&mgmt->info, in, in_len, out, out_len);
+	if (err)
+		shutdown_umh(mgmt);
+out:
+	mutex_unlock(&mgmt->lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(umd_mgmt_send_recv);
+
+/**
+ * umd_mgmt_load - Load and start the UMD Handler.
+ * @mgmt: user mode driver management structure
+ * @start: start address of the binary blob of the UMD Handler
+ * @end: end address of the binary blob of the UMD Handler
+ *
+ * Copy the UMD Handler binary from the specified location to a private tmpfs
+ * filesystem. Then, start the UMD Handler.
+ *
+ * Return: Zero on success, a negative value otherwise.
+ */
+int umd_mgmt_load(struct umd_mgmt *mgmt, char *start, char *end)
+{
+	int err;
+
+	err = umd_load_blob(&mgmt->info, start, end - start);
+	if (err)
+		return err;
+
+	mutex_lock(&mgmt->lock);
+	err = start_umh(mgmt);
+	if (!err)
+		mgmt->kmod_loaded = true;
+	mutex_unlock(&mgmt->lock);
+	if (err)
+		umd_unload_blob(&mgmt->info);
+	return err;
+}
+EXPORT_SYMBOL_GPL(umd_mgmt_load);
+
+/**
+ * umd_mgmt_unload - Terminate and unload the UMD Handler.
+ * @mgmt: user mode driver management structure
+ *
+ * Terminate the UMD Handler, and cleanup the private tmpfs filesystem with the
+ * UMD Handler binary.
+ */
+void umd_mgmt_unload(struct umd_mgmt *mgmt)
+{
+	mutex_lock(&mgmt->lock);
+	shutdown_umh(mgmt);
+	mgmt->kmod_loaded = false;
+	mutex_unlock(&mgmt->lock);
+
+	umd_unload_blob(&mgmt->info);
+}
+EXPORT_SYMBOL_GPL(umd_mgmt_unload);