1*c9615059SWei Yang // SPDX-License-Identifier: GPL-2.0 2*c9615059SWei Yang /* 3*c9615059SWei Yang * RMAP functional tests 4*c9615059SWei Yang * 5*c9615059SWei Yang * Author(s): Wei Yang <richard.weiyang@gmail.com> 6*c9615059SWei Yang */ 7*c9615059SWei Yang 8*c9615059SWei Yang #include "../kselftest_harness.h" 9*c9615059SWei Yang #include <strings.h> 10*c9615059SWei Yang #include <pthread.h> 11*c9615059SWei Yang #include <numa.h> 12*c9615059SWei Yang #include <numaif.h> 13*c9615059SWei Yang #include <sys/mman.h> 14*c9615059SWei Yang #include <sys/prctl.h> 15*c9615059SWei Yang #include <sys/types.h> 16*c9615059SWei Yang #include <signal.h> 17*c9615059SWei Yang #include <time.h> 18*c9615059SWei Yang #include <sys/sem.h> 19*c9615059SWei Yang #include <unistd.h> 20*c9615059SWei Yang #include <fcntl.h> 21*c9615059SWei Yang 22*c9615059SWei Yang #include "vm_util.h" 23*c9615059SWei Yang 24*c9615059SWei Yang #define TOTAL_LEVEL 5 25*c9615059SWei Yang #define MAX_CHILDREN 3 26*c9615059SWei Yang 27*c9615059SWei Yang #define FAIL_ON_CHECK (1 << 0) 28*c9615059SWei Yang #define FAIL_ON_WORK (1 << 1) 29*c9615059SWei Yang 30*c9615059SWei Yang struct sembuf sem_wait = {0, -1, 0}; 31*c9615059SWei Yang struct sembuf sem_signal = {0, 1, 0}; 32*c9615059SWei Yang 33*c9615059SWei Yang enum backend_type { 34*c9615059SWei Yang ANON, 35*c9615059SWei Yang SHM, 36*c9615059SWei Yang NORM_FILE, 37*c9615059SWei Yang }; 38*c9615059SWei Yang 39*c9615059SWei Yang #define PREFIX "kst_rmap" 40*c9615059SWei Yang #define MAX_FILENAME_LEN 256 41*c9615059SWei Yang const char *suffixes[] = { 42*c9615059SWei Yang "", 43*c9615059SWei Yang "_shm", 44*c9615059SWei Yang "_file", 45*c9615059SWei Yang }; 46*c9615059SWei Yang 47*c9615059SWei Yang struct global_data; 48*c9615059SWei Yang typedef int (*work_fn)(struct global_data *data); 49*c9615059SWei Yang typedef int (*check_fn)(struct global_data *data); 50*c9615059SWei Yang typedef void (*prepare_fn)(struct global_data *data); 51*c9615059SWei Yang 52*c9615059SWei Yang struct global_data { 53*c9615059SWei Yang int worker_level; 54*c9615059SWei Yang 55*c9615059SWei Yang int semid; 56*c9615059SWei Yang int pipefd[2]; 57*c9615059SWei Yang 58*c9615059SWei Yang unsigned int mapsize; 59*c9615059SWei Yang unsigned int rand_seed; 60*c9615059SWei Yang char *region; 61*c9615059SWei Yang 62*c9615059SWei Yang prepare_fn do_prepare; 63*c9615059SWei Yang work_fn do_work; 64*c9615059SWei Yang check_fn do_check; 65*c9615059SWei Yang 66*c9615059SWei Yang enum backend_type backend; 67*c9615059SWei Yang char filename[MAX_FILENAME_LEN]; 68*c9615059SWei Yang 69*c9615059SWei Yang unsigned long *expected_pfn; 70*c9615059SWei Yang }; 71*c9615059SWei Yang 72*c9615059SWei Yang /* 73*c9615059SWei Yang * Create a process tree with TOTAL_LEVEL height and at most MAX_CHILDREN 74*c9615059SWei Yang * children for each. 75*c9615059SWei Yang * 76*c9615059SWei Yang * It will randomly select one process as 'worker' process which will 77*c9615059SWei Yang * 'do_work' until all processes are created. And all other processes will 78*c9615059SWei Yang * wait until 'worker' finish its work. 79*c9615059SWei Yang */ 80*c9615059SWei Yang void propagate_children(struct __test_metadata *_metadata, struct global_data *data) 81*c9615059SWei Yang { 82*c9615059SWei Yang pid_t root_pid, pid; 83*c9615059SWei Yang unsigned int num_child; 84*c9615059SWei Yang int status; 85*c9615059SWei Yang int ret = 0; 86*c9615059SWei Yang int curr_child, worker_child; 87*c9615059SWei Yang int curr_level = 1; 88*c9615059SWei Yang bool is_worker = true; 89*c9615059SWei Yang 90*c9615059SWei Yang root_pid = getpid(); 91*c9615059SWei Yang repeat: 92*c9615059SWei Yang num_child = rand_r(&data->rand_seed) % MAX_CHILDREN + 1; 93*c9615059SWei Yang worker_child = is_worker ? rand_r(&data->rand_seed) % num_child : -1; 94*c9615059SWei Yang 95*c9615059SWei Yang for (curr_child = 0; curr_child < num_child; curr_child++) { 96*c9615059SWei Yang pid = fork(); 97*c9615059SWei Yang 98*c9615059SWei Yang if (pid < 0) { 99*c9615059SWei Yang perror("Error: fork\n"); 100*c9615059SWei Yang } else if (pid == 0) { 101*c9615059SWei Yang curr_level++; 102*c9615059SWei Yang 103*c9615059SWei Yang if (curr_child != worker_child) 104*c9615059SWei Yang is_worker = false; 105*c9615059SWei Yang 106*c9615059SWei Yang if (curr_level == TOTAL_LEVEL) 107*c9615059SWei Yang break; 108*c9615059SWei Yang 109*c9615059SWei Yang data->rand_seed += curr_child; 110*c9615059SWei Yang goto repeat; 111*c9615059SWei Yang } 112*c9615059SWei Yang } 113*c9615059SWei Yang 114*c9615059SWei Yang if (data->do_prepare) 115*c9615059SWei Yang data->do_prepare(data); 116*c9615059SWei Yang 117*c9615059SWei Yang close(data->pipefd[1]); 118*c9615059SWei Yang 119*c9615059SWei Yang if (is_worker && curr_level == data->worker_level) { 120*c9615059SWei Yang /* This is the worker process, first wait last process created */ 121*c9615059SWei Yang char buf; 122*c9615059SWei Yang 123*c9615059SWei Yang while (read(data->pipefd[0], &buf, 1) > 0) 124*c9615059SWei Yang ; 125*c9615059SWei Yang 126*c9615059SWei Yang if (data->do_work) 127*c9615059SWei Yang ret = data->do_work(data); 128*c9615059SWei Yang 129*c9615059SWei Yang /* Kick others */ 130*c9615059SWei Yang semctl(data->semid, 0, IPC_RMID); 131*c9615059SWei Yang } else { 132*c9615059SWei Yang /* Wait worker finish */ 133*c9615059SWei Yang semop(data->semid, &sem_wait, 1); 134*c9615059SWei Yang if (data->do_check) 135*c9615059SWei Yang ret = data->do_check(data); 136*c9615059SWei Yang } 137*c9615059SWei Yang 138*c9615059SWei Yang /* Wait all child to quit */ 139*c9615059SWei Yang while (wait(&status) > 0) { 140*c9615059SWei Yang if (WIFEXITED(status)) 141*c9615059SWei Yang ret |= WEXITSTATUS(status); 142*c9615059SWei Yang } 143*c9615059SWei Yang 144*c9615059SWei Yang if (getpid() == root_pid) { 145*c9615059SWei Yang if (ret & FAIL_ON_WORK) 146*c9615059SWei Yang SKIP(return, "Failed in worker"); 147*c9615059SWei Yang 148*c9615059SWei Yang ASSERT_EQ(ret, 0); 149*c9615059SWei Yang } else { 150*c9615059SWei Yang exit(ret); 151*c9615059SWei Yang } 152*c9615059SWei Yang } 153*c9615059SWei Yang 154*c9615059SWei Yang FIXTURE(migrate) 155*c9615059SWei Yang { 156*c9615059SWei Yang struct global_data data; 157*c9615059SWei Yang }; 158*c9615059SWei Yang 159*c9615059SWei Yang FIXTURE_SETUP(migrate) 160*c9615059SWei Yang { 161*c9615059SWei Yang struct global_data *data = &self->data; 162*c9615059SWei Yang 163*c9615059SWei Yang if (numa_available() < 0) 164*c9615059SWei Yang SKIP(return, "NUMA not available"); 165*c9615059SWei Yang if (numa_bitmask_weight(numa_all_nodes_ptr) <= 1) 166*c9615059SWei Yang SKIP(return, "Not enough NUMA nodes available"); 167*c9615059SWei Yang 168*c9615059SWei Yang data->mapsize = getpagesize(); 169*c9615059SWei Yang 170*c9615059SWei Yang data->expected_pfn = mmap(0, sizeof(unsigned long), 171*c9615059SWei Yang PROT_READ | PROT_WRITE, 172*c9615059SWei Yang MAP_SHARED | MAP_ANONYMOUS, -1, 0); 173*c9615059SWei Yang ASSERT_NE(data->expected_pfn, MAP_FAILED); 174*c9615059SWei Yang 175*c9615059SWei Yang /* Prepare semaphore */ 176*c9615059SWei Yang data->semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); 177*c9615059SWei Yang ASSERT_NE(data->semid, -1); 178*c9615059SWei Yang ASSERT_NE(semctl(data->semid, 0, SETVAL, 0), -1); 179*c9615059SWei Yang 180*c9615059SWei Yang /* Prepare pipe */ 181*c9615059SWei Yang ASSERT_NE(pipe(data->pipefd), -1); 182*c9615059SWei Yang 183*c9615059SWei Yang data->rand_seed = time(NULL); 184*c9615059SWei Yang srand(data->rand_seed); 185*c9615059SWei Yang 186*c9615059SWei Yang data->worker_level = rand() % TOTAL_LEVEL + 1; 187*c9615059SWei Yang 188*c9615059SWei Yang data->do_prepare = NULL; 189*c9615059SWei Yang data->do_work = NULL; 190*c9615059SWei Yang data->do_check = NULL; 191*c9615059SWei Yang 192*c9615059SWei Yang data->backend = ANON; 193*c9615059SWei Yang }; 194*c9615059SWei Yang 195*c9615059SWei Yang FIXTURE_TEARDOWN(migrate) 196*c9615059SWei Yang { 197*c9615059SWei Yang struct global_data *data = &self->data; 198*c9615059SWei Yang 199*c9615059SWei Yang if (data->region != MAP_FAILED) 200*c9615059SWei Yang munmap(data->region, data->mapsize); 201*c9615059SWei Yang data->region = MAP_FAILED; 202*c9615059SWei Yang if (data->expected_pfn != MAP_FAILED) 203*c9615059SWei Yang munmap(data->expected_pfn, sizeof(unsigned long)); 204*c9615059SWei Yang data->expected_pfn = MAP_FAILED; 205*c9615059SWei Yang semctl(data->semid, 0, IPC_RMID); 206*c9615059SWei Yang data->semid = -1; 207*c9615059SWei Yang 208*c9615059SWei Yang close(data->pipefd[0]); 209*c9615059SWei Yang 210*c9615059SWei Yang switch (data->backend) { 211*c9615059SWei Yang case ANON: 212*c9615059SWei Yang break; 213*c9615059SWei Yang case SHM: 214*c9615059SWei Yang shm_unlink(data->filename); 215*c9615059SWei Yang break; 216*c9615059SWei Yang case NORM_FILE: 217*c9615059SWei Yang unlink(data->filename); 218*c9615059SWei Yang break; 219*c9615059SWei Yang } 220*c9615059SWei Yang } 221*c9615059SWei Yang 222*c9615059SWei Yang void access_region(struct global_data *data) 223*c9615059SWei Yang { 224*c9615059SWei Yang /* 225*c9615059SWei Yang * Force read "region" to make sure page fault in. 226*c9615059SWei Yang */ 227*c9615059SWei Yang FORCE_READ(*data->region); 228*c9615059SWei Yang } 229*c9615059SWei Yang 230*c9615059SWei Yang int try_to_move_page(char *region) 231*c9615059SWei Yang { 232*c9615059SWei Yang int ret; 233*c9615059SWei Yang int node; 234*c9615059SWei Yang int status = 0; 235*c9615059SWei Yang int failures = 0; 236*c9615059SWei Yang 237*c9615059SWei Yang ret = move_pages(0, 1, (void **)®ion, NULL, &status, MPOL_MF_MOVE_ALL); 238*c9615059SWei Yang if (ret != 0) { 239*c9615059SWei Yang perror("Failed to get original numa"); 240*c9615059SWei Yang return FAIL_ON_WORK; 241*c9615059SWei Yang } 242*c9615059SWei Yang 243*c9615059SWei Yang /* Pick up a different target node */ 244*c9615059SWei Yang for (node = 0; node <= numa_max_node(); node++) { 245*c9615059SWei Yang if (numa_bitmask_isbitset(numa_all_nodes_ptr, node) && node != status) 246*c9615059SWei Yang break; 247*c9615059SWei Yang } 248*c9615059SWei Yang 249*c9615059SWei Yang if (node > numa_max_node()) { 250*c9615059SWei Yang ksft_print_msg("Couldn't find available numa node for testing\n"); 251*c9615059SWei Yang return FAIL_ON_WORK; 252*c9615059SWei Yang } 253*c9615059SWei Yang 254*c9615059SWei Yang while (1) { 255*c9615059SWei Yang ret = move_pages(0, 1, (void **)®ion, &node, &status, MPOL_MF_MOVE_ALL); 256*c9615059SWei Yang 257*c9615059SWei Yang /* migrate successfully */ 258*c9615059SWei Yang if (!ret) 259*c9615059SWei Yang break; 260*c9615059SWei Yang 261*c9615059SWei Yang /* error happened */ 262*c9615059SWei Yang if (ret < 0) { 263*c9615059SWei Yang ksft_perror("Failed to move pages"); 264*c9615059SWei Yang return FAIL_ON_WORK; 265*c9615059SWei Yang } 266*c9615059SWei Yang 267*c9615059SWei Yang /* migration is best effort; try again */ 268*c9615059SWei Yang if (++failures >= 100) 269*c9615059SWei Yang return FAIL_ON_WORK; 270*c9615059SWei Yang } 271*c9615059SWei Yang 272*c9615059SWei Yang return 0; 273*c9615059SWei Yang } 274*c9615059SWei Yang 275*c9615059SWei Yang int move_region(struct global_data *data) 276*c9615059SWei Yang { 277*c9615059SWei Yang int ret; 278*c9615059SWei Yang int pagemap_fd; 279*c9615059SWei Yang 280*c9615059SWei Yang ret = try_to_move_page(data->region); 281*c9615059SWei Yang if (ret != 0) 282*c9615059SWei Yang return ret; 283*c9615059SWei Yang 284*c9615059SWei Yang pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 285*c9615059SWei Yang if (pagemap_fd == -1) 286*c9615059SWei Yang return FAIL_ON_WORK; 287*c9615059SWei Yang *data->expected_pfn = pagemap_get_pfn(pagemap_fd, data->region); 288*c9615059SWei Yang 289*c9615059SWei Yang return 0; 290*c9615059SWei Yang } 291*c9615059SWei Yang 292*c9615059SWei Yang int has_same_pfn(struct global_data *data) 293*c9615059SWei Yang { 294*c9615059SWei Yang unsigned long pfn; 295*c9615059SWei Yang int pagemap_fd; 296*c9615059SWei Yang 297*c9615059SWei Yang if (data->region == MAP_FAILED) 298*c9615059SWei Yang return 0; 299*c9615059SWei Yang 300*c9615059SWei Yang pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 301*c9615059SWei Yang if (pagemap_fd == -1) 302*c9615059SWei Yang return FAIL_ON_CHECK; 303*c9615059SWei Yang 304*c9615059SWei Yang pfn = pagemap_get_pfn(pagemap_fd, data->region); 305*c9615059SWei Yang if (pfn != *data->expected_pfn) 306*c9615059SWei Yang return FAIL_ON_CHECK; 307*c9615059SWei Yang 308*c9615059SWei Yang return 0; 309*c9615059SWei Yang } 310*c9615059SWei Yang 311*c9615059SWei Yang TEST_F(migrate, anon) 312*c9615059SWei Yang { 313*c9615059SWei Yang struct global_data *data = &self->data; 314*c9615059SWei Yang 315*c9615059SWei Yang /* Map an area and fault in */ 316*c9615059SWei Yang data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE, 317*c9615059SWei Yang MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 318*c9615059SWei Yang ASSERT_NE(data->region, MAP_FAILED); 319*c9615059SWei Yang memset(data->region, 0xcf, data->mapsize); 320*c9615059SWei Yang 321*c9615059SWei Yang data->do_prepare = access_region; 322*c9615059SWei Yang data->do_work = move_region; 323*c9615059SWei Yang data->do_check = has_same_pfn; 324*c9615059SWei Yang 325*c9615059SWei Yang propagate_children(_metadata, data); 326*c9615059SWei Yang } 327*c9615059SWei Yang 328*c9615059SWei Yang TEST_F(migrate, shm) 329*c9615059SWei Yang { 330*c9615059SWei Yang int shm_fd; 331*c9615059SWei Yang struct global_data *data = &self->data; 332*c9615059SWei Yang 333*c9615059SWei Yang snprintf(data->filename, MAX_FILENAME_LEN, "%s%s", PREFIX, suffixes[SHM]); 334*c9615059SWei Yang shm_fd = shm_open(data->filename, O_CREAT | O_RDWR, 0666); 335*c9615059SWei Yang ASSERT_NE(shm_fd, -1); 336*c9615059SWei Yang ftruncate(shm_fd, data->mapsize); 337*c9615059SWei Yang data->backend = SHM; 338*c9615059SWei Yang 339*c9615059SWei Yang /* Map a shared area and fault in */ 340*c9615059SWei Yang data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE, 341*c9615059SWei Yang MAP_SHARED, shm_fd, 0); 342*c9615059SWei Yang ASSERT_NE(data->region, MAP_FAILED); 343*c9615059SWei Yang memset(data->region, 0xcf, data->mapsize); 344*c9615059SWei Yang close(shm_fd); 345*c9615059SWei Yang 346*c9615059SWei Yang data->do_prepare = access_region; 347*c9615059SWei Yang data->do_work = move_region; 348*c9615059SWei Yang data->do_check = has_same_pfn; 349*c9615059SWei Yang 350*c9615059SWei Yang propagate_children(_metadata, data); 351*c9615059SWei Yang } 352*c9615059SWei Yang 353*c9615059SWei Yang TEST_F(migrate, file) 354*c9615059SWei Yang { 355*c9615059SWei Yang int fd; 356*c9615059SWei Yang struct global_data *data = &self->data; 357*c9615059SWei Yang 358*c9615059SWei Yang snprintf(data->filename, MAX_FILENAME_LEN, "%s%s", PREFIX, suffixes[NORM_FILE]); 359*c9615059SWei Yang fd = open(data->filename, O_CREAT | O_RDWR | O_EXCL, 0666); 360*c9615059SWei Yang ASSERT_NE(fd, -1); 361*c9615059SWei Yang ftruncate(fd, data->mapsize); 362*c9615059SWei Yang data->backend = NORM_FILE; 363*c9615059SWei Yang 364*c9615059SWei Yang /* Map a shared area and fault in */ 365*c9615059SWei Yang data->region = mmap(0, data->mapsize, PROT_READ | PROT_WRITE, 366*c9615059SWei Yang MAP_SHARED, fd, 0); 367*c9615059SWei Yang ASSERT_NE(data->region, MAP_FAILED); 368*c9615059SWei Yang memset(data->region, 0xcf, data->mapsize); 369*c9615059SWei Yang close(fd); 370*c9615059SWei Yang 371*c9615059SWei Yang data->do_prepare = access_region; 372*c9615059SWei Yang data->do_work = move_region; 373*c9615059SWei Yang data->do_check = has_same_pfn; 374*c9615059SWei Yang 375*c9615059SWei Yang propagate_children(_metadata, data); 376*c9615059SWei Yang } 377*c9615059SWei Yang 378*c9615059SWei Yang void prepare_local_region(struct global_data *data) 379*c9615059SWei Yang { 380*c9615059SWei Yang /* Allocate range and set the same data */ 381*c9615059SWei Yang data->region = mmap(NULL, data->mapsize, PROT_READ|PROT_WRITE, 382*c9615059SWei Yang MAP_PRIVATE|MAP_ANON, -1, 0); 383*c9615059SWei Yang if (data->region == MAP_FAILED) 384*c9615059SWei Yang return; 385*c9615059SWei Yang 386*c9615059SWei Yang memset(data->region, 0xcf, data->mapsize); 387*c9615059SWei Yang } 388*c9615059SWei Yang 389*c9615059SWei Yang int merge_and_migrate(struct global_data *data) 390*c9615059SWei Yang { 391*c9615059SWei Yang int pagemap_fd; 392*c9615059SWei Yang int ret = 0; 393*c9615059SWei Yang 394*c9615059SWei Yang if (data->region == MAP_FAILED) 395*c9615059SWei Yang return FAIL_ON_WORK; 396*c9615059SWei Yang 397*c9615059SWei Yang if (ksm_start() < 0) 398*c9615059SWei Yang return FAIL_ON_WORK; 399*c9615059SWei Yang 400*c9615059SWei Yang ret = try_to_move_page(data->region); 401*c9615059SWei Yang 402*c9615059SWei Yang pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 403*c9615059SWei Yang if (pagemap_fd == -1) 404*c9615059SWei Yang return FAIL_ON_WORK; 405*c9615059SWei Yang *data->expected_pfn = pagemap_get_pfn(pagemap_fd, data->region); 406*c9615059SWei Yang 407*c9615059SWei Yang return ret; 408*c9615059SWei Yang } 409*c9615059SWei Yang 410*c9615059SWei Yang TEST_F(migrate, ksm) 411*c9615059SWei Yang { 412*c9615059SWei Yang int ret; 413*c9615059SWei Yang struct global_data *data = &self->data; 414*c9615059SWei Yang 415*c9615059SWei Yang if (ksm_stop() < 0) 416*c9615059SWei Yang SKIP(return, "accessing \"/sys/kernel/mm/ksm/run\") failed"); 417*c9615059SWei Yang if (ksm_get_full_scans() < 0) 418*c9615059SWei Yang SKIP(return, "accessing \"/sys/kernel/mm/ksm/full_scan\") failed"); 419*c9615059SWei Yang 420*c9615059SWei Yang ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0); 421*c9615059SWei Yang if (ret < 0 && errno == EINVAL) 422*c9615059SWei Yang SKIP(return, "PR_SET_MEMORY_MERGE not supported"); 423*c9615059SWei Yang else if (ret) 424*c9615059SWei Yang ksft_exit_fail_perror("PR_SET_MEMORY_MERGE=1 failed"); 425*c9615059SWei Yang 426*c9615059SWei Yang data->do_prepare = prepare_local_region; 427*c9615059SWei Yang data->do_work = merge_and_migrate; 428*c9615059SWei Yang data->do_check = has_same_pfn; 429*c9615059SWei Yang 430*c9615059SWei Yang propagate_children(_metadata, data); 431*c9615059SWei Yang } 432*c9615059SWei Yang 433*c9615059SWei Yang TEST_HARNESS_MAIN 434