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: