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