git: 04811d46d3f2 - main - bhyve: Generalize the IPC thread

From: Bojan Novković <bnovkov_at_FreeBSD.org>
Date: Sun, 23 Aug 2026 12:03:32 UTC
The branch main has been updated by bnovkov:

URL: https://cgit.FreeBSD.org/src/commit/?id=04811d46d3f2176af245f6d5e7e06b422f5d6216

commit 04811d46d3f2176af245f6d5e7e06b422f5d6216
Author:     Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2025-11-18 11:59:01 +0000
Commit:     Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2026-08-23 12:02:49 +0000

    bhyve: Generalize the IPC thread
    
    Move the code for the snapshotting IPC thread into a separate file
    and define macros for adding new IPC commands.
    No functional change intended.
    
    Reviewed by:    rew
    Differential Revision:  https://reviews.freebsd.org/D54650
---
 usr.sbin/bhyve/Makefile                   |   1 +
 usr.sbin/bhyve/aarch64/bhyverun_machdep.c |   2 +
 usr.sbin/bhyve/amd64/bhyverun_machdep.c   |   3 +-
 usr.sbin/bhyve/bhyverun.c                 |   9 +-
 usr.sbin/bhyve/ipc.c                      | 194 ++++++++++++++++++++++++++++++
 usr.sbin/bhyve/ipc.h                      |  17 +--
 usr.sbin/bhyve/riscv/bhyverun_machdep.c   |   2 +
 usr.sbin/bhyve/snapshot.c                 | 131 ++------------------
 usr.sbin/bhyve/snapshot.h                 |   1 -
 9 files changed, 220 insertions(+), 140 deletions(-)

diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index b86c56991039..eb7cfe7b602e 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -32,6 +32,7 @@ SRCS=	\
 	ctl_util.c		\
 	hda_codec.c		\
 	iov.c			\
+	ipc.c			\
 	mem.c			\
 	mevent.c		\
 	net_backend_netmap.c	\
diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
index 56390719471b..1f13ad38dedb 100644
--- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
@@ -49,6 +49,7 @@
 #include "debug.h"
 #include "fdt.h"
 #include "mem.h"
+#include "ipc.h"
 #include "pci_emul.h"
 #include "pci_irq.h"
 #include "rtc_pl031.h"
@@ -87,6 +88,7 @@ bhyve_init_config(void)
 	set_config_bool("acpi_tables", false);
 	set_config_bool("acpi_tables_in_memory", false);
 	set_config_value("memory.size", "256M");
+	set_config_value("rundir", BHYVE_RUN_DIR);
 }
 
 void
diff --git a/usr.sbin/bhyve/amd64/bhyverun_machdep.c b/usr.sbin/bhyve/amd64/bhyverun_machdep.c
index 50085c928fde..ea1bad0d4eaa 100644
--- a/usr.sbin/bhyve/amd64/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/amd64/bhyverun_machdep.c
@@ -46,6 +46,7 @@
 #include "e820.h"
 #include "fwctl.h"
 #include "ioapic.h"
+#include "ipc.h"
 #include "inout.h"
 #include "kernemu_dev.h"
 #include "mptbl.h"
@@ -68,9 +69,7 @@ bhyve_init_config(void)
 	set_config_bool("x86.strictmsr", true);
 	set_config_bool("x86.verbosemsr", false);
 	set_config_value("lpc.fwcfg", "bhyve");
-#ifdef BHYVE_SNAPSHOT
 	set_config_value("rundir", BHYVE_RUN_DIR);
-#endif
 }
 
 void
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 339cf5071906..aa1a28f406e0 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -82,6 +82,7 @@
 #ifdef BHYVE_GDB
 #include "gdb.h"
 #endif
+#include "ipc.h"
 #include "mem.h"
 #include "mevent.h"
 #include "pci_emul.h"
@@ -1107,13 +1108,11 @@ main(int argc, char *argv[])
 	 */
 	setproctitle("%s", vmname);
 
-#ifdef BHYVE_SNAPSHOT
 	/*
-	 * checkpointing thread for communication with bhyvectl
+	 * Thread for handling bhyvectl commands.
 	 */
-	if (init_checkpoint_thread(ctx, get_config_value("rundir")) != 0)
-		errx(EX_OSERR, "Failed to start checkpoint thread");
-#endif
+	if (init_ipc_thread(ctx, get_config_value("rundir")) != 0)
+		errx(EX_OSERR, "Failed to start IPC thread");
 
 #ifndef WITHOUT_CAPSICUM
 	caph_cache_catpages();
