FS#10509 - parseblock can't handle by-uuid devices
Attached to Project:
Arch Linux
Opened by Bryan Ischo (bji) - Monday, 26 May 2008, 18:21 GMT
Last edited by Greg (dolby) - Tuesday, 10 June 2008, 12:12 GMT
Opened by Bryan Ischo (bji) - Monday, 26 May 2008, 18:21 GMT
Last edited by Greg (dolby) - Tuesday, 10 June 2008, 12:12 GMT
|
Details
Description:
The parseblock program provided in the klibc-extras 2.4-1 package does not handle by-uuid devices. I boot Arch Linux from a USB flash drive and the initcpio scripts attempt to create the root device file if it doesn't exist otherwise. In my particular situation, the solution is to force the scripts to wait until the root device is created by udevd, but the initcpio scripts as they are written allow one to not use udevd and instead the device is created by the scripts with the help of the parseblock program. In my case, since my root device was specified as a by-uuid device: root=/dev/disk/by-uuid/a65cbce9-59dd-4ca6-bf61-3f7ca7289a50 When the initcpio's init script got to its logic that noticed that this device did not exist, it tried to use parseblock to determine what the name and major and minor numbers of the device would be. I am not entirely sure what parseblock is supposed to do; my original assumption was that it simply provided mappings from the 'known' device names (/dev/sdc1, dev/sdd2, etc) to major and minor numbers, and that it simply didn't know what to do with my by-uuid device name. However, a little more testing with parseblock leads me to believe that it just reports the major and minor numbers of existing device files, so at this point I don't quite understand what the purpose of parseblock is in the initcpio scripts. I base my deduction of the purpose of parsebock on its behavior in my running Arch Linux system. If I run /usr/lib/klibc/bin/parseblock and give it the path of any existing block device, it reports the name and major and minor numbers of that device. If I run it and give it the path of any block device which *does not* exist but which I think it ought to be able to guess what the major and minor numbers of should be, it returns "UNKNOWN". For example: bji$ ls -l /dev/sd* brw-rw---- 1 root disk 8, 0 2008-05-26 10:47 /dev/sda brw-rw---- 1 root disk 8, 1 2008-05-26 10:47 /dev/sda1 brw-rw---- 1 root disk 8, 2 2008-05-26 10:47 /dev/sda2 brw-rw---- 1 root disk 8, 5 2008-05-26 10:47 /dev/sda5 brw-rw---- 1 root disk 8, 16 2008-05-26 10:47 /dev/sdb brw-rw---- 1 root disk 8, 17 2008-05-26 10:47 /dev/sdb1 brw-rw---- 1 root disk 8, 18 2008-05-26 10:47 /dev/sdb2 brw-rw---- 1 root storage 8, 32 2008-05-26 10:47 /dev/sdc brw-rw---- 1 root storage 8, 33 2008-05-26 10:47 /dev/sdc1 brw-rw---- 1 root storage 8, 34 2008-05-26 10:47 /dev/sdc2 brw-rw---- 1 root storage 8, 48 2008-05-26 10:47 /dev/sdd brw-rw---- 1 root storage 8, 64 2008-05-26 10:47 /dev/sde brw-rw---- 1 root storage 8, 80 2008-05-26 10:47 /dev/sdf brw-rw---- 1 root storage 8, 96 2008-05-26 10:47 /dev/sdg bji$ for i in {a,b,c,d,e,f,g,h,i,j}; do /usr/lib/klibc/bin/parseblock /dev/sd${i}1; done BLOCKNAME="/dev/sda1" BLOCKDEVICE="8 1" BLOCKNAME="/dev/sdb1" BLOCKDEVICE="8 17" BLOCKNAME="/dev/sdc1" BLOCKDEVICE="8 33" BLOCKNAME="/dev/root" BLOCKDEVICE="8 49" BLOCKNAME="/dev/root" BLOCKDEVICE="8 65" BLOCKNAME="/dev/root" BLOCKDEVICE="8 81" BLOCKNAME="/dev/root" BLOCKDEVICE="8 97" BLOCKNAME="unknown" BLOCKDEVICE="" BLOCKNAME="unknown" BLOCKDEVICE="" BLOCKNAME="unknown" BLOCKDEVICE="" Here we can see that what parseblock did is: * For any device file which already existed, it reported its values * For device files for which the whole-disk device (e.g. /dev/sdd) existed but the specific partition device (i.e. /dev/sdd1) didn't exist, it reported the BLOCKNAME as /dev/root and the major and minor numbers as what they *would be* if the device existed * For any device which does not exist, it reported "unknown" I guess I expected that parseblock's function to simply be to do a reverse-mapping from device file paths to major and minor numbers. This reverse mapping could simply be extracted from the LINUX ALLOCATED DEVICES (2.6+ version) list, at: http://www.lanana.org/docs/device-list/devices-2.6+.txt But it doesn't quite appear that this is what parseblock does, I guess it is able to figure out what device numbers would be for nonexistent partition devices only if the whole-disk device already existed. And it can't do anything at all with by-uuid devices, even using the full reverse mapping available from the LINUX ALLOCATED DEVICES list, because the by-uuid devices are unique and don't have a known reverse mapping. Unless parseblock has some way to do its own device detection and uuid name assignment which I doubt it has. So the gist of this bug is this: parseblock cannot detect the major and minor number of by-uuid devices, and so the initcpio scripts which depend on it do not work for root devices which are specified using by-uuid devices - which, I believe, is going to become an issue of greater and greater importance as highlighted by this document: http://wiki.archlinux.org/index.php/Persistent_block_device_naming I am not sure that there is a solution to this really, but my investigation has I think at least revealed that parseblock could be improved; it doesn't need to require the whole-disk devices exist before being able to report on partition devices, in fact it doesn't need to probe the system at all, it could just work based entirely off of the LINUX ALLOCATED DEVICES document. Additional info: * package version(s) * config and/or log files etc. klibc-extras 2.4-1 mkinitcpio 0.5.18.1-1 Steps to reproduce: Boot arch linux from a usb flash drive with a by-uuid root device. You need to use a usb flash drive that takes time to 'settle'. If udevd has not yet made the root device for the usb flash drive before the initial ram disk's init script tries to create it using parseblock to detect the major and minor numbers for the device, then the init script will fail. |
This task depends upon
#TODO do symlinks to devices work at this point? I hope so
if device exists
> symlink to /dev/root
else
> poll for the device for N seconds...
> if device exists
> > symlink to /dev/root
> else
> > try parseblock
Note the '>' added because flyspray sucks at indentation
Your logic seems fine, although if it were me I think I'd break the process up into a couple of steps:
# Step 1: Try to poll or wait for the device if necessary
if root device does not exist
> if rootpoll is set
> > poll for the root device for N seconds
> else if rootdelay is set
> > sleep for N seconds
> fi
fi
# Step 2: Try parseblock as a last resort if necessary
if root device does not exist
> parseblock
> if parseblock succeeded
> > make the device
> fi
fi
# Step 3: Make the /dev/root link or fail
if root device exists
> link root device to /dev/root
else
> error "Root device does not exist"
fi
I would replace the error with something sufficiently descriptive to let the user know what to try. Something like:
Error - root device ${root} does not exist and could not be created. Please see ${link to Persistent_block_device_naming page} for information on how to select your root device so that it will work regardless of the order in which your system's devices are discovered. You might also try the rootpoll kernel parameter to cause the boot process to wait for your device to be ready before proceeding, which can help if your device takes time to be recognized (as most USB disks do).
Do you have any comment on my suggestion that the parseblock program should just be a reverse mapping of device names to major/minor numbers using the LINUX ALLOCATED DEVICES document as a reference? It looks to me like right now it's trying to do some computation based on what it's finding on the drive, and I don't quite see the point of that. But I do see the point of a program that maps the namespaces of known device names that the filesystem uses back to the major/minor numbers that the kernel uses, for the purposes of figuring out how to create the device at bootup time.
Additionally, see this comment:
/*
* parseblock: parse a block device node name. Made for
* early userspace under klibc. Most of this code is taken
* from the kernel init/ and kinit source.
*/
It actually uses sysfs to get the proper device IDs and numbers, however. Sysfs is probably more thorough than a text document.
However, I do think it's weird that parseblock can't figure out what the major/minor numbers for /dev/sdi1 would be, when the docs I referenced show that it would be major 8, minor 145.
I assume that testing has been done to show that when the initial ramdisk is run on a system that isn't booted with udevd, and thus parseblock must be invoked to figure out what the device numbers of the root device to create should be, that parseblock works correctly for the standard disk devices (i.e. /dev/hd{a,b,c,d}{1,2,3,4,5,6,7,8}, /dev/sd{a,b,c,d,e,f,g}{1,2,3,4,5,6,7,8}. It troubles me that parseblock on my system didn't know what /dev/sdi1 would be, but probably nobody would be booting Arch Linux off of their 9th drive anyway ...