Put compressed kernel modules and firmware in an uncompressed cpio

initramfs-tools decompresses kernel modules and firmware files before
putting them into the initramfs. This has several drawbacks:

* The compressed kernel modules and firmware files might be compressed
  with a higher level. So recompressing them in the initramfs can
  increase the size.
* Decompressing and compressing these files during mkinitramfs will take
  time.
* The size of the decompressed initramfs will be bigger. That size is
  needed in memory during boot.

So put compressed kernel modules and firmware files in an uncompressed
cpio instead. To make this change have an impact the files need to be
shipped compressed.

The benchmarks were done either on an AMD Ryzen 7 5700G with schroot and
overlay on tmpfs or on the hardware mentioned. All tests were running
the Ubuntu mantic development release in July 2023.

* minimal: schroot with linux-image-generic initramfs-tools zstd
* full: minimal + busybox-initramfs cryptsetup-initramfs
  isc-dhcp-client kbd lvm2 mdadm ntfs-3g plymouth plymouth-theme-spinner
* nvidia: full + linux-headers-generic nvidia-driver-525
* nvidia fw: nvidia + compressed /lib/firmware/nvidia/525.125.06/
* VisionFive 2: VisionFive 2 RISC-V board
* RPi Zero 2: Raspberry Pi Zero 2 ARM board (running armhf)

"next" means using kernel and firmware packages from a PPA that ship the
files compressed with `zstd -19` plus initramfs-tools with this patch.

|                | build   | size               | uncompressed size  |
| test           | time    | in bytes  | in MiB | in bytes  | in MiB |
|----------------|---------|-----------|--------|--------------------|
| minimal        | 4.30 s  |  66701818 |  63.6  | 224087608 | 213.7  |
| minimal next   | 4.54 s  |  59935186 |  57.2  |  67738810 |  64.6  |
| full           | 7.15 s  | 118007038 | 112.5  | 387976843 | 370.0  |
| full next      | 7.29 s  | 106937908 | 102.0  | 128610985 | 122.7  |
| nvidia         | 7.04 s  | 209200523 | 199.5  | 513554279 | 489.8  |
| nvidia next    | 7.21 s  | 195246287 | 186.2  | 235288174 | 224.4  |
| nvidia fw next | 7.16 s  | 191329102 | 182.5  | 213078234 | 203.2  |
| VisionFive 2   | 142.9 s | 121895035 | 116.2  | 411160836 | 392.1  |
| VF 2 next      | 126.7 s | 111651453 | 106.5  | 134120804 | 127.9  |
| RPi Zero 2     | 109.5 s |  39803044 |  40.0  |  69592789 |  66.4  |
| RPi Zero 2 ²   | 103.5 s |  39804499 |  40.0  |  69592789 |  66.4  |
| RPi Zero 2 next| 101.2 s |  31463352 |  30.0  |  41145762 |  39.2  |

² Updated initramfs-tools (but no compressed modules or firmware)

The build time was averaged over five runs.

| improvement  | build time | size   | uncompressed size |
|--------------|------------|--------|-------------------|
| minimal      |  105.6 %   | 89.9 % |      30.2 %       |
| full         |  102.0 %   | 90.6 % |      33.1 %       |
| nvidia       |  101.7 %   | 91.5 % |      41.5 %       |
| VisionFive 2 |   88.7 %   | 91.6 % |      32.6 %       |
| RPi Zero 2   |   92.4 %   | 79.0 % |      59.1 %       |

See https://lists.ubuntu.com/archives/ubuntu-devel/2023-July/042707.html

LP: #2028567
Signed-off-by: Benjamin Drung <benjamin.drung@canonical.com>
This commit is contained in:
Benjamin Drung 2024-10-23 13:37:47 +02:00
parent 6d0c792102
commit 81fd41f72d

View File

@ -276,6 +276,7 @@ fi
# Prepare to clean up temporary files on exit
DESTDIR=
__TMPMAINFILES=
__TMPUNCOMPRESSEDFILES=
__TMPCPIOGZ=
__TMPEARLYCPIO=
__MODULES_TO_ADD=
@ -284,10 +285,11 @@ clean_on_exit() {
if [ "${keep}" = "y" ]; then
echo "Working files in ${DESTDIR:-<not yet created>}," \
"list of files for main initramfs in ${__TMPMAINFILES:-<not yet created>}," \
"list of files for uncompressed initramfs in ${__TMPUNCOMPRESSEDFILES:-<not yet created>}," \
"early initramfs in ${__TMPEARLYCPIO:-<not yet created>} and" \
"overlay in ${__TMPCPIOGZ:-<not yet created>}"
else
for path in "${DESTDIR}" "${__TMPMAINFILES}" "${__TMPCPIOGZ}" "${__TMPEARLYCPIO}" "${__MODULES_TO_ADD}"; do
for path in "${DESTDIR}" "${__TMPMAINFILES}" "${__TMPUNCOMPRESSEDFILES}" "${__TMPCPIOGZ}" "${__TMPEARLYCPIO}" "${__MODULES_TO_ADD}"; do
test -z "${path}" || rm -rf "${path}"
done
fi
@ -300,6 +302,7 @@ trap "exit 1" INT TERM # makes the EXIT trap effective even when killed
DESTDIR="$(mktemp -d "${TMPDIR:-/var/tmp}/mkinitramfs_XXXXXX")" || exit 1
chmod 755 "${DESTDIR}"
__TMPMAINFILES="$(mktemp "${TMPDIR:-/var/tmp}/mkinitramfs-MAIN_files_XXXXXX")" || exit 1
__TMPUNCOMPRESSEDFILES="$(mktemp "${TMPDIR:-/var/tmp}/mkinitramfs-UNCOMPRESSED_files_XXXXXX")" || exit 1
__TMPCPIOGZ="$(mktemp "${TMPDIR:-/var/tmp}/mkinitramfs-OL_XXXXXX")" || exit 1
__TMPEARLYCPIO="$(mktemp "${TMPDIR:-/var/tmp}/mkinitramfs-FW_XXXXXX")" || exit 1
__MODULES_TO_ADD="$(mktemp "${TMPDIR:-/var/tmp}/modules_XXXXXX")" || exit 1
@ -443,24 +446,6 @@ apply_add_modules || exit $?
# Resolve hidden dependencies
hidden_dep_add_modules
# decompress modules for boot speed, if possible
find "${DESTDIR}/${MODULESDIR}" -name '*.ko.*' | while read -r ko; do
case "$ko" in
*.xz)
if ! command -v xz >/dev/null 2>&1; then
break
fi
xz -d "${ko}"
;;
*.zst)
if ! command -v zstd >/dev/null 2>&1; then
break
fi
zstd -q -d --rm "${ko}"
;;
esac
done
# generate module deps
depmod -a -b "${DESTDIR}" "${version}"
rm -f "${DESTDIR}/lib/modules/${version}"/modules.*map
@ -498,15 +483,45 @@ if [ -n "${SOURCE_DATE_EPOCH}" ]; then
cpio_reproducible="--reproducible"
fi
# Read list of files and echo them plus all leading directories.
# The same directories might be printed multiple times (even with sorted input)!
add_directories() {
local last_dir path dir
while read -r path; do
dir="${path%/*}"
parent="${dir}"
while [ "$parent" != "$last_dir" ] && [ "$parent" != "." ]; do
echo "$parent"
parent="${parent%/*}"
done
last_dir="$dir"
echo "$path"
done
if [ -n "$last_dir" ]; then
echo "."
fi
}
cd "${DESTDIR}" || exit 1
# work around lack of "set -o pipefail" for pipes.
# Write exit code to FD 3 in case of a failure to pass it through the pipes.
{
cd "${DESTDIR}"
{
find . || { echo "E: mkinitramfs failure find $?" >&2; echo 1 >&3; exit; }
} | {
find . \( ! -type d ! -name '*.zst' ! -name '*.xz' \) -o \( -type d -empty \) ||
{ echo "E: mkinitramfs failure main find $?" >&2; echo 1 >&3; exit; }
} | add_directories | {
LC_ALL=C sort || { echo "E: mkinitramfs failure sort $?" >&2; echo 1 >&3; exit; }
} > "${__TMPMAINFILES}" || echo $? >&3
} | uniq > "${__TMPMAINFILES}" || { echo "E: mkinitramfs failure uniq $?" >&2; echo 1 >&3; exit; }
} 3>&1 | { read -r exit_code; exit "${exit_code:-0}"; } || exit $?
{
{
find . -name '*.zst' -o -name '*.xz' ||
{ echo "E: mkinitramfs failure uncompressed find $?" >&2; echo 1 >&3; exit; }
} | add_directories | {
LC_ALL=C sort || { echo "E: mkinitramfs failure sort $?" >&2; echo 1 >&3; exit; }
} | uniq > "${__TMPUNCOMPRESSEDFILES}" || { echo "E: mkinitramfs failure uniq $?" >&2; echo 1 >&3; exit; }
} 3>&1 | { read -r exit_code; exit "${exit_code:-0}"; } || exit $?
{
@ -515,6 +530,12 @@ fi
cat "${__TMPEARLYCPIO}" || { echo 1 >&3; exit; }
fi
if [ -s "${__TMPUNCOMPRESSEDFILES}" ]; then
# shellcheck disable=SC2086
cpio --quiet $cpio_owner_root $cpio_reproducible -o -H newc -D "${DESTDIR}" <"${__TMPUNCOMPRESSEDFILES}" ||
{ echo "E: mkinitramfs failure uncompressed cpio $?" >&2; echo 1 >&3; exit; }
fi
{
# shellcheck disable=SC2086
cpio --quiet $cpio_owner_root $cpio_reproducible -o -H newc -D "${DESTDIR}" <"${__TMPMAINFILES}" ||