3

Arch Linux is UEFI booted from USB flash drive with GPT and three partitions:

  1. EFI (vfat file system)
  2. root (btrfs file system, was converted from ext4)
  3. 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).

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

  1. I have tried to remove udev -> boot error
  2. I have tried to add the btrfs module -> boot error
  3. I have added btrfs hook -> boot error
  4. Changed root=PARTUUID= to root=UUID= notation -> boot error
  5. adding paramters rootfstype=btrfs -> boot error
  6. rootdelay=0 -> boot error
  7. rootdelay=10 -> boot error
  8. 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.

2

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_handler add a run_hook is used to poll until blkid returns a value, or (a timeout of) 10 seconds has passed. Thus the "udev" hook can be removed from mkinitcpio config.

Notes

  • This solution was created with the help of falconindy.
  • There was an error message in the early boot phase involving fsck. To remove that message the without-udev hook has been rewritten to use a run_hook instead of a mount_handler. The newer code is even shorter.

$ cat /usr/lib/initcpio/hooks/without-udev
#!/bin/ash
# Minimal initramfs files are created without udev.
# This hooks provides a polling disk mount replacement for udev.
# Udev hook can be removed, resulting in smaller initramfs files.

run_hook () {
    local dev timeout sleepval device=$root
    # if udev is running then exit
    [ "$udevd_running" -eq 1 ] && return
    # try for (timeout * sleepval =) 10 seconds to handle slow (USB) devices
    timeout=1000
    sleepval=0.01

    case $device in
        # label to resolve, when resolved the kernel block device also exists
        UUID=*|LABEL=*|PARTUUID=*|PARTLABEL=*)
            while [ $timeout -gt 0 ]; do
                timeout=$((timeout - 1))
                dev=$(blkid -lt "$device" -o device)
                [ -n "$dev" ] && timeout=0 || sleep $sleepval
            done
            ;;
        # kernel named block device, poll for existence
        /dev/*)
            while [ $timeout -gt 0 ]; do
                timeout=$((timeout -1))
                if [ -b "$device" ]; then
                    dev=$device
                    timeout=0
                else
                    sleep $sleepval
                fi
            done
            ;;
    esac
}

# vim:set syntax=sh:

$ cat /usr/lib/initcpio/install/without-udev
#!/bin/bash

build() {
    add_runscript
}

help() {
    cat <<HELPEOF
This hook provides support for booting without the "udev" hook,
including support for UUID, LABEL, PARTUUID, PARTLABEL.
HELPEOF
}

# vim: set ft=sh ts=4 sw=4 et:
  • Should I be getting ERROR: Device 'PARTUUID:XXXX..' not found. Skipping fsck. with this workaround? I boot fine, but that error gets listed. – ZeroPhase May 1 '17 at 13:02
  • @ZeroPhase I my initcpio-without-udev setup, I do indeed also get the message ERROR: Device 'PARTUUID:XXXX..' not found. Skipping fsck. during boot. – Pro Backup May 10 '17 at 19:20
  • That's not going to cause issues with fsck? – ZeroPhase May 10 '17 at 22:23
  • @ZeroPhase I don't think that ERROR: Device PARTUUID message will cause issues with fsck. A little later in code fsck is done again on /dev/sd(a). That is the line prepended with the comment: # call fsck again because in main loop $root device was unknown at that time. – Pro Backup May 11 '17 at 11:14
  • 1
    @ZeroPhase The without-udev hook code has been updated to no longer output the ERROR: Device 'PARTUUID:XXXX..' not found. Skipping fsck. message. As a side effect the code is even shorter. – Pro Backup May 11 '17 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.