FILE
prctl_thp_disable.c
File preview with syntax highlighting.
linux-6.19.14/tools/testing/selftests/mm/prctl_thp_disable.c
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 16#include "kselftest_harness.h" 17#include "thp_settings.h" 18#include "vm_util.h" 19 20#ifndef PR_THP_DISABLE_EXCEPT_ADVISED 21#define PR_THP_DISABLE_EXCEPT_ADVISED (1 << 1) 22#endif 23 24enum thp_collapse_type { 25 THP_COLLAPSE_NONE, 26 THP_COLLAPSE_MADV_NOHUGEPAGE, 27 THP_COLLAPSE_MADV_HUGEPAGE, /* MADV_HUGEPAGE before access */ 28 THP_COLLAPSE_MADV_COLLAPSE, /* MADV_COLLAPSE after access */ 29}; 30 31/* 32 * Function to mmap a buffer, fault it in, madvise it appropriately (before 33 * page fault for MADV_HUGE, and after for MADV_COLLAPSE), and check if the 34 * mmap region is huge. 35 * Returns: 36 * 0 if test doesn't give hugepage 37 * 1 if test gives a hugepage 38 * -errno if mmap fails 39 */ 40static int test_mmap_thp(enum thp_collapse_type madvise_buf, size_t pmdsize) 41{ 42 char *mem, *mmap_mem; 43 size_t mmap_size; 44 int ret; 45 46 /* For alignment purposes, we need twice the THP size. */ 47 mmap_size = 2 * pmdsize; 48 mmap_mem = (char *)mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, 49 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 50 if (mmap_mem == MAP_FAILED) 51 return -errno; 52 53 /* We need a THP-aligned memory area. */ 54 mem = (char *)(((uintptr_t)mmap_mem + pmdsize) & ~(pmdsize - 1)); 55 56 if (madvise_buf == THP_COLLAPSE_MADV_HUGEPAGE) 57 madvise(mem, pmdsize, MADV_HUGEPAGE); 58 else if (madvise_buf == THP_COLLAPSE_MADV_NOHUGEPAGE) 59 madvise(mem, pmdsize, MADV_NOHUGEPAGE); 60 61 /* Ensure memory is allocated */ 62 memset(mem, 1, pmdsize); 63 64 if (madvise_buf == THP_COLLAPSE_MADV_COLLAPSE) 65 madvise(mem, pmdsize, MADV_COLLAPSE); 66 67 /* HACK: make sure we have a separate VMA that we can check reliably. */ 68 mprotect(mem, pmdsize, PROT_READ); 69 70 ret = check_huge_anon(mem, 1, pmdsize); 71 munmap(mmap_mem, mmap_size); 72 return ret; 73} 74 75static void prctl_thp_disable_completely_test(struct __test_metadata *const _metadata, 76 size_t pmdsize, 77 enum thp_enabled thp_policy) 78{ 79 ASSERT_EQ(prctl(PR_GET_THP_DISABLE, NULL, NULL, NULL, NULL), 1); 80 81 /* tests after prctl overrides global policy */ 82 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize), 0); 83 84 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0); 85 86 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize), 0); 87 88 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 0); 89 90 /* Reset to global policy */ 91 ASSERT_EQ(prctl(PR_SET_THP_DISABLE, 0, NULL, NULL, NULL), 0); 92 93 /* tests after prctl is cleared, and only global policy is effective */ 94 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize), 95 thp_policy == THP_ALWAYS ? 1 : 0); 96 97 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0); 98 99 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize), 100 thp_policy == THP_NEVER ? 0 : 1); 101 102 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 1); 103} 104 105FIXTURE(prctl_thp_disable_completely) 106{ 107 struct thp_settings settings; 108 size_t pmdsize; 109}; 110 111FIXTURE_VARIANT(prctl_thp_disable_completely) 112{ 113 enum thp_enabled thp_policy; 114}; 115 116FIXTURE_VARIANT_ADD(prctl_thp_disable_completely, never) 117{ 118 .thp_policy = THP_NEVER, 119}; 120 121FIXTURE_VARIANT_ADD(prctl_thp_disable_completely, madvise) 122{ 123 .thp_policy = THP_MADVISE, 124}; 125 126FIXTURE_VARIANT_ADD(prctl_thp_disable_completely, always) 127{ 128 .thp_policy = THP_ALWAYS, 129}; 130 131FIXTURE_SETUP(prctl_thp_disable_completely) 132{ 133 if (!thp_available()) 134 SKIP(return, "Transparent Hugepages not available\n"); 135 136 self->pmdsize = read_pmd_pagesize(); 137 if (!self->pmdsize) 138 SKIP(return, "Unable to read PMD size\n"); 139 140 if (prctl(PR_SET_THP_DISABLE, 1, NULL, NULL, NULL)) 141 SKIP(return, "Unable to disable THPs completely for the process\n"); 142 143 thp_save_settings(); 144 thp_read_settings(&self->settings); 145 self->settings.thp_enabled = variant->thp_policy; 146 self->settings.hugepages[sz2ord(self->pmdsize, getpagesize())].enabled = THP_INHERIT; 147 thp_write_settings(&self->settings); 148} 149 150FIXTURE_TEARDOWN(prctl_thp_disable_completely) 151{ 152 thp_restore_settings(); 153} 154 155TEST_F(prctl_thp_disable_completely, nofork) 156{ 157 prctl_thp_disable_completely_test(_metadata, self->pmdsize, variant->thp_policy); 158} 159 160TEST_F(prctl_thp_disable_completely, fork) 161{ 162 int ret = 0; 163 pid_t pid; 164 165 /* Make sure prctl changes are carried across fork */ 166 pid = fork(); 167 ASSERT_GE(pid, 0); 168 169 if (!pid) { 170 prctl_thp_disable_completely_test(_metadata, self->pmdsize, variant->thp_policy); 171 return; 172 } 173 174 wait(&ret); 175 if (WIFEXITED(ret)) 176 ret = WEXITSTATUS(ret); 177 else 178 ret = -EINVAL; 179 ASSERT_EQ(ret, 0); 180} 181 182static void prctl_thp_disable_except_madvise_test(struct __test_metadata *const _metadata, 183 size_t pmdsize, 184 enum thp_enabled thp_policy) 185{ 186 ASSERT_EQ(prctl(PR_GET_THP_DISABLE, NULL, NULL, NULL, NULL), 3); 187 188 /* tests after prctl overrides global policy */ 189 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize), 0); 190 191 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0); 192 193 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize), 194 thp_policy == THP_NEVER ? 0 : 1); 195 196 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 1); 197 198 /* Reset to global policy */ 199 ASSERT_EQ(prctl(PR_SET_THP_DISABLE, 0, NULL, NULL, NULL), 0); 200 201 /* tests after prctl is cleared, and only global policy is effective */ 202 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_NONE, pmdsize), 203 thp_policy == THP_ALWAYS ? 1 : 0); 204 205 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_NOHUGEPAGE, pmdsize), 0); 206 207 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_HUGEPAGE, pmdsize), 208 thp_policy == THP_NEVER ? 0 : 1); 209 210 ASSERT_EQ(test_mmap_thp(THP_COLLAPSE_MADV_COLLAPSE, pmdsize), 1); 211} 212 213FIXTURE(prctl_thp_disable_except_madvise) 214{ 215 struct thp_settings settings; 216 size_t pmdsize; 217}; 218 219FIXTURE_VARIANT(prctl_thp_disable_except_madvise) 220{ 221 enum thp_enabled thp_policy; 222}; 223 224FIXTURE_VARIANT_ADD(prctl_thp_disable_except_madvise, never) 225{ 226 .thp_policy = THP_NEVER, 227}; 228 229FIXTURE_VARIANT_ADD(prctl_thp_disable_except_madvise, madvise) 230{ 231 .thp_policy = THP_MADVISE, 232}; 233 234FIXTURE_VARIANT_ADD(prctl_thp_disable_except_madvise, always) 235{ 236 .thp_policy = THP_ALWAYS, 237}; 238 239FIXTURE_SETUP(prctl_thp_disable_except_madvise) 240{ 241 if (!thp_available()) 242 SKIP(return, "Transparent Hugepages not available\n"); 243 244 self->pmdsize = read_pmd_pagesize(); 245 if (!self->pmdsize) 246 SKIP(return, "Unable to read PMD size\n"); 247 248 if (prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, NULL, NULL)) 249 SKIP(return, "Unable to set PR_THP_DISABLE_EXCEPT_ADVISED\n"); 250 251 thp_save_settings(); 252 thp_read_settings(&self->settings); 253 self->settings.thp_enabled = variant->thp_policy; 254 self->settings.hugepages[sz2ord(self->pmdsize, getpagesize())].enabled = THP_INHERIT; 255 thp_write_settings(&self->settings); 256} 257 258FIXTURE_TEARDOWN(prctl_thp_disable_except_madvise) 259{ 260 thp_restore_settings(); 261} 262 263TEST_F(prctl_thp_disable_except_madvise, nofork) 264{ 265 prctl_thp_disable_except_madvise_test(_metadata, self->pmdsize, variant->thp_policy); 266} 267 268TEST_F(prctl_thp_disable_except_madvise, fork) 269{ 270 int ret = 0; 271 pid_t pid; 272 273 /* Make sure prctl changes are carried across fork */ 274 pid = fork(); 275 ASSERT_GE(pid, 0); 276 277 if (!pid) { 278 prctl_thp_disable_except_madvise_test(_metadata, self->pmdsize, 279 variant->thp_policy); 280 return; 281 } 282 283 wait(&ret); 284 if (WIFEXITED(ret)) 285 ret = WEXITSTATUS(ret); 286 else 287 ret = -EINVAL; 288 ASSERT_EQ(ret, 0); 289} 290 291TEST_HARNESS_MAIN
~/
Read-only file preview.