Faulting and dirtying one page of a swapped-out large folio can leave
sibling PTEs referring to its existing offload-only swap allocation.
Exercise this path with zram behind dm-delay: ordinary reclaim must not
rewrite the allocation, while proactive reclaim must still be able to.

Check backing-device writes, refusal counters and data integrity. Derive
the folio and expected I/O sizes from the architecture's PMD huge-page
size. Keep this test separate from basic routing coverage because it
also requires transparent huge pages, MADV_COLLAPSE and dm-delay.

Signed-off-by: Matthias Goergens <[email protected]>
---
 tools/testing/selftests/zram/Makefile       |   2 +-
 tools/testing/selftests/zram/config         |   4 +
 tools/testing/selftests/zram/swap_offload.c | 182 +++++++++-
 tools/testing/selftests/zram/zram05.sh      | 360 ++++++++++++++++++++
 4 files changed, 546 insertions(+), 2 deletions(-)
 create mode 100755 tools/testing/selftests/zram/zram05.sh

diff --git a/tools/testing/selftests/zram/Makefile 
b/tools/testing/selftests/zram/Makefile
index 781d10a20e56..b83e6a1563b2 100644
--- a/tools/testing/selftests/zram/Makefile
+++ b/tools/testing/selftests/zram/Makefile
@@ -2,7 +2,7 @@
 all:
 
 TEST_GEN_FILES := swap_offload workingset_offload
-TEST_PROGS := zram.sh zram03.sh zram04.sh
+TEST_PROGS := zram.sh zram03.sh zram04.sh zram05.sh
 TEST_FILES := zram01.sh zram02.sh zram_lib.sh
 EXTRA_CLEAN := err.log
 
diff --git a/tools/testing/selftests/zram/config 
b/tools/testing/selftests/zram/config
index c59b8c3806a5..018429d8f5ca 100644
--- a/tools/testing/selftests/zram/config
+++ b/tools/testing/selftests/zram/config
@@ -1,6 +1,10 @@
 CONFIG_CGROUPS=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DELAY=y
 CONFIG_MEMCG=y
 CONFIG_SWAP=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_VM_EVENT_COUNTERS=y
 CONFIG_ZSMALLOC=y
 CONFIG_ZRAM=y
 CONFIG_ZSWAP=y
diff --git a/tools/testing/selftests/zram/swap_offload.c 
b/tools/testing/selftests/zram/swap_offload.c
index b2b94cd6ee3a..1850b3b4809f 100644
--- a/tools/testing/selftests/zram/swap_offload.c
+++ b/tools/testing/selftests/zram/swap_offload.c
@@ -3,6 +3,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <sched.h>
 #include <signal.h>
 #include <stdio.h>
@@ -17,6 +18,7 @@
 #define SWAP_FLAG_DISCARD_ONCE 0x20000
 #define SWAP_FLAG_DISCARD_PAGES        0x40000
 #define SWAP_FLAG_OFFLOAD_ONLY 0x80000
