[PATCH v5] hugetlb/hugeshmat: Add hugeshmat06 migrated from libhugetlbfs shm-perms
Samir Mulani <[email protected]>
| Newsgroups | gmane.linux.ltp |
|---|---|
| Message-ID | <[email protected]> |
Test shared memory behavior when multiple processes attach to a hugepage-backed segment with different permissions. At one point, reservation accounting of free hugepages between the parent and child processes may become inconsistent during memory operations. The parent creates a shared memory segment backed by 4 hugepages (permission 0640), attaches it read-write, initialises each hugepage with a pattern (0x55), then detaches. Child processes are forked in a loop, each reattaching the segment read-only via SHM_RDONLY, verifying the data pattern, detaching, and exiting. If the reservation accounting leaks, repeated read-only attaches will exhaust the hugepage pool and shmat() will fail. The test uses raw shmat() in each child and reports this failure as TFAIL. Signed-off-by: Samir Mulani <[email protected]> --- Changes in v5: 1. Use raw shmat() instead of SAFE_SHMAT() and report pool exhaustion as TFAIL. HugetlbPages from /proc/<pid>/status only tracks mapped pages and can return zero after shmdt() even when resv_huge_pages is still leaked, so it is not a reliable oracle for this regression. [LTP AI Reviewer] 2. Remove get_proc_hugetlb_kb() helper and the HugetlbPages check as they are no longer needed. [LTP AI Reviewer] 3. Use IPC_PRIVATE instead of a fixed SEGMENT_KEY to avoid accidentally reusing or destroying a pre-existing segment. [LTP AI Reviewer] 4. Drop the deprecated [Description] tag from the /*\ block. [LTP AI Reviewer] Link: https://lore.kernel.org/ltp/49b1dbd9-4c51-414a-806c-1d5cc6b5e1c7-tEXmvtCZX7AybS5Ee8rs3A@public.gmane.org/ # v3 Link: https://lore.kernel.org/ltp/20260820042516.46631-1-samir-tEXmvtCZX7AybS5Ee8rs3A@public.gmane.org/ # v4 runtest/hugetlb | 1 + testcases/kernel/mem/.gitignore | 1 + .../mem/hugetlb/hugeshmat/hugeshmat06.c | 152 ++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c diff --git a/runtest/hugetlb b/runtest/hugetlb index 6b35c1f42..1eb5c3339 100644 --- a/runtest/hugetlb +++ b/runtest/hugetlb @@ -49,6 +49,7 @@ hugeshmat02 hugeshmat02 -i 5 hugeshmat03 hugeshmat03 -i 5 hugeshmat04 hugeshmat04 -i 5 hugeshmat05 hugeshmat05 -i 5 +hugeshmat06 hugeshmat06 -i 5 hugeshmctl01 hugeshmctl01 -i 5 hugeshmctl02 hugeshmctl02 -i 5 diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore index e63a6dde7..704f89d5e 100644 --- a/testcases/kernel/mem/.gitignore +++ b/testcases/kernel/mem/.gitignore @@ -45,6 +45,7 @@ /hugetlb/hugeshmat/hugeshmat03 /hugetlb/hugeshmat/hugeshmat04 /hugetlb/hugeshmat/hugeshmat05 +/hugetlb/hugeshmat/hugeshmat06 /hugetlb/hugeshmctl/hugeshmctl01 /hugetlb/hugeshmctl/hugeshmctl02 /hugetlb/hugeshmctl/hugeshmctl03 diff --git a/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c new file mode 100644 index 000000000..440d85672 --- /dev/null +++ b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2005-2006 IBM Corporation. + * Author: David Gibson & Adam Litke + */ + +/*\ + * Tests shared memory behavior when multiple processes attach to a + * hugepage-backed segment with different permissions. + * + * The parent creates a shared memory segment (permission 0640) backed by + * hugepages, attaches it read-write, initialises each hugepage with a + * pattern (0x55), then detaches. A number of child processes are then + * forked; each child reattaches the segment read-only (SHM_RDONLY), + * verifies the expected pattern in every hugepage, detaches, and exits. + * + * The original libhugetlbfs shm-perms regression leaked resv_huge_pages + * on each read-only attach, eventually exhausting the hugepage reservation + * pool. This is detected by using raw shmat() in each child so that a + * pool-exhaustion failure is reported as TFAIL rather than TBROK. + */ + +#include "hugetlb.h" +#include "tst_safe_sysv_ipc.h" + +#define MNTPOINT "hugetlbfs/" +#define HPAGES_IN_SEG 4 +#define PATTERN 0x55 +#define MAX_CHILDREN 128 + +static int global_shmid = -1; +static long hpage_size; +static long segment_size; + +static void setup(void) +{ + int shmid; + char *p; + int i; + + hpage_size = tst_get_hugepage_size(); + if (!hpage_size) + tst_brk(TCONF, "Hugepages are not supported"); + + segment_size = HPAGES_IN_SEG * hpage_size; + + /* Create the hugepage SHM segment with 0640 permissions */ + shmid = SAFE_SHMGET(IPC_PRIVATE, segment_size, + IPC_CREAT | SHM_HUGETLB | 0640); + global_shmid = shmid; + + /* Attach read-write, write a known pattern into each hugepage */ + p = SAFE_SHMAT(shmid, NULL, 0); + + for (i = 0; i < HPAGES_IN_SEG; i++) + memset(p + (i * hpage_size), PATTERN, hpage_size); + + SAFE_SHMDT((const void *)p); +} + +static void cleanup(void) +{ + if (global_shmid >= 0) + SAFE_SHMCTL(global_shmid, IPC_RMID, NULL); +} + +static void run_test(void) +{ + int i, iterations; + pid_t pid; + + /* + * Number of attach/detach cycles to exercise reservation accounting. + * Use tst_hugepages (reserved by the framework) but cap at + * MAX_CHILDREN to avoid spawning an unreasonable number of children + * on large-memory machines. + */ + iterations = MIN((long)tst_hugepages, (long)MAX_CHILDREN); + + tst_res(TINFO, "Running %d child attach/detach iterations", iterations); + + for (i = 0; i < iterations; i++) { + pid = SAFE_FORK(); + if (pid == 0) { + /* ---- child ---- */ + char *shmaddr; + int j; + + /* + * Use raw shmat() instead of SAFE_SHMAT() so that a + * failure caused by resv_huge_pages exhaustion is + * reported as TFAIL, not TBROK. + */ + shmaddr = shmat(global_shmid, NULL, SHM_RDONLY); + if (shmaddr == (void *)-1) { + tst_res(TFAIL | TERRNO, + "Child %d: shmat() failed, hugepage reservation pool may be exhausted", + getpid()); + exit(EXIT_FAILURE); + } + + tst_res(TINFO, "Child %d attached segment successfully", + getpid()); + + /* Verify the pattern written by the parent */ + for (j = 0; j < HPAGES_IN_SEG; j++) { + unsigned char val = + *((unsigned char *)shmaddr + + (j * hpage_size)); + + if (val != PATTERN) { + tst_res(TFAIL, + "Child %d: hugepage[%d] data mismatch: got 0x%02x, expected 0x%02x", + getpid(), j, (unsigned int)val, PATTERN); + SAFE_SHMDT((const void *)shmaddr); + exit(EXIT_FAILURE); + } + } + + SAFE_SHMDT((const void *)shmaddr); + exit(EXIT_SUCCESS); + } + } + + /* + * Wait for all children. tst_reap_children() calls tst_brk(TBROK) + * if any child exited non-zero, so a TFAIL inside a child is + * automatically promoted to a test-level failure here. + */ + tst_reap_children(); + + tst_res(TPASS, + "All %d children attached read-only and verified reservation accounting", + iterations); +} + +static struct tst_test test = { + .needs_root = 1, + .mntpoint = MNTPOINT, + .needs_hugetlbfs = 1, + .needs_tmpdir = 1, + .forks_child = 1, + .setup = setup, + .cleanup = cleanup, + .test_all = run_test, + /* + * Request HPAGES_IN_SEG hugepages; that is all the test needs. + * TST_NEEDS means the test is skipped if the kernel cannot + * provide them. + */ + .hugepages = {HPAGES_IN_SEG, TST_NEEDS}, +}; -- 2.52.0 -- Mailing list info: https://lists.linux.it/listinfo/ltp