Arch Linux is UEFI booted from USB flash drive with GPT and three partitions:
- EFI (vfat file system)
- root (btrfs file system, was converted from ext4)
- home (btrfs file system, was converted from ext4)
The btrfs partitions have no sub-volumes and are on a single disk (the USB flash memory drive). LVM is not in use here.
Task
Trying to create a minimal initramfs by removing udev and a lot of other hooks. Optimizing Bootup With mkinitcpio was used as inspiration too.
The effective mkinitcpio
hooks are: base, autodetect and modconf.
Btrfs hook
The btrfs hook is not enabled because mkinitcpio
hooks documentation lists for btrfs hook:
This hook is not required for using Btrfs on a single device.
Regression
- I have tried to remove udev -> boot error
- I have tried to add the btrfs module -> boot error
- I have added btrfs hook -> boot error
- Changed root=PARTUUID= to root=UUID= notation -> boot error
- adding paramters rootfstype=btrfs -> boot error
- rootdelay=0 -> boot error
- rootdelay=10 -> boot error
- Mounting using /dev/sda2 from emergency shell -> ok
Error
Only after inserting udev or systemd hooks the system will root into the btrfs root partition, otherwise this error appears:
ERROR: device 'PARTUUID=c2...c13' not found. Skipping fsck.
:: mounting 'PARTUUID=c2...c13' on real root
mount: can't find 'PARTUUID=c2...c13'
You are now being dropped into an emergency shell.
Runtime init debug/log output
Enabling boot parameters rd.debug
and rd.log
shows that "premount" calls the resolve_device function and that returns an empty lookup.
resolve_device PARTUUID=c2...c13
local major minor dev tag device=PARTUUID=c2...c13
blkid -lt PARTUUID=c2...c13 -o device
dev=
The last empty dev is causing the device not found error.
initramfs mount command
mount_handler=default_mount_handler
...
# Mount root at /new_root
"$mount_handler" /new_root
source: https://git.archlinux.org/mkinitcpio.git/tree/init
default_mount_handler() {
msg ":: mounting '$root' on real root"
mount ${rootfstype:+-t $rootfstype} -o ${rwopt:-ro}${rootflags:+,$rootflags} "$root" "$1"
source: https://git.archlinux.org/mkinitcpio.git/tree/init_functions#n375
initramfs mount version
[rootfs ]# mount -V
mount from util-linux 2.29.2 (libmount 2.29.2: btrfs, assert, debug)
initramfs contents
$ lsinitcpio -a /boot/initramfs-linux-tiny.img
==> Image: /boot/initramfs-linux-tiny.img
==> Created with mkinitcpio 23
==> Kernel: 4.10.3-1-ARCH
==> Size: 3.53 MiB
==> Compressed with: lz4 -l
-> Uncompressed size: 8.32 MiB (.424 ratio)
-> Estimated extraction time: 0.028s
==> Included modules:
ahci [explicit] hid-generic [explicit] raid6_pq usbcore
atkbd [explicit] i8042 [explicit] scsi_mod usbhid [explicit]
btrfs [explicit] libahci sd_mod [explicit] xhci-hcd
crc32c-intel [explicit] libata serio xhci-pci [explicit]
crc32c_generic libcrc32c serio_raw [explicit] xor
ehci-hcd libps2 uas [explicit]
ehci-pci [explicit] ohci-hcd usb-common
hid ohci-pci [explicit] usb-storage
==> Included binaries:
blkid busybox dosfsck fsck fsck.vfat kmod mount switch_root
The emergency shell its blkid
command lists the correct (PART)UUID value. Could the mounting using using (PART)UUID fail because there is no /dev/disk/
?
Question
What is necessary to boot into a non-raid non-subvolume single-drive root btrfs partition located on a USB flash drive without udev?
PS This error might be caused by a RACE condition, UUID/PARTUUID not yet available when initramfs/init
executes the mount ... UUID=...
command.
Best Answer
Cause
In version 23, the mkinitcpio resolve_device() function is called only once. When at execution time the drive labels are not yet read,
blkid
can't lookup the kernel drive (/dev/...
) name for the requested label.Solution
By adding the "without-udev" hook, as listed below, the resolve_device function is left untouched. Though standard available mkinitcpio functionality to
override the mount_handleradd a run_hook is used to poll untilblkid
returns a value, or (a timeout of) 10 seconds has passed. Thus the "udev" hook can be removed from mkinitcpio config.Notes
run_hook
instead of a. The newer code is even shorter.mount_handler