1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2023 Oracle and/or its affiliates.
4f08c3bdfSopenharmony_ci */
5f08c3bdfSopenharmony_ci
6f08c3bdfSopenharmony_ci/*\
7f08c3bdfSopenharmony_ci * [Description]
8f08c3bdfSopenharmony_ci *
9f08c3bdfSopenharmony_ci * Stress a possible race condition between memory pages allocation
10f08c3bdfSopenharmony_ci * and soft-offline of unrelated pages as explained in the commit:
11f08c3bdfSopenharmony_ci *   d4ae9916ea29 (mm: soft-offline: close the race against page allocation)
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * Control that soft-offlined pages get correctly replaced: with the
14f08c3bdfSopenharmony_ci * same content and without SIGBUS generation when accessed.
15f08c3bdfSopenharmony_ci */
16f08c3bdfSopenharmony_ci
17f08c3bdfSopenharmony_ci#include <errno.h>
18f08c3bdfSopenharmony_ci#include <mntent.h>
19f08c3bdfSopenharmony_ci#include <pthread.h>
20f08c3bdfSopenharmony_ci#include <stdio.h>
21f08c3bdfSopenharmony_ci#include <stdlib.h>
22f08c3bdfSopenharmony_ci#include <time.h>
23f08c3bdfSopenharmony_ci#include <unistd.h>
24f08c3bdfSopenharmony_ci#include <sys/types.h>
25f08c3bdfSopenharmony_ci#include <sys/klog.h>
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_ci#include "tst_test.h"
28f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h"
29f08c3bdfSopenharmony_ci#include "tst_safe_stdio.h"
30f08c3bdfSopenharmony_ci#include "lapi/mmap.h"
31f08c3bdfSopenharmony_ci
32f08c3bdfSopenharmony_ci#define NUM_LOOPS	5
33f08c3bdfSopenharmony_ci#define NUM_PAGES	32
34f08c3bdfSopenharmony_ci#define NUM_PAGES_OFFSET	5
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_ci/* Needed module to online back memory pages */
37f08c3bdfSopenharmony_ci#define HW_MODULE	"hwpoison_inject"
38f08c3bdfSopenharmony_ci
39f08c3bdfSopenharmony_cistatic pthread_t *thread_ids;
40f08c3bdfSopenharmony_cistatic int number_threads;
41f08c3bdfSopenharmony_cistatic int run_iterations;
42f08c3bdfSopenharmony_cistatic int maximum_pfns;
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic volatile int sigbus_received;
45f08c3bdfSopenharmony_cistatic pthread_cond_t sigbus_received_cv;
46f08c3bdfSopenharmony_cistatic pthread_mutex_t sigbus_received_mtx = PTHREAD_MUTEX_INITIALIZER;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_cistatic long pagesize;
49f08c3bdfSopenharmony_cistatic char beginning_tag[BUFSIZ];
50f08c3bdfSopenharmony_cistatic int hwpoison_probe;
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_cistatic void my_yield(void)
53f08c3bdfSopenharmony_ci{
54f08c3bdfSopenharmony_ci	static const struct timespec t0 = { 0, 0 };
55f08c3bdfSopenharmony_ci
56f08c3bdfSopenharmony_ci	nanosleep(&t0, NULL);
57f08c3bdfSopenharmony_ci}
58f08c3bdfSopenharmony_ci
59f08c3bdfSopenharmony_ci/* a SIGBUS received is a confirmation of test failure */
60f08c3bdfSopenharmony_cistatic void sigbus_handler(int signum LTP_ATTRIBUTE_UNUSED)
61f08c3bdfSopenharmony_ci{
62f08c3bdfSopenharmony_ci	pthread_mutex_lock(&sigbus_received_mtx);
63f08c3bdfSopenharmony_ci	sigbus_received++;
64f08c3bdfSopenharmony_ci	pthread_cond_signal(&sigbus_received_cv);
65f08c3bdfSopenharmony_ci	pthread_mutex_unlock(&sigbus_received_mtx);
66f08c3bdfSopenharmony_ci	pause();
67f08c3bdfSopenharmony_ci}
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_cistatic void *sigbus_monitor(void *arg LTP_ATTRIBUTE_UNUSED)
70f08c3bdfSopenharmony_ci{
71f08c3bdfSopenharmony_ci	pthread_mutex_lock(&sigbus_received_mtx);
72f08c3bdfSopenharmony_ci	while (!sigbus_received)
73f08c3bdfSopenharmony_ci		pthread_cond_wait(&sigbus_received_cv, &sigbus_received_mtx);
74f08c3bdfSopenharmony_ci	pthread_mutex_unlock(&sigbus_received_mtx);
75f08c3bdfSopenharmony_ci	tst_res(TFAIL, "SIGBUS Received");
76f08c3bdfSopenharmony_ci	exit(1);
77f08c3bdfSopenharmony_ci}
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci/*
80f08c3bdfSopenharmony_ci * Allocate a page and write a sentinel value into it.
81f08c3bdfSopenharmony_ci */
82f08c3bdfSopenharmony_cistatic void *allocate_write(int sentinel)
83f08c3bdfSopenharmony_ci{
84f08c3bdfSopenharmony_ci	void *p;
85f08c3bdfSopenharmony_ci	int *s;
86f08c3bdfSopenharmony_ci
87f08c3bdfSopenharmony_ci	p = SAFE_MMAP(NULL, pagesize, PROT_READ|PROT_WRITE,
88f08c3bdfSopenharmony_ci		      MAP_SHARED|MAP_ANONYMOUS, -1, 0);
89f08c3bdfSopenharmony_ci	s = (int *)p;
90f08c3bdfSopenharmony_ci	*s = sentinel;
91f08c3bdfSopenharmony_ci	return p;
92f08c3bdfSopenharmony_ci}
93f08c3bdfSopenharmony_ci
94f08c3bdfSopenharmony_ci/*
95f08c3bdfSopenharmony_ci * Verify and unmap the given page.
96f08c3bdfSopenharmony_ci */
97f08c3bdfSopenharmony_cistatic int verif_unmap(void *page, int sentinel)
98f08c3bdfSopenharmony_ci{
99f08c3bdfSopenharmony_ci	int *s = (int *)page;
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ci	if (*s != sentinel) {
102f08c3bdfSopenharmony_ci		tst_res(TFAIL, "pid[%d]: fail: bad sentinel value seen: %d expected: %d\n", getpid(), *s, sentinel);
103f08c3bdfSopenharmony_ci		return 1;
104f08c3bdfSopenharmony_ci	}
105f08c3bdfSopenharmony_ci
106f08c3bdfSopenharmony_ci	return SAFE_MUNMAP(page, pagesize);
107f08c3bdfSopenharmony_ci}
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_ci/*
110f08c3bdfSopenharmony_ci * allocate_offline() - Allocate and offline test called per-thread
111f08c3bdfSopenharmony_ci *
112f08c3bdfSopenharmony_ci * This function does the allocation and offline by mmapping an
113f08c3bdfSopenharmony_ci * anonymous page and offlining it.
114f08c3bdfSopenharmony_ci */
115f08c3bdfSopenharmony_cistatic int allocate_offline(int tnum)
116f08c3bdfSopenharmony_ci{
117f08c3bdfSopenharmony_ci	int loop;
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_ci	for (loop = 0; loop < NUM_LOOPS; loop++) {
120f08c3bdfSopenharmony_ci		long *ptrs[NUM_PAGES];
121f08c3bdfSopenharmony_ci		int num_alloc;
122f08c3bdfSopenharmony_ci		int i;
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_ci		for (num_alloc = 0; num_alloc < NUM_PAGES; num_alloc++) {
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_ci			ptrs[num_alloc] = allocate_write((tnum << NUM_PAGES_OFFSET) | num_alloc);
127f08c3bdfSopenharmony_ci			if (ptrs[num_alloc] == NULL)
128f08c3bdfSopenharmony_ci				return -1;
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci			if (madvise(ptrs[num_alloc], pagesize, MADV_SOFT_OFFLINE) == -1) {
131f08c3bdfSopenharmony_ci				if (errno != EINVAL)
132f08c3bdfSopenharmony_ci					tst_res(TFAIL | TERRNO, "madvise failed");
133f08c3bdfSopenharmony_ci				if (errno == EINVAL)
134f08c3bdfSopenharmony_ci					tst_res(TCONF, "madvise() didn't support MADV_SOFT_OFFLINE");
135f08c3bdfSopenharmony_ci				return errno;
136f08c3bdfSopenharmony_ci			}
137f08c3bdfSopenharmony_ci		}
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci		for (i = 0; i < num_alloc; i++) {
140f08c3bdfSopenharmony_ci			if (verif_unmap(ptrs[i], (tnum << NUM_PAGES_OFFSET) | i) != 0)
141f08c3bdfSopenharmony_ci				return 1;
142f08c3bdfSopenharmony_ci		}
143f08c3bdfSopenharmony_ci
144f08c3bdfSopenharmony_ci		my_yield();
145f08c3bdfSopenharmony_ci		if (!tst_remaining_runtime()) {
146f08c3bdfSopenharmony_ci			tst_res(TINFO, "Thread [%d]: Test runtime is over, exiting", tnum);
147f08c3bdfSopenharmony_ci			break;
148f08c3bdfSopenharmony_ci		}
149f08c3bdfSopenharmony_ci	}
150f08c3bdfSopenharmony_ci
151f08c3bdfSopenharmony_ci	return 0;
152f08c3bdfSopenharmony_ci}
153f08c3bdfSopenharmony_ci
154f08c3bdfSopenharmony_cistatic void *alloc_mem(void *threadnum)
155f08c3bdfSopenharmony_ci{
156f08c3bdfSopenharmony_ci	int err;
157f08c3bdfSopenharmony_ci	int tnum = (int)(uintptr_t)threadnum;
158f08c3bdfSopenharmony_ci
159f08c3bdfSopenharmony_ci	/* waiting for other threads starting */
160f08c3bdfSopenharmony_ci	TST_CHECKPOINT_WAIT(0);
161f08c3bdfSopenharmony_ci
162f08c3bdfSopenharmony_ci	err = allocate_offline(tnum);
163f08c3bdfSopenharmony_ci	tst_res(TINFO,
164f08c3bdfSopenharmony_ci		"Thread [%d] returned %d, %s.", tnum, err, (err ? "failed" : "succeeded"));
165f08c3bdfSopenharmony_ci	return (void *)(uintptr_t) (err ? -1 : 0);
166f08c3bdfSopenharmony_ci}
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_cistatic void stress_alloc_offl(void)
169f08c3bdfSopenharmony_ci{
170f08c3bdfSopenharmony_ci	int thread_index;
171f08c3bdfSopenharmony_ci	int thread_failure = 0;
172f08c3bdfSopenharmony_ci	pthread_t sigbus_monitor_t;
173f08c3bdfSopenharmony_ci
174f08c3bdfSopenharmony_ci	run_iterations++;
175f08c3bdfSopenharmony_ci
176f08c3bdfSopenharmony_ci	SAFE_PTHREAD_CREATE(&sigbus_monitor_t, NULL, sigbus_monitor, NULL);
177f08c3bdfSopenharmony_ci	pthread_detach(sigbus_monitor_t);
178f08c3bdfSopenharmony_ci
179f08c3bdfSopenharmony_ci	for (thread_index = 0; thread_index < number_threads; thread_index++) {
180f08c3bdfSopenharmony_ci		SAFE_PTHREAD_CREATE(&thread_ids[thread_index], NULL, alloc_mem,
181f08c3bdfSopenharmony_ci				    (void *)(uintptr_t)thread_index);
182f08c3bdfSopenharmony_ci	}
183f08c3bdfSopenharmony_ci
184f08c3bdfSopenharmony_ci	TST_CHECKPOINT_WAKE2(0, number_threads);
185f08c3bdfSopenharmony_ci
186f08c3bdfSopenharmony_ci	for (thread_index = 0; thread_index < number_threads; thread_index++) {
187f08c3bdfSopenharmony_ci		void *status;
188f08c3bdfSopenharmony_ci
189f08c3bdfSopenharmony_ci		SAFE_PTHREAD_JOIN(thread_ids[thread_index], &status);
190f08c3bdfSopenharmony_ci		if ((intptr_t)status != 0) {
191f08c3bdfSopenharmony_ci			tst_res(TFAIL, "thread [%d] - exited with errors",
192f08c3bdfSopenharmony_ci				thread_index);
193f08c3bdfSopenharmony_ci			thread_failure++;
194f08c3bdfSopenharmony_ci		}
195f08c3bdfSopenharmony_ci	}
196f08c3bdfSopenharmony_ci
197f08c3bdfSopenharmony_ci	if (thread_failure == 0)
198f08c3bdfSopenharmony_ci		tst_res(TPASS, "soft-offline / mmap race still clean");
199f08c3bdfSopenharmony_ci}
200f08c3bdfSopenharmony_ci
201f08c3bdfSopenharmony_ci/*
202f08c3bdfSopenharmony_ci * ------------
203f08c3bdfSopenharmony_ci * Cleanup code:
204f08c3bdfSopenharmony_ci * The idea is to retrieve all the pfn numbers that have been soft-offined
205f08c3bdfSopenharmony_ci * (generating a "Soft offlining pfn 0x..." message in the kernel ring buffer)
206f08c3bdfSopenharmony_ci * by the current test (since a "beginning_tag" message we write when starting).
207f08c3bdfSopenharmony_ci * And to put these pages back online by writing the pfn number to the
208f08c3bdfSopenharmony_ci * <debugfs>/hwpoison/unpoison-pfn special file.
209f08c3bdfSopenharmony_ci * ------------
210f08c3bdfSopenharmony_ci */
211f08c3bdfSopenharmony_ci#define OFFLINE_PATTERN "Soft offlining pfn 0x"
212f08c3bdfSopenharmony_ci#define OFFLINE_PATTERN_LEN sizeof(OFFLINE_PATTERN)
213f08c3bdfSopenharmony_ci
214f08c3bdfSopenharmony_ci/* return the pfn if the kmsg msg is a soft-offline indication*/
215f08c3bdfSopenharmony_cistatic unsigned long parse_kmsg_soft_offlined_pfn(char *line, ssize_t len)
216f08c3bdfSopenharmony_ci{
217f08c3bdfSopenharmony_ci	char *pos;
218f08c3bdfSopenharmony_ci	unsigned long addr = 0UL;
219f08c3bdfSopenharmony_ci
220f08c3bdfSopenharmony_ci	pos = strstr(line, OFFLINE_PATTERN);
221f08c3bdfSopenharmony_ci	if (pos == NULL)
222f08c3bdfSopenharmony_ci		return 0UL;
223f08c3bdfSopenharmony_ci
224f08c3bdfSopenharmony_ci	pos += OFFLINE_PATTERN_LEN-1;
225f08c3bdfSopenharmony_ci	if (pos > (line + len))
226f08c3bdfSopenharmony_ci		return 0UL;
227f08c3bdfSopenharmony_ci
228f08c3bdfSopenharmony_ci	addr = strtoul(pos, NULL, 16);
229f08c3bdfSopenharmony_ci	if ((addr == ULONG_MAX) && (errno == ERANGE))
230f08c3bdfSopenharmony_ci		return 0UL;
231f08c3bdfSopenharmony_ci
232f08c3bdfSopenharmony_ci	return addr;
233f08c3bdfSopenharmony_ci}
234f08c3bdfSopenharmony_ci
235f08c3bdfSopenharmony_ci/* return the pfns seen in kernel message log */
236f08c3bdfSopenharmony_cistatic int populate_from_klog(char *begin_tag, unsigned long *pfns, int max)
237f08c3bdfSopenharmony_ci{
238f08c3bdfSopenharmony_ci	int found = 0, fd, beginning_tag_found = 0;
239f08c3bdfSopenharmony_ci	ssize_t sz;
240f08c3bdfSopenharmony_ci	unsigned long pfn;
241f08c3bdfSopenharmony_ci	char buf[BUFSIZ];
242f08c3bdfSopenharmony_ci
243f08c3bdfSopenharmony_ci	fd = SAFE_OPEN("/dev/kmsg", O_RDONLY|O_NONBLOCK);
244f08c3bdfSopenharmony_ci
245f08c3bdfSopenharmony_ci	while (found < max) {
246f08c3bdfSopenharmony_ci		sz = read(fd, buf, sizeof(buf));
247f08c3bdfSopenharmony_ci		/* kmsg returns EPIPE if record was modified while reading */
248f08c3bdfSopenharmony_ci		if (sz < 0 && errno == EPIPE)
249f08c3bdfSopenharmony_ci			continue;
250f08c3bdfSopenharmony_ci		if (sz <= 0)
251f08c3bdfSopenharmony_ci			break;
252f08c3bdfSopenharmony_ci		if (!beginning_tag_found) {
253f08c3bdfSopenharmony_ci			if (strstr(buf, begin_tag))
254f08c3bdfSopenharmony_ci				beginning_tag_found = 1;
255f08c3bdfSopenharmony_ci			continue;
256f08c3bdfSopenharmony_ci		}
257f08c3bdfSopenharmony_ci		pfn = parse_kmsg_soft_offlined_pfn(buf, sz);
258f08c3bdfSopenharmony_ci		if (pfn)
259f08c3bdfSopenharmony_ci			pfns[found++] = pfn;
260f08c3bdfSopenharmony_ci	}
261f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
262f08c3bdfSopenharmony_ci	return found;
263f08c3bdfSopenharmony_ci}
264f08c3bdfSopenharmony_ci
265f08c3bdfSopenharmony_ci/*
266f08c3bdfSopenharmony_ci * Read the given file to search for the key.
267f08c3bdfSopenharmony_ci * Return 1 if the key is found.
268f08c3bdfSopenharmony_ci */
269f08c3bdfSopenharmony_cistatic int find_in_file(char *path, char *key)
270f08c3bdfSopenharmony_ci{
271f08c3bdfSopenharmony_ci	char line[4096];
272f08c3bdfSopenharmony_ci	int found = 0;
273f08c3bdfSopenharmony_ci	FILE *file = SAFE_FOPEN(path, "r");
274f08c3bdfSopenharmony_ci
275f08c3bdfSopenharmony_ci	while (fgets(line, sizeof(line), file)) {
276f08c3bdfSopenharmony_ci		if (strstr(line, key)) {
277f08c3bdfSopenharmony_ci			found = 1;
278f08c3bdfSopenharmony_ci			break;
279f08c3bdfSopenharmony_ci		}
280f08c3bdfSopenharmony_ci	}
281f08c3bdfSopenharmony_ci	SAFE_FCLOSE(file);
282f08c3bdfSopenharmony_ci	return found;
283f08c3bdfSopenharmony_ci}
284f08c3bdfSopenharmony_ci
285f08c3bdfSopenharmony_cistatic void unpoison_this_pfn(unsigned long pfn, int fd)
286f08c3bdfSopenharmony_ci{
287f08c3bdfSopenharmony_ci	char pfn_str[19];
288f08c3bdfSopenharmony_ci
289f08c3bdfSopenharmony_ci	snprintf(pfn_str, sizeof(pfn_str), "0x%lx", pfn);
290f08c3bdfSopenharmony_ci	SAFE_WRITE(0, fd, pfn_str, strlen(pfn_str));
291f08c3bdfSopenharmony_ci}
292f08c3bdfSopenharmony_ci
293f08c3bdfSopenharmony_ci/* Find and open the <debugfs>/hwpoison/unpoison-pfn special file */
294f08c3bdfSopenharmony_cistatic int open_unpoison_pfn(void)
295f08c3bdfSopenharmony_ci{
296f08c3bdfSopenharmony_ci	char *added_file_path = "/hwpoison/unpoison-pfn";
297f08c3bdfSopenharmony_ci	const char *const cmd_modprobe[] = {"modprobe", HW_MODULE, NULL};
298f08c3bdfSopenharmony_ci	char debugfs_fp[4096];
299f08c3bdfSopenharmony_ci	struct mntent *mnt;
300f08c3bdfSopenharmony_ci	FILE *mntf;
301f08c3bdfSopenharmony_ci
302f08c3bdfSopenharmony_ci	if (!find_in_file("/proc/modules", HW_MODULE) && tst_check_builtin_driver(HW_MODULE))
303f08c3bdfSopenharmony_ci		hwpoison_probe = 1;
304f08c3bdfSopenharmony_ci
305f08c3bdfSopenharmony_ci	/* probe hwpoison only if it isn't already there */
306f08c3bdfSopenharmony_ci	if (hwpoison_probe)
307f08c3bdfSopenharmony_ci		SAFE_CMD(cmd_modprobe, NULL, NULL);
308f08c3bdfSopenharmony_ci
309f08c3bdfSopenharmony_ci	/* debugfs mount point */
310f08c3bdfSopenharmony_ci	mntf = setmntent("/etc/mtab", "r");
311f08c3bdfSopenharmony_ci	if (!mntf) {
312f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO, "Can't open /etc/mtab");
313f08c3bdfSopenharmony_ci		return -1;
314f08c3bdfSopenharmony_ci	}
315f08c3bdfSopenharmony_ci	while ((mnt = getmntent(mntf)) != NULL) {
316f08c3bdfSopenharmony_ci		if (strcmp(mnt->mnt_type, "debugfs") == 0) {
317f08c3bdfSopenharmony_ci			strcpy(debugfs_fp, mnt->mnt_dir);
318f08c3bdfSopenharmony_ci			strcat(debugfs_fp, added_file_path);
319f08c3bdfSopenharmony_ci			break;
320f08c3bdfSopenharmony_ci		}
321f08c3bdfSopenharmony_ci	}
322f08c3bdfSopenharmony_ci	endmntent(mntf);
323f08c3bdfSopenharmony_ci	if (!mnt)
324f08c3bdfSopenharmony_ci		return -1;
325f08c3bdfSopenharmony_ci
326f08c3bdfSopenharmony_ci	TEST(open(debugfs_fp, O_WRONLY));
327f08c3bdfSopenharmony_ci
328f08c3bdfSopenharmony_ci	if (TST_RET == -1 && TST_ERR == EPERM && tst_lockdown_enabled() > 0) {
329f08c3bdfSopenharmony_ci		tst_res(TINFO,
330f08c3bdfSopenharmony_ci			"Cannot restore soft-offlined memory due to lockdown");
331f08c3bdfSopenharmony_ci		return TST_RET;
332f08c3bdfSopenharmony_ci	}
333f08c3bdfSopenharmony_ci
334f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
335f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO, "open(%s) failed", debugfs_fp);
336f08c3bdfSopenharmony_ci	} else if (TST_RET < 0) {
337f08c3bdfSopenharmony_ci		tst_brk(TBROK | TTERRNO, "Invalid open() return value %ld",
338f08c3bdfSopenharmony_ci			TST_RET);
339f08c3bdfSopenharmony_ci	}
340f08c3bdfSopenharmony_ci
341f08c3bdfSopenharmony_ci	return TST_RET;
342f08c3bdfSopenharmony_ci}
343f08c3bdfSopenharmony_ci
344f08c3bdfSopenharmony_ci/*
345f08c3bdfSopenharmony_ci * Get all the Offlined PFNs indicated in the dmesg output
346f08c3bdfSopenharmony_ci * starting after the given beginning tag, and request a debugfs
347f08c3bdfSopenharmony_ci * hwpoison/unpoison-pfn for each of them.
348f08c3bdfSopenharmony_ci */
349f08c3bdfSopenharmony_cistatic void unpoison_pfn(char *begin_tag)
350f08c3bdfSopenharmony_ci{
351f08c3bdfSopenharmony_ci	unsigned long *pfns;
352f08c3bdfSopenharmony_ci	const char *const cmd_rmmod[] = {"rmmod", HW_MODULE, NULL};
353f08c3bdfSopenharmony_ci	int found_pfns, fd;
354f08c3bdfSopenharmony_ci
355f08c3bdfSopenharmony_ci	pfns = SAFE_MALLOC(sizeof(pfns) * maximum_pfns * run_iterations);
356f08c3bdfSopenharmony_ci
357f08c3bdfSopenharmony_ci	fd = open_unpoison_pfn();
358f08c3bdfSopenharmony_ci	if (fd >= 0) {
359f08c3bdfSopenharmony_ci		found_pfns = populate_from_klog(begin_tag, pfns, maximum_pfns * run_iterations);
360f08c3bdfSopenharmony_ci
361f08c3bdfSopenharmony_ci		tst_res(TINFO, "Restore %d Soft-offlined pages", found_pfns);
362f08c3bdfSopenharmony_ci		/* unpoison in reverse order */
363f08c3bdfSopenharmony_ci		while (found_pfns-- > 0)
364f08c3bdfSopenharmony_ci			unpoison_this_pfn(pfns[found_pfns], fd);
365f08c3bdfSopenharmony_ci
366f08c3bdfSopenharmony_ci		SAFE_CLOSE(fd);
367f08c3bdfSopenharmony_ci	}
368f08c3bdfSopenharmony_ci	/* remove hwpoison only if we probed it */
369f08c3bdfSopenharmony_ci	if (hwpoison_probe)
370f08c3bdfSopenharmony_ci		SAFE_CMD(cmd_rmmod, NULL, NULL);
371f08c3bdfSopenharmony_ci}
372f08c3bdfSopenharmony_ci
373f08c3bdfSopenharmony_ci/*
374f08c3bdfSopenharmony_ci * Create and write a beginning tag to the kernel buffer to be used on cleanup
375f08c3bdfSopenharmony_ci * when trying to restore the soft-offlined pages of our test run.
376f08c3bdfSopenharmony_ci */
377f08c3bdfSopenharmony_cistatic void write_beginning_tag_to_kmsg(void)
378f08c3bdfSopenharmony_ci{
379f08c3bdfSopenharmony_ci	int fd;
380f08c3bdfSopenharmony_ci
381f08c3bdfSopenharmony_ci	fd = SAFE_OPEN("/dev/kmsg", O_WRONLY);
382f08c3bdfSopenharmony_ci	snprintf(beginning_tag, sizeof(beginning_tag),
383f08c3bdfSopenharmony_ci		 "Soft-offlining pages test starting (pid: %ld)",
384f08c3bdfSopenharmony_ci		 (long)getpid());
385f08c3bdfSopenharmony_ci	SAFE_WRITE(1, fd, beginning_tag, strlen(beginning_tag));
386f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
387f08c3bdfSopenharmony_ci}
388f08c3bdfSopenharmony_ci
389f08c3bdfSopenharmony_cistatic void setup(void)
390f08c3bdfSopenharmony_ci{
391f08c3bdfSopenharmony_ci	struct sigaction my_sigaction;
392f08c3bdfSopenharmony_ci
393f08c3bdfSopenharmony_ci	number_threads = (int)sysconf(_SC_NPROCESSORS_ONLN) * 2;
394f08c3bdfSopenharmony_ci	if (number_threads <= 1)
395f08c3bdfSopenharmony_ci		number_threads = 2;
396f08c3bdfSopenharmony_ci	else if (number_threads > 5)
397f08c3bdfSopenharmony_ci		number_threads = 5;
398f08c3bdfSopenharmony_ci
399f08c3bdfSopenharmony_ci	maximum_pfns = number_threads * NUM_LOOPS * NUM_PAGES;
400f08c3bdfSopenharmony_ci	thread_ids = SAFE_MALLOC(sizeof(pthread_t) * number_threads);
401f08c3bdfSopenharmony_ci	pagesize = sysconf(_SC_PAGESIZE);
402f08c3bdfSopenharmony_ci
403f08c3bdfSopenharmony_ci	/* SIGBUS is the main failure criteria */
404f08c3bdfSopenharmony_ci	my_sigaction.sa_handler = sigbus_handler;
405f08c3bdfSopenharmony_ci	if (sigaction(SIGBUS, &my_sigaction, NULL) == -1)
406f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "Signal handler attach failed");
407f08c3bdfSopenharmony_ci
408f08c3bdfSopenharmony_ci	write_beginning_tag_to_kmsg();
409f08c3bdfSopenharmony_ci	tst_res(TINFO, "Spawning %d threads, with a total of %d memory pages",
410f08c3bdfSopenharmony_ci		number_threads, maximum_pfns);
411f08c3bdfSopenharmony_ci}
412f08c3bdfSopenharmony_ci
413f08c3bdfSopenharmony_cistatic void cleanup(void)
414f08c3bdfSopenharmony_ci{
415f08c3bdfSopenharmony_ci	unpoison_pfn(beginning_tag);
416f08c3bdfSopenharmony_ci}
417f08c3bdfSopenharmony_ci
418f08c3bdfSopenharmony_cistatic struct tst_test test = {
419f08c3bdfSopenharmony_ci	.needs_root = 1,
420f08c3bdfSopenharmony_ci	.needs_drivers = (const char *const []) {
421f08c3bdfSopenharmony_ci		HW_MODULE,
422f08c3bdfSopenharmony_ci		NULL
423f08c3bdfSopenharmony_ci	},
424f08c3bdfSopenharmony_ci	.needs_cmds = (const char *[]) {
425f08c3bdfSopenharmony_ci		"modprobe",
426f08c3bdfSopenharmony_ci		"rmmod",
427f08c3bdfSopenharmony_ci		NULL
428f08c3bdfSopenharmony_ci	},
429f08c3bdfSopenharmony_ci	.max_runtime = 30,
430f08c3bdfSopenharmony_ci	.needs_checkpoints = 1,
431f08c3bdfSopenharmony_ci	.setup = setup,
432f08c3bdfSopenharmony_ci	.cleanup = cleanup,
433f08c3bdfSopenharmony_ci	.test_all = stress_alloc_offl,
434f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
435f08c3bdfSopenharmony_ci		{"linux-git", "d4ae9916ea29"},
436f08c3bdfSopenharmony_ci		{}
437f08c3bdfSopenharmony_ci	}
438f08c3bdfSopenharmony_ci};
439