File Index
Files and directories
Sign in
FILE

hugetlb_madv_vs_map.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/mm/hugetlb_madv_vs_map.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  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_hugepages
  5 *
  6 * During setup, the test allocates the only available page, and starts three threads:
  7 *  - thread1:
  8 *	* madvise(MADV_DONTNEED) on the allocated huge page
  9 *  - thread 2:
 10 *	* Write to the allocated huge page
 11 *  - 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 SIGBUS
 17 *
 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 100
 31
 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 mapping
 54 * The other hugepage should be flipping from used <-> reserved, because
 55 * 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 SIGBUG
 68			 * 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 ~10
 85	 * interactions
 86	 */
 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}
~/ Read-only file preview.