[arch-projects] [mkinitcpio][PATCH] shutdown: disassemble devices on shutdown v2

Dave Reisner d at falconindy.com
Sun Mar 25 15:11:25 EDT 2012

sysfs contains enough information about block devices to be able to
determine the order of stacked devices such as lvm, raid, or crypto. By
looking at the device symlinks from the holders/ attributes of a block
device, we can walk down each device chain until we reach the most
descendant device.

For each of these devices at the end of a chain, detect its type and
perform the appropriate action to disassemble it. Then, walk back up the
device chain, disassembling each parent device.

To save ourselves some pain and make sure we're fairly accurate, lsblk
is brought in for detection of device types.

Thanks-To: Florian Pritz <bluewind at xinu.at>
Thanks-To: Tom Gundersen <teg at jklm.no>
Signed-off-by: Dave Reisner <dreisner at archlinux.org>
v2 changes: This gets rid of the get_child and get_parent logic and replaces it
with a recursive function which walks down the device chain. When it gets to
the bottom, it unwinds, and disassembles as it walks back up -- much simpler,
and much easier to maintain.

Thanks to Florian for pointing out a better way of getting the containing
vgname of an LV, and to Tom for telling me my initial implementation sucked.

You can stop reading here you don't care about some editorial remarks.

Re-tested on all the root configs I mentioned in v1 with a new intentionally
absurd addition which splits a raid10 array into 2 partitions, each of which is
labelled as a PV and VG. Each VG has a single LV, one of which is encrypted.
lsblk shows one of the raid10 members as...

NAME                             TYPE   MOUNTPOINT
vde                              disk
└─md127                          raid10
  ├─md127p1                      md
  │ └─VolGroup01-volroot (dm-1)  lvm    /
  └─md127p2                      md
    └─VolGroup02-volhome (dm-0)  lvm
      └─home (dm-2)              crypt  /home

And we correctly tear this down as:

lvm vgchange -an VolGroup01
lvm vgchange -an VolGroup02
mdadm --stop md127

init takes care of teardown for the encrypted home since it was responsible for
the setup, but the logic is indeed robust enough to handle this extra step.

I still have no idea how dmraid works.

 install/shutdown |    2 +-
 shutdown         |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/install/shutdown b/install/shutdown
index 5b56f17..600209e 100644
--- a/install/shutdown
+++ b/install/shutdown
@@ -1,7 +1,7 @@
 build() {
-    BINARIES='cp'
+    BINARIES='cp lsblk'
     add_file "/usr/lib/initcpio/shutdown" "/shutdown"
diff --git a/shutdown b/shutdown
index aad0198..2ed9f7f 100755
--- a/shutdown
+++ b/shutdown
@@ -1,5 +1,57 @@
+# teardown a single device by node name
+#   $1: device node name, e.g. md0, dm-2
+stop_device() {
+    local devtype= devname=
+    # the device must still be active
+    [ -e "/sys/class/block/$1" ] || return 1
+    # this can sometimes fail on stopped md devices, when the
+    # sysfs node doesn't go away as quickly as i'd like it to.
+    devtype=$(lsblk -drno TYPE "/dev/$1" 2>/dev/null) || return 1
+    case $devtype in
+        crypt)
+            read devname <"$1/dm/name"
+            cryptsetup luksClose "$devname"
+            ;;
+        dm|lvm)
+            # disassemble the parent VG
+            read devname <"$1/dm/name"
+            lvm lvdisplay -c "/dev/mapper/$devname" | {
+                IFS=: read _ vgname _
+                lvm vgchange -an "$vgname"
+            }
+            ;;
+        raid*)
+            mdadm --stop "/dev/$1"
+            ;;
+        dmraid)
+            # XXX: i have no idea how dmraid works
+            dmraid -an
+            ;;
+        # silently ignore unstacked devices
+    esac
+# recursely disassemble a device chain
+#   $1: device node name, e.g. md0, dm-2
+disassemble() {
+    for holder in "$1"/holders/*; do
+        if [ ! -e "$holder" ]; then
+            # end of the chain, recurse back up
+            stop_device "$1"
+            return
+        fi
+        disassemble "${holder##*/}"
+        stop_device "$1"
+    done
+printf '%s\n' "Unmounting all devices."
+# unmount everything
 findmnt -Rruno TARGET /oldroot | awk '
 BEGIN { i = 0 }
 ! /^\/(proc|dev|sys)/ {
@@ -15,6 +67,16 @@ END {
     umount -l "$mount"
+printf '%s\n' "Disassembling stacked devices."
+# chdir, so that we can avoid a lot of path chopping
+cd /sys/class/block
+# iterate over devices with holders
+for part in */holders/*; do
+    [ -e "$part" ] && disassemble "${part%%/*}"
 case $1 in
         "$1" -f

More information about the arch-projects mailing list