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