diff --git a/usr.sbin/bhyve/ipc.c b/usr.sbin/bhyve/ipc.c
new file mode 100644
index 000000000000..cdf07e18ed3f
--- /dev/null
+++ b/usr.sbin/bhyve/ipc.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2016 Flavius Anton
+ * Copyright (c) 2016 Mihai Tiganus
+ * Copyright (c) 2016-2019 Mihai Carabas
+ * Copyright (c) 2017-2019 Darius Mihai
+ * Copyright (c) 2017-2019 Elena Mihailescu
+ * Copyright (c) 2018-2019 Sergiu Weisz
+ * Copyright (c) 2025 Bojan Novković <bnovkov@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "ipc.h"
+
+#ifndef WITHOUT_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+#include <sys/types.h>
+#include <sys/linker_set.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#ifndef WITHOUT_CAPSICUM
+#include <capsicum_helpers.h>
+#endif
+#include <sys/cpuset.h>
+#include <sys/ioctl.h>
+
+#include <machine/vmm.h>
+#include <machine/vmm_snapshot.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <pthread_np.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <vmmapi.h>
+
+#include "bhyverun.h"
+#include "debug.h"
+
+#define IPC_COMMAND_FOREACH(pvar, set) SET_FOREACH(pvar, set)
+
+SET_DECLARE(ipc_cmd_set, struct ipc_command);
+
+static struct ipc_thread_ctx {
+	struct vmctx *vmctx;
+	int sockfd;
+} thr_ctx;
+
+static nvlist_t *
+handle_message(struct vmctx *ctx, nvlist_t *nvl)
+{
+	const char *cmd;
+	struct ipc_command **ipc_cmd;
+
+	cmd = nvlist_get_string(nvl, "cmd");
+	IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set)
+	{
+		if (strcmp(cmd, (*ipc_cmd)->name) == 0)
+			return ((*ipc_cmd)->handler(ctx, nvl));
+	}
+
+	return (NULL);
+}
+
+/*
+ * Listen for commands from bhyvectl.
+ */
+static void *
+ipc_thread(void *param)
+{
+	int fd;
+	nvlist_t *nvl, *reply;
+	const char *cmdname;
+	struct ipc_thread_ctx *ctx;
+
+	pthread_set_name_np(pthread_self(), "IPC thread");
+	ctx = (struct ipc_thread_ctx *)param;
+	while ((fd = accept(ctx->sockfd, NULL, NULL)) != -1) {
+		nvl = nvlist_recv(fd, 0);
+		if (nvl == NULL) {
+			EPRINTLN("%s: nvlist_recv() failed: %s", __func__,
+			    strerror(errno));
+			close(fd);
+			continue;
+		}
+
+		cmdname = nvlist_get_string(nvl, "cmd");
+		if (cmdname == NULL) {
+			nvlist_add_string(nvl, "error", "missing command name");
+			nvlist_send(fd, nvl);
+			nvlist_destroy(nvl);
+			close(fd);
+			continue;
+		}
+		reply = handle_message(ctx->vmctx, nvl);
+		if (reply == NULL) {
+			reply = nvlist_create(0);
+			nvlist_add_stringf(reply, "error", "command '%s' not found",
+			    cmdname);
+		}
+		/* The handler should set the error message if need be. */
+		nvlist_send(fd, reply);
+
+		nvlist_destroy(nvl);
+		nvlist_destroy(reply);
+		close(fd);
+	}
+
+	return (NULL);
+}
+
+/*
+ * Create the listening socket for IPC with bhyvectl.
+ */
+int
+init_ipc_thread(struct vmctx *ctx, const char *bhyve_run_dir)
+{
+	struct sockaddr_un addr;
+	pthread_t ipc_pthread;
+	int socket_fd;
+	int err, ret;
+#ifndef WITHOUT_CAPSICUM
+	cap_rights_t rights;
+#endif
+
+	memset(&addr, 0, sizeof(addr));
+
+	socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (socket_fd < 0) {
+		EPRINTLN("Socket creation failed: %s", strerror(errno));
+		err = -1;
+		goto fail;
+	}
+
+	addr.sun_family = AF_UNIX;
+
+	ret = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+	    bhyve_run_dir, vm_get_name(ctx));
+	if ((ret < 0) || ((size_t)ret >= sizeof(addr.sun_path))) {
+		EPRINTLN("%s: error setting socket path (%d)", __func__, ret);
+		err = -1;
+		goto fail;
+	}
+
+	addr.sun_len = SUN_LEN(&addr);
+	unlink(addr.sun_path);
+
+	if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) {
+		EPRINTLN("Failed to bind socket \"%s\": %s\n",
+		    addr.sun_path, strerror(errno));
+		err = -1;
+		goto fail;
+	}
+
+	if (listen(socket_fd, 10) < 0) {
+		EPRINTLN("ipc socket listen: %s\n", strerror(errno));
+		err = errno;
+		goto fail;
+	}
+
+#ifndef WITHOUT_CAPSICUM
+	cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE,
+	    CAP_SEND, CAP_GETSOCKOPT);
+
+	if (caph_rights_limit(socket_fd, &rights) == -1)
+		errx(EX_OSERR, "Unable to apply rights for sandbox");
+#endif
+	memset(&thr_ctx, 0, sizeof(thr_ctx));
+	thr_ctx.vmctx = ctx;
+	thr_ctx.sockfd = socket_fd;
+
+	err = pthread_create(&ipc_pthread, NULL, ipc_thread,
+		&thr_ctx);
+	if (err != 0)
+		goto fail;
+
+	return (0);
+fail:
+	if (socket_fd > 0)
+		close(socket_fd);
+	unlink(addr.sun_path);
+
+	return (err);
+}
diff --git a/usr.sbin/bhyve/ipc.h b/usr.sbin/bhyve/ipc.h
index d7a3a173e068..36063e47b61d 100644
--- a/usr.sbin/bhyve/ipc.h
+++ b/usr.sbin/bhyve/ipc.h
@@ -33,18 +33,19 @@
 #include <sys/linker_set.h>
 #include <sys/nv.h>
 
