• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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