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