+#define BHYVE_RUN_DIR "/var/run/bhyve/"
+
+struct vmctx;
+
 struct ipc_command {
 	const char *name;
-	int (*handler)(struct vmctx *ctx, const nvlist_t *nvl);
+	nvlist_t *(*handler)(struct vmctx *ctx, const nvlist_t *nvl);
 };
 
-#define IPC_COMMAND(set, name, function)			\
-	static struct ipc_command name ## _ipc_command =	\
-	{ #name, function };					\
-	DATA_SET(set, name ## _ipc_command)
-
-#define IPC_COMMAND_FOREACH(pvar, set)	SET_FOREACH(pvar, set)
+#define IPC_COMMAND(name, function)                                         \
+	static struct ipc_command name##_ipc_command = { #name, function }; \
+	DATA_SET(ipc_cmd_set, name##_ipc_command)
 
-SET_DECLARE(ipc_cmd_set, struct ipc_command);
+int init_ipc_thread(struct vmctx *ctx, const char *bhyve_run_dir);
 
 #endif /* _IPC_H_ */
diff --git a/usr.sbin/bhyve/riscv/bhyverun_machdep.c b/usr.sbin/bhyve/riscv/bhyverun_machdep.c
index 8efaa2aa4124..b467ff9f52d9 100644
--- a/usr.sbin/bhyve/riscv/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/riscv/bhyverun_machdep.c
@@ -51,6 +51,7 @@
 #include "config.h"
 #include "debug.h"
 #include "fdt.h"
+#include "ipc.h"
 #include "mem.h"
 #include "pci_emul.h"
 #include "pci_irq.h"
@@ -82,6 +83,7 @@ bhyve_init_config(void)
 	set_config_bool("acpi_tables", false);
 	set_config_bool("acpi_tables_in_memory", false);
 	set_config_value("memory.size", "256M");
+	set_config_value("rundir", BHYVE_RUN_DIR);
 }
 
 void
diff --git a/usr.sbin/bhyve/snapshot.c b/usr.sbin/bhyve/snapshot.c
index fdb5941bf1d0..1d3d337ec9b7 100644
--- a/usr.sbin/bhyve/snapshot.c
+++ b/usr.sbin/bhyve/snapshot.c
@@ -1331,56 +1331,13 @@ done:
 	return (error);
 }
 
-static int
-handle_message(struct vmctx *ctx, nvlist_t *nvl)
-{
-	const char *cmd;
-	struct ipc_command **ipc_cmd;
-
-	if (!nvlist_exists_string(nvl, "cmd"))
-		return (EINVAL);
-
-	cmd = nvlist_get_string(nvl, "cmd");
-	IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set) {
-		if (strcmp(cmd, (*ipc_cmd)->name) == 0)
-			return ((*ipc_cmd)->handler(ctx, nvl));
-	}
-
-	return (EOPNOTSUPP);
-}
-
-/*
- * Listen for commands from bhyvectl
- */
-void *
-checkpoint_thread(void *param)
-{
-	int fd;
-	struct checkpoint_thread_info *thread_info;
-	nvlist_t *nvl;
-
-	pthread_set_name_np(pthread_self(), "checkpoint thread");
-	thread_info = (struct checkpoint_thread_info *)param;
-
-	while ((fd = accept(thread_info->socket_fd, NULL, NULL)) != -1) {
-		nvl = nvlist_recv(fd, 0);
-		if (nvl != NULL)
-			handle_message(thread_info->ctx, nvl);
-		else
-			EPRINTLN("nvlist_recv() failed: %s", strerror(errno));
-
-		close(fd);
-		nvlist_destroy(nvl);
-	}
-
-	return (NULL);
-}
-
-static int
+static nvlist_t *
 vm_do_checkpoint(struct vmctx *ctx, const nvlist_t *nvl)
 {
 	int error;
+	nvlist_t *reply;
 
+	reply = nvlist_create(0);
 	if (!nvlist_exists_string(nvl, "filename") ||
 	    !nvlist_exists_bool(nvl, "suspend") ||
 	    !nvlist_exists_descriptor(nvl, "fddir"))
@@ -1390,86 +1347,12 @@ vm_do_checkpoint(struct vmctx *ctx, const nvlist_t *nvl)
 		    nvlist_get_descriptor(nvl, "fddir"),
 		    nvlist_get_string(nvl, "filename"),
 		    nvlist_get_bool(nvl, "suspend"));
