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
Task Type Bug Report
Category System
Status Closed
Assigned To Aaron Griffin (phrakture)
Thomas Bächler (brain0)
Architecture All
Severity Medium
Priority Normal
Reported Version 2007.08-2
Due in Version Undecided
Due Date Undecided
Percent Complete 100%
Votes 0
Private No

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

Closed by  Greg (dolby)
Tuesday, 10 June 2008, 12:12 GMT
Reason for closing:  Not a bug
Comment by Aaron Griffin (phrakture) - Tuesday, 27 May 2008, 18:12 GMT
Haha, first time I ever did a 'tl;dr' to a bug report. /me goes back to the top to read.
Comment by Aaron Griffin (phrakture) - Tuesday, 27 May 2008, 18:16 GMT
Well, ok. Let's look at this from a different angle. Your point about the scripts not using udevd for root devices is fair. How about we change the logic to something of the form:

#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
Comment by Bryan Ischo (bji) - Tuesday, 27 May 2008, 20:09 GMT
Well I didn't know what 'tl;dr' meant until I did a google search on it. Sorry my bug report was so long but I'm just trying to be thorough. Probably it took me longer to read the initcpio scripts and figure out what they were trying to do than it took you to read this bug report, if that makes you feel any better.

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.
Comment by Bryan Ischo (bji) - Tuesday, 27 May 2008, 20:10 GMT
(note that by 'based what it's finding on the drive' I meant 'what it's finding in /dev')
Comment by Aaron Griffin (phrakture) - Tuesday, 27 May 2008, 20:16 GMT
parseblock is currently based on the kinit code which does the same thing. I don't really think mucking with it too much is a good idea, especially considering that we can replace it with udev and some polling. I trust udev's device loading much more than code I write myself.

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.
Comment by Bryan Ischo (bji) - Tuesday, 27 May 2008, 20:54 GMT
Agreed on all counts.

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 ...

Comment by Aaron Griffin (phrakture) - Tuesday, 27 May 2008, 20:58 GMT
That's because it parses sysfs, and sysfs only has info for devices on your system. If you HAVE an sdi1, it will be able to do it.

Loading...