[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#678696: Event based block device handling (fixes USB and nested devices problem)



Package: initramfs-tools
Version: 0.106
Severity: wishlist
Tags: patch

Hi,

the attached patch adds an event based loop for block devices to the
init script. New blockdevices are recorded in
/run/initramfs/block-events by an udev rule as they appear. The init
script repeadately waits for that and then calls
/scripts/local-block/* with a list of new devices storedin NEWDEVS
until $ROOT and $resume (if set) exists or a timeout is reached.

This fixes the problem that USB devices take too long to be
discovered and crypto, raid, lvm or multipath can't be started on
them. It also adds support for arbitrary nestings of them, e.g. raid5
over raid1.

For the event loop I needed the $resume device translated to the
device node earlier. This was previously done in
scripts/local-premount/resume. It was essentially the same code used
to translate the $ROOT so I created a new function translate_device()
in script/functions and used that for both $ROOT and $resume.

Note: hooks/block-event and scripts/block-event are executable, not
represented in the patch.

MfG
	Goswin

-- System Information:
Debian Release: wheezy/sid
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: amd64 (x86_64)

Kernel: Linux 3.2.0-2-amd64 (SMP w/4 CPU cores)
Locale: LANG=C, LC_CTYPE=de_DE (charmap=ISO-8859-1)
Shell: /bin/sh linked to /bin/dash

Versions of packages initramfs-tools depends on:
ii  cpio               2.11-7
ii  klibc-utils        2.0~rc3-1
ii  module-init-tools  3.16-1
ii  udev               175-3.1

Versions of packages initramfs-tools recommends:
ii  busybox  1:1.19.3-5

Versions of packages initramfs-tools suggests:
ii  bash-completion  1:1.99-3

-- no debconf information
Description: event based loop
 This patch adds an event based loop for block devices to the init
 script. New blockdevices are recorded in /run/initramfs/block-events
 by an udev rule as they appear. The init script repeadately waits for
 that and then calls /scripts/local-block/* with a list of new devices
 storedin NEWDEVS until $ROOT and $resume (if set) exists or a timeout
 is reached.
 .
 This fixes the problem that USB devices take too long to be
 discovered and crypto, raid, lvm or multipath can't be started on
 them. It also adds support for arbitrary nestings of them, e.g. raid5
 over raid1.
 .
 For the event loop I needed the $resume device translated to the
 device node earlier. This was previously done in
 scripts/local-premount/resume. It was essentially the same code used
 to translate the $ROOT so I created a new function translate_device()
 in script/functions and used that for both $ROOT and $resume.
 .
 Note: hooks/block-event and scripts/block-event are executable, not
 represented in the patch.
Author: Goswin von Brederlow
Last-Update: 2012-06-23

--

diff -Nru initramfs-tools-0.106/hooks/block-event initramfs-tools-0.106a0mrvn1/hooks/block-event
--- initramfs-tools-0.106/hooks/block-event	1970-01-01 01:00:00.000000000 +0100
+++ initramfs-tools-0.106a0mrvn1/hooks/block-event	2012-06-23 18:52:54.000000000 +0200
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+mkdir -p $DESTDIR/etc/udev/rules.d
+
+cat > $DESTDIR/etc/udev/rules.d/99-block-event.rules << EOF
+# we are only interested in add and change actions for block devices
+ACTION=="remove",                       GOTO="block_event_end"
+SUBSYSTEM!="block",                     GOTO="block_event_end"
+
+RUN+="/scripts/block-event $name"
+
+LABEL="block_event_end"
+EOF
diff -Nru initramfs-tools-0.106/init initramfs-tools-0.106a0mrvn1/init
--- initramfs-tools-0.106/init	2012-06-06 15:04:52.000000000 +0200
+++ initramfs-tools-0.106a0mrvn1/init	2012-06-23 21:14:40.000000000 +0200
@@ -69,41 +69,10 @@
 		init=${x#init=}
 		;;
 	root=*)
-		ROOT=${x#root=}
-		case $ROOT in
-		LABEL=*)
-			ROOT="${ROOT#LABEL=}"
-
-			# support any / in LABEL= path (escape to \x2f)
-			case "${ROOT}" in
-			*/*)
-			if command -v sed >/dev/null 2>&1; then
-				ROOT="$(echo ${ROOT} | sed 's,/,\\x2f,g')"
-			else
-				if [ "${ROOT}" != "${ROOT#/}" ]; then
-					ROOT="\x2f${ROOT#/}"
-				fi
-				if [ "${ROOT}" != "${ROOT%/}" ]; then
-					ROOT="${ROOT%/}\x2f"
-				fi
-				IFS='/'
-				newroot=
-				for s in $ROOT; do
-					newroot="${newroot:+${newroot}\\x2f}${s}"
-				done
-				unset IFS
-				ROOT="${newroot}"
-			fi
-			esac
-			ROOT="/dev/disk/by-label/${ROOT}"
-			;;
-		UUID=*)
-			ROOT="/dev/disk/by-uuid/${ROOT#UUID=}"
-			;;
-		/dev/nfs)
+		ROOT=$(translate_device ${x#root=})
+		if [ $ROOT = "/dev/nfs" ]; then
 			[ -z "${BOOT}" ] && BOOT=nfs
-			;;
-		esac
+		fi
 		;;
 	rootflags=*)
 		ROOTFLAGS="-o ${x#rootflags=}"
@@ -190,7 +159,7 @@
 	export noresume
 	unset resume
 else
-	resume=${RESUME:-}
+	resume=$(translate_device ${RESUME:-})
 fi
 
 maybe_break top
@@ -205,6 +174,39 @@
 
 [ -n "${netconsole}" ] && modprobe netconsole netconsole="${netconsole}"
 
+maybe_break events
+TIMEOUT=20
+COUNT=$TIMEOUT
+echo "Waiting for $ROOT"
+if [ -n "$resume" ]; then
+	echo "Waiting for $resume"
+fi
+# run at least once
+touch /dev/.initramfs/block-events
+while [ $COUNT -gt 0 ]; do
+	sleep 1
+	COUNT=$((COUNT-1))
+	echo -ne "Waiting ($COUNT) \r"
+	wait_for_udev
+	mv 2>/dev/null /run/initramfs/block-events /run/initramfs/block-events.processing && \
+	{
+		echo "Found new block devices...   "
+		COUNT=$TIMEOUT
+		export NEWDEVS=$(cat /run/initramfs/block-events.processing)
+		wait_for_udev
+		run_scripts /scripts/local-block
+	}
+	if [ -n "$resume" ]; then
+	    if [ -e $ROOT ] && [ -e $resume ]; then
+		break
+	    fi
+	else
+	    if [ -e $ROOT ]; then
+		break;
+	    fi
+	fi
+done
+
 maybe_break premount
 [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"
 run_scripts /scripts/init-premount
@@ -309,6 +311,10 @@
 unset readonly
 unset resume
 unset resume_offset
+unset COUNT
+unset TIMEOUT
+unset WAIT
+unset NEWDEVS
 
 # Move virtual filesystems over to the real filesystem
 mount -n -o move /sys ${rootmnt}/sys
diff -Nru initramfs-tools-0.106/scripts/block-event initramfs-tools-0.106a0mrvn1/scripts/block-event
--- initramfs-tools-0.106/scripts/block-event	1970-01-01 01:00:00.000000000 +0100
+++ initramfs-tools-0.106a0mrvn1/scripts/block-event	2012-06-23 18:48:41.000000000 +0200
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo >>/run/initramfs/block-events $1
diff -Nru initramfs-tools-0.106/scripts/functions initramfs-tools-0.106a0mrvn1/scripts/functions
--- initramfs-tools-0.106/scripts/functions	2012-06-06 15:04:52.000000000 +0200
+++ initramfs-tools-0.106a0mrvn1/scripts/functions	2012-06-23 19:34:36.000000000 +0200
@@ -423,3 +423,47 @@
 	command -v udevadm >/dev/null 2>&1 || return 0
 	udevadm settle ${1:+--timeout=$1}
 }
+
+# Translate device name to device node
+# Parameter: root or resume argument
+# Echos path to device node to stdout
+translate_device()
+{
+	local DEV newdev
+	DEV=$1
+	case $DEV in
+		LABEL=*)
+			DEV="${DEV#LABEL=}"
+
+			# support any / in LABEL= path (escape to \x2f)
+			case "${DEV}" in
+			*/*)
+			if command -v sed >/dev/null 2>&1; then
+				DEV="$(echo ${DEV} | sed 's,/,\\x2f,g')"
+			else
+				if [ "${DEV}" != "${DEV#/}" ]; then
+					DEV="\x2f${DEV#/}"
+				fi
+				if [ "${DEV}" != "${DEV%/}" ]; then
+					DEV="${DEV%/}\x2f"
+				fi
+				IFS='/'
+				newdev=
+				for s in $DEV; do
+					newdev="${newdev:+${newdev}\\x2f}${s}"
+				done
+				unset IFS
+				DEV="${newdev}"
+			fi
+			esac
+			DEV="/dev/disk/by-label/${DEV}"
+			;;
+		UUID=*)
+			DEV="/dev/disk/by-uuid/${DEV#UUID=}"
+			;;
+		/dev/nfs)
+			DEV=
+			;;
+	esac
+	echo $DEV
+}
diff -Nru initramfs-tools-0.106/scripts/local-premount/resume initramfs-tools-0.106a0mrvn1/scripts/local-premount/resume
--- initramfs-tools-0.106/scripts/local-premount/resume	2010-06-17 14:15:13.000000000 +0200
+++ initramfs-tools-0.106a0mrvn1/scripts/local-premount/resume	2012-06-23 21:15:34.000000000 +0200
@@ -19,38 +19,6 @@
 	exit 0
 fi
 
-case $resume in
-LABEL=*)
-	resume="${resume#LABEL=}"
-
-	# support any / in LABEL= path (escape to \x2f)
-	case "${resume}" in
-	*/*)
-		if  command -v sed >/dev/null 2>&1; then
-			resume="$(echo ${resume} | sed 's,/,\\x2f,g')"
-		else
-			if [ "${resume}" != "${resume#/}" ]; then
-				resume="\x2f${resume#/}"
-			fi
-			if [ "${resume}" != "${resume%/}" ]; then
-				resume="${resume%/}\x2f"
-			fi
-			IFS='/'
-			newresume=
-			for s in $resume; do
-				newresume="${newresume:+${newresume}\\x2f}${s}"
-			done
-			unset IFS
-			resume="${newresume}"
-		fi
-	esac
-	resume="/dev/disk/by-label/${resume}"
-	;;
-UUID=*)
-	resume="/dev/disk/by-uuid/${resume#UUID=}"
-	;;
-esac
-
 [ ! -e "${resume}" ] && exit 0
 
 [ ! -e /sys/power/resume ] && exit 0

Reply to: