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