+#define KSFT_SKIP      4
 
 static int activate(const char *path, int priority, int discard_flags)
 {
@@ -201,11 +203,187 @@ static int allocate(const char *size_arg, const char 
*procs,
                pause();
 }
 
+static int create_marker(const char *path, int write_pid)
+{
+       char *temporary;
+       int fd, ret = 1;
+
+       if (asprintf(&temporary, "%s.XXXXXX", path) < 0) {
+               perror("asprintf marker path");
+               return 1;
+       }
+       fd = mkstemp(temporary);
+       if (fd < 0) {
+               perror(path);
+               goto out_free;
+       }
+       if (write_pid && dprintf(fd, "%d\n", getpid()) < 0) {
+               perror("write marker");
+               close(fd);
+               goto out_unlink;
+       }
+       if (close(fd)) {
+               perror("close marker");
+               goto out_unlink;
+       }
+       /* Publish only after the payload is complete for the shell reader. */
+       if (rename(temporary, path)) {
+               perror("publish marker");
+               goto out_unlink;
+       }
+       ret = 0;
+out_unlink:
+       unlink(temporary);
+out_free:
+       free(temporary);
+       return ret;
+}
+
+static unsigned char retained_byte(unsigned long offset, long page_size,
+                                  int touched)
+{
+       unsigned char value = offset / page_size % 251 + 1;
+
+       if (touched && !offset)
+               value ^= 0x5a;
+       return value;
+}
+
+static int verify_retained(unsigned char *memory, unsigned long size,
+                          long page_size, int full)
+{
+       unsigned long limit = full ? size : 1;
+
+       for (unsigned long offset = 0; offset < limit; offset++) {
+               unsigned char expected = retained_byte(offset, page_size, 1);
+
+               if (memory[offset] != expected) {
+                       fprintf(stderr,
+                               "retained data mismatch at %lu: got %u, 
expected %u\n",
+                               offset, memory[offset], expected);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int retained(const char *size_arg, const char *procs,
+                   const char *ready, const char *touched,
+                   const char *verified)
+{
+       unsigned long allocation_size, mapping_start, size;
+       unsigned char *mapping, *memory;
+       char *end;
+       sigset_t signals;
+       long page_size;
+       int signal;
+
+       errno = 0;
+       size = strtoul(size_arg, &end, 0);
+       if (errno || *end || !size || (size & (size - 1))) {
+               fprintf(stderr, "allocation size must be a power of two\n");
+               return 1;
+       }
+       if (join_cgroup(procs))
+               return 1;
+
+       page_size = sysconf(_SC_PAGESIZE);
+       if (page_size <= 0) {
+               perror("sysconf _SC_PAGESIZE");
+               return 1;
+       }
+       if (size < (unsigned long)page_size || size % page_size) {
+               fprintf(stderr, "allocation size must contain whole pages\n");
+               return 1;
+       }
+       if (size > ULONG_MAX / 2) {
+               fprintf(stderr, "allocation size is too large\n");
+               return 1;
+       }
+       allocation_size = size * 2;
+       mapping = mmap(NULL, allocation_size, PROT_READ | PROT_WRITE,
+                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (mapping == MAP_FAILED) {
+               perror("mmap");
+               return 1;
+       }
+       mapping_start = (unsigned long)mapping;
+       memory = (unsigned char *)((mapping_start + size - 1) & ~(size - 1));
+       if ((unsigned long)memory != mapping_start)
+               munmap((void *)mapping_start,
+                      (unsigned long)memory - mapping_start);
+       munmap(memory + size, allocation_size - size -
+              ((unsigned long)memory - mapping_start));
+       if (madvise(memory, size, MADV_HUGEPAGE)) {
+               perror("madvise MADV_HUGEPAGE");
+               return 1;
+       }
+       for (unsigned long offset = 0; offset < size; offset += page_size)
+               memset(memory + offset, retained_byte(offset, page_size, 0),
+                      page_size);
+#ifdef MADV_COLLAPSE
+       if (madvise(memory, size, MADV_COLLAPSE)) {
+               int error = errno;
+
+               perror("madvise MADV_COLLAPSE");
+               if (error == EAGAIN || error == EINVAL || error == ENOMEM)
+                       return KSFT_SKIP;
+               return 1;
+       }
+#else
+       fprintf(stderr, "MADV_COLLAPSE is unavailable\n");
+       return KSFT_SKIP;
+#endif
+
+       sigemptyset(&signals);
+       sigaddset(&signals, SIGUSR1);
+       sigaddset(&signals, SIGUSR2);
+       sigaddset(&signals, SIGALRM);
+       if (sigprocmask(SIG_BLOCK, &signals, NULL)) {
+               perror("sigprocmask");
+               return 1;
+       }
+       if (create_marker(ready, 1))
+               return 1;
+
+       for (;;) {
+               errno = sigwait(&signals, &signal);
+               if (errno) {
+                       perror("sigwait");
+                       return 1;
+               }
+               if (signal == SIGUSR1) {
+                       unsigned char value;
+
+                       if (mprotect(memory, page_size, PROT_READ)) {
+                               perror("mprotect read");
+                               return 1;
+                       }
+                       value = memory[0];
+                       if (mprotect(memory, page_size, PROT_READ | 
PROT_WRITE)) {
+                               perror("mprotect write");
+                               return 1;
+                       }
+                       memory[0] = value ^ 0x5a;
+                       if (create_marker(touched, 0))
+                               return 1;
+               } else {
+                       if (verify_retained(memory, size, page_size,
+                                           signal == SIGALRM))
+                               return 1;
+                       if (create_marker(verified, 0))
+                               return 1;
+               }
+       }
+}
+
 int main(int argc, char **argv)
 {
        if (argc == 4 && !strcmp(argv[1], "activate"))
                return activate(argv[2], atoi(argv[3]),
                                SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE);
+       if (argc == 4 && !strcmp(argv[1], "activate-no-discard"))
+               return activate(argv[2], atoi(argv[3]), 0);
        if (argc == 4 && !strcmp(argv[1], "reject-page-discard"))
                return reject_page_discard(argv[2], atoi(argv[3]));
        if (argc == 4 && !strcmp(argv[1], "accept-discard-once-pages"))
@@ -214,9 +392,11 @@ int main(int argc, char **argv)
                return pin_to_one_cpu(argv[2]);
        if (argc == 6 && !strcmp(argv[1], "allocate"))
                return allocate(argv[2], argv[3], argv[4], argv[5]);
+       if (argc == 7 && !strcmp(argv[1], "retained"))
+               return retained(argv[2], argv[3], argv[4], argv[5], argv[6]);
 
        fprintf(stderr,
-               "usage: %s activate DEVICE PRIORITY | reject-page-discard 
DEVICE PRIORITY | accept-discard-once-pages DEVICE PRIORITY | pin PID | 
allocate BYTES CGROUP.PROCS READY VERIFIED\n",
+               "usage: %s activate|activate-no-discard DEVICE PRIORITY | 
reject-page-discard DEVICE PRIORITY | accept-discard-once-pages DEVICE PRIORITY 
| pin PID | allocate BYTES CGROUP.PROCS READY VERIFIED | retained BYTES 
CGROUP.PROCS READY TOUCHED VERIFIED\n",
                argv[0]);
        return 1;
 }
diff --git a/tools/testing/selftests/zram/zram05.sh 
b/tools/testing/selftests/zram/zram05.sh
new file mode 100755
index 000000000000..e4bbf6bfa0e9
--- /dev/null
+++ b/tools/testing/selftests/zram/zram05.sh
@@ -0,0 +1,360 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Test retained offload-only entries under ordinary and proactive reclaim.
+
+set -eu
+
+# shellcheck source=zram_lib.sh
+. ./zram_lib.sh
+
+TCID="zram05"
+cg="/sys/fs/cgroup/zram-retained-$$"
+cgroup_root="/sys/fs/cgroup"
+tmp="${TMPDIR:-/var/tmp}/zram-retained-$$"
+ready="$tmp/ready"
+touched="$tmp/touched"
+verified="$tmp/verified"
+holder_pid=""
+safe=""
+safe_active=0
+offload_backing=""
+offload=""
+offload_active=0
+dm_name="zram-retained-$$"
+dm_active=0
+dm_node_created=0
+zswap_enabled=""
+thp_size=0
+thp_kib=0
+thp_sectors=0
+page_kib=0
+
+fail()
+{
+       echo "$TCID: [FAIL] $*" >&2
+       exit 1
+}
+
+skip()
+{
+       echo "$TCID: [SKIP] $*" >&2
+       exit "$ksft_skip"
+}
+
+cleanup()
+{
+       set +e
+       if [ -n "$holder_pid" ]; then
+               kill "$holder_pid"
+               wait "$holder_pid"
+       fi
+       if [ "$offload_active" -eq 1 ]; then
+               swapoff "$offload" >/dev/null 2>&1
+       fi
+       if [ "$safe_active" -eq 1 ]; then
+               swapoff "$safe" >/dev/null 2>&1
+       fi
+       if [ "$dm_active" -eq 1 ]; then
+               dmsetup --noudevsync --noudevrules remove "$dm_name" \
+                       >/dev/null 2>&1
+       fi
+       if [ "$dm_node_created" -eq 1 ]; then
+               rm -f "$offload"
+       fi
+       rm -rf "$tmp"
+       rmdir "$cg"
+       cgroup_disable_memory_controller "$cgroup_root"
+       if [ "$dev_end" -ge "$dev_start" ]; then
+               zram_cleanup
+       fi
+       if [ -n "$zswap_enabled" ]; then
+               echo "$zswap_enabled" > /sys/module/zswap/parameters/enabled
+       fi
+}
+
+wait_file()
+{
+       for _ in $(seq 1 400); do
+               [ -e "$1" ] && return 0
+               if [ -n "$holder_pid" ]; then
+                       holder_state=$(awk '{ print $3 }' \
+                               "/proc/$holder_pid/stat" 2>/dev/null || :)
+               else
+                       holder_state=""
+               fi
+               if [ -n "$holder_pid" ] &&
+                  { ! kill -0 "$holder_pid" 2>/dev/null ||
+                    [ "$holder_state" = Z ]; }; then
+                       if wait "$holder_pid"; then
+                               helper_status=0
+                       else
+                               helper_status=$?
+                       fi
+                       holder_pid=""
+                       return 2
+               fi
+               sleep 0.05
+       done
+       return 1
+}
+
+require_helper_file()
+{
+       if wait_file "$1"; then
+               return 0
+       else
+               status=$?
+       fi
+       if [ "$status" -eq 2 ]; then
+               [ "$helper_status" -eq "$ksft_skip" ] && skip "$2 is 
unavailable"
+               fail "retained helper exited with status $helper_status while 
$2"
+       fi
+       fail "retained helper timed out while $2"
+}
+
+written_sectors()
+{
+       awk '{ print $7 }' "/sys/block/${offload_backing##*/}/stat"
+}
+
+vmstat_value()
+{
+       awk -v name="$1" '$1 == name { print $2 }' /proc/vmstat
+}
+
+wait_offload_quiet()
+{
+       # A bio queued inside dm-delay is not yet visible in either the backing
+       # device statistics or the mapped device's inflight counters.
+       sleep 4
+       previous=-1
+       stable=0
+       for _ in $(seq 1 240); do
+               current=$(written_sectors)
+               read -r reads writes < "$dm_inflight"
+               if [ "$current" -eq "$previous" ] && \
+                  [ "$reads" -eq 0 ] && [ "$writes" -eq 0 ]; then
+                       stable=$((stable + 1))
+                       [ "$stable" -ge 20 ] && return 0
+               else
+                       stable=0
+               fi
+               previous=$current
+               sleep 0.05
+       done
+       return 1
+}
+
+check_prereqs
+[ -x ./swap_offload ] || skip "swap_offload helper is unavailable"
+command -v dmsetup >/dev/null 2>&1 || skip "dmsetup is unavailable"
+command -v blockdev >/dev/null 2>&1 || skip "blockdev is unavailable"
+[ -e /sys/fs/cgroup/cgroup.controllers ] ||
+       skip "cgroup v2 controllers are unavailable"
+grep -qw memory /sys/fs/cgroup/cgroup.controllers ||
+       skip "memory controller is unavailable"
+[ -d /sys/kernel/mm/transparent_hugepage ] ||
+       skip "transparent huge pages are unavailable"
+[ -r /sys/kernel/mm/transparent_hugepage/hpage_pmd_size ] ||
+       skip "PMD huge-page size is unavailable"
+grep -q '^swpout_offload_refused ' /proc/vmstat ||
+       skip "offload refusal counters are unavailable"
+
+thp_size=$(cat /sys/kernel/mm/transparent_hugepage/hpage_pmd_size)
+case "$thp_size" in
+       ''|*[!0-9]*) skip "invalid PMD huge-page size: $thp_size" ;;
+esac
+[ "$thp_size" -gt 0 ] || skip "PMD huge-page size is zero"
+page_kib=$(awk '/KernelPageSize:/ { print $2; exit }' /proc/self/smaps)
+[ "${page_kib:-0}" -gt 0 ] || skip "cannot determine the base page size"
+page_size=$((page_kib * 1024))
+[ "$page_size" -gt 0 ] || skip "base page size is zero"
+[ $((thp_size % page_size)) -eq 0 ] ||
+       skip "PMD huge-page size is not page aligned"
+thp_kib=$((thp_size / 1024))
+thp_sectors=$((thp_size / 512))
+expected_retained_kib=$((thp_kib - page_kib))
+expected_refused=$((thp_size / page_size))
+
+mkdir "$tmp"
+trap cleanup EXIT
+trap 'exit 129' HUP
+trap 'exit 130' INT
+trap 'exit 143' TERM
+dmsetup targets > "$tmp/dm-targets" 2>/dev/null ||
+       skip "cannot query device-mapper targets"
+grep -q '^delay[[:space:]]' "$tmp/dm-targets" ||
+       skip "device-mapper delay target is unavailable"
+
+if [ -e /sys/module/zswap/parameters/enabled ]; then
+       zswap_enabled=$(cat /sys/module/zswap/parameters/enabled)
+       echo N > /sys/module/zswap/parameters/enabled ||
+               skip "cannot disable zswap"
+fi
+
+# Swap priorities are global.  The ordinary zram device must be preferred to
+# any pre-existing swap, while the delayed offload device remains first for
+# eligible proactive reclaim.
+max_prio=$(awk 'BEGIN { max = -1 } NR > 1 && $5 > max { max = $5 } END { print 
max }' /proc/swaps)
+[ "$max_prio" -le 32765 ] ||
+       skip "cannot outrank existing swap priority $max_prio"
+safe_prio=$((max_prio + 1))
+offload_prio=$((max_prio + 2))
+
+dev_num=2
+zram_size=$((thp_size * 4))
+[ "$zram_size" -ge 67108864 ] || zram_size=67108864
+zram_sizes="$zram_size $zram_size"
+zram_load
+zram_set_disksizes
+safe="/dev/zram${dev_start}"
+offload_backing="/dev/zram$((dev_start + 1))"
+
+sectors=$(blockdev --getsz "$offload_backing") ||
+       skip "cannot read offload backing size"
+[ "$sectors" -gt 0 ] || skip "offload backing has zero size"
+dm_table="0 $sectors delay $offload_backing 0 0 $offload_backing 0 3000"
+dmsetup --noudevsync --noudevrules create "$dm_name" --table "$dm_table" ||
+       skip "cannot create delayed offload device"
+dm_active=1
+dmsetup --noudevsync --noudevrules info --columns --noheadings \
+       --separator ' ' -o major,minor "$dm_name" > "$tmp/dm-devno" ||
+       skip "cannot identify delayed offload device"
+read -r dm_major dm_minor < "$tmp/dm-devno"
+offload="/dev/mapper/$dm_name"
+if [ ! -e "$offload" ]; then
+       mkdir -p /dev/mapper
+       mknod "$offload" b "$dm_major" "$dm_minor" ||
+               skip "cannot create delayed offload device node"
+       dm_node_created=1
+fi
+dm_inflight="/sys/dev/block/$dm_major:$dm_minor/inflight"
+[ -r "$dm_inflight" ] || skip "cannot observe delayed offload I/O"
+
+mkswap "$safe" >/dev/null || fail "cannot initialise safe swap"
+mkswap "$offload" >/dev/null || fail "cannot initialise offload swap"
+swapon -p "$safe_prio" "$safe" || fail "cannot activate safe swap"
+dev_makeswap=$dev_start
+safe_active=1
+./swap_offload activate-no-discard "$offload" "$offload_prio" ||
+       fail "cannot activate offload-only swap"
+offload_active=1
+
+cgroup_enable_memory_controller "$cgroup_root" ||
+       skip "cannot enable the cgroup v2 memory controller"
+mkdir "$cg" || skip "cannot create test cgroup"
+[ -e "$cg/memory.max" ] ||
+       skip "cgroup v2 memory controller is unavailable"
+echo max > "$cg/memory.swap.max"
+
+./swap_offload retained "$thp_size" "$cg/cgroup.procs" "$ready" \
+       "$touched" "$verified" &
+holder_pid=$!
+require_helper_file "$ready" "creating a PMD-sized anonymous huge folio"
+read -r reported_pid < "$ready"
+[ "$reported_pid" -eq "$holder_pid" ] ||
+       fail "retained helper reported the wrong pid"
+anon_huge_kib=$(awk '/AnonHugePages:/ { print $2 }' \
+       "/proc/$holder_pid/smaps_rollup")
+[ "${anon_huge_kib:-0}" -ge "$thp_kib" ] ||
+       skip "anonymous huge folio was not created"
+echo "$TCID: anonymous_huge_kib=$anon_huge_kib"
+
+# This is the only reclaim before the retained entry is made dirty.  It puts
+# the whole folio on offload-only swap so the later single-page fault leaves
+# sibling swap PTEs referring to the existing slot.
+echo "$thp_size swappiness=max" > "$cg/memory.reclaim" 2>/dev/null || :
+wait_offload_quiet || fail "offload device did not quiesce after setup"
+kill -0 "$holder_pid" || fail "retained helper died during setup reclaim"
+kill -USR1 "$holder_pid" || fail "cannot request the dirty-page transition"
+require_helper_file "$touched" "faulting and dirtying the retained entry"
+retained_kib=$(awk '/VmSwap:/ { print $2 }' "/proc/$holder_pid/status")
+[ "${retained_kib:-0}" -eq "$expected_retained_kib" ] ||
+       fail "retained $retained_kib KiB, expected $expected_retained_kib KiB"
+echo "$TCID: retained_kib=$retained_kib"
+
+wait_offload_quiet || fail "offload device did not quiesce before pressure"
+ordinary_before=$(written_sectors)
+refused_before=$(vmstat_value swpout_offload_refused)
+echo $((thp_size / 2)) > "$cg/memory.high" ||
+       fail "ordinary pressure reclaim failed"
+wait_offload_quiet || fail "offload device did not quiesce after pressure"
+ordinary_after=$(written_sectors)
+refused_after=$(vmstat_value swpout_offload_refused)
+ordinary_writes=$((ordinary_after - ordinary_before))
+refused_pages=$((refused_after - refused_before))
+echo "$TCID: ordinary_sectors=$ordinary_writes refused_pages=$refused_pages"
+[ "$ordinary_writes" -eq 0 ] ||
+       fail "ordinary reclaim wrote $ordinary_writes offload sectors"
+[ "$refused_pages" -ge "$expected_refused" ] ||
+       fail "ordinary reclaim refused $refused_pages pages, expected at least 
$expected_refused"
+kill -0 "$holder_pid" || fail "retained helper died after refused write"
+rm -f "$verified"
+kill -USR2 "$holder_pid" || fail "cannot request dirty-byte verification"
+require_helper_file "$verified" "verifying the dirty retained byte"
+
+# Proactive reclaim must still be able to rewrite the retained PMD-sized slot.
+# Repeated requests make the test insensitive to a short-lived writeback
+# collision; the backing-sector delta still requires exactly one folio write.
+wait_offload_quiet || fail "offload device did not quiesce before recovery"
+recovery_before=$(written_sectors)
+echo max > "$cg/memory.high"
+for _ in $(seq 1 8); do
+       echo "$thp_size swappiness=max" > "$cg/memory.reclaim" 2>/dev/null || :
+done
+wait_offload_quiet || fail "offload device did not quiesce after recovery"
+recovery_after=$(written_sectors)
+recovery_writes=$((recovery_after - recovery_before))
+echo "$TCID: recovery_sectors=$recovery_writes"
+[ "$recovery_writes" -eq "$thp_sectors" ] ||
+       fail "proactive recovery wrote $recovery_writes sectors, expected 
$thp_sectors"
+kill -0 "$holder_pid" || fail "retained helper died during recovery"
+rm -f "$verified"
+kill -ALRM "$holder_pid" || fail "cannot request full data verification"
+require_helper_file "$verified" "verifying all retained data"
+kill -0 "$holder_pid" || fail "retained helper failed full data verification"
+
+kill "$holder_pid" || fail "cannot stop retained helper"
+if wait "$holder_pid"; then
+       :
+else
+       status=$?
+       [ "$status" -eq 143 ] || fail "retained helper exited with status 
$status"
+fi
+holder_pid=""
+swapoff "$offload" || fail "cannot deactivate offload-only swap"
+offload_active=0
+swapoff "$safe" || fail "cannot deactivate safe swap"
+safe_active=0
+dev_makeswap=-1
+dmsetup --noudevsync --noudevrules remove "$dm_name" ||
+       fail "cannot remove delayed offload device"
+dm_active=0
+if [ "$dm_node_created" -eq 1 ]; then
+       rm -f "$offload" || fail "cannot remove delayed offload device node"
+       dm_node_created=0
+fi
+rmdir "$cg" || fail "cannot remove test cgroup"
+cg=""
+cgroup_disable_memory_controller "$cgroup_root" ||
+       fail "cannot restore the cgroup memory controller"
+for i in $(seq "$dev_start" "$dev_end"); do
+       echo 1 > "/sys/block/zram${i}/reset" || fail "cannot reset zram$i"
+       if [ "$sys_control" -eq 1 ]; then
+               echo "$i" > /sys/class/zram-control/hot_remove ||
+                       fail "cannot remove zram$i"
+       fi
+done
+if [ "$module_load" -eq 1 ]; then
+       rmmod zram || fail "cannot unload zram"
+fi
+dev_end=-1
+if [ -n "$zswap_enabled" ]; then
+       echo "$zswap_enabled" > /sys/module/zswap/parameters/enabled ||
+               fail "cannot restore zswap"
+       zswap_enabled=""
+fi
+rm -rf "$tmp" || fail "cannot remove temporary files"
+tmp=""
+
+echo "$TCID: [PASS]"
-- 
2.55.0


Reply via email to