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