[PATCH] net/9p: add vsock transport

From: skvarlamatus

Date: Wed May 27 2026 - 03:36:29 EST


From: Matus Skvarla <skvarlamatus@xxxxxxxxx>

Add vsock as a transport option for 9P client connections. This
allows mounting 9P filesystems over VM sockets without requiring TCP/IP
networking or additional userspace tools.

The implementation extends trans_fd.c with vsock support, reusing the
existing socket infrastructure. A new p9_fd_create_vsock() function
handles vsock connection setup by parsing the CID from the mount source,
creating an AF_VSOCK socket, and connecting to the specified endpoint.
All other transport operations (close, request, cancel) use the shared
fd transport implementation.

Add CONFIG_NET_9P_VSOCK option that conditionally compiles vsock support
into 9pnet_fd.ko. This follows the pattern where socket-based transports
(TCP, Unix, vsock) share trans_fd.c, while specialized hardware transports
(virtio, xen, rdma) have dedicated files.

Usage:
mount -t 9p -o trans=vsock[,port=<port>] <CID> /mnt/point

Signed-off-by: Matus Skvarla <skvarlamatus@xxxxxxxxx>
---
include/net/9p/client.h | 6 ++-
net/9p/Kconfig | 9 +++++
net/9p/trans_fd.c | 89 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index 838a94218b59..4eb2f6b22496 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -122,8 +122,12 @@ struct p9_client {
struct {
u16 port;
bool privport;
-
} tcp;
+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+ struct {
+ u16 port;
+ } vsock;
+#endif
} trans_opts;

struct idr fids;
diff --git a/net/9p/Kconfig b/net/9p/Kconfig
index 22f8c167845d..63a5b5b25063 100644
--- a/net/9p/Kconfig
+++ b/net/9p/Kconfig
@@ -32,6 +32,15 @@ config NET_9P_VIRTIO
This builds support for a transports between
guest partitions and a host partition.

+config NET_9P_VSOCK
+ depends on VSOCKETS
+ bool "9P VSOCK Transport"
+ help
+ This builds support for a transport for 9pfs over
+ virtual sockets (vsock). This is useful for communication
+ between virtual machines and their host without requiring
+ TCP/IP networking configuration or additional userspace software.
+
config NET_9P_XEN
depends on XEN
select XEN_XENBUS_FRONTEND
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index dbad3213ba84..6f35f2d84313 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -28,6 +28,9 @@
#include <net/9p/9p.h>
#include <net/9p/client.h>
#include <net/9p/transport.h>
+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+#include <linux/vm_sockets.h>
+#endif

#include <linux/syscalls.h> /* killme */

@@ -36,6 +39,9 @@

static struct p9_trans_module p9_tcp_trans;
static struct p9_trans_module p9_fd_trans;
+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+static struct p9_trans_module p9_vsock_trans;
+#endif

enum {
Rworksched = 1, /* read work scheduled or running */
@@ -714,6 +720,12 @@ static int p9_fd_show_options(struct seq_file *m, struct p9_client *clnt)
if (clnt->trans_opts.fd.wfd != ~0)
seq_printf(m, ",wfd=%u", clnt->trans_opts.fd.wfd);
}
+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+ else if (clnt->trans_mod == &p9_vsock_trans) {
+ if (clnt->trans_opts.vsock.port != P9_FD_PORT)
+ seq_printf(m, ",port=%u", clnt->trans_opts.vsock.port);
+ }
+#endif
return 0;
}

@@ -968,6 +980,60 @@ p9_fd_create_unix(struct p9_client *client, struct fs_context *fc)
return p9_socket_open(client, csocket);
}

+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+static int
+p9_fd_create_vsock(struct p9_client *client, struct fs_context *fc)
+{
+ const char *addr = fc->source;
+ struct v9fs_context *ctx = fc->fs_private;
+ int err;
+ struct socket *csocket;
+ struct sockaddr_vm addr_vm = { 0 };
+ struct p9_fd_opts opts;
+ unsigned int cid;
+
+ /* opts are already parsed in context */
+ opts = ctx->fd_opts;
+
+ if (!addr)
+ return -EINVAL;
+
+ err = kstrtouint(addr, 10, &cid);
+ if (err < 0) {
+ pr_err("%s (%d): invalid CID: %s\n",
+ __func__, task_pid_nr(current), addr);
+ return err;
+ }
+
+ csocket = NULL;
+
+ client->trans_opts.vsock.port = opts.port;
+ err = __sock_create(current->nsproxy->net_ns, AF_VSOCK,
+ SOCK_STREAM, 0, &csocket, 1);
+ if (err) {
+ pr_err("%s (%d): problem creating socket\n",
+ __func__, task_pid_nr(current));
+ return err;
+ }
+
+ addr_vm.svm_family = AF_VSOCK;
+ addr_vm.svm_port = opts.port;
+ addr_vm.svm_cid = cid;
+
+ err = READ_ONCE(csocket->ops)->connect(csocket,
+ (struct sockaddr_unsized *)&addr_vm,
+ sizeof(addr_vm), 0);
+ if (err < 0) {
+ pr_err("%s (%d): problem connecting socket to %s:%u\n",
+ __func__, task_pid_nr(current), addr, opts.port);
+ sock_release(csocket);
+ return err;
+ }
+
+ return p9_socket_open(client, csocket);
+}
+#endif /* CONFIG_NET_9P_VSOCK */
+
static int
p9_fd_create(struct p9_client *client, struct fs_context *fc)
{
@@ -1038,6 +1104,23 @@ static struct p9_trans_module p9_fd_trans = {
};
MODULE_ALIAS_9P("fd");

+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+static struct p9_trans_module p9_vsock_trans = {
+ .name = "vsock",
+ .maxsize = MAX_SOCK_BUF,
+ .def = false,
+ .supports_vmalloc = true,
+ .create = p9_fd_create_vsock,
+ .close = p9_fd_close,
+ .request = p9_fd_request,
+ .cancel = p9_fd_cancel,
+ .cancelled = p9_fd_cancelled,
+ .show_options = p9_fd_show_options,
+ .owner = THIS_MODULE,
+};
+MODULE_ALIAS_9P("vsock");
+#endif
+
/**
* p9_poll_workfn - poll worker thread
* @work: work queue
@@ -1075,6 +1158,9 @@ static int __init p9_trans_fd_init(void)
v9fs_register_trans(&p9_tcp_trans);
v9fs_register_trans(&p9_unix_trans);
v9fs_register_trans(&p9_fd_trans);
+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+ v9fs_register_trans(&p9_vsock_trans);
+#endif

return 0;
}
@@ -1085,6 +1171,9 @@ static void __exit p9_trans_fd_exit(void)
v9fs_unregister_trans(&p9_tcp_trans);
v9fs_unregister_trans(&p9_unix_trans);
v9fs_unregister_trans(&p9_fd_trans);
+#if IS_ENABLED(CONFIG_NET_9P_VSOCK)
+ v9fs_unregister_trans(&p9_vsock_trans);
+#endif
}

module_init(p9_trans_fd_init);
--
2.47.1