Categories
SysOps

How to configure swap using zram at boot

Configure compressed swap using zram device at system boot.

There are two distinct packages that provide systemd service to configure compressed swap using zram device at system boot.

There is nothing difficult here, but studying different solutions will help you to create your own solution.

Install extra modules for the running kernel.

$ sudo apt install linux-modules-extra-$(uname -r)

zram-tools

Install zram-tools package.

$ sudo apt install zram-tools

Inspect default configuration.

$ cat /etc/default/zramswap
# Specifies amount of zram devices to create.
# By default, zramswap-start will use all available cores.
#CORES=1

# Specifies the amount of RAM that should be used for zram
# based on a percentage the total amount of available memory
#PERCENTAGE=10

# Specifies a static amount of RAM that should be used for
# the ZRAM devices, this is in MiB
#ALLOCATION=256

# Specifies the priority for the swap devices, see swapon(2)
# for more details.
#PRIORITY=100

Inspect service.

$ systemctl status zramswap.service
● zramswap.service - Linux zramswap setup
     Loaded: loaded (/lib/systemd/system/zramswap.service; enabled; vendor preset: enabled)
     Active: active (exited) since Mon 2021-01-04 21:31:41 UTC; 5s ago
       Docs: man:zramswap(8)
    Process: 11068 ExecStart=/usr/sbin/zramswap start (code=exited, status=0/SUCCESS)
   Main PID: 11068 (code=exited, status=0/SUCCESS)

Inspect zram devices.

$ zramctl
NAME       DISKSIZE DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram1     128M   4K   73B lzo-rle         2          0   12K        0B      12K       0B [SWAP]
/dev/zram0     128M  84K 13.5K lzo-rle         2          0  168K        0B     168K       0B [SWAP]

Inspect swap devices.

$ swapon -status
Filename                                Type            Size    Used    Priority
/dev/zram0                              partition       131068  256     100
/dev/zram1                              partition       131068  0       100

This solution uses a simple service file …

[Unit]
Description=Linux zramswap setup
Documentation=man:zramswap(8)

[Service]
EnvironmentFile=-/etc/default/zramswap
ExecStart=/usr/sbin/zramswap start
ExecStop=/usr/sbin/zramswap stop
ExecReload=/usr/sbin/zramswap restart
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

… and a slightly outdated shell scripts.

#!/bin/bash                                                                      
# This script does the following:                                                
# zramswap start:                                                                
#  * Space is assigned to each zram device, then swap is initialized on
#    there                   
# zramswap stop:                                                                 
#  * Undo start
#  * Also attempts to remove zram module at the end
# TODO:                                 
# * Migrate to using zramctl from util-linux for the setup,
#   (this will close debian bug #917643):
#   then also:
#   - add option for compression algorythm
#   - ammount of compression streams
#   - Make use of the zramctl stats too

function start {
    #Set some defaults:
    ALLOCATION=256 # ZRAM Swap you want assigned, in MiB
    PRIORITY=100   # Swap priority, see swapon(2) for more details

    # Get amount of available CPU cores, set to 1 if not detected correctly
    if [ ! -f /proc/cpuinfo ]; then
        echo "WARNING: Can't find /proc/cpuinfo, is proc mounted?"
        echo "         Using a single core for zramswap..."
        CORES=1
    else
        CORES=$(grep -c ^processor /proc/cpuinfo)
    fi

    # Override  above from config file, if it exists
    if [ -f /etc/default/zramswap ]; then
        . /etc/default/zramswap
    fi

    ALLOCATION=$((ALLOCATION * 1024 * 1024)) # convert amount from MiB to bytes

    if [ -n "$PERCENTAGE" ]; then
        totalmemory=$(awk '/MemTotal/{print $2}' /proc/meminfo) # in KiB
        ALLOCATION=$((totalmemory * 1024 * $PERCENTAGE / 100))
    fi

    # Initialize zram devices
    modprobe zram num_devices=$CORES
                                                                                                                                                          
    # Assign memory to zram devices, initialize swap and activate
    # Decrementing $CORE, because cores start counting at 0
    for CORE in $(seq 0 $(($CORES - 1))); do
        echo $(($ALLOCATION / $CORES)) > /sys/block/zram$CORE/disksize
        mkswap /dev/zram$CORE
        swapon -p $PRIORITY /dev/zram$CORE
    done
}

function status {
    orig_data_size="0"
    for file in /sys/block/zram*/*_data_size ; do
        if [ $file = "/sys/block/zram*/*_data_size" ]; then
            compress_ratio="0"
            break
        fi
        read file_content < $file
        what=$(basename $file)
        eval "$what=\$(($what + $file_content))"
        compress_ratio=$(echo "scale=2; $orig_data_size / $compr_data_size" | bc) 
    done
    echo "compr_data_size: $((compr_data_size / 1024)) KiB"
    echo "orig_data_size:  $((orig_data_size  / 1024)) KiB"
    echo "compression-ratio: $compress_ratio"
}

function stop {
    for swapspace in $(swapon -s | awk '/zram/{print $1}'); do
        swapoff $swapspace
    done
    modprobe -r zram
}

function usage {
    echo "Usage:"
    echo "   zramswap start - start zram swap"
    echo "   zramswap stop - stop zram swap"
    echo "   zramswap status - prints some statistics"
}

if [ "$1" = "start" ]; then
    start
fi

if [ "$1" = "stop" ]; then
    stop
fi

if [ "$1" = "status" ]; then
    status
fi

if [ "$1" = "" ]; then
    usage
fi

There is a bug report #917643 that suggests a more modern approach.

KERNEL=="zram0", SUBSYSTEM=="block", DRIVER=="", ACTION=="add", ATTR{initstate}=="0", PROGRAM="/bin/sh -c 'echo $(($(LANG=C free -k | grep -e \"^Mem:\" | sed -e \"s/^Mem: *//\" -e \"s/  *.*//\")*2))KiB'", ATTR{disksize}="$result", RUN+="/sbin/mkswap $env{DEVNAME}", TAG+="systemd"

zram-config

Install zram-config package.

$ sudo apt install zram-config

Inspect service.

$ systemctl status zram-config.service 
● zram-config.service - Initializes zram swaping
     Loaded: loaded (/lib/systemd/system/zram-config.service; enabled; vendor preset: enabled)
     Active: inactive (dead)

Start service.

$ sudo systemctl start zram-config.service

Inspect service status.

$ systemctl status zram-config.service
● zram-config.service - Initializes zram swaping
     Loaded: loaded (/lib/systemd/system/zram-config.service; enabled; vendor preset: enabled)
     Active: active (exited) since Mon 2021-01-04 22:16:22 UTC; 4s ago
    Process: 1555 ExecStart=/usr/bin/init-zram-swapping (code=exited, status=0/SUCCESS)
   Main PID: 1555 (code=exited, status=0/SUCCESS)

Inspect zram devices.

$ zramctl --output-all
NAME       DISKSIZE DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram1   984.1M   4K   74B lzo-rle         2          0   12K        0B      12K       0B [SWAP]
/dev/zram0   984.1M   4K   74B lzo-rle         2          0   12K        0B      12K       0B [SWAP]

Inspect swap devices.

$ swapon -status
Filename                                Type            Size    Used    Priority
Filename                                Type            Size    Used    Priority
/dev/zram0                              partition       1007668 0       5
/dev/zram1                              partition       1007668 0       5

This solution uses a simple service file …

[Unit]
Description=Initializes zram swaping

[Service]
ExecStart=/usr/bin/init-zram-swapping
ExecStop=/usr/bin/end-zram-swapping
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=multi-user.target

… and a slightly outdated shell scripts.

#!/bin/sh

# load dependency modules
NRDEVICES=$(grep -c ^processor /proc/cpuinfo | sed 's/^0$/1/')
if modinfo zram | grep -q ' zram_num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="zram_num_devices=${NRDEVICES}"
elif modinfo zram | grep -q ' num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="num_devices=${NRDEVICES}"
else
  exit 1
fi
modprobe zram $MODPROBE_ARGS

# Calculate memory to use for zram (1/2 of ram)
totalmem=`LC_ALL=C free | grep -e "^Mem:" | sed -e 's/^Mem: *//' -e 's/  *.*//'`
mem=$(((totalmem / 2 / ${NRDEVICES}) * 1024))

# initialize the devices
for i in $(seq ${NRDEVICES}); do
  DEVNUMBER=$((i - 1))
  echo $mem > /sys/block/zram${DEVNUMBER}/disksize
  mkswap /dev/zram${DEVNUMBER}
  swapon -p 5 /dev/zram${DEVNUMBER}
done
#!/bin/sh

if DEVICES=$(grep zram /proc/swaps | awk '{print $1}'); then
  for i in $DEVICES; do
    swapoff $i
  done
fi
rmmod zram

Additional notes

These tools do not use udev or zramctl utility, so the best possible solution is to read about compressed RAM based block devices and create your own customized solution.