Am Mi, den 11.02.2004 schrieb Kevin P. Fleming um 15:18: > > Aren't the disks the ATARAID is made of usually on the same controller? > > Then you only have to scan that one. > > Yes, that would be a simple optimization for this case. I was > envisioning the future tools to handle existing MD and LVM autodetection > and that would require looking at all potential block devices. I've been prototyping something as a shell script. The shell script needs to be run from hotplug (/etc/hotplug.d/defaults/ symlink) and udev (as first in udev.rules: PROGRAM="/path/to/bdev.sh %k %M:%m", NAME=""). adding devices is caught through udev, removing through hotplug (udev doesn't call programs when devices get removed). The udev part could be dropped but I thought that someone should tell udev if either the device should be created as usual (unhandled), not be created or created under a different name. Unfortunately the ignore rule in udev is currently broken and I found out that returning an empty string from the program makes udev try to mknod /dev and chmod /dev (and sets it to 666, argh) instead of ignoring the device creation. Hmm. Well, the script maintains a stupid database: /dev/.bdev/of/<devname> for devices that have been recognized by the script. The contents of the file is the major:minor pair and a list of compound devices that use the device. and /dev/.bdev/to/<major>:<minor> for created compound devices (or partitions). It contains the type of the device, the assigned name and some private data. /dev/.bdev/uptodate is created when all devices were scanned. BTW: I added some dumb locking (which is needed) using a package called dotlockfile. What happens? When the script is started it scans /dev/block/* for all devices and creates all /dev/.bdev/of/ files and touches uptodate. If uptodate already existed, it registers only the new device. Partitions detected by the kernel are completely ignored (and it tries to tell udev not to create the device nodes, currently broken). Then it calls the add_dev function. Here all checking should be done (ataraid, other raid, whatever) and as a last resort partition detection. It currently tries to do partition detection. It creates a temporary device node, calls sfdisk on it to dump the partition information, assigns the partition devices a name (the one sfdisk chooses), calls dmsetup to create the mapped device and registers the device in the /dev/.bdev/to/<major>:<minor> database and lists it in the original /dev/.bdev/of/<oldname> file. Now the kernel will call udev again with the dm device, bdev.sh will be called, register the device and see that a /dev/.bdev/to/<major>:<minor> exists for it. It will then create the device node with the name it registered in the database (and try to tell udev to not care). When a device is removed everything is done in reverse order. When a device is removed that has partitions a function notify_dev kicks in which probably wants to remove the mappings (partitions, etc...). With tail -f /tmp/log you can watch the debug messages. I've tried it using a LVM device which has a partition table on it. lvchange -a y /dev/vg/test The kernel will send a notify that a dm-2 254:2 was created. bdev.sh will find a partition table, call dmsetup to create a "part-dm-2p1" dm device and create a /dev/.bdev/to/254:3 with dm-2p1 as name. The kernel will call bdev.sh again with dm-3 254:3, bdev.sh sees the /dev/.bdev/to/254:3 and create the device node /dev/dm-2p1 dmsetup remove part-dm-2p1 will remove the device node and the database will be updated accordingly. Well, it's hard to explain my thoughts here because it's somewhat complicated... perhaps someone understands what I'm trying to prove here. :/
#!/bin/sh DATABASE=/dev/.bdev mk_dev() { NAME=$1-${2/\//-} ( /sbin/dmsetup -v create $NAME || \ /sbin/dmsetup remove $NAME &> /dev/null ) | \ sed -e '/minor:/!d;s/^[^0-9]*\([0-9]*\),[^0-9]*\([0-9]*\).*$/\1:\2/' } rm_dev() { NAME=$1-${2/\//-} /sbin/dmsetup remove $NAME &> /dev/null } mk_nod() { /bin/mknod /dev/$1 b ${2%:[0-9]*} ${2#[0-9]*:} } rm_nod() { rm -f /dev/$1 } #################################################################### mk_part() { echo part $1 $2 $3 $4 >> /tmp/log NDEV=$(echo "0 $3 linear $4 $2" | mk_dev part $1 $4) if [ -z "$NDEV" ]; then return 1 fi OF=$DATABASE/of/$5 TO=$DATABASE/to/$NDEV echo TYPE=part > $TO echo NAME=$1 >> $TO echo "LIST=\"\$LIST $NDEV\"" >> $OF } get_parts() { /sbin/sfdisk -dfqL /dev/$1 2> /dev/null | \ sed -s '/start=/!d;s/[=,]/ /g;s/^\/dev\/tmp-//' | \ awk '{ print $1 " " $4 " " $6 }' } check_parts() { get_parts $4 | \ while read PART START SIZE; do if [ "$SIZE" -gt 0 ]; then mk_part $PART $START $SIZE $3 $2 fi done } rm_part() { rm_dev part $1 } ################################################################## register_dev() { echo register $1 $2 >> /tmp/log echo DEV=$2 > $DATABASE/of/$1 } unregister_dev() { echo unregister $1 $2 >> /tmp/log rm -f $DATABASE/of/$1 } add_dev() { if [ -f $DATABASE/to/$2 ]; then source $DATABASE/to/$2 else NAME=$1 RET=1 fi echo add "$NAME $2" >> /tmp/log TMP=tmp-$NAME mk_nod $TMP $2 check_parts $1 $NAME $2 $TMP rm_nod $TMP } notify_dev() { echo notify $TYPE $1 $2 >> /tmp/log case $TYPE in part) rm_part $1 $2 ;; esac } remove_dev() { if [ -f $DATABASE/to/$2 ]; then source $DATABASE/to/$2 rm -f $DATABASE/to/$2 else NAME=$1 RET=1 fi echo remove $NAME $2 >> /tmp/log for i in $LIST; do if [ -e $DATABASE/to/$i ]; then source $DATABASE/to/$i notify_dev $NAME $i fi done } ################################################################## if [ ${DEVPATH} == ${DEVPATH#/block} ]; then exit 1 fi if [ -n "$2" ]; then ACTION=add KERNEL=$1 DEV=$2 if [ ! -f /sys/block/$KERNEL/dev ]; then exit 0 fi else if [ "$ACTION" != remove ]; then exit 0 fi KERNEL=${DEVPATH##*/} fi dotlockfile -r3 -p $DATABASE/lock || exit 1 RET=0 NAME="" echo action $ACTION >> /tmp/log case "$ACTION" in add) if [ ! -f $DATABASE/uptodate ]; then rm -Rf $DATABASE/of $DATABASE/to mkdir -p $DATABASE/of $DATABASE/to for i in /sys/block/*; do register_dev ${i#/sys/block/} $(<$i/dev) done touch $DATABASE/uptodate if [ -e $DATABASE/of/$KERNEL ]; then add_dev $KERNEL $DEV fi else if [ ! -e $DATABASE/of/$KERNEL ]; then register_dev $KERNEL $DEV if [ -e $DATABASE/of/$KERNEL ]; then add_dev $KERNEL $DEV fi fi fi ;; remove) if [ -e $DATABASE/of/$KERNEL ]; then LIST="" source $DATABASE/of/$KERNEL remove_dev $KERNEL $DEV unregister_dev $KERNEL $DEV fi ;; esac dotlockfile -u $DATABASE/lock if [ $RET -gt 0 ]; then exit 1 else if [ -n "$NAME" ]; then echo result $NAME >> /tmp/log case $ACTION in add) mk_nod $NAME $DEV ;; remove) rm_nod $NAME esac else echo ignore >> /tmp/log fi exit 0 fi