From 96098f751038703cc0fda4f018236d240a86930d Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Mon, 7 Jun 2021 07:09:20 +0800 Subject: [PATCH] 9p: add support for root file systems This introduces a new kernel command-line option called 'v9fsroot=' which will tell the kernel to mount the root file system by utilizing the 9p protocol. This allows us to mount host folder as rootfs for guest linux in qemu. Bellow is an example which mounts v9fs with tag 'r' as rootfs in qemu guest via virtio transport. $ qemu-system-x86_64 -enable-kvm -cpu host -m 1024 \ -virtfs local,path=$rootfs_dir,mount_tag=r,security_model=passthrough,id=r \ -kernel /path/to/linux/arch/x86/boot/bzImage -nographic \ -append "root=/dev/v9fs v9fsroot=r,trans=virtio rw console=ttyS0 3" Signed-off-by: Changbin Du --- MAINTAINERS | 5 ++++ fs/9p/Kconfig | 6 ++++ fs/9p/Makefile | 1 + fs/9p/v9fsroot.c | 64 ++++++++++++++++++++++++++++++++++++++++ include/linux/root_dev.h | 1 + init/do_mounts.c | 55 ++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 fs/9p/v9fsroot.c diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f20..0fbccb6d15ee59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,6 +239,11 @@ F: include/trace/events/9p.h F: include/uapi/linux/virtio_9p.h F: net/9p/ +9P FILE SYSTEM ROOTFS SUPPORT +R: Changbin Du +S: Supported +F: fs/9p/v9fsroot.c + A8293 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/fs/9p/Kconfig b/fs/9p/Kconfig index 09fd4a185fd215..71c5a49f9a277d 100644 --- a/fs/9p/Kconfig +++ b/fs/9p/Kconfig @@ -42,3 +42,9 @@ config 9P_FS_SECURITY If you are not using a security module that requires using extended attributes for file security labels, say N. + +config 9P_FS_ROOT + bool "9p root file system" + depends on 9P_FS=y + help + Enables root file system support over 9p protocol. diff --git a/fs/9p/Makefile b/fs/9p/Makefile index e7800a5c73959f..bc2a4ef100494b 100644 --- a/fs/9p/Makefile +++ b/fs/9p/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_9P_FS) := 9p.o 9p-$(CONFIG_9P_FSCACHE) += cache.o 9p-$(CONFIG_9P_FS_POSIX_ACL) += acl.o +9p-$(CONFIG_9P_FS_ROOT) += v9fsroot.o diff --git a/fs/9p/v9fsroot.c b/fs/9p/v9fsroot.c new file mode 100644 index 00000000000000..6c9f7e335c1a1f --- /dev/null +++ b/fs/9p/v9fsroot.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 9p root file system support + * + * Copyright (c) 2021 Changbin Du + */ +#include +#include +#include +#include +#include +#include +#include + +static char root_dev[2048] __initdata = ""; +static char root_opts[1024] __initdata = ""; + +/* v9fsroot=[,options] */ +static int __init v9fs_root_setup(char *line) +{ + char *s; + int len; + + if (strlen(line) >= 1) { + /* make s point to ',' or '\0' at end of line */ + s = strchrnul(line, ','); + /* len is strlen(unc) + '\0' */ + len = s - line + 1; + if (len > sizeof(root_dev)) { + pr_err("Root-V9FS: path too long\n"); + return 1; + } + strscpy(root_dev, line, len); + + if (*s) { + int n = snprintf(root_opts, + sizeof(root_opts), "%s", + s + 1); + if (n >= sizeof(root_opts)) { + pr_err("Root-V9FS: mount options string too long\n"); + root_opts[sizeof(root_opts)-1] = '\0'; + return 1; + } + } + } + + ROOT_DEV = Root_V9FS; + return 1; +} + +__setup("v9fsroot=", v9fs_root_setup); + +int __init v9fs_root_data(char **dev, char **opts) +{ + if (!root_dev[0]) { + pr_err("Root-V9FS: no rootdev specified\n"); + return -1; + } + + *dev = root_dev; + *opts = root_opts; + + return 0; +} diff --git a/include/linux/root_dev.h b/include/linux/root_dev.h index 4e78651371ba92..becd0ee2ff874d 100644 --- a/include/linux/root_dev.h +++ b/include/linux/root_dev.h @@ -9,6 +9,7 @@ enum { Root_NFS = MKDEV(UNNAMED_MAJOR, 255), Root_CIFS = MKDEV(UNNAMED_MAJOR, 254), + Root_V9FS = MKDEV(UNNAMED_MAJOR, 253), Root_RAM0 = MKDEV(RAMDISK_MAJOR, 0), Root_RAM1 = MKDEV(RAMDISK_MAJOR, 1), Root_FD0 = MKDEV(FLOPPY_MAJOR, 0), diff --git a/init/do_mounts.c b/init/do_mounts.c index a78e44ee6adb8d..952e91f6efcb8d 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -287,6 +287,8 @@ dev_t name_to_dev_t(const char *name) return Root_NFS; if (strcmp(name, "/dev/cifs") == 0) return Root_CIFS; + if (strcmp(name, "/dev/v9fs") == 0) + return Root_V9FS; if (strcmp(name, "/dev/ram") == 0) return Root_RAM0; #ifdef CONFIG_BLOCK @@ -536,6 +538,52 @@ static int __init mount_cifs_root(void) } #endif +#ifdef CONFIG_9P_FS_ROOT + +extern int v9fs_root_data(char **dev, char **opts); + +#define V9FSROOT_TIMEOUT_MIN 5 +#define V9FSROOT_TIMEOUT_MAX 30 +#define V9FSROOT_RETRY_MAX 5 + +static bool v9fs_should_retry(char *mount_opts) +{ + if (strstr(mount_opts, "trans=virtio") || strstr(mount_opts, "trans=fd")) + return false; + return true; +} + +static int __init mount_v9fs_root(void) +{ + char *root_dev, *root_data; + unsigned int timeout = V9FSROOT_TIMEOUT_MIN; + bool should_retry; + int try, err; + + err = v9fs_root_data(&root_dev, &root_data); + if (err != 0) + return 0; + + should_retry = v9fs_should_retry(root_data); + for (try = 1; ; try++) { + err = do_mount_root(root_dev, "9p", + root_mountflags, root_data); + if (err == 0) + return 1; + + if (!should_retry || try > V9FSROOT_RETRY_MAX) + break; + + /* Wait, in case the server refused us immediately */ + ssleep(timeout); + timeout <<= 1; + if (timeout > V9FSROOT_TIMEOUT_MAX) + timeout = V9FSROOT_TIMEOUT_MAX; + } + return 0; +} +#endif + void __init mount_root(void) { #ifdef CONFIG_ROOT_NFS @@ -552,6 +600,13 @@ void __init mount_root(void) return; } #endif +#ifdef CONFIG_9P_FS_ROOT + if (ROOT_DEV == Root_V9FS) { + if (!mount_v9fs_root()) + pr_err("VFS: Unable to mount root fs via 9p.\n"); + return; + } +#endif #ifdef CONFIG_BLOCK { int err = create_dev("/dev/root", ROOT_DEV);