Monday, October 17, 2022

/dev/root, what is it?

If you are in the Linux embedded system world, you may have noticed in the mount-table, the presence of this /dev/root device backing the root file system.

In desktop distribution the /dev/root presence is by far less frequent, but in some unusual environment, you might find this either, and when it is there, it is frequent to find it as a symbolic link to a real block device.

On the other hands, in embedded system it is not unusual to have an even more weird situation:

Having /dev/root mounted and not have anything at the /dev/root position (no file named root in the /dev directory).

How is this possible?


Under the hood

It is important, for understanding these phenomena, looking at what’s under the hood:

This image represents the kernel init process to the point where the /dev/root device is mounted.


The function checks if a device is passed through the command line ("saved_root_name" is set to the boot command line "root="), if not, tries the initramfs way. In the end, if ROOT_DEV that is the rootfs device is null, and no rootwait is specified it invokates the mount_root().

The next step is calling mount_root()


void __init prepare_namespace(void)

{

if (root_delay) {

printk(KERN_INFO "Waiting %d sec before mounting root device...\n",

      root_delay);

ssleep(root_delay);

}


/*

* wait for the known devices to complete their probing

*

* Note: this is a potential source of long boot delays.

* For example, it is not atypical to wait 5 seconds here

* for the touchpad of a laptop to initialize.

*/

wait_for_device_probe();


md_run_setup();


if (saved_root_name[0]) {

root_device_name = saved_root_name;

if (!strncmp(root_device_name, "mtd", 3) ||

    !strncmp(root_device_name, "ubi", 3)) {

mount_block_root(root_device_name, root_mountflags);

goto out;

}

ROOT_DEV = name_to_dev_t(root_device_name);

if (strncmp(root_device_name, "/dev/", 5) == 0)

root_device_name += 5;

}


if (initrd_load())

goto out;


/* wait for any asynchronous scanning to complete */

if ((ROOT_DEV == 0) && root_wait) {

printk(KERN_INFO "Waiting for root device %s...\n",

saved_root_name);

while (driver_probe_done() != 0 ||

(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)

msleep(5);

async_synchronize_full();

}


mount_root();

out:

devtmpfs_mount();

init_mount(".", "/", NULL, MS_MOVE, NULL);

init_chroot(".");

}


The function checks if a device is passed through the command line ("saved_root_name" is set to the boot command line "root="), if not, tries the initramfs way. In the end, if ROOT_DEV that is the rootfs device is null, and no rootwait is specified it invokates the mount_root().


The next step is calling mount_root()

void __init mount_root(void)

{

#ifdef CONFIG_ROOT_NFS

if (ROOT_DEV == Root_NFS) {

if (!mount_nfs_root())

printk(KERN_ERR "VFS: Unable to mount root fs via NFS.\n");

return;

}

#endif

#ifdef CONFIG_CIFS_ROOT

if (ROOT_DEV == Root_CIFS) {

if (!mount_cifs_root())

printk(KERN_ERR "VFS: Unable to mount root fs via SMB.\n");

return;

}

#endif

if (ROOT_DEV == 0 && root_device_name && root_fs_names) {

if (mount_nodev_root() == 0)

return;

}

#ifdef CONFIG_BLOCK

{

int err = create_dev("/dev/root", ROOT_DEV);


if (err < 0)

pr_emerg("Failed to create /dev/root: %d\n", err);

mount_block_root("/dev/root", root_mountflags);

}

#endif

}

In this function, a certain number of #ifcfg are visible. Apparently, the rootfs can exist if all these switches are off. In this case, the rootfs is mounted by using its name root_device_name and its fstype root_device_name. In the vast majority of cases, when the rootfs is backed by a block device, the ROOT_DEV has already a value and the CONFIG_BLOCK part of the code is executed.

Here a couple of points are visible:

  • The kernel creates the /dev/root device

  • After that, it mounts the rootfs


But if the device is not yet mounted, where is this /dev/root actually created?

The answer is that the kernel had a special instance of the ramfs called rootfs mounted on the / at the time it created the /dev/root, and therefore, when the new filesystem is mounted over the / directory, unless it already contains the device, /dev/root is simply not present.

Considering that tempfs is typically mounted over the /dev directory, init scripts need to make sure that the /dev/root exists. 

If none creates it, the system has the proc/mountinfo table populated with the /dev/root device, but it is not backed by any file in the /dev directory.

1 comment:

  1. I have one problem. /dev/root creation failing with -2. what is missing?

    ReplyDelete