migration.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * The main purpose of the tests here is to exercise the migration entry code
  4. * paths in the kernel.
  5. */
  6. #include "../kselftest_harness.h"
  7. #include <strings.h>
  8. #include <pthread.h>
  9. #include <numa.h>
  10. #include <numaif.h>
  11. #include <sys/mman.h>
  12. #include <sys/types.h>
  13. #include <signal.h>
  14. #include <time.h>
  15. #define TWOMEG (2<<20)
  16. #define RUNTIME (60)
  17. #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
  18. FIXTURE(migration)
  19. {
  20. pthread_t *threads;
  21. pid_t *pids;
  22. int nthreads;
  23. int n1;
  24. int n2;
  25. };
  26. FIXTURE_SETUP(migration)
  27. {
  28. int n;
  29. ASSERT_EQ(numa_available(), 0);
  30. self->nthreads = numa_num_task_cpus() - 1;
  31. self->n1 = -1;
  32. self->n2 = -1;
  33. for (n = 0; n < numa_max_possible_node(); n++)
  34. if (numa_bitmask_isbitset(numa_all_nodes_ptr, n)) {
  35. if (self->n1 == -1) {
  36. self->n1 = n;
  37. } else {
  38. self->n2 = n;
  39. break;
  40. }
  41. }
  42. self->threads = malloc(self->nthreads * sizeof(*self->threads));
  43. ASSERT_NE(self->threads, NULL);
  44. self->pids = malloc(self->nthreads * sizeof(*self->pids));
  45. ASSERT_NE(self->pids, NULL);
  46. };
  47. FIXTURE_TEARDOWN(migration)
  48. {
  49. free(self->threads);
  50. free(self->pids);
  51. }
  52. int migrate(uint64_t *ptr, int n1, int n2)
  53. {
  54. int ret, tmp;
  55. int status = 0;
  56. struct timespec ts1, ts2;
  57. if (clock_gettime(CLOCK_MONOTONIC, &ts1))
  58. return -1;
  59. while (1) {
  60. if (clock_gettime(CLOCK_MONOTONIC, &ts2))
  61. return -1;
  62. if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
  63. return 0;
  64. ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
  65. MPOL_MF_MOVE_ALL);
  66. if (ret) {
  67. if (ret > 0)
  68. printf("Didn't migrate %d pages\n", ret);
  69. else
  70. perror("Couldn't migrate pages");
  71. return -2;
  72. }
  73. tmp = n2;
  74. n2 = n1;
  75. n1 = tmp;
  76. }
  77. return 0;
  78. }
  79. void *access_mem(void *ptr)
  80. {
  81. uint64_t y = 0;
  82. volatile uint64_t *x = ptr;
  83. while (1) {
  84. pthread_testcancel();
  85. y += *x;
  86. }
  87. return NULL;
  88. }
  89. /*
  90. * Basic migration entry testing. One thread will move pages back and forth
  91. * between nodes whilst other threads try and access them triggering the
  92. * migration entry wait paths in the kernel.
  93. */
  94. TEST_F_TIMEOUT(migration, private_anon, 2*RUNTIME)
  95. {
  96. uint64_t *ptr;
  97. int i;
  98. if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
  99. SKIP(return, "Not enough threads or NUMA nodes available");
  100. ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
  101. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  102. ASSERT_NE(ptr, MAP_FAILED);
  103. memset(ptr, 0xde, TWOMEG);
  104. for (i = 0; i < self->nthreads - 1; i++)
  105. if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
  106. perror("Couldn't create thread");
  107. ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
  108. for (i = 0; i < self->nthreads - 1; i++)
  109. ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
  110. }
  111. /*
  112. * Same as the previous test but with shared memory.
  113. */
  114. TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME)
  115. {
  116. pid_t pid;
  117. uint64_t *ptr;
  118. int i;
  119. if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
  120. SKIP(return, "Not enough threads or NUMA nodes available");
  121. ptr = mmap(NULL, TWOMEG, PROT_READ | PROT_WRITE,
  122. MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  123. ASSERT_NE(ptr, MAP_FAILED);
  124. memset(ptr, 0xde, TWOMEG);
  125. for (i = 0; i < self->nthreads - 1; i++) {
  126. pid = fork();
  127. if (!pid)
  128. access_mem(ptr);
  129. else
  130. self->pids[i] = pid;
  131. }
  132. ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
  133. for (i = 0; i < self->nthreads - 1; i++)
  134. ASSERT_EQ(kill(self->pids[i], SIGTERM), 0);
  135. }
  136. /*
  137. * Tests the pmd migration entry paths.
  138. */
  139. TEST_F_TIMEOUT(migration, private_anon_thp, 2*RUNTIME)
  140. {
  141. uint64_t *ptr;
  142. int i;
  143. if (self->nthreads < 2 || self->n1 < 0 || self->n2 < 0)
  144. SKIP(return, "Not enough threads or NUMA nodes available");
  145. ptr = mmap(NULL, 2*TWOMEG, PROT_READ | PROT_WRITE,
  146. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  147. ASSERT_NE(ptr, MAP_FAILED);
  148. ptr = (uint64_t *) ALIGN((uintptr_t) ptr, TWOMEG);
  149. ASSERT_EQ(madvise(ptr, TWOMEG, MADV_HUGEPAGE), 0);
  150. memset(ptr, 0xde, TWOMEG);
  151. for (i = 0; i < self->nthreads - 1; i++)
  152. if (pthread_create(&self->threads[i], NULL, access_mem, ptr))
  153. perror("Couldn't create thread");
  154. ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0);
  155. for (i = 0; i < self->nthreads - 1; i++)
  156. ASSERT_EQ(pthread_cancel(self->threads[i]), 0);
  157. }
  158. TEST_HARNESS_MAIN