From 313c47f4fe4d07eb2969f429a66ad331fe2b3b6f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 14 Jan 2026 11:22:54 +0100 Subject: [PATCH] fs: use nullfs unconditionally as the real rootfs Remove the "nullfs_rootfs" boot parameter and simply always use nullfs. The mutable rootfs will be mounted on top of it. Systems that don't use pivot_root() to pivot away from the real rootfs will have an additional mount stick around but that shouldn't be a problem at all. If it is we'll rever this commit. This also simplifies the boot process and removes the need for the traditional switch_root workarounds. Suggested-by: Jeff Layton Signed-off-by: Christian Brauner --- .../filesystems/ramfs-rootfs-initramfs.rst | 24 ++----- fs/namespace.c | 64 ++++++------------- init/do_mounts.c | 20 ++---- init/do_mounts.h | 1 - 4 files changed, 32 insertions(+), 77 deletions(-) diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst index a8899f849e903..165117a721cee 100644 --- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst +++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst @@ -76,13 +76,8 @@ What is rootfs? --------------- Rootfs is a special instance of ramfs (or tmpfs, if that's enabled), which is -always present in 2.6 systems. Traditionally, you can't unmount rootfs for -approximately the same reason you can't kill the init process; rather than -having special code to check for and handle an empty list, it's smaller and -simpler for the kernel to just make sure certain lists can't become empty. - -However, if the kernel is booted with "nullfs_rootfs", an immutable empty -filesystem called nullfs is used as the true root, with the mutable rootfs +always present in Linux systems. The kernel uses an immutable empty filesystem +called nullfs as the true root of the VFS hierarchy, with the mutable rootfs (tmpfs/ramfs) mounted on top of it. This allows pivot_root() and unmounting of the initramfs to work normally. @@ -126,25 +121,14 @@ All this differs from the old initrd in several ways: program. See the switch_root utility, below.) - When switching another root device, initrd would pivot_root and then - umount the ramdisk. Traditionally, initramfs is rootfs: you can neither - pivot_root rootfs, nor unmount it. Instead delete everything out of - rootfs to free up the space (find -xdev / -exec rm '{}' ';'), overmount - rootfs with the new root (cd /newmount; mount --move . /; chroot .), - attach stdin/stdout/stderr to the new /dev/console, and exec the new init. - - Since this is a remarkably persnickety process (and involves deleting - commands before you can run them), the klibc package introduced a helper - program (utils/run_init.c) to do all this for you. Most other packages - (such as busybox) have named this command "switch_root". - - However, if the kernel is booted with "nullfs_rootfs", pivot_root() works + umount the ramdisk. With nullfs as the true root, pivot_root() works normally from the initramfs. Userspace can simply do:: chdir(new_root); pivot_root(".", "."); umount2(".", MNT_DETACH); - This is the preferred method when nullfs_rootfs is enabled. + This is the preferred method for switching root filesystems. Populating initramfs: --------------------- diff --git a/fs/namespace.c b/fs/namespace.c index a44ebb2f1161d..53d1055c18257 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -75,17 +75,6 @@ static int __init initramfs_options_setup(char *str) __setup("initramfs_options=", initramfs_options_setup); -bool nullfs_rootfs = false; - -static int __init nullfs_rootfs_setup(char *str) -{ - if (*str) - return 0; - nullfs_rootfs = true; - return 1; -} -__setup("nullfs_rootfs", nullfs_rootfs_setup); - static u64 event; static DEFINE_XARRAY_FLAGS(mnt_id_xa, XA_FLAGS_ALLOC); static DEFINE_IDA(mnt_group_ida); @@ -4593,10 +4582,9 @@ int path_pivot_root(struct path *new, struct path *old) * pointed to by put_old must yield the same directory as new_root. No other * file system may be mounted on put_old. After all, new_root is a mountpoint. * - * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem - * unless the kernel was booted with "nullfs_rootfs". See - * Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives - * in this situation. + * The immutable nullfs filesystem is mounted as the true root of the VFS + * hierarchy. The mutable rootfs (tmpfs/ramfs) is layered on top of this, + * allowing pivot_root() to work normally from initramfs. * * Notes: * - we don't move root/cwd if they are not at the root (reason: if something @@ -5993,49 +5981,39 @@ static void __init init_mount_tree(void) struct path root; /* - * When nullfs is used, we create two mounts: + * We create two mounts: * * (1) nullfs with mount id 1 * (2) mutable rootfs with mount id 2 * * with (2) mounted on top of (1). */ - if (nullfs_rootfs) { - nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL); - if (IS_ERR(nullfs_mnt)) - panic("VFS: Failed to create nullfs"); - } + nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL); + if (IS_ERR(nullfs_mnt)) + panic("VFS: Failed to create nullfs"); mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", initramfs_options); if (IS_ERR(mnt)) panic("Can't create rootfs"); - if (nullfs_rootfs) { - VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1); - VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2); + VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1); + VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2); - /* The namespace root is the nullfs mnt. */ - mnt_root = real_mount(nullfs_mnt); - init_mnt_ns.root = mnt_root; + /* The namespace root is the nullfs mnt. */ + mnt_root = real_mount(nullfs_mnt); + init_mnt_ns.root = mnt_root; - /* Mount mutable rootfs on top of nullfs. */ - root.mnt = nullfs_mnt; - root.dentry = nullfs_mnt->mnt_root; + /* Mount mutable rootfs on top of nullfs. */ + root.mnt = nullfs_mnt; + root.dentry = nullfs_mnt->mnt_root; - LOCK_MOUNT_EXACT(mp, &root); - if (unlikely(IS_ERR(mp.parent))) - panic("VFS: Failed to mount rootfs on nullfs"); - scoped_guard(mount_writer) - attach_mnt(real_mount(mnt), mp.parent, mp.mp); + LOCK_MOUNT_EXACT(mp, &root); + if (unlikely(IS_ERR(mp.parent))) + panic("VFS: Failed to mount rootfs on nullfs"); + scoped_guard(mount_writer) + attach_mnt(real_mount(mnt), mp.parent, mp.mp); - pr_info("VFS: Finished mounting rootfs on nullfs\n"); - } else { - VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 1); - - /* The namespace root is the mutable rootfs. */ - mnt_root = real_mount(mnt); - init_mnt_ns.root = mnt_root; - } + pr_info("VFS: Finished mounting rootfs on nullfs\n"); /* * We've dropped all locks here but that's fine. Not just are we diff --git a/init/do_mounts.c b/init/do_mounts.c index 675397c8a7a4a..df6847bcf1f25 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -493,21 +493,15 @@ void __init prepare_namespace(void) out: devtmpfs_mount(); - if (nullfs_rootfs) { - if (init_pivot_root(".", ".")) { - pr_err("VFS: Failed to pivot into new rootfs\n"); - return; - } - if (init_umount(".", MNT_DETACH)) { - pr_err("VFS: Failed to unmount old rootfs\n"); - return; - } - pr_info("VFS: Pivoted into new rootfs\n"); + if (init_pivot_root(".", ".")) { + pr_err("VFS: Failed to pivot into new rootfs\n"); return; } - - init_mount(".", "/", NULL, MS_MOVE, NULL); - init_chroot("."); + if (init_umount(".", MNT_DETACH)) { + pr_err("VFS: Failed to unmount old rootfs\n"); + return; + } + pr_info("VFS: Pivoted into new rootfs\n"); } static bool is_tmpfs; diff --git a/init/do_mounts.h b/init/do_mounts.h index fbfee810aa89e..6069ea3eb80d7 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -15,7 +15,6 @@ void mount_root_generic(char *name, char *pretty_name, int flags); void mount_root(char *root_device_name); extern int root_mountflags; -extern bool nullfs_rootfs; static inline __init int create_dev(char *name, dev_t dev) { -- 2.47.3