1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker * Copyright (C) 2012-2017 Red Hat, Inc.
4*49cdfc7eSAndroid Build Coastguard Worker */
5*49cdfc7eSAndroid Build Coastguard Worker
6*49cdfc7eSAndroid Build Coastguard Worker /*\
7*49cdfc7eSAndroid Build Coastguard Worker * [Description]
8*49cdfc7eSAndroid Build Coastguard Worker *
9*49cdfc7eSAndroid Build Coastguard Worker * Detect heavy swapping during first time swap use.
10*49cdfc7eSAndroid Build Coastguard Worker *
11*49cdfc7eSAndroid Build Coastguard Worker * This case is used for testing kernel commit:
12*49cdfc7eSAndroid Build Coastguard Worker * 50a15981a1fa ("[S390] reference bit testing for unmapped pages")
13*49cdfc7eSAndroid Build Coastguard Worker *
14*49cdfc7eSAndroid Build Coastguard Worker * The upstream commit fixed a issue on s390/x platform that heavy
15*49cdfc7eSAndroid Build Coastguard Worker * swapping might occur in some condition, however since the patch
16*49cdfc7eSAndroid Build Coastguard Worker * was quite general, this testcase will be run on all supported
17*49cdfc7eSAndroid Build Coastguard Worker * platforms to ensure no regression been introduced.
18*49cdfc7eSAndroid Build Coastguard Worker *
19*49cdfc7eSAndroid Build Coastguard Worker * Details of the kernel fix:
20*49cdfc7eSAndroid Build Coastguard Worker *
21*49cdfc7eSAndroid Build Coastguard Worker * On x86 a page without a mapper is by definition not referenced / old.
22*49cdfc7eSAndroid Build Coastguard Worker * The s390 architecture keeps the reference bit in the storage key and
23*49cdfc7eSAndroid Build Coastguard Worker * the current code will check the storage key for page without a mapper.
24*49cdfc7eSAndroid Build Coastguard Worker * This leads to an interesting effect: the first time an s390 system
25*49cdfc7eSAndroid Build Coastguard Worker * needs to write pages to swap it only finds referenced pages. This
26*49cdfc7eSAndroid Build Coastguard Worker * causes a lot of pages to get added and written to the swap device.
27*49cdfc7eSAndroid Build Coastguard Worker * To avoid this behaviour change page_referenced to query the storage
28*49cdfc7eSAndroid Build Coastguard Worker * key only if there is a mapper of the page.
29*49cdfc7eSAndroid Build Coastguard Worker *
30*49cdfc7eSAndroid Build Coastguard Worker * [Algorithm]
31*49cdfc7eSAndroid Build Coastguard Worker *
32*49cdfc7eSAndroid Build Coastguard Worker * Try to allocate memory which size is slightly larger than current
33*49cdfc7eSAndroid Build Coastguard Worker * available memory. After allocation done, continue loop for a while
34*49cdfc7eSAndroid Build Coastguard Worker * and calculate the used swap size. The used swap size should be small
35*49cdfc7eSAndroid Build Coastguard Worker * enough, else it indicates that heavy swapping is occurred unexpectedly.
36*49cdfc7eSAndroid Build Coastguard Worker */
37*49cdfc7eSAndroid Build Coastguard Worker
38*49cdfc7eSAndroid Build Coastguard Worker #include <sys/types.h>
39*49cdfc7eSAndroid Build Coastguard Worker #include <sys/wait.h>
40*49cdfc7eSAndroid Build Coastguard Worker #include <stdlib.h>
41*49cdfc7eSAndroid Build Coastguard Worker #include <string.h>
42*49cdfc7eSAndroid Build Coastguard Worker #include <unistd.h>
43*49cdfc7eSAndroid Build Coastguard Worker #include "tst_safe_stdio.h"
44*49cdfc7eSAndroid Build Coastguard Worker #include "mem.h"
45*49cdfc7eSAndroid Build Coastguard Worker
46*49cdfc7eSAndroid Build Coastguard Worker /* allow swapping 1 * phy_mem in maximum */
47*49cdfc7eSAndroid Build Coastguard Worker #define COE_DELTA 1
48*49cdfc7eSAndroid Build Coastguard Worker /* will try to alloc 1.3 * phy_mem */
49*49cdfc7eSAndroid Build Coastguard Worker #define COE_SLIGHT_OVER 0.3
50*49cdfc7eSAndroid Build Coastguard Worker #define MEM_SIZE 1024 * 1024
51*49cdfc7eSAndroid Build Coastguard Worker
52*49cdfc7eSAndroid Build Coastguard Worker static void init_meminfo(void);
53*49cdfc7eSAndroid Build Coastguard Worker static void do_alloc(int allow_raise);
54*49cdfc7eSAndroid Build Coastguard Worker static void check_swapping(void);
55*49cdfc7eSAndroid Build Coastguard Worker
56*49cdfc7eSAndroid Build Coastguard Worker static long mem_available_init;
57*49cdfc7eSAndroid Build Coastguard Worker static long swap_free_init;
58*49cdfc7eSAndroid Build Coastguard Worker static long mem_over;
59*49cdfc7eSAndroid Build Coastguard Worker static long mem_over_max;
60*49cdfc7eSAndroid Build Coastguard Worker static pid_t pid;
61*49cdfc7eSAndroid Build Coastguard Worker static unsigned int start_runtime;
62*49cdfc7eSAndroid Build Coastguard Worker
test_swapping(void)63*49cdfc7eSAndroid Build Coastguard Worker static void test_swapping(void)
64*49cdfc7eSAndroid Build Coastguard Worker {
65*49cdfc7eSAndroid Build Coastguard Worker FILE *file;
66*49cdfc7eSAndroid Build Coastguard Worker char line[PATH_MAX];
67*49cdfc7eSAndroid Build Coastguard Worker
68*49cdfc7eSAndroid Build Coastguard Worker start_runtime = tst_remaining_runtime();
69*49cdfc7eSAndroid Build Coastguard Worker
70*49cdfc7eSAndroid Build Coastguard Worker file = SAFE_FOPEN("/proc/swaps", "r");
71*49cdfc7eSAndroid Build Coastguard Worker while (fgets(line, sizeof(line), file)) {
72*49cdfc7eSAndroid Build Coastguard Worker if (strstr(line, "/dev/zram")) {
73*49cdfc7eSAndroid Build Coastguard Worker SAFE_FCLOSE(file);
74*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "zram-swap is being used!");
75*49cdfc7eSAndroid Build Coastguard Worker }
76*49cdfc7eSAndroid Build Coastguard Worker }
77*49cdfc7eSAndroid Build Coastguard Worker SAFE_FCLOSE(file);
78*49cdfc7eSAndroid Build Coastguard Worker
79*49cdfc7eSAndroid Build Coastguard Worker init_meminfo();
80*49cdfc7eSAndroid Build Coastguard Worker
81*49cdfc7eSAndroid Build Coastguard Worker switch (pid = SAFE_FORK()) {
82*49cdfc7eSAndroid Build Coastguard Worker case 0:
83*49cdfc7eSAndroid Build Coastguard Worker TST_PRINT_MEMINFO();
84*49cdfc7eSAndroid Build Coastguard Worker do_alloc(0);
85*49cdfc7eSAndroid Build Coastguard Worker TST_PRINT_MEMINFO();
86*49cdfc7eSAndroid Build Coastguard Worker do_alloc(1);
87*49cdfc7eSAndroid Build Coastguard Worker exit(0);
88*49cdfc7eSAndroid Build Coastguard Worker default:
89*49cdfc7eSAndroid Build Coastguard Worker check_swapping();
90*49cdfc7eSAndroid Build Coastguard Worker }
91*49cdfc7eSAndroid Build Coastguard Worker }
92*49cdfc7eSAndroid Build Coastguard Worker
init_meminfo(void)93*49cdfc7eSAndroid Build Coastguard Worker static void init_meminfo(void)
94*49cdfc7eSAndroid Build Coastguard Worker {
95*49cdfc7eSAndroid Build Coastguard Worker swap_free_init = SAFE_READ_MEMINFO("SwapFree:");
96*49cdfc7eSAndroid Build Coastguard Worker mem_available_init = tst_available_mem();
97*49cdfc7eSAndroid Build Coastguard Worker mem_over = mem_available_init * COE_SLIGHT_OVER;
98*49cdfc7eSAndroid Build Coastguard Worker mem_over_max = mem_available_init * COE_DELTA;
99*49cdfc7eSAndroid Build Coastguard Worker
100*49cdfc7eSAndroid Build Coastguard Worker if (swap_free_init < mem_over_max)
101*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "Not enough swap space to test: swap_free_init(%ldkB) < mem_over_max(%ldkB)",
102*49cdfc7eSAndroid Build Coastguard Worker swap_free_init, mem_over_max);
103*49cdfc7eSAndroid Build Coastguard Worker }
104*49cdfc7eSAndroid Build Coastguard Worker
memset_blocks(char * ptr,int mem_count,int sleep_time_ms)105*49cdfc7eSAndroid Build Coastguard Worker static void memset_blocks(char *ptr, int mem_count, int sleep_time_ms) {
106*49cdfc7eSAndroid Build Coastguard Worker for (int i = 0; i < mem_count / 1024; i++) {
107*49cdfc7eSAndroid Build Coastguard Worker memset(ptr + (i * MEM_SIZE), 1, MEM_SIZE);
108*49cdfc7eSAndroid Build Coastguard Worker usleep(sleep_time_ms * 1000);
109*49cdfc7eSAndroid Build Coastguard Worker }
110*49cdfc7eSAndroid Build Coastguard Worker }
111*49cdfc7eSAndroid Build Coastguard Worker
do_alloc(int allow_raise)112*49cdfc7eSAndroid Build Coastguard Worker static void do_alloc(int allow_raise)
113*49cdfc7eSAndroid Build Coastguard Worker {
114*49cdfc7eSAndroid Build Coastguard Worker long mem_count;
115*49cdfc7eSAndroid Build Coastguard Worker void *s;
116*49cdfc7eSAndroid Build Coastguard Worker
117*49cdfc7eSAndroid Build Coastguard Worker if (allow_raise == 1)
118*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "available physical memory: %ld MB",
119*49cdfc7eSAndroid Build Coastguard Worker mem_available_init / 1024);
120*49cdfc7eSAndroid Build Coastguard Worker
121*49cdfc7eSAndroid Build Coastguard Worker mem_count = mem_available_init + mem_over;
122*49cdfc7eSAndroid Build Coastguard Worker
123*49cdfc7eSAndroid Build Coastguard Worker if (allow_raise == 1)
124*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "try to allocate: %ld MB", mem_count / 1024);
125*49cdfc7eSAndroid Build Coastguard Worker s = SAFE_MALLOC(mem_count * 1024);
126*49cdfc7eSAndroid Build Coastguard Worker memset_blocks(s, mem_count, 1);
127*49cdfc7eSAndroid Build Coastguard Worker
128*49cdfc7eSAndroid Build Coastguard Worker if ((allow_raise == 1) && (raise(SIGSTOP) == -1)) {
129*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "memory allocated: %ld MB", mem_count / 1024);
130*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK | TERRNO, "kill");
131*49cdfc7eSAndroid Build Coastguard Worker }
132*49cdfc7eSAndroid Build Coastguard Worker
133*49cdfc7eSAndroid Build Coastguard Worker free(s);
134*49cdfc7eSAndroid Build Coastguard Worker }
135*49cdfc7eSAndroid Build Coastguard Worker
check_swapping(void)136*49cdfc7eSAndroid Build Coastguard Worker static void check_swapping(void)
137*49cdfc7eSAndroid Build Coastguard Worker {
138*49cdfc7eSAndroid Build Coastguard Worker int status;
139*49cdfc7eSAndroid Build Coastguard Worker long swap_free_now, swapped;
140*49cdfc7eSAndroid Build Coastguard Worker
141*49cdfc7eSAndroid Build Coastguard Worker /* wait child stop */
142*49cdfc7eSAndroid Build Coastguard Worker SAFE_WAITPID(pid, &status, WUNTRACED);
143*49cdfc7eSAndroid Build Coastguard Worker if (!WIFSTOPPED(status))
144*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK, "child was not stopped.");
145*49cdfc7eSAndroid Build Coastguard Worker
146*49cdfc7eSAndroid Build Coastguard Worker /* Still occupying memory, loop for a while */
147*49cdfc7eSAndroid Build Coastguard Worker while (tst_remaining_runtime() > start_runtime/2) {
148*49cdfc7eSAndroid Build Coastguard Worker swap_free_now = SAFE_READ_MEMINFO("SwapFree:");
149*49cdfc7eSAndroid Build Coastguard Worker sleep(1);
150*49cdfc7eSAndroid Build Coastguard Worker long diff = labs(swap_free_now - SAFE_READ_MEMINFO("SwapFree:"));
151*49cdfc7eSAndroid Build Coastguard Worker
152*49cdfc7eSAndroid Build Coastguard Worker if (diff < 10)
153*49cdfc7eSAndroid Build Coastguard Worker break;
154*49cdfc7eSAndroid Build Coastguard Worker
155*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "SwapFree difference %li", diff);
156*49cdfc7eSAndroid Build Coastguard Worker }
157*49cdfc7eSAndroid Build Coastguard Worker
158*49cdfc7eSAndroid Build Coastguard Worker swapped = SAFE_READ_PROC_STATUS(pid, "VmSwap:");
159*49cdfc7eSAndroid Build Coastguard Worker if (swapped > mem_over_max) {
160*49cdfc7eSAndroid Build Coastguard Worker TST_PRINT_MEMINFO();
161*49cdfc7eSAndroid Build Coastguard Worker kill(pid, SIGCONT);
162*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TFAIL, "heavy swapping detected: %ld MB swapped",
163*49cdfc7eSAndroid Build Coastguard Worker swapped / 1024);
164*49cdfc7eSAndroid Build Coastguard Worker }
165*49cdfc7eSAndroid Build Coastguard Worker
166*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "no heavy swapping detected, %ld MB swapped.",
167*49cdfc7eSAndroid Build Coastguard Worker swapped / 1024);
168*49cdfc7eSAndroid Build Coastguard Worker kill(pid, SIGCONT);
169*49cdfc7eSAndroid Build Coastguard Worker /* wait child exit */
170*49cdfc7eSAndroid Build Coastguard Worker SAFE_WAITPID(pid, &status, 0);
171*49cdfc7eSAndroid Build Coastguard Worker }
172*49cdfc7eSAndroid Build Coastguard Worker
173*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
174*49cdfc7eSAndroid Build Coastguard Worker .needs_root = 1,
175*49cdfc7eSAndroid Build Coastguard Worker .forks_child = 1,
176*49cdfc7eSAndroid Build Coastguard Worker .min_mem_avail = 10,
177*49cdfc7eSAndroid Build Coastguard Worker .max_runtime = 600,
178*49cdfc7eSAndroid Build Coastguard Worker .test_all = test_swapping,
179*49cdfc7eSAndroid Build Coastguard Worker .skip_in_compat = 1,
180*49cdfc7eSAndroid Build Coastguard Worker .needs_kconfigs = (const char *[]) {
181*49cdfc7eSAndroid Build Coastguard Worker "CONFIG_SWAP=y",
182*49cdfc7eSAndroid Build Coastguard Worker NULL
183*49cdfc7eSAndroid Build Coastguard Worker },
184*49cdfc7eSAndroid Build Coastguard Worker .tags = (const struct tst_tag[]) {
185*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "50a15981a1fa"},
186*49cdfc7eSAndroid Build Coastguard Worker {}
187*49cdfc7eSAndroid Build Coastguard Worker }
188*49cdfc7eSAndroid Build Coastguard Worker };
189