Browse Source

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>
master
Kairui Song 7 years ago committed by Harald Hoyer
parent
commit
095e1f37c4
  1. 114
      dracut.sh
  2. 9
      modules.d/99squash/clear-squash.sh
  3. 7
      modules.d/99squash/init.sh
  4. 29
      modules.d/99squash/module-setup.sh
  5. 61
      modules.d/99squash/setup-squash.sh
  6. 7
      modules.d/99squash/shutdown.sh
  7. 19
      modules.d/99squash/squash-mnt-clear.service

114
dracut.sh

@ -1747,6 +1747,120 @@ fi @@ -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"

9
modules.d/99squash/clear-squash.sh

@ -0,0 +1,9 @@ @@ -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[@]}

7
modules.d/99squash/init.sh

@ -0,0 +1,7 @@ @@ -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

29
modules.d/99squash/module-setup.sh

@ -0,0 +1,29 @@ @@ -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"
}

61
modules.d/99squash/setup-squash.sh

@ -0,0 +1,61 @@ @@ -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

7
modules.d/99squash/shutdown.sh

@ -0,0 +1,7 @@ @@ -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

19
modules.d/99squash/squash-mnt-clear.service

@ -0,0 +1,19 @@ @@ -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
Loading…
Cancel
Save