127 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * A test case that must run on a system with one and only one huge page available.4 * # echo 1 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages5 *6 * During setup, the test allocates the only available page, and starts three threads:7 * - thread1:8 * * madvise(MADV_DONTNEED) on the allocated huge page9 * - thread 2:10 * * Write to the allocated huge page11 * - thread 3:12 * * Try to allocated an extra huge page (which must not available)13 *14 * The test fails if thread3 is able to allocate a page.15 *16 * Touching the first page after thread3's allocation will raise a SIGBUS17 *18 * Author: Breno Leitao <leitao@debian.org>19 */20#include <pthread.h>21#include <stdio.h>22#include <stdlib.h>23#include <sys/mman.h>24#include <sys/types.h>25#include <unistd.h>26 27#include "vm_util.h"28#include "../kselftest.h"29 30#define INLOOP_ITER 10031 32size_t mmap_size;33char *huge_ptr;34 35/* Touch the memory while it is being madvised() */36void *touch(void *unused)37{38 for (int i = 0; i < INLOOP_ITER; i++)39 huge_ptr[0] = '.';40 41 return NULL;42}43 44void *madv(void *unused)45{46 for (int i = 0; i < INLOOP_ITER; i++)47 madvise(huge_ptr, mmap_size, MADV_DONTNEED);48 49 return NULL;50}51 52/*53 * We got here, and there must be no huge page available for mapping54 * The other hugepage should be flipping from used <-> reserved, because55 * of madvise(DONTNEED).56 */57void *map_extra(void *unused)58{59 void *ptr;60 61 for (int i = 0; i < INLOOP_ITER; i++) {62 ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,63 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,64 -1, 0);65 66 if ((long)ptr != -1) {67 /* Touching the other page now will cause a SIGBUG68 * huge_ptr[0] = '1';69 */70 return ptr;71 }72 }73 74 return NULL;75}76 77int main(void)78{79 pthread_t thread1, thread2, thread3;80 unsigned long free_hugepages;81 void *ret;82 83 /*84 * On kernel 6.7, we are able to reproduce the problem with ~1085 * interactions86 */87 int max = 10;88 89 free_hugepages = get_free_hugepages();90 91 if (free_hugepages != 1) {92 ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n",93 free_hugepages);94 }95 96 mmap_size = default_huge_page_size();97 98 while (max--) {99 huge_ptr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,100 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,101 -1, 0);102 103 if ((unsigned long)huge_ptr == -1) {104 ksft_test_result_fail("Failed to allocate huge page\n");105 return KSFT_FAIL;106 }107 108 pthread_create(&thread1, NULL, madv, NULL);109 pthread_create(&thread2, NULL, touch, NULL);110 pthread_create(&thread3, NULL, map_extra, NULL);111 112 pthread_join(thread1, NULL);113 pthread_join(thread2, NULL);114 pthread_join(thread3, &ret);115 116 if (ret) {117 ksft_test_result_fail("Unexpected huge page allocation\n");118 return KSFT_FAIL;119 }120 121 /* Unmap and restart */122 munmap(huge_ptr, mmap_size);123 }124 125 return KSFT_PASS;126}127