-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/mm: new test that steals pages
This test stresses the race between of madvise(DONTNEED), a page fault and a parallel huge page mmap, which should fail due to lack of available page available for mapping. This test case must run on a system with one and only one huge page available. # echo 1 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages During setup, the test allocates the only available page, and starts three threads: - thread 1: * madvise(MADV_DONTNEED) on the allocated huge page - thread 2: * Write to the allocated huge page - thread 3: * Tries to allocated (steal) an extra huge page (which is not available) thread 3 should never succeed in the allocation, since the only huge page was never unmapped, and should be reserved. Touching the old page after thread3 allocation will raise a SIGBUS. Link: https://lkml.kernel.org/r/20240105155419.1939484-2-leitao@debian.org Signed-off-by: Breno Leitao <leitao@debian.org> Cc: Mike Rapoport (IBM) <rppt@kernel.org> Cc: Muchun Song <songmuchun@bytedance.com> Cc: Rik van Riel <riel@surriel.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Vegard Nossum <vegard.nossum@oracle.com> Cc: Yang Shi <shy828301@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
- Loading branch information
Breno Leitao
authored and
Andrew Morton
committed
Feb 22, 2024
1 parent
d749cc7
commit 8c407e0
Showing
3 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,3 +46,4 @@ gup_longterm | |
mkdirty | ||
va_high_addr_switch | ||
hugetlb_fault_after_madv | ||
hugetlb_madv_vs_map |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* A test case that must run on a system with one and only one huge page available. | ||
* # echo 1 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages | ||
* | ||
* During setup, the test allocates the only available page, and starts three threads: | ||
* - thread1: | ||
* * madvise(MADV_DONTNEED) on the allocated huge page | ||
* - thread 2: | ||
* * Write to the allocated huge page | ||
* - thread 3: | ||
* * Try to allocated an extra huge page (which must not available) | ||
* | ||
* The test fails if thread3 is able to allocate a page. | ||
* | ||
* Touching the first page after thread3's allocation will raise a SIGBUS | ||
* | ||
* Author: Breno Leitao <leitao@debian.org> | ||
*/ | ||
#include <pthread.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <sys/mman.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
#include "vm_util.h" | ||
#include "../kselftest.h" | ||
|
||
#define MMAP_SIZE (1 << 21) | ||
#define INLOOP_ITER 100 | ||
|
||
char *huge_ptr; | ||
|
||
/* Touch the memory while it is being madvised() */ | ||
void *touch(void *unused) | ||
{ | ||
for (int i = 0; i < INLOOP_ITER; i++) | ||
huge_ptr[0] = '.'; | ||
|
||
return NULL; | ||
} | ||
|
||
void *madv(void *unused) | ||
{ | ||
for (int i = 0; i < INLOOP_ITER; i++) | ||
madvise(huge_ptr, MMAP_SIZE, MADV_DONTNEED); | ||
|
||
return NULL; | ||
} | ||
|
||
/* | ||
* We got here, and there must be no huge page available for mapping | ||
* The other hugepage should be flipping from used <-> reserved, because | ||
* of madvise(DONTNEED). | ||
*/ | ||
void *map_extra(void *unused) | ||
{ | ||
void *ptr; | ||
|
||
for (int i = 0; i < INLOOP_ITER; i++) { | ||
ptr = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, | ||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, | ||
-1, 0); | ||
|
||
if ((long)ptr != -1) { | ||
/* Touching the other page now will cause a SIGBUG | ||
* huge_ptr[0] = '1'; | ||
*/ | ||
return ptr; | ||
} | ||
} | ||
|
||
return NULL; | ||
} | ||
|
||
int main(void) | ||
{ | ||
pthread_t thread1, thread2, thread3; | ||
unsigned long free_hugepages; | ||
void *ret; | ||
|
||
/* | ||
* On kernel 6.7, we are able to reproduce the problem with ~10 | ||
* interactions | ||
*/ | ||
int max = 10; | ||
|
||
free_hugepages = get_free_hugepages(); | ||
|
||
if (free_hugepages != 1) { | ||
ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", | ||
free_hugepages); | ||
} | ||
|
||
while (max--) { | ||
huge_ptr = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, | ||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, | ||
-1, 0); | ||
|
||
if ((unsigned long)huge_ptr == -1) { | ||
ksft_exit_skip("Failed to allocated huge page\n"); | ||
return KSFT_SKIP; | ||
} | ||
|
||
pthread_create(&thread1, NULL, madv, NULL); | ||
pthread_create(&thread2, NULL, touch, NULL); | ||
pthread_create(&thread3, NULL, map_extra, NULL); | ||
|
||
pthread_join(thread1, NULL); | ||
pthread_join(thread2, NULL); | ||
pthread_join(thread3, &ret); | ||
|
||
if (ret) { | ||
ksft_test_result_fail("Unexpected huge page allocation\n"); | ||
return KSFT_FAIL; | ||
} | ||
|
||
/* Unmap and restart */ | ||
munmap(huge_ptr, MMAP_SIZE); | ||
} | ||
|
||
return KSFT_PASS; | ||
} |