[LTP] [PATCH v4] hugetlb/hugeshmat: Add hugeshmat06 migrated from libhugetlbfs shm-perms
Samir Mulani <[email protected]>
| Newsgroups | it.linux.lists.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. Per-process HugetlbPages: from /proc/<pid>/status is checked before and after each child's attach/detach cycle. A non-zero value after detach indicates a reservation accounting leak. Using a per-process counter instead of the system-wide HugePages_Free: from /proc/meminfo avoids false failures from concurrent hugepage users on the system. Signed-off-by: Samir Mulani <[email protected]> --- Changes in v4: 1. Replace HugePages_Free: (system-wide /proc/meminfo) with HugetlbPages: (per-process /proc/<pid>/status) for reservation accounting check to avoid false failures when other processes use hugepages concurrently. [Cyril Hrubis] 2. Use tst_hugepages directly instead of re-reading MEMINFO_HPAGE_TOTAL since the LTP framework already stores the reserved count. [Li Wang] 3. Cap iteration count with MIN(tst_hugepages, MAX_CHILDREN) instead of computing it from total hugepages * hpage_size to avoid spawning excessive children on large-memory machines. [Li Wang] 4. Reduce .hugepages request from 32 to HPAGES_IN_SEG (4) since only 4 hugepages are ever needed. [Li Wang] 5. Move all child-local variables into the child process block to fix variable aliasing between the outer fork loop index and the inner hugepage read loop index. [Li Wang] 6. Add data coherence check: each child verifies every hugepage contains the expected 0x55 pattern written by the parent, since children attach with SHM_RDONLY. [Li Wang] 7. Move segment creation and pattern initialisation into setup() to keep run_test() focused on attach/verify/detach/accounting. 8. Add TINFO message per child reporting successful attach. [Li Wang] 9. Fix checkpatch.pl quoted string split across lines warnings. Link: https://lore.kernel.org/ltp/[email protected]/ # v3 runtest/hugetlb | 1 + testcases/kernel/mem/.gitignore | 1 + .../mem/hugetlb/hugeshmat/hugeshmat06.c | 187 ++++++++++++++++++ 3 files changed, 189 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..b8c40cea0 --- /dev/null +++ b/testcases/kernel/mem/hugetlb/hugeshmat/hugeshmat06.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2005-2006 IBM Corporation. + * Author: David Gibson & Adam Litke + */ + +/*\ + * [Description] + * + * 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 test validates two things: + * 1. Per-process HugetlbPages accounting (from /proc/self/status) must + * return to zero after the child detaches — catching reservation- + * accounting leaks described in the original libhugetlbfs shm-perms + * test. + * 2. Read-only children can successfully read the data written by the + * parent (data coherence check). + * + * Using per-process /proc/self/status HugetlbPages: rather than the + * system-wide HugePages_Free: counter avoids false failures caused by + * concurrent users of hugepages on the system. + */ + +#include "hugetlb.h" +#include "tst_safe_sysv_ipc.h" + +#define SEGMENT_KEY (0x82ba15ff) +#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; + +/* + * get_proc_hugetlb_kb - return HugetlbPages value (in kB) for a given pid + * as reported by /proc/<pid>/status. + */ +static long get_proc_hugetlb_kb(pid_t pid) +{ + return SAFE_READ_PROC_STATUS(pid, "HugetlbPages:"); +} + +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(SEGMENT_KEY, 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; + long hugetlb_before, hugetlb_after; + int j; + + /* + * HugetlbPages should be 0 before we attach + * (no hugepages mapped in this fresh child). + */ + hugetlb_before = get_proc_hugetlb_kb(getpid()); + if (hugetlb_before != 0) { + tst_res(TWARN, + "Child %d: HugetlbPages before attach is %ld kB (expected 0)", + getpid(), hugetlb_before); + } + + /* Re-open existing segment (size 0 = use existing) */ + shmaddr = SAFE_SHMAT(global_shmid, NULL, SHM_RDONLY); + + 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); + + /* + * After detaching, HugetlbPages must drop back to 0. + * A non-zero value indicates a reservation accounting + * leak. + */ + hugetlb_after = get_proc_hugetlb_kb(getpid()); + if (hugetlb_after != 0) { + tst_res(TFAIL, + "Child %d: HugetlbPages after detach is %ld kB (expected 0), reservation accounting leak", + getpid(), hugetlb_after); + exit(EXIT_FAILURE); + } + + 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