+	if (error != 0)
+		nvlist_add_string(reply, "error", strerror(error));
 
-	return (error);
-}
-IPC_COMMAND(ipc_cmd_set, checkpoint, vm_do_checkpoint);
-
-/*
- * Create the listening socket for IPC with bhyvectl
- */
-int
-init_checkpoint_thread(struct vmctx *ctx, const char *bhyve_run_dir)
-{
-	struct checkpoint_thread_info *checkpoint_info = NULL;
-	struct sockaddr_un addr;
-	int socket_fd;
-	pthread_t checkpoint_pthread;
-	int err, ret;
-#ifndef WITHOUT_CAPSICUM
-	cap_rights_t rights;
-#endif
-
-	memset(&addr, 0, sizeof(addr));
-
-	socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
-	if (socket_fd < 0) {
-		EPRINTLN("Socket creation failed: %s", strerror(errno));
-		err = -1;
-		goto fail;
-	}
-
-	addr.sun_family = AF_UNIX;
-
-	ret = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
-	    bhyve_run_dir, vm_get_name(ctx));
-	if ((ret < 0) || ((size_t)ret >= sizeof(addr.sun_path))) {
-		EPRINTLN("%s: error setting socket path (%d)", __func__, ret);
-		err = -1;
-		goto fail;
-	}
-
-	addr.sun_len = SUN_LEN(&addr);
-	unlink(addr.sun_path);
-
-	if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) {
-		EPRINTLN("Failed to bind socket \"%s\": %s\n",
-		    addr.sun_path, strerror(errno));
-		err = -1;
-		goto fail;
-	}
-
-	if (listen(socket_fd, 10) < 0) {
-		EPRINTLN("ipc socket listen: %s\n", strerror(errno));
-		err = errno;
-		goto fail;
-	}
-
-#ifndef WITHOUT_CAPSICUM
-	cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE,
-	    CAP_SEND, CAP_GETSOCKOPT);
-
-	if (caph_rights_limit(socket_fd, &rights) == -1)
-		errx(EX_OSERR, "Unable to apply rights for sandbox");
-#endif
-	checkpoint_info = calloc(1, sizeof(*checkpoint_info));
-	checkpoint_info->ctx = ctx;
-	checkpoint_info->socket_fd = socket_fd;
-
-	err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread,
-		checkpoint_info);
-	if (err != 0)
-		goto fail;
-
-	return (0);
-fail:
-	free(checkpoint_info);
-	if (socket_fd > 0)
-		close(socket_fd);
-	unlink(addr.sun_path);
-
-	return (err);
+	return (reply);
 }
+IPC_COMMAND(checkpoint, vm_do_checkpoint);
 
 void
 vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op)
diff --git a/usr.sbin/bhyve/snapshot.h b/usr.sbin/bhyve/snapshot.h
index 5c71640f3f09..eb66edd61f56 100644
--- a/usr.sbin/bhyve/snapshot.h
+++ b/usr.sbin/bhyve/snapshot.h
@@ -40,7 +40,6 @@
 #include <libxo/xo.h>
 #include <ucl.h>
 
-#define BHYVE_RUN_DIR "/var/run/bhyve/"
 #define MAX_SNAPSHOT_FILENAME PATH_MAX
 
 struct vmctx;