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

Bug#652525: [PATCHv2] Make initramfs-tools faster by avoiding forks in the per-module hot path



On Sat, Dec 17, 2011 at 10:37:29PM -0800, Josh Triplett wrote:
> In the last few versions of initramfs-tools, update-initramfs started
> taking an excessively long time to generate the initramfs:
> 
> ~$ time sudo update-initramfs -u
> update-initramfs: Generating /boot/initrd.img-3.1.0-1-amd64
> 
> real    0m26.197s
> user    0m14.785s
> sys     0m1.196s
> 
> This results in a long delay every time I upgrade either a kernel or any
> package with an update-initramfs hook.
> 
> As far as I can tell, a large part of this processing time seems to
> consist of the per-module processing for the ~500 kernel modules copied
> into the initramfs, forking off a pile of programs for each one.  I
> tried optimizing the per-module hot path to eliminate as many forks as
> possible, and managed to speed it up considerably:
> 
> ~$ time sudo update-initramfs -u
> update-initramfs: Generating /boot/initrd.img-3.1.0-1-amd64
> 
> real    0m21.385s
> user    0m14.393s
> sys     0m0.740s
> 
> I've attached a patch implementing these optimizations.
> 
> A few other possible improvements:
> 
> - Generate a list of modules and processing them all in batch at the
>   end of update-initramfs, to allow calling modprobe and modinfo only
>   once.  Likely to make a huge difference.
> - In the absence of the above, make manual_add_modules take a list of
>   modules and process them all, and make the various hooks pass a list
>   of modules to manual_add_modules rather than calling it repeatedly.

I implemented this addition, making manual_add_modules take multiple
arguments, and it provided another significant performance improvement:

~$ time sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-3.1.0-1-amd64

real    0m18.763s
user    0m14.569s
sys     0m0.720s

The difference between the original and patched versions in terms of
executed programs:

~$ grep -c 'execve' /tmp/update-initramfs.orig.strace
6355
~$ grep -c 'execve' /tmp/update-initramfs.patched.strace
2153

And more specifically the calls to modprobe:

~$ grep -c 'execve.*modprobe' /tmp/update-initramfs.orig.strace
549
~$ grep -c 'execve.*modprobe' /tmp/update-initramfs.patched.strace
34

Implementing full batching would reduce the modprobe calls down to 1,
but 34 still represents a significant improvement with relatively little
effort.

Patch attached.  (This subsumes the original patch.)

- Josh Triplett
--- hook-functions.orig	2011-12-17 22:38:01.142656068 -0800
+++ hook-functions	2011-12-17 23:28:59.840142744 -0800
@@ -10,10 +10,11 @@
 	cat "${1}" >>"${__TMPCPIOGZ}"
 }
 
+# force_load module [args...]
 force_load()
 {
-		manual_add_modules ${@}
-		echo "${@}" >>"${DESTDIR}/conf/modules"
+	manual_add_modules "$1"
+	echo "${@}" >>"${DESTDIR}/conf/modules"
 }
 
 # Takes a file containing a list of modules to be added as an
@@ -42,17 +43,20 @@
 # Add dependent modules + eventual firmware
 manual_add_modules()
 {
-	local kmod firmware
+	local prefix kmod firmware
+
+	modprobe --all --set-version="${version}" --ignore-install --quiet --show-depends "$@" |
+	while read prefix kmod ; do
+		if [ "${prefix}" != "insmod" ]; then
+			continue
+		fi
 
-	for kmod in $(modprobe --set-version="${version}" --ignore-install \
-	--quiet --show-depends "${1}" | awk '/^insmod/ { print $2 }'); do
 		# Prune duplicates
 		if [ -e "${DESTDIR}/${kmod}" ]; then
 			continue
 		fi
 
-		mkdir -p "${DESTDIR}/$(dirname "${kmod}")"
-		cp -pL "${kmod}" "${DESTDIR}/$(dirname "${kmod}")"
+		install -Dpm 644 "$kmod" "${DESTDIR}/$kmod"
 		if [ "${verbose}" = "y" ]; then
 			echo "Adding module ${kmod}"
 		fi
@@ -74,10 +78,9 @@
 					continue
 				fi
 
-				if grep -q "^$(basename "${kmod}" .ko)[[:space:]]" \
-				/proc/modules \
-				|| grep -q "^$(basename "${kmod}" .ko)" \
-				"${CONFDIR}/modules"; then
+				kmod_modname="${kmod##*/}"
+				kmod_modname="${kmod_modname%.ko}"
+				if grep -q "^$kmod_modname\\>" /proc/modules "${CONFDIR}/modules"; then
 					echo "W: Possible missing firmware /lib/firmware/${firmware} for module $(basename ${kmod} .ko)" >&2
 				fi
 				continue
@@ -157,6 +160,7 @@
 copy_modules_dir()
 {
 	local kmod exclude
+        local modules=
 	local dir="$1"
 	shift
 
@@ -173,9 +177,10 @@
 		exclude="${exclude:-} -name $1 -prune -o "
 		shift
 	done
-	for kmod in $(find "${MODULESDIR}/${dir}" ${exclude:-} -name '*.ko' -print); do
-		manual_add_modules $(basename ${kmod} .ko)
+	for kmod in $(find "${MODULESDIR}/${dir}" ${exclude:-} -name '*.ko' -printf '%f\n'); do
+		modules="$modules ${kmod%.ko}"
 	done
+	manual_add_modules $modules
 }
 
 # walk /sys for relevant modules
@@ -216,7 +221,8 @@
 # find and only copy root relevant modules
 dep_add_modules()
 {
-	local block minor root FSTYPE root_dev_path x
+	local block minor root FSTYPE root_dev_path
+	local modules=
 
 	# require mounted sysfs
 	if [ ! -d /sys/devices/ ]; then
@@ -270,7 +276,7 @@
 	fi
 
 	# Add rootfs
-	manual_add_modules "${FSTYPE}"
+	modules="$modules ${FSTYPE}"
 
 	# lvm or luks root
 	if [ "${root#/dev/mapper/}" != "${root}" ] \
@@ -365,20 +371,19 @@
 	# catch old-style IDE
 	if [ -d "${DESTDIR}/lib/modules/${version}/kernel/drivers/ide" ]; then
 		sys_walk_modalias ${root_dev_path}
-		manual_add_modules ide-gd_mod
-		manual_add_modules ide-cd
+		modules="$modules ide-gd_mod ide-cd"
 	fi
 
 	if [ -d "${DESTDIR}/lib/modules/${version}/kernel/drivers/scsi" ]; then
-		manual_add_modules sd_mod
+		modules="$modules sd_mod"
 	fi
 
 	if [ -e /sys/bus/mmc/devices/ ]; then
-		manual_add_modules mmc_block
+		modules="$modules mmc_block"
 	fi
 
 	if [ -e /sys/bus/virtio ] ; then
-		manual_add_modules virtio_pci
+		modules="$modules virtio_pci"
 	fi
 
 	if [ -e /sys/bus/i2o/devices/ ]; then
@@ -387,97 +392,78 @@
 	fi
 
 	if [ -e /sys/bus/ps3_system_bus/ ]; then
-		for x in ps3disk ps3rom ps3-gelic ps3_sys_manager; do
-			manual_add_modules "${x}"
-		done
+		modules="$modules ps3disk ps3rom ps3-gelic ps3_sys_manager"
 	fi
 
 	if [ -e /sys/bus/vio/ ]; then
-		for x in sunvnet sunvdc; do
-			manual_add_modules "${x}"
-		done
+		modules="$modules sunvnet sunvdc"
 	fi
+
+	manual_add_modules $modules
 }
 
 
 # The modules "most" classes added per default to the initramfs
 auto_add_modules()
 {
-	case "${1:-}" in
-	base)
-		for x in ehci-hcd ohci-hcd uhci-hcd usbhid xhci xhci-hcd \
-		hid-apple hid-cherry hid-logitech hid-microsoft hid-sunplus \
-		btrfs ext2 ext3 ext4 ext4dev isofs jfs nfs reiserfs udf xfs \
-		af_packet atkbd i8042 virtio_pci; do
-			manual_add_modules "${x}"
-		done
-	;;
-	net)
-		copy_modules_dir kernel/drivers/net \
-			appletalk arcnet bonding can hamradio irda pcmcia \
-			tokenring usb wan wimax wireless
-	;;
-	ide)
-		copy_modules_dir kernel/drivers/ide
-	;;
-	mmc)
-		copy_modules_dir kernel/drivers/mmc
-	;;
-	scsi)
-		copy_modules_dir kernel/drivers/scsi
-		for x in mptfc mptsas mptscsih mptspi zfcp; do
-			manual_add_modules "${x}"
-		done
-	;;
-	ata)
-		copy_modules_dir kernel/drivers/ata
-	;;
-	block)
-		copy_modules_dir kernel/drivers/block
-	;;
-	ubi)
-		for x in deflate zlib lzo ubi ubifs; do
-			manual_add_modules "${x}"
-		done
-	;;
-	ieee1394)
-		for x in ohci1394 sbp2; do
-			manual_add_modules "${x}"
-		done
-	;;
-	firewire)
-		for x in firewire-ohci  firewire-sbp2; do
-			manual_add_modules "${x}"
-		done
-	;;
-	i2o)
-		for x in i2o_block; do
-			manual_add_modules "${x}"
-		done
-	;;
-	dasd)
-		for x in dasd_diag_mod dasd_eckd_mod dasd_fba_mod; do
-			manual_add_modules "${x}"
-		done
-	;;
-	usb_storage)
-		copy_modules_dir kernel/drivers/usb/storage
-	;;
-	*)
-		auto_add_modules base
-		auto_add_modules net
-		auto_add_modules ide
-		auto_add_modules scsi
-		auto_add_modules block
-		auto_add_modules ata
-		auto_add_modules i2o
-		auto_add_modules dasd
-		auto_add_modules ieee1394
-		auto_add_modules firewire
-		auto_add_modules mmc
-		auto_add_modules usb_storage
-	;;
-	esac
+	local arg
+	local modules=
+
+	if [ "$#" -eq 0 ] ; then
+		set -- base net ide scsi block ata i2o dasd ieee1394 firewire mmc usb_storage
+	fi
+
+	for arg in "$@" ; do
+		case "$arg" in
+		base)
+			modules="$modules ehci-hcd ohci-hcd uhci-hcd usbhid xhci xhci-hcd"
+			modules="$modules hid-apple hid-cherry hid-logitech hid-microsoft hid-sunplus"
+			modules="$modules btrfs ext2 ext3 ext4 ext4dev isofs jfs nfs reiserfs udf xfs"
+			modules="$modules af_packet atkbd i8042 virtio_pci"
+		;;
+		net)
+			copy_modules_dir kernel/drivers/net \
+				appletalk arcnet bonding can hamradio irda pcmcia \
+				tokenring usb wan wimax wireless
+		;;
+		ide)
+			copy_modules_dir kernel/drivers/ide
+		;;
+		mmc)
+			copy_modules_dir kernel/drivers/mmc
+		;;
+		scsi)
+			copy_modules_dir kernel/drivers/scsi
+			modules="$modules mptfc mptsas mptscsih mptspi zfcp"
+		;;
+		ata)
+			copy_modules_dir kernel/drivers/ata
+		;;
+		block)
+			copy_modules_dir kernel/drivers/block
+		;;
+		ubi)
+			modules="$modules deflate zlib lzo ubi ubifs"
+		;;
+		ieee1394)
+			modules="$modules ohci1394 sbp2"
+		;;
+		firewire)
+			modules="$modules firewire-ohci firewire-sbp2"
+		;;
+		i2o)
+			modules="$modules i2o_block"
+		;;
+		dasd)
+			modules="$modules dasd_diag_mod dasd_eckd_mod dasd_fba_mod"
+		;;
+		usb_storage)
+			copy_modules_dir kernel/drivers/usb/storage
+		;;
+		esac
+	done
+
+	manual_add_modules $modules
 }
 
 # 'depmod' only looks at symbol dependencies; there is no way for
@@ -486,16 +472,15 @@
 # fixed, we need to handle those hidden dependencies.
 hidden_dep_add_modules()
 {
+	local modules=
 	for dep in "lib/libcrc32c crc32c" "fs/ubifs/ubifs deflate zlib lzo"; do
 		set -- $dep
 		if [ -f "${DESTDIR}/lib/modules/${version}/kernel/$1.ko" ]; then
 			shift
-			for i in "$@" ; do
-				manual_add_modules "$i"
-				shift
-			done
+			modules="$modules $@"
 		fi
 	done
+	manual_add_modules $modules
 }
 
 # mkinitramfs help message

Reply to: