@@ -166,6 +166,7 @@ tags
/src/unwritten_mmap
/src/unwritten_sync
/src/usemem
+/src/usemem_and_swapoff
/src/writemod
/src/writev_on_pagefault
/src/xfsctl
@@ -17,4 +17,9 @@ _require_cgroup2()
fi
}
+_require_memcg()
+{
+ test -e "$CGROUP2_PATH/memory.stat" || _notrun "memcg required for test"
+}
+
/bin/true
@@ -272,6 +272,7 @@ export MKFS_REISER4_PROG=$(type -P mkfs.reiser4)
export E2FSCK_PROG=$(type -P e2fsck)
export TUNE2FS_PROG=$(type -P tune2fs)
export FSCK_OVERLAY_PROG=$(type -P fsck.overlay)
+export SYSTEMD_RUN_PROG="$(type -P systemd-run)"
# SELinux adds extra xattrs which can mess up our expected output.
# So, mount with a context, and they won't be created.
@@ -2593,10 +2593,8 @@ _format_swapfile() {
}
_swapon_file() {
- local fname="$1"
-
# Ignore permission complaints on filesystems that don't support perms
- swapon "$fname" 2> >(grep -v "insecure permissions" >&2)
+ swapon "$@" 2> >(grep -v "insecure permissions" >&2)
}
# Check that the filesystem supports swapfiles
@@ -31,7 +31,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
dio-invalidate-cache stat_test t_encrypted_d_revalidate \
attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \
- detached_mounts_propagation
+ detached_mounts_propagation usemem_and_swapoff
EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \
btrfs_crc32c_forged_name.py
new file mode 100644
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ *
+ * Test program to try to force the VMM to swap pages in and out of memory.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/swap.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+int main(int argc, char *argv[])
+{
+ struct rlimit rlim;
+ long long nr_bytes;
+ long pagesize;
+ char *p, *pstart, *pend;
+ int ret;
+
+ if (argc != 2 && argc != 4) {
+ printf("Usage: %s mem_in_bytes [swapfile1 swapfile2]\n",
+ argv[0]);
+ return 1;
+ }
+
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize < 1) {
+ fprintf(stderr, "Cannot determine system page size.\n");
+ return 2;
+ }
+
+ errno = 0;
+ nr_bytes = strtol(argv[1], &p, 0);
+ if (errno) {
+ perror(argv[1]);
+ return 3;
+ }
+
+ printf("Allocating %llu memory.\n", nr_bytes);
+ fflush(stdout);
+
+ /* Allocate a large memory buffer. */
+ pstart = malloc(nr_bytes);
+ if (!pstart) {
+ perror("malloc");
+ return 4;
+ }
+ pend = pstart + nr_bytes;
+
+ printf("Dirtying memory.\n");
+ fflush(stdout);
+
+ /* Dirty the memory to force this program to be swapped out. */
+ for (p = pstart; p < pend; p += pagesize)
+ *p = 'X';
+
+ /*
+ * If the caller does not provide any swapfile names, mlock the buffer
+ * to test if memory usage enforcement actually works.
+ */
+ if (argc == 2) {
+ printf("Now mlocking memory.\n");
+ fflush(stdout);
+
+ rlim.rlim_cur = RLIM_INFINITY;
+ rlim.rlim_max = RLIM_INFINITY;
+ ret = setrlimit(RLIMIT_MEMLOCK, &rlim);
+ if (ret) {
+ perror("setrlimit");
+ }
+
+ ret = mlock(pstart, nr_bytes);
+ if (ret) {
+ perror("mlock");
+ return 0;
+ }
+
+ printf("Should not have survived mlock!\n");
+ fflush(stdout);
+ return 6;
+ }
+
+ /*
+ * Try to force the system to swap this program back into memory by
+ * activating the second swapfile (at maximum priority) and
+ * deactivating the first swapfile.
+ */
+ printf("Now activating swapfile2.\n");
+ fflush(stdout);
+
+ ret = swapon(argv[3], SWAP_FLAG_PREFER | SWAP_FLAG_PRIO_MASK);
+ if (ret) {
+ perror("swapon");
+ return 7;
+ }
+
+ printf("Now deactivating swapfile1.\n");
+ fflush(stdout);
+
+ ret = swapoff(argv[2]);
+ if (ret) {
+ perror("swapoff");
+ return 8;
+ }
+
+ /* Dirty the memory again to ensure that we're still running. */
+ for (p = pstart; p < pend; p += pagesize)
+ *p = 'Y';
+
+ return 0;
+}
new file mode 100755
@@ -0,0 +1,141 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Oracle. All Rights Reserved.
+#
+# FS QA Test 728
+#
+# Test swapping process pages in and out of a swapfile. Because we cannot
+# direct the virtual memory manager to flush pages to swap and do not wish to
+# spend a large amount of time flooding the system with dirty anonymous pages,
+# some trickery is employed here to speed things up.
+#
+# Specifically, this test employs the memory cgroup controller to run a modest
+# memory consuming process with an artificially low limit on the amount of
+# physical memory that it can use. Combined with swap files configured with
+# maximum priority, the hope is that the mem-user process will get paged out
+# to the swapfile and some clever uses of swapon/swapoff will force it to be
+# paged back in.
+#
+# Note that we use systemd-run to configure the memory controller so that we
+# don't have to open-code all the cgroupv1 and cgroupv2 configuration logic in
+# the form of shell scripts.
+#
+. ./common/preamble
+_begin_fstest auto swap
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ test -n "$swapfile1" && swapoff $swapfile1 &> /dev/null
+ test -n "$swapfile2" && swapoff $swapfile2 &> /dev/null
+}
+
+. ./common/cgroup2
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch_swapfile
+_require_command "$SYSTEMD_RUN_PROG" systemd-run
+_require_test_program "usemem_and_swapoff"
+_require_memcg
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+swapfile1=$SCRATCH_MNT/swapfile1
+swapfile2=$SCRATCH_MNT/swapfile2
+_format_swapfile $swapfile1 100m >> $seqres.full
+_format_swapfile $swapfile2 100m >> $seqres.full
+
+# The maximum swap priority is 32767 as defined by the kernel swap flag mask.
+SWAP_FLAG_PRIO_MASK=32767 # 0x7ffff
+
+# Add the swapfile with the highest priority so that pages get sent here before
+# any other configured swap space.
+_swapon_file $swapfile1 -p $SWAP_FLAG_PRIO_MASK
+
+# Walk the swap list to make sure that our swapfile has the highest priority
+# to boost the chances that it will get used for /some/ swap activity.
+cat /proc/swaps >> $seqres.full
+saw_swapfile1=0
+saw_swapfile2=0
+while read fname type size used priority junk; do
+ test "$fname" = "Filename" && continue
+ if [ "$fname" -ef "$swapfile1" ]; then
+ test "$priority" -eq "$SWAP_FLAG_PRIO_MASK" || \
+ echo "swapfile1 has wrong priority $priority?"
+ saw_swapfile1=1
+ continue
+ fi
+ test "$fname" -ef "$swapfile2" && saw_swapfile2=1
+ test "$priority" -lt "$SWAP_FLAG_PRIO_MASK" || \
+ echo "$fname: swap at same priority as test swapfile, test may fail"
+done < /proc/swaps
+
+test "$saw_swapfile1" -gt 0 || \
+ echo "swapfile1 shold be present in /proc/swaps"
+test "$saw_swapfile2" -eq 0 || \
+ echo "swapfile2 should not be present in /proc/swaps"
+
+# Configure ourselves to run a test program in a specially crafted systemd
+# scope where DRAM usage is constrained to 10MB maximum. The test program
+# will try to get itself swapped out to disk.
+runargs=(--quiet --scope --no-ask-password --same-dir -p MemoryMax=10M)
+cmdline="$here/src/usemem_and_swapoff $((50 * 1048576))"
+
+# The first time, the program allocates 50MB of space and dirties every page to
+# try to get the program swapped out to disk. It will then try to mlock all
+# 50MB, which it shouldn't be able to do if the memory controller is doing its
+# job. This is critical to test swapping the program back in.
+#
+# Note: The bash stdout/stderr redirection muddiness is needed to capture bash
+# complaining about the usemem_and_swapoff program being killed by the kernel.
+prog=( bash -c "$cmdline &> $tmp.prog" )
+( $SYSTEMD_RUN_PROG "${runargs[@]}" "${prog[@]}" &> $tmp.wrap ) ; ret=$?
+
+# Record the output of the first attempt.
+cat $tmp.prog | tee -a $seqres.full
+grep -q Killed $tmp.wrap || \
+ echo "mlock didn't cause OOM kill; memory limit enforcement might not be working"
+cat $tmp.wrap >> $seqres.full
+echo "return value $ret" >> $seqres.full
+
+# The second time, the program again allocates 50MB and dirties it. However,
+# this time the program tries to enable swapfile2 and disable swapfile1, which
+# should result in the program being paged in from swapfile1 and paged out to
+# swapfile2. The program should be able to continue accessing its memory.
+prog=( bash -c "$cmdline $swapfile1 $swapfile2 &> $tmp.prog" )
+( $SYSTEMD_RUN_PROG "${runargs[@]}" "${prog[@]}" &> $tmp.wrap ) ; ret=$?
+
+# Record the output of the second attempt.
+cat $tmp.prog | tee -a $seqres.full
+grep -q Killed $tmp.wrap && echo "swapfile swap should not have killed program"
+cat $tmp.wrap >> $seqres.full
+echo "return value $ret" >> $seqres.full
+
+# Make sure the final state of the swap devices is what we think it should be.
+cat /proc/swaps >> $seqres.full
+saw_swapfile1=0
+saw_swapfile2=0
+while read fname type size used priority junk; do
+ test "$fname" = "Filename" && continue
+ test "$fname" -ef "$swapfile1" && saw_swapfile1=1
+ if [ "$fname" -ef "$swapfile2" ]; then
+ test "$priority" -eq "$SWAP_FLAG_PRIO_MASK" || \
+ echo "swapfile2 has wrong priority $priority?"
+ saw_swapfile2=1
+ continue
+ fi
+done < /proc/swaps
+
+test "$saw_swapfile1" -eq 0 || \
+ echo "swapfile1 should not be present in /proc/swaps"
+test "$saw_swapfile2" -eq 1 || \
+ echo "swapfile2 should be present in /proc/swaps"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
new file mode 100644
@@ -0,0 +1,9 @@
+QA output created by 728
+Allocating 52428800 memory.
+Dirtying memory.
+Now mlocking memory.
+Allocating 52428800 memory.
+Dirtying memory.
+Now activating swapfile2.
+Now deactivating swapfile1.
+Silence is golden