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 <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-01-14 11:22:54 +01:00
parent 7416634fd6
commit 313c47f4fe
No known key found for this signature in database
GPG key ID: 91C61BC06578DCA2
4 changed files with 32 additions and 77 deletions

View file

@ -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:
---------------------

View file

@ -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,24 +5981,21 @@ 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");
}
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);
@ -6029,13 +6014,6 @@ static void __init init_mount_tree(void)
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;
}
/*
* We've dropped all locks here but that's fine. Not just are we

View file

@ -493,7 +493,6 @@ 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;
@ -503,11 +502,6 @@ out:
return;
}
pr_info("VFS: Pivoted into new rootfs\n");
return;
}
init_mount(".", "/", NULL, MS_MOVE, NULL);
init_chroot(".");
}
static bool is_tmpfs;

View file

@ -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)
{