prctl_thp_disable.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Basic tests for PR_GET/SET_THP_DISABLE prctl calls
  4. *
  5. * Author(s): Usama Arif <usamaarif642@gmail.com>
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. #include <sys/mman.h>
  12. #include <linux/mman.h>
  13. #include <sys/prctl.h>
  14. #include <sys/wait.h>
  15. #include "kselftest_harness.h"
  16. #include "thp_settings.h"
  17. #include "vm_util.h"
  18. #ifndef PR_THP_DISABLE_EXCEPT_ADVISED
  19. #define PR_THP_DISABLE_EXCEPT_ADVISED (1 << 1)
  20. #endif
  21. enum thp_collapse_type {
  22. THP_COLLAPSE_NONE,
  23. THP_COLLAPSE_MADV_NOHUGEPAGE,
  24. THP_COLLAPSE_MADV_HUGEPAGE, /* MADV_HUGEPAGE before access */
  25. THP_COLLAPSE_MADV_COLLAPSE, /* MADV_COLLAPSE after access */
  26. };
  27. /*
  28. * Function to mmap a buffer, fault it in, madvise it appropriately (before
  29. * page fault for MADV_HUGE, and after for MADV_COLLAPSE), and check if the
  30. * mmap region is huge.
  31. * Returns:
  32. * 0 if test doesn't give hugepage
  33. * 1 if test gives a hugepage
  34. * -errno if mmap fails
  35. */
  36. static int test_mmap_thp(enum thp_collapse_type madvise_buf, size_t pmdsize)
  37. {
  38. char *mem, *mmap_mem;
  39. size_t mmap_size;
  40. int ret;
  41. /* For alignment purposes, we need twice the THP size. */
  42. mmap_size = 2 * pmdsize;
  43. mmap_mem = (char *)mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
  44. MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  45. if (mmap_mem == MAP_FAILED)
  46. return -errno;
  47. /* We need a THP-aligned memory area. */
  48. mem = (char *)(((uintptr_t)mmap_mem + pmdsize) & ~(pmdsize - 1));
  49. if (madvise_buf == THP_COLLAPSE_MADV_HUGEPAGE)
  50. madvise(mem, pmdsize, MADV_HUGEPAGE);
  51. else if (madvise_buf == THP_COLLAPSE_MADV_NOHUGEPAGE)
  52. madvise(mem, pmdsize, MADV_NOHUGEPAGE);
  53. /* Ensure memory is allocated */
  54. memset(mem, 1, pmdsize);
  55. if (madvise_buf == THP_COLLAPSE_MADV_COLLAPSE)
  56. madvise(mem, pmdsize, MADV_COLLAPSE);
  57. /* HACK: make sure we have a separate VMA that we can check reliably. */
  58. mprotect(mem, pmdsize, PROT_READ);
  59. ret = check_huge_anon(mem, 1, pmdsize);
  60. munmap(mmap_mem, mmap_size);
  61. return ret;
  62. }
  63. static void prctl_thp_disable_completely_test(struct __test_metadata *const _metadata,
  64. size_t pmdsize,
  65. enum thp_enabled thp_policy)
  66. {
  67. ASSERT_EQ(prctl(PR_GET_THP_DISABLE, NULL, NULL, NULL, NULL), 1);
  68. /* tests after prctl overrides global policy */
  69. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize), 0);
  70. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0);
  71. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize), 0);
  72. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 0);
  73. /* Reset to global policy */
  74. ASSERT_EQ(prctl(PR_SET_THP_DISABLE, 0, NULL, NULL, NULL), 0);
  75. /* tests after prctl is cleared, and only global policy is effective */
  76. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize),
  77. thp_policy == THP_ALWAYS ? 1 : 0);
  78. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0);
  79. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize),
  80. thp_policy == THP_NEVER ? 0 : 1);
  81. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 1);
  82. }
  83. FIXTURE(prctl_thp_disable_completely)
  84. {
  85. struct thp_settings settings;
  86. size_t pmdsize;
  87. };
  88. FIXTURE_VARIANT(prctl_thp_disable_completely)
  89. {
  90. enum thp_enabled thp_policy;
  91. };
  92. FIXTURE_VARIANT_ADD(prctl_thp_disable_completely, never)
  93. {
  94. .thp_policy = THP_NEVER,
  95. };
  96. FIXTURE_VARIANT_ADD(prctl_thp_disable_completely, madvise)
  97. {
  98. .thp_policy = THP_MADVISE,
  99. };
  100. FIXTURE_VARIANT_ADD(prctl_thp_disable_completely, always)
  101. {
  102. .thp_policy = THP_ALWAYS,
  103. };
  104. FIXTURE_SETUP(prctl_thp_disable_completely)
  105. {
  106. if (!thp_available())
  107. SKIP(return, "Transparent Hugepages not available\n");
  108. self->pmdsize = read_pmd_pagesize();
  109. if (!self->pmdsize)
  110. SKIP(return, "Unable to read PMD size\n");
  111. if (prctl(PR_SET_THP_DISABLE, 1, NULL, NULL, NULL))
  112. SKIP(return, "Unable to disable THPs completely for the process\n");
  113. thp_save_settings();
  114. thp_read_settings(&self->settings);
  115. self->settings.thp_enabled = variant->thp_policy;
  116. self->settings.hugepages[sz2ord(self->pmdsize, getpagesize())].enabled = THP_INHERIT;
  117. thp_write_settings(&self->settings);
  118. }
  119. FIXTURE_TEARDOWN(prctl_thp_disable_completely)
  120. {
  121. thp_restore_settings();
  122. }
  123. TEST_F(prctl_thp_disable_completely, nofork)
  124. {
  125. prctl_thp_disable_completely_test(_metadata, self->pmdsize, variant->thp_policy);
  126. }
  127. TEST_F(prctl_thp_disable_completely, fork)
  128. {
  129. int ret = 0;
  130. pid_t pid;
  131. /* Make sure prctl changes are carried across fork */
  132. pid = fork();
  133. ASSERT_GE(pid, 0);
  134. if (!pid) {
  135. prctl_thp_disable_completely_test(_metadata, self->pmdsize, variant->thp_policy);
  136. return;
  137. }
  138. wait(&ret);
  139. if (WIFEXITED(ret))
  140. ret = WEXITSTATUS(ret);
  141. else
  142. ret = -EINVAL;
  143. ASSERT_EQ(ret, 0);
  144. }
  145. static void prctl_thp_disable_except_madvise_test(struct __test_metadata *const _metadata,
  146. size_t pmdsize,
  147. enum thp_enabled thp_policy)
  148. {
  149. ASSERT_EQ(prctl(PR_GET_THP_DISABLE, NULL, NULL, NULL, NULL), 3);
  150. /* tests after prctl overrides global policy */
  151. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize), 0);
  152. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0);
  153. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize),
  154. thp_policy == THP_NEVER ? 0 : 1);
  155. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 1);
  156. /* Reset to global policy */
  157. ASSERT_EQ(prctl(PR_SET_THP_DISABLE, 0, NULL, NULL, NULL), 0);
  158. /* tests after prctl is cleared, and only global policy is effective */
  159. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize),
  160. thp_policy == THP_ALWAYS ? 1 : 0);
  161. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0);
  162. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize),
  163. thp_policy == THP_NEVER ? 0 : 1);
  164. ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 1);
  165. }
  166. FIXTURE(prctl_thp_disable_except_madvise)
  167. {
  168. struct thp_settings settings;
  169. size_t pmdsize;
  170. };
  171. FIXTURE_VARIANT(prctl_thp_disable_except_madvise)
  172. {
  173. enum thp_enabled thp_policy;
  174. };
  175. FIXTURE_VARIANT_ADD(prctl_thp_disable_except_madvise, never)
  176. {
  177. .thp_policy = THP_NEVER,
  178. };
  179. FIXTURE_VARIANT_ADD(prctl_thp_disable_except_madvise, madvise)
  180. {
  181. .thp_policy = THP_MADVISE,
  182. };
  183. FIXTURE_VARIANT_ADD(prctl_thp_disable_except_madvise, always)
  184. {
  185. .thp_policy = THP_ALWAYS,
  186. };
  187. FIXTURE_SETUP(prctl_thp_disable_except_madvise)
  188. {
  189. if (!thp_available())
  190. SKIP(return, "Transparent Hugepages not available\n");
  191. self->pmdsize = read_pmd_pagesize();
  192. if (!self->pmdsize)
  193. SKIP(return, "Unable to read PMD size\n");
  194. if (prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, NULL, NULL))
  195. SKIP(return, "Unable to set PR_THP_DISABLE_EXCEPT_ADVISED\n");
  196. thp_save_settings();
  197. thp_read_settings(&self->settings);
  198. self->settings.thp_enabled = variant->thp_policy;
  199. self->settings.hugepages[sz2ord(self->pmdsize, getpagesize())].enabled = THP_INHERIT;
  200. thp_write_settings(&self->settings);
  201. }
  202. FIXTURE_TEARDOWN(prctl_thp_disable_except_madvise)
  203. {
  204. thp_restore_settings();
  205. }
  206. TEST_F(prctl_thp_disable_except_madvise, nofork)
  207. {
  208. prctl_thp_disable_except_madvise_test(_metadata, self->pmdsize, variant->thp_policy);
  209. }
  210. TEST_F(prctl_thp_disable_except_madvise, fork)
  211. {
  212. int ret = 0;
  213. pid_t pid;
  214. /* Make sure prctl changes are carried across fork */
  215. pid = fork();
  216. ASSERT_GE(pid, 0);
  217. if (!pid) {
  218. prctl_thp_disable_except_madvise_test(_metadata, self->pmdsize,
  219. variant->thp_policy);
  220. return;
  221. }
  222. wait(&ret);
  223. if (WIFEXITED(ret))
  224. ret = WEXITSTATUS(ret);
  225. else
  226. ret = -EINVAL;
  227. ASSERT_EQ(ret, 0);
  228. }
  229. TEST_HARNESS_MAIN