* [LTP] [PATCH v7] hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
@ 2026-09-19 17:49 Pavithra
2026-09-19 20:14 ` [LTP] " linuxtestproject.agent
0 siblings, 1 reply; 4+ messages in thread
From: Pavithra @ 2026-09-19 17:49 UTC (permalink / raw)
To: ltp; +Cc: pavrampu
Stress test fallocate() on hugetlbfs with three concurrent threads:
one continuously punches and fills holes, one faults in hugepages,
and one continuously mmaps and munmaps the same file range.
Verify no hugepage leak occurs after the stress run.
Signed-off-by: Pavithra <pavrampu@linux.ibm.com>
---
v6 -> v7:
- Add volatile thread_err flag to propagate fallocate() failures
from thread_fallocate to the final pass/fail verdict
- Add pthread_mutex_t guards for fault_mmap_addr and mmap_munmap_addr
to fix data races between threads and their cleanup handlers
- Change .hugepages = {2, ...} to {MAX_PAGES_TO_USE, ...}
Link to v6: https://lore.kernel.org/ltp/20250928122544.2176739-1-pavrampu@linux.ibm.com/
---
runtest/hugetlb | 1 +
testcases/kernel/mem/.gitignore | 1 +
.../hugetlb/hugefallocate/hugefallocate03.c | 233 ++++++++++++++++++
3 files changed, 235 insertions(+)
create mode 100644 testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate03.c
diff --git a/runtest/hugetlb b/runtest/hugetlb
index 621c9718a..cd1c2794e 100644
--- a/runtest/hugetlb
+++ b/runtest/hugetlb
@@ -1,5 +1,6 @@
hugefallocate01 hugefallocate01
hugefallocate02 hugefallocate02
+hugefallocate03 hugefallocate03
hugefork01 hugefork01
hugefork02 hugefork02
diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore
index 9e706e1c8..3086d1ac6 100644
--- a/testcases/kernel/mem/.gitignore
+++ b/testcases/kernel/mem/.gitignore
@@ -2,6 +2,7 @@
/cpuset/cpuset02
/hugetlb/hugefallocate/hugefallocate01
/hugetlb/hugefallocate/hugefallocate02
+/hugetlb/hugefallocate/hugefallocate03
/hugetlb/hugefork/hugefork01
/hugetlb/hugefork/hugefork02
/hugetlb/hugemmap/hugemmap01
diff --git a/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate03.c b/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate03.c
new file mode 100644
index 000000000..e947a87eb
--- /dev/null
+++ b/testcases/kernel/mem/hugetlb/hugefallocate/hugefallocate03.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2015 Oracle Corporation
+ * Author: Mike Kravetz
+ * Copyright (c) 2026 Pavithra <pavrampu@linux.ibm.com>
+ */
+
+/*\
+ * Stress test fallocate. This test starts three threads.
+ * Thread one will continually punch/fill holes via falloc.
+ * Thread two will continually fault in those same pages.
+ * Thread three will continually mmap/munmap that page range.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/mount.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#include "hugetlb.h"
+#include "lapi/fallocate.h"
+#include "tst_safe_pthread.h"
+
+#define MNTPOINT "hugetlbfs/"
+#define MAX_PAGES_TO_USE 100
+#define FALLOCATE_ITERATIONS 100000
+
+static int fd = -1;
+static unsigned long max_hpages;
+static long hpage_size;
+static volatile int thread_err;
+
+static void *thread_fallocate(void *arg)
+{
+ int i, err;
+ long tpage;
+
+ (void)arg;
+
+ for (i = 0; i < FALLOCATE_ITERATIONS; i++) {
+ tpage = ((long long)random()) % (max_hpages);
+ err = fallocate(fd,
+ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ tpage * hpage_size, hpage_size);
+ if (err) {
+ tst_res(TFAIL | TERRNO, "fallocate(PUNCH_HOLE) failed at page %ld:", tpage);
+ thread_err = 1;
+ }
+ err = fallocate(fd, 0, tpage * hpage_size, hpage_size);
+ if (err) {
+ tst_res(TFAIL | TERRNO, "fallocate(fill) failed at page %ld:", tpage);
+ thread_err = 1;
+ }
+ }
+ return NULL;
+}
+
+static pthread_mutex_t fault_addr_lock = PTHREAD_MUTEX_INITIALIZER;
+static void *fault_mmap_addr;
+
+static void thread_fault_cleanup(void *arg)
+{
+ (void)arg;
+
+ pthread_mutex_lock(&fault_addr_lock);
+ if (fault_mmap_addr) {
+ munmap(fault_mmap_addr, max_hpages * hpage_size);
+ fault_mmap_addr = NULL;
+ }
+ pthread_mutex_unlock(&fault_addr_lock);
+}
+
+static void *thread_fault(void *arg)
+{
+ long tpage;
+ char foo;
+ struct timespec ts = {};
+
+ (void)arg;
+
+ pthread_mutex_lock(&fault_addr_lock);
+ fault_mmap_addr = SAFE_MMAP(NULL, max_hpages * hpage_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0);
+ pthread_mutex_unlock(&fault_addr_lock);
+
+ pthread_cleanup_push(thread_fault_cleanup, NULL);
+
+ while (1) {
+ tpage = ((long long)random()) % (max_hpages);
+ pthread_mutex_lock(&fault_addr_lock);
+ foo = *((char *)(fault_mmap_addr + (tpage * hpage_size)));
+ *((char *)(fault_mmap_addr + (tpage * hpage_size))) = foo;
+ pthread_mutex_unlock(&fault_addr_lock);
+
+ nanosleep(&ts, NULL); /* thread cancellation point */
+ }
+
+ pthread_cleanup_pop(1);
+
+ return NULL;
+}
+
+static pthread_mutex_t mmap_munmap_lock = PTHREAD_MUTEX_INITIALIZER;
+static void *mmap_munmap_addr;
+
+static void thread_mmap_munmap_cleanup(void *arg)
+{
+ (void)arg;
+
+ pthread_mutex_lock(&mmap_munmap_lock);
+ if (mmap_munmap_addr) {
+ munmap(mmap_munmap_addr, max_hpages * hpage_size);
+ mmap_munmap_addr = NULL;
+ }
+ pthread_mutex_unlock(&mmap_munmap_lock);
+}
+
+static void *thread_mmap_munmap(void *arg)
+{
+ struct timespec ts = {};
+
+ (void)arg;
+
+ pthread_cleanup_push(thread_mmap_munmap_cleanup, NULL);
+
+ while (1) {
+ pthread_mutex_lock(&mmap_munmap_lock);
+ mmap_munmap_addr = SAFE_MMAP(NULL, max_hpages * hpage_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ SAFE_MUNMAP(mmap_munmap_addr, max_hpages * hpage_size);
+ mmap_munmap_addr = NULL;
+ pthread_mutex_unlock(&mmap_munmap_lock);
+
+ nanosleep(&ts, NULL); /* thread cancellation point */
+ }
+
+ pthread_cleanup_pop(1);
+ return NULL;
+}
+
+static void run_test(void)
+{
+ int err;
+ long nr_hpages_free;
+ unsigned long free_before, free_after;
+ unsigned long rsvd_before, rsvd_after;
+ pthread_t falloc_th, fault_th, mmap_munmap_th;
+ void *falloc_th_ret, *fault_th_ret, *mmap_munmap_th_ret;
+
+ fd = tst_creat_unlinked(MNTPOINT, 0, 0600);
+
+ unsigned int seed = (int)getpid() * time(NULL);
+
+ srandom(seed);
+ tst_res(TINFO, "Seed = %d", seed);
+ nr_hpages_free = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
+ max_hpages = MIN(nr_hpages_free, MAX_PAGES_TO_USE);
+ free_before = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
+ rsvd_before = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD);
+
+ /* First preallocate file with max_hpages pages */
+ err = fallocate(fd, 0, 0, hpage_size * max_hpages);
+ if (err) {
+ if (errno == EOPNOTSUPP)
+ tst_brk(TCONF, "fallocate() Operation is not supported");
+ tst_res(TFAIL | TERRNO, "fallocate():");
+ goto windup;
+ }
+
+ free_after = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
+ if (free_before - free_after != max_hpages) {
+ tst_res(TFAIL, "fallocate did not preallocate %ld huge pages",
+ max_hpages);
+ goto windup;
+ }
+
+ thread_err = 0;
+
+ SAFE_PTHREAD_CREATE(&falloc_th, NULL, thread_fallocate, NULL);
+
+ SAFE_PTHREAD_CREATE(&fault_th, NULL, thread_fault, NULL);
+
+ SAFE_PTHREAD_CREATE(&mmap_munmap_th, NULL, thread_mmap_munmap, NULL);
+
+ SAFE_PTHREAD_JOIN(falloc_th, &falloc_th_ret);
+
+ SAFE_PTHREAD_CANCEL(fault_th);
+
+ SAFE_PTHREAD_JOIN(fault_th, &fault_th_ret);
+
+ SAFE_PTHREAD_CANCEL(mmap_munmap_th);
+
+ SAFE_PTHREAD_JOIN(mmap_munmap_th, &mmap_munmap_th_ret);
+
+windup:
+ SAFE_CLOSE(fd);
+
+ free_after = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
+ rsvd_after = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD);
+
+ if (thread_err)
+ tst_res(TFAIL, "fallocate() failed during stress, see above");
+ else if (free_after != free_before || rsvd_after != rsvd_before)
+ tst_res(TFAIL, "free or reserve counts incorrect after fallocate stress test");
+ else
+ tst_res(TPASS, "fallocate stress test passed");
+}
+
+static void setup(void)
+{
+ hpage_size = tst_get_hugepage_size();
+}
+
+static void cleanup(void)
+{
+ if (fd >= 0)
+ SAFE_CLOSE(fd);
+}
+
+static struct tst_test test = {
+ .needs_root = 1,
+ .mntpoint = MNTPOINT,
+ .needs_hugetlbfs = 1,
+ .needs_tmpdir = 1,
+ .setup = setup,
+ .cleanup = cleanup,
+ .test_all = run_test,
+ .hugepages = {MAX_PAGES_TO_USE, TST_NEEDS},
+};
--
2.55.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [LTP] hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
2026-09-19 17:49 [LTP] [PATCH v7] hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs Pavithra
@ 2026-09-19 20:14 ` linuxtestproject.agent
0 siblings, 0 replies; 4+ messages in thread
From: linuxtestproject.agent @ 2026-09-19 20:14 UTC (permalink / raw)
To: Pavithra; +Cc: ltp
Hi Pavithra,
On Sat Sep 19 23:19:20 2026 +0530, Pavithra <pavrampu@linux.ibm.com> wrote:
> hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
> +/*\
> + * Stress test fallocate. This test starts three threads.
> + * Thread one will continually punch/fill holes via falloc.
> + * Thread two will continually fault in those same pages.
> + * Thread three will continually mmap/munmap that page range.
> + */
Format syscall names using the `:manpage:` role (e.g.
`:manpage:`fallocate(2)``, `:manpage:`mmap(2)``, `:manpage:`munmap(2)``)
and avoid abbreviations like "falloc". Document why root privileges are
required (.needs_root = 1).
> + while (1) {
> + tpage = ((long long)random()) % (max_hpages);
> + pthread_mutex_lock(&fault_addr_lock);
> + foo = *((char *)(fault_mmap_addr + (tpage * hpage_size)));
> + *((char *)(fault_mmap_addr + (tpage * hpage_size))) = foo;
> + pthread_mutex_unlock(&fault_addr_lock);
> +
> + nanosleep(&ts, NULL); /* thread cancellation point */
> + }
fault_addr_lock is not accessed by any other thread and can be removed.
Cast to volatile char * when writing back to prevent the compiler from
optimizing out the write. Replace nanosleep(&ts, NULL) with
pthread_testcancel() for thread cancellation.
> + while (1) {
> + pthread_mutex_lock(&mmap_munmap_lock);
> + mmap_munmap_addr = SAFE_MMAP(NULL, max_hpages * hpage_size,
> + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
> + SAFE_MUNMAP(mmap_munmap_addr, max_hpages * hpage_size);
> + mmap_munmap_addr = NULL;
> + pthread_mutex_unlock(&mmap_munmap_lock);
> +
> + nanosleep(&ts, NULL); /* thread cancellation point */
> + }
mmap_munmap_lock is unnecessary since no other thread accesses this
address; mmap_munmap_addr can be a local variable. Replace
nanosleep(&ts, NULL) with pthread_testcancel().
> + unsigned int seed = (int)getpid() * time(NULL);
> +
> + srandom(seed);
> + tst_res(TINFO, "Seed = %d", seed);
seed is unsigned int; use %u instead of %d.
> + /* First preallocate file with max_hpages pages */
> + err = fallocate(fd, 0, 0, hpage_size * max_hpages);
> + if (err) {
> + if (errno == EOPNOTSUPP)
> + tst_brk(TCONF, "fallocate() Operation is not supported");
> + tst_res(TFAIL | TERRNO, "fallocate():");
> + goto windup;
> + }
> +
> + free_after = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
> + if (free_before - free_after != max_hpages) {
> + tst_res(TFAIL, "fallocate did not preallocate %ld huge pages",
> + max_hpages);
> + goto windup;
> + }
If fallocate or preallocation fails, branching to windup causes the test
to report TPASS in the subsequent check because SAFE_CLOSE(fd) restores
the free counts and thread_err remains 0. Use tst_brk(TBROK | TERRNO, ...)
to abort on preallocation failure. Also use %lu instead of %ld for
max_hpages.
> +windup:
> + SAFE_CLOSE(fd);
> +
> + free_after = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
> + rsvd_after = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD);
> +
> + if (thread_err)
> + tst_res(TFAIL, "fallocate() failed during stress, see above");
> + else if (free_after != free_before || rsvd_after != rsvd_before)
> + tst_res(TFAIL, "free or reserve counts incorrect after fallocate stress test");
> + else
> + tst_res(TPASS, "fallocate stress test passed");
Use TST_EXP_EQ_LU(free_after, free_before) and
TST_EXP_EQ_LU(rsvd_after, rsvd_before) to report free and reserve count
comparisons.
> +static void cleanup(void)
> +{
> + if (fd >= 0)
> + SAFE_CLOSE(fd);
> +}
Use if (fd != -1) instead of if (fd >= 0).
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [LTP] hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
2026-09-20 11:35 [LTP] [PATCH v8] " Pavithra
@ 2026-09-20 14:43 ` linuxtestproject.agent
0 siblings, 0 replies; 4+ messages in thread
From: linuxtestproject.agent @ 2026-09-20 14:43 UTC (permalink / raw)
To: Pavithra; +Cc: ltp
Hi Pavithra,
On Sun Sep 20 17:05:19 2026 +0530, Pavithra wrote:
> hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <sys/mount.h>
> +#include <limits.h>
> +#include <sys/param.h>
> +#include <sys/types.h>
> +#include <pthread.h>
> +#include <signal.h>
> +#include <setjmp.h>
Add missing #include <time.h> for time().
> + if (err) {
> + tst_res(TFAIL | TERRNO, "fallocate(PUNCH_HOLE) failed at page %ld:", tpage);
> + thread_err = 1;
> + }
> + err = fallocate(fd, 0, tpage * hpage_size, hpage_size);
> + if (err) {
> + tst_res(TFAIL | TERRNO, "fallocate(fill) failed at page %ld:", tpage);
> + thread_err = 1;
> + }
Drop the trailing colons in the format strings; TERRNO automatically appends
": <errno>". Remove thread_err as tst_res() already records the failure.
Break the loop or return early on failure to avoid flooding the test log
with thousands of failure messages.
> +static void thread_fault_cleanup(void *arg)
> +{
> + (void)arg;
> +
> + if (fault_mmap_addr) {
> + munmap(fault_mmap_addr, max_hpages * hpage_size);
> + fault_mmap_addr = NULL;
> + }
> +}
Use SAFE_MUNMAP() instead of munmap().
> + nr_hpages_free = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
> + max_hpages = MIN(nr_hpages_free, MAX_PAGES_TO_USE);
> + free_before = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
Assign free_before = nr_hpages_free instead of reading /proc/meminfo
twice in consecutive lines.
> + if (thread_err)
> + tst_res(TFAIL, "fallocate() failed during stress, see above");
Remove this check; tst_res() in thread_fallocate() already marks the test
as failed.
> + .hugepages = {MAX_PAGES_TO_USE, TST_NEEDS},
Using TST_NEEDS with MAX_PAGES_TO_USE (100) causes unnecessary TCONF on
systems where hugepages are large (e.g. 16MB on ppc64le, 512MB on arm64)
or where memory is limited. Use TST_REQUEST, or require a smaller minimum
in .hugepages to keep the test portable.
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [LTP] hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
2026-09-23 8:01 [LTP] [PATCH v9] " Pavithra
@ 2026-09-23 10:00 ` linuxtestproject.agent
0 siblings, 0 replies; 4+ messages in thread
From: linuxtestproject.agent @ 2026-09-23 10:00 UTC (permalink / raw)
To: Pavithra; +Cc: ltp
Hi Pavithra,
On Wed Sep 23 13:31:50 2026 +0530, Pavithra wrote:
> hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs
> +static int fd = -1;
> +static unsigned long max_hpages;
> +static long hpage_size;
> +static void *thread_fallocate(void *arg)
Add a blank line between variable declarations and function definitions.
> + SAFE_SIGACTION(SIGBUS, &sa, NULL);
> +
> + fault_mmap_addr = SAFE_MMAP(NULL, max_hpages * hpage_size,
> + PROT_READ | PROT_WRITE, MAP_SHARED,
> + fd, 0);
> +
> + pthread_cleanup_push(thread_fault_cleanup, NULL);
> +
> + while (1) {
> + tpage = ((long long)random()) % (max_hpages);
> +
> + if (sigsetjmp(fault_jmp, 1) == 0) {
> + foo = *((char *)(fault_mmap_addr + (tpage * hpage_size)));
> + *((volatile char *)(fault_mmap_addr + (tpage * hpage_size))) = foo;
> + }
Save the original SIGBUS handler and restore it upon thread termination or in
cleanup. If SIGBUS is raised after this thread terminates, siglongjmp() jumps
to an invalid stack.
> + while (1) {
> + addr = mmap(NULL, max_hpages * hpage_size,
> + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
> + if (addr != MAP_FAILED)
> + SAFE_MUNMAP(addr, max_hpages * hpage_size);
> +
> + pthread_testcancel();
> + }
Use SAFE_MMAP() instead of mmap() and drop the MAP_FAILED check.
> + nr_hpages_free = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
> + max_hpages = MIN(nr_hpages_free, MAX_PAGES_TO_USE);
> + free_before = nr_hpages_free;
> + rsvd_before = SAFE_READ_MEMINFO(MEMINFO_HPAGE_RSVD);
> +
> + /* First preallocate file with max_hpages pages */
> + err = fallocate(fd, 0, 0, hpage_size * max_hpages);
Because .hugepages uses TST_REQUEST, nr_hpages_free and max_hpages can be 0.
When max_hpages is 0, fallocate() fails with EINVAL (causing TBROK instead of
TCONF), and the worker threads divide by zero. Add a check to abort with
tst_brk(TCONF, ...) when max_hpages is 0.
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-23 10:01 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-19 17:49 [LTP] [PATCH v7] hugetlb/hugefallocate: Add hugefallocate03 to stress test fallocate on hugetlbfs Pavithra
2026-09-19 20:14 ` [LTP] " linuxtestproject.agent
-- strict thread matches above, loose matches on Subject: below --
2026-09-20 11:35 [LTP] [PATCH v8] " Pavithra
2026-09-20 14:43 ` [LTP] " linuxtestproject.agent
2026-09-23 8:01 [LTP] [PATCH v9] " Pavithra
2026-09-23 10:00 ` [LTP] " linuxtestproject.agent
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.