350 lines
11 KiB
Diff
350 lines
11 KiB
Diff
From 095e1f37c41e8995c95635a47dbe2bf61d8ee2bc Mon Sep 17 00:00:00 2001
|
|
From: Kairui Song <kasong@redhat.com>
|
|
Date: Tue, 11 Sep 2018 19:32:24 +0800
|
|
Subject: [PATCH] Add support for building a squashed initramfs
|
|
|
|
With all files stored in ramfs, and most of them are not compressed,
|
|
the initramfs will take up a lot of memory. Besides, if the file number
|
|
is large, each file will waste some memory due to page fragmetation.
|
|
This is due to ramfs' design, at least one page will be allocated for
|
|
one file however small the file is. On machine with large page size,
|
|
this will become worse and waste too many memory.
|
|
|
|
One approach to reducing the memory usage is to reduce the number of
|
|
files that got directly loaded into the root ramfs, and compress files
|
|
by put most files will into a read-only squash image and keep a minimum
|
|
set of executable and libraries outside as the loader for the squash
|
|
image. After the squash image is mounted, the real 'init' will be
|
|
executed and then everything behaves as usual.
|
|
|
|
This patch will introduce a '99squash' module which will never be
|
|
included by default. User can force add it, and if it is included,
|
|
dracut will perform some extra steps before creating the final image:
|
|
|
|
For now, "/etc" and "/usr" will be moved into the squashfs image.
|
|
"/init" will be renamed to "/init.stock" and replaced by "/init.squash".
|
|
Files and folders need to be accessible before mounting the image will
|
|
be still avaliable at their original place. And due to squashfs is
|
|
readonly, an overlayfs layer will be created on top of squashfs mount
|
|
point, as many dracut module require readwrite access to "/etc" and
|
|
"/usr", "init.squash" will ultimately call "/init.stock".
|
|
|
|
An extra systemd service will be installed. This service will umount all
|
|
squashfs related mount points right before switch-root to release
|
|
resources properly. This service will not actually do anything if
|
|
switch-root is not used.
|
|
|
|
This is very helpful when mem resource is very limited, like Kdump.
|
|
According to my tests, this squash module can help save about 35MB of
|
|
memory with 64K page size, or about 15MB with 4K page size on an
|
|
ordinary kdump capture routine. This module could also help reduce
|
|
memory usage for normal boot up process.
|
|
|
|
Won't change any behavior if squash module is not enabled.
|
|
|
|
Signed-off-by: Kairui Song <kasong@redhat.com>
|
|
---
|
|
dracut.sh | 114 ++++++++++++++++++++++++++++
|
|
modules.d/99squash/clear-squash.sh | 9 +++
|
|
modules.d/99squash/init.sh | 7 ++
|
|
modules.d/99squash/module-setup.sh | 29 +++++++
|
|
modules.d/99squash/setup-squash.sh | 61 +++++++++++++++
|
|
modules.d/99squash/shutdown.sh | 7 ++
|
|
modules.d/99squash/squash-mnt-clear.service | 19 +++++
|
|
7 files changed, 246 insertions(+)
|
|
|
|
diff --git a/dracut.sh b/dracut.sh
|
|
index 8ee8c168..e683a9bc 100755
|
|
--- a/dracut.sh
|
|
+++ b/dracut.sh
|
|
@@ -1747,6 +1747,120 @@ fi
|
|
|
|
dinfo "*** Creating image file '$outfile' ***"
|
|
|
|
+if dracut_module_included "squash"; then
|
|
+ if ! check_kernel_config CONFIG_SQUASHFS; then
|
|
+ dfatal "CONFIG_SQUASHFS have to be enabled for dracut squash module to work"
|
|
+ exit 1
|
|
+ fi
|
|
+ if ! check_kernel_config CONFIG_OVERLAY_FS; then
|
|
+ dfatal "CONFIG_OVERLAY_FS have to be enabled for dracut squash module to work"
|
|
+ exit 1
|
|
+ fi
|
|
+ if ! check_kernel_config CONFIG_DEVTMPFS; then
|
|
+ dfatal "CONFIG_DEVTMPFS have to be enabled for dracut squash module to work"
|
|
+ exit 1
|
|
+ fi
|
|
+
|
|
+ readonly squash_dir="${DRACUT_TMPDIR}/squashfs"
|
|
+ readonly squash_img=$initdir/squash/root.img
|
|
+
|
|
+ # Currently only move "usr" "etc" to squashdir
|
|
+ readonly squash_candidate=( "usr" "etc" )
|
|
+
|
|
+ mkdir -m 0755 -p $squash_dir
|
|
+ for folder in "${squash_candidate[@]}"; do
|
|
+ mv $initdir/$folder $squash_dir/$folder
|
|
+ done
|
|
+
|
|
+ # Reinstall required files, because we have moved some important folders to $squash_dir
|
|
+ inst_multiple "echo" "sh" "mount" "modprobe" "mkdir" \
|
|
+ "systemctl" "udevadm" "$systemdutildir/systemd"
|
|
+ hostonly="" instmods "loop" "squashfs" "overlay"
|
|
+
|
|
+ for folder in "${squash_candidate[@]}"; do
|
|
+ # Remove duplicated files in squashfs image, save some more space
|
|
+ [[ ! -d $initdir/$folder/ ]] && continue
|
|
+ for file in $(find $initdir/$folder/ -not -type d);
|
|
+ do
|
|
+ if [[ -e $squash_dir${file#$initdir} ]]; then
|
|
+ mv $squash_dir${file#$initdir} $file
|
|
+ fi
|
|
+ done
|
|
+ done
|
|
+
|
|
+ # Move some files out side of the squash image, including:
|
|
+ # - Files required to boot and mount the squashfs image
|
|
+ # - Files need to be accessable without mounting the squash image
|
|
+ required_in_root() {
|
|
+ local file=$1
|
|
+ local _sqsh_file=$squash_dir/$file
|
|
+ local _init_file=$initdir/$file
|
|
+
|
|
+ if [[ -e $_init_file ]]; then
|
|
+ return
|
|
+ fi
|
|
+
|
|
+ if [[ ! -e $_sqsh_file ]] && [[ ! -L $_sqsh_file ]]; then
|
|
+ derror "$file is required to boot a squashed initramfs but it's not installed!"
|
|
+ return
|
|
+ fi
|
|
+
|
|
+ if [[ ! -d $(dirname $_init_file) ]]; then
|
|
+ required_in_root $(dirname $file)
|
|
+ fi
|
|
+
|
|
+ if [[ -d $_sqsh_file ]]; then
|
|
+ if [[ -L $_sqsh_file ]]; then
|
|
+ cp --preserve=all -P $_sqsh_file $_init_file
|
|
+ else
|
|
+ mkdir $_init_file
|
|
+ fi
|
|
+ else
|
|
+ if [[ -L $_sqsh_file ]]; then
|
|
+ cp --preserve=all -P $_sqsh_file $_init_file
|
|
+ _sqsh_file=$(realpath $_sqsh_file 2>/dev/null)
|
|
+ if [[ -e $_sqsh_file ]] && [[ "$_sqsh_file" == "$squash_dir"* ]]; then
|
|
+ # Relative symlink
|
|
+ required_in_root ${_sqsh_file#$squash_dir/}
|
|
+ return
|
|
+ fi
|
|
+ if [[ -e $squash_dir$_sqsh_file ]]; then
|
|
+ # Absolute symlink
|
|
+ required_in_root ${_sqsh_file#/}
|
|
+ return
|
|
+ fi
|
|
+ required_in_root ${module_spec#$squash_dir/}
|
|
+ else
|
|
+ mv $_sqsh_file $_init_file
|
|
+ fi
|
|
+ fi
|
|
+ }
|
|
+
|
|
+ required_in_root etc/initrd-release
|
|
+
|
|
+ for module_spec in $squash_dir/usr/lib/modules/*/modules.*;
|
|
+ do
|
|
+ required_in_root ${module_spec#$squash_dir/}
|
|
+ done
|
|
+
|
|
+ for dracut_spec in $squash_dir/usr/lib/dracut/*;
|
|
+ do
|
|
+ required_in_root ${dracut_spec#$squash_dir/}
|
|
+ done
|
|
+
|
|
+ mv $initdir/init $initdir/init.stock
|
|
+ mv $initdir/shutdown $initdir/shutdown.stock
|
|
+ ln -s squash/init.sh $initdir/init
|
|
+ ln -s squash/shutdown.sh $initdir/shutdown
|
|
+
|
|
+ mksquashfs $squash_dir $squash_img -comp xz -b 64K -Xdict-size 100% &> /dev/null
|
|
+
|
|
+ if [[ $? != 0 ]]; then
|
|
+ dfatal "dracut: Failed making squash image"
|
|
+ exit 1
|
|
+ fi
|
|
+fi
|
|
+
|
|
if [[ $uefi = yes ]]; then
|
|
readonly uefi_outdir="$DRACUT_TMPDIR/uefi"
|
|
mkdir "$uefi_outdir"
|
|
diff --git a/modules.d/99squash/clear-squash.sh b/modules.d/99squash/clear-squash.sh
|
|
new file mode 100755
|
|
index 00000000..34cb4cf5
|
|
--- /dev/null
|
|
+++ b/modules.d/99squash/clear-squash.sh
|
|
@@ -0,0 +1,9 @@
|
|
+#!/bin/sh
|
|
+SQUASH_MNT_REC=/squash/mounts
|
|
+SQUASH_MNTS=( )
|
|
+
|
|
+while read mnt; do
|
|
+ SQUASH_MNTS+=( "$mnt" )
|
|
+done <<< "$(cat $SQUASH_MNT_REC)"
|
|
+
|
|
+umount --lazy -- ${SQUASH_MNTS[@]}
|
|
diff --git a/modules.d/99squash/init.sh b/modules.d/99squash/init.sh
|
|
new file mode 100755
|
|
index 00000000..bca49db5
|
|
--- /dev/null
|
|
+++ b/modules.d/99squash/init.sh
|
|
@@ -0,0 +1,7 @@
|
|
+#!/bin/sh
|
|
+/squash/setup-squash.sh
|
|
+
|
|
+exec /init.stock
|
|
+
|
|
+echo "Something went wrong when trying to start original init executable!"
|
|
+exit 1
|
|
diff --git a/modules.d/99squash/module-setup.sh b/modules.d/99squash/module-setup.sh
|
|
new file mode 100644
|
|
index 00000000..935fd721
|
|
--- /dev/null
|
|
+++ b/modules.d/99squash/module-setup.sh
|
|
@@ -0,0 +1,29 @@
|
|
+#!/bin/bash
|
|
+
|
|
+check() {
|
|
+ return 255
|
|
+}
|
|
+
|
|
+depends() {
|
|
+ echo "bash systemd systemd-initrd"
|
|
+ return 0
|
|
+}
|
|
+
|
|
+installkernel() {
|
|
+ hostonly="" instmods squashfs loop overlay
|
|
+}
|
|
+
|
|
+install() {
|
|
+ if ! type -P mksquashfs >/dev/null || ! type -P unsquashfs >/dev/null ; then
|
|
+ derror "squash module requires squashfs-tools to be installed."
|
|
+ return 1
|
|
+ fi
|
|
+
|
|
+ inst_multiple kmod modprobe mount mkdir ln echo
|
|
+ inst $moddir/setup-squash.sh /squash/setup-squash.sh
|
|
+ inst $moddir/clear-squash.sh /squash/clear-squash.sh
|
|
+ inst $moddir/init.sh /squash/init.sh
|
|
+
|
|
+ inst "$moddir/squash-mnt-clear.service" "$systemdsystemunitdir/squash-mnt-clear.service"
|
|
+ ln_r "$systemdsystemunitdir/squash-mnt-clear.service" "$systemdsystemunitdir/initrd.target.wants/squash-mnt-clear.service"
|
|
+}
|
|
diff --git a/modules.d/99squash/setup-squash.sh b/modules.d/99squash/setup-squash.sh
|
|
new file mode 100755
|
|
index 00000000..d2740e7c
|
|
--- /dev/null
|
|
+++ b/modules.d/99squash/setup-squash.sh
|
|
@@ -0,0 +1,61 @@
|
|
+#!/bin/sh
|
|
+PATH=/bin:/sbin
|
|
+
|
|
+SQUASH_IMG=/squash/root.img
|
|
+SQUASH_MNT=/squash/root
|
|
+SQUASH_MNT_REC=/squash/mounts
|
|
+SQUASHED_MNT="usr etc"
|
|
+
|
|
+echo $SQUASH_MNT > $SQUASH_MNT_REC
|
|
+
|
|
+# Following mount points are neccessary for mounting a squash image
|
|
+
|
|
+[ ! -d /proc/self ] && \
|
|
+ mount -t proc -o nosuid,noexec,nodev proc /proc
|
|
+
|
|
+[ ! -d /sys/kernel ] && \
|
|
+ mount -t sysfs -o nosuid,noexec,nodev sysfs /sys
|
|
+
|
|
+[ ! -e /dev/loop-control ] && \
|
|
+ mount -t devtmpfs -o mode=0755,noexec,nosuid,strictatime devtmpfs /dev
|
|
+
|
|
+# Need a loop device backend, overlayfs, and squashfs module
|
|
+modprobe loop
|
|
+if [ $? != 0 ]; then
|
|
+ echo "Unable to setup loop module"
|
|
+fi
|
|
+
|
|
+modprobe squashfs
|
|
+if [ $? != 0 ]; then
|
|
+ echo "Unable to setup squashfs module"
|
|
+fi
|
|
+
|
|
+modprobe overlay
|
|
+if [ $? != 0 ]; then
|
|
+ echo "Unable to setup overlay module"
|
|
+fi
|
|
+
|
|
+[ ! -d "$SQUASH_MNT" ] && \
|
|
+ mkdir -m 0755 -p $SQUASH_MNT
|
|
+
|
|
+# Mount the squashfs image
|
|
+mount -t squashfs -o ro,loop $SQUASH_IMG $SQUASH_MNT
|
|
+
|
|
+if [ $? != 0 ]; then
|
|
+ echo "Unable to mount squashed initramfs image"
|
|
+fi
|
|
+
|
|
+for file in $SQUASHED_MNT; do
|
|
+ lowerdir=$SQUASH_MNT/$file
|
|
+ workdir=/squash/overlay-work/$file
|
|
+ upperdir=/$file
|
|
+ mntdir=/$file
|
|
+
|
|
+ mkdir -m 0755 -p $workdir
|
|
+ mkdir -m 0755 -p $mntdir
|
|
+
|
|
+ mount -t overlay overlay -o\
|
|
+ lowerdir=$lowerdir,upperdir=$upperdir,workdir=$workdir $mntdir
|
|
+
|
|
+ echo $mntdir >> $SQUASH_MNT_REC
|
|
+done
|
|
diff --git a/modules.d/99squash/shutdown.sh b/modules.d/99squash/shutdown.sh
|
|
new file mode 100755
|
|
index 00000000..535779f4
|
|
--- /dev/null
|
|
+++ b/modules.d/99squash/shutdown.sh
|
|
@@ -0,0 +1,7 @@
|
|
+#!/bin/sh
|
|
+/squash/setup-squash.sh
|
|
+
|
|
+exec /shutdown.stock
|
|
+
|
|
+echo "Something went wrong when trying to start original shutdown executable!"
|
|
+exit 1
|
|
diff --git a/modules.d/99squash/squash-mnt-clear.service b/modules.d/99squash/squash-mnt-clear.service
|
|
new file mode 100644
|
|
index 00000000..8dd17812
|
|
--- /dev/null
|
|
+++ b/modules.d/99squash/squash-mnt-clear.service
|
|
@@ -0,0 +1,19 @@
|
|
+# This file is part of dracut.
|
|
+#
|
|
+
|
|
+[Unit]
|
|
+Description=Cleanup squashfs mounts when switch root
|
|
+DefaultDependencies=no
|
|
+After=initrd.target
|
|
+After=dracut-initqueue.service dracut-pre-pivot.service
|
|
+Before=initrd-cleanup.service
|
|
+ConditionPathExists=/squash/root
|
|
+Conflicts=initrd-switch-root.target
|
|
+
|
|
+[Service]
|
|
+Type=oneshot
|
|
+RemainAfterExit=yes
|
|
+StandardInput=null
|
|
+StandardOutput=syslog+console
|
|
+StandardError=syslog+console
|
|
+ExecStop=/squash/clear-squash.sh
|
|
|