* [PATCH v3 1/4] tools/lib/mm: add shared file helpers
2026-05-21 11:17 [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
@ 2026-05-21 11:17 ` Sarthak Sharma
2026-05-24 17:06 ` Mike Rapoport
2026-05-21 11:17 ` [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests Sarthak Sharma
` (3 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-21 11:17 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
Move read_file(), write_file(), read_num(), and write_num() out of
tools/testing/selftests/mm/vm_util.c into a new shared helper under
tools/lib/mm/.
These helpers are used by mm selftests today and will also be needed by
shared hugepage helpers in subsequent patches. Move them to a generic
location and drop the kselftest-specific dependency from their
implementation so they can be reused outside selftests as well.
Update the current mm selftest users to include the new header directly,
and link the new helper into the selftests/mm build.
Add tools/lib/mm/ to the MEMORY MANAGEMENT - MISC entry in MAINTAINERS.
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
MAINTAINERS | 1 +
tools/lib/mm/file_utils.c | 83 +++++++++++++++++++
tools/lib/mm/file_utils.h | 12 +++
tools/testing/selftests/mm/Makefile | 6 +-
.../testing/selftests/mm/hugepage_settings.c | 3 +-
tools/testing/selftests/mm/khugepaged.c | 1 +
.../selftests/mm/split_huge_page_test.c | 2 +
tools/testing/selftests/mm/vm_util.c | 65 +--------------
tools/testing/selftests/mm/vm_util.h | 5 --
9 files changed, 106 insertions(+), 72 deletions(-)
create mode 100644 tools/lib/mm/file_utils.c
create mode 100644 tools/lib/mm/file_utils.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 46ed0f0e76d8..7887a3263373 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16936,6 +16936,7 @@ F: mm/memory-tiers.c
F: mm/page_idle.c
F: mm/pgalloc-track.h
F: mm/process_vm_access.c
+F: tools/lib/mm/
F: tools/testing/selftests/mm/
MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION
diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
new file mode 100644
index 000000000000..0f9322f2cf41
--- /dev/null
+++ b/tools/lib/mm/file_utils.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "file_utils.h"
+
+int read_file(const char *path, char *buf, size_t buflen)
+{
+ int fd;
+ ssize_t numread;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return 0;
+
+ numread = read(fd, buf, buflen - 1);
+ if (numread < 1) {
+ close(fd);
+ return 0;
+ }
+
+ buf[numread] = '\0';
+ close(fd);
+
+ return (unsigned int)numread;
+}
+
+void write_file(const char *path, const char *buf, size_t buflen)
+{
+ int fd, saved_errno;
+ ssize_t numwritten;
+
+ if (buflen < 2) {
+ fprintf(stderr, "Incorrect buffer len: %zu\n", buflen);
+ exit(EXIT_FAILURE);
+ }
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1) {
+ fprintf(stderr, "%s open failed: %s\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ numwritten = write(fd, buf, buflen - 1);
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ if (numwritten < 0) {
+ fprintf(stderr, "%s write(%.*s) failed: %s\n",
+ path, (int)(buflen - 1), buf, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (numwritten != (ssize_t)(buflen - 1)) {
+ fprintf(stderr,
+ "%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n",
+ path, (int)(buflen - 1), buf, buflen - 1, numwritten);
+ exit(EXIT_FAILURE);
+ }
+}
+
+unsigned long read_num(const char *path)
+{
+ char buf[21];
+
+ if (read_file(path, buf, sizeof(buf)) < 0) {
+ perror("read_file()");
+ exit(EXIT_FAILURE);
+ }
+
+ return strtoul(buf, NULL, 10);
+}
+
+void write_num(const char *path, unsigned long num)
+{
+ char buf[21];
+
+ snprintf(buf, sizeof(buf), "%lu", num);
+ write_file(path, buf, strlen(buf) + 1);
+}
diff --git a/tools/lib/mm/file_utils.h b/tools/lib/mm/file_utils.h
new file mode 100644
index 000000000000..060a84725c9d
--- /dev/null
+++ b/tools/lib/mm/file_utils.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __MM_FILE_UTILS_H__
+#define __MM_FILE_UTILS_H__
+
+#include <stddef.h>
+
+int read_file(const char *path, char *buf, size_t buflen);
+void write_file(const char *path, const char *buf, size_t buflen);
+unsigned long read_num(const char *path);
+void write_num(const char *path, unsigned long num);
+
+#endif
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index e6df968f0971..b5fb4b6ab31b 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -37,7 +37,7 @@ endif
# LDLIBS.
MAKEFLAGS += --no-builtin-rules
-CFLAGS = -Wall -O2 -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
+CFLAGS = -Wall -O2 -I $(top_srcdir) -I $(top_srcdir)/tools/lib $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
CFLAGS += -Wunreachable-code
LDLIBS = -lrt -lpthread -lm
@@ -187,8 +187,8 @@ TEST_FILES += write_hugetlb_memory.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c
-$(TEST_GEN_FILES): vm_util.c hugepage_settings.c
+$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
index 2eab2110ac6a..5e947abb7425 100644
--- a/tools/testing/selftests/mm/hugepage_settings.c
+++ b/tools/testing/selftests/mm/hugepage_settings.c
@@ -8,8 +8,9 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <mm/file_utils.h>
-#include "vm_util.h"
+#include "kselftest.h"
#include "hugepage_settings.h"
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 10e8dedcb087..ae5945e2bfac 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -18,6 +18,7 @@
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/vfs.h>
+#include <mm/file_utils.h>
#include "linux/magic.h"
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 50c80ceb4988..4a2ccc055c8b 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -19,6 +19,8 @@
#include <malloc.h>
#include <stdbool.h>
#include <time.h>
+#include <mm/file_utils.h>
+
#include "vm_util.h"
#include "kselftest.h"
#include "hugepage_settings.h"
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 311fc5b4513e..631dc2baff97 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -8,6 +8,8 @@
#include <linux/fs.h>
#include <sys/syscall.h>
#include <unistd.h>
+#include <mm/file_utils.h>
+
#include "kselftest.h"
#include "vm_util.h"
@@ -698,69 +700,6 @@ int unpoison_memory(unsigned long pfn)
return ret > 0 ? 0 : -errno;
}
-int read_file(const char *path, char *buf, size_t buflen)
-{
- int fd;
- ssize_t numread;
-
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return 0;
-
- numread = read(fd, buf, buflen - 1);
- if (numread < 1) {
- close(fd);
- return 0;
- }
-
- buf[numread] = '\0';
- close(fd);
-
- return (unsigned int) numread;
-}
-
-void write_file(const char *path, const char *buf, size_t buflen)
-{
- int fd, saved_errno;
- ssize_t numwritten;
-
- if (buflen < 2)
- ksft_exit_fail_msg("Incorrect buffer len: %zu\n", buflen);
-
- fd = open(path, O_WRONLY);
- if (fd == -1)
- ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno));
-
- numwritten = write(fd, buf, buflen - 1);
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- if (numwritten < 0)
- ksft_exit_fail_msg("%s write(%.*s) failed: %s\n", path, (int)(buflen - 1),
- buf, strerror(errno));
- if (numwritten != buflen - 1)
- ksft_exit_fail_msg("%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n",
- path, (int)(buflen - 1), buf, buflen - 1, numwritten);
-}
-
-unsigned long read_num(const char *path)
-{
- char buf[21];
-
- if (read_file(path, buf, sizeof(buf)) < 0)
- ksft_exit_fail_perror("read_file()");
-
- return strtoul(buf, NULL, 10);
-}
-
-void write_num(const char *path, unsigned long num)
-{
- char buf[21];
-
- sprintf(buf, "%lu", num);
- write_file(path, buf, strlen(buf) + 1);
-}
-
static unsigned long shmall, shmmax;
void __shm_limits_restore(void)
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index ea8fc8fdf0eb..d0932cdd9a34 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -164,11 +164,6 @@ int unpoison_memory(unsigned long pfn);
#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
-void write_file(const char *path, const char *buf, size_t buflen);
-int read_file(const char *path, char *buf, size_t buflen);
-unsigned long read_num(const char *path);
-void write_num(const char *path, unsigned long num);
-
void shm_limits_prepare(unsigned long length);
void __shm_limits_restore(void);
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v3 1/4] tools/lib/mm: add shared file helpers
2026-05-21 11:17 ` [PATCH v3 1/4] tools/lib/mm: add shared file helpers Sarthak Sharma
@ 2026-05-24 17:06 ` Mike Rapoport
2026-05-25 6:29 ` Sarthak Sharma
0 siblings, 1 reply; 14+ messages in thread
From: Mike Rapoport @ 2026-05-24 17:06 UTC (permalink / raw)
To: Sarthak Sharma
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On Thu, 21 May 2026 16:47:58 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
Hi Sarthak,
>
> diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
> new file mode 100644
> index 000000000000..0f9322f2cf41
> --- /dev/null
> +++ b/tools/lib/mm/file_utils.c
> @@ -0,0 +1,83 @@
> [ ... skip 48 lines ... ]
> + saved_errno = errno;
> + close(fd);
> + errno = saved_errno;
> + if (numwritten < 0) {
> + fprintf(stderr, "%s write(%.*s) failed: %s\n",
> + path, (int)(buflen - 1), buf, strerror(errno));
This would break TAP formatting for selftests.
> + exit(EXIT_FAILURE);
and while EXIT_FAILURE == KSFT_FAIL I'm not sure it's robust enough.
>
> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
> index 2eab2110ac6a..5e947abb7425 100644
> --- a/tools/testing/selftests/mm/hugepage_settings.c
> +++ b/tools/testing/selftests/mm/hugepage_settings.c
> @@ -8,8 +8,9 @@
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
> +#include <mm/file_utils.h>
>
> -#include "vm_util.h"
I think it would be fine to include file_utils.h in vm_utils.h and avoid
further churn.
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 1/4] tools/lib/mm: add shared file helpers
2026-05-24 17:06 ` Mike Rapoport
@ 2026-05-25 6:29 ` Sarthak Sharma
2026-05-26 9:01 ` Mike Rapoport
0 siblings, 1 reply; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-25 6:29 UTC (permalink / raw)
To: Mike Rapoport
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
Hi Mike!
On 5/24/26 10:36 PM, Mike Rapoport wrote:
> On Thu, 21 May 2026 16:47:58 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
>
> Hi Sarthak,
>
>>
>> diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
>> new file mode 100644
>> index 000000000000..0f9322f2cf41
>> --- /dev/null
>> +++ b/tools/lib/mm/file_utils.c
>> @@ -0,0 +1,83 @@
>> [ ... skip 48 lines ... ]
>> + saved_errno = errno;
>> + close(fd);
>> + errno = saved_errno;
>> + if (numwritten < 0) {
>> + fprintf(stderr, "%s write(%.*s) failed: %s\n",
>> + path, (int)(buflen - 1), buf, strerror(errno));
>
> This would break TAP formatting for selftests.
Yes, thanks for pointing it out.
>
>> + exit(EXIT_FAILURE);
>
> and while EXIT_FAILURE == KSFT_FAIL I'm not sure it's robust enough.
I used EXIT_FAILURE here because the helper is moving out of selftests
and should not include kselftest.h anymore. The helper already
terminated the process on these paths, so I tried to preserve that
behavior while removing the ksft dependency.
We can change this to return errors instead of calling exit() and update
the selftest callers to report failures through the ksft_* helpers. I
agree this is cleaner, but it would grow the series a bit.
If you feel strongly, I can include these changes in v4. Otherwise I
feel we can handle it separately later to avoid growing this series.
>
>>
>> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
>> index 2eab2110ac6a..5e947abb7425 100644
>> --- a/tools/testing/selftests/mm/hugepage_settings.c
>> +++ b/tools/testing/selftests/mm/hugepage_settings.c
>> @@ -8,8 +8,9 @@
>> #include <stdlib.h>
>> #include <string.h>
>> #include <unistd.h>
>> +#include <mm/file_utils.h>
>>
>> -#include "vm_util.h"
>
> I think it would be fine to include file_utils.h in vm_utils.h and avoid
> further churn.
Okay, I'll change this.
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 1/4] tools/lib/mm: add shared file helpers
2026-05-25 6:29 ` Sarthak Sharma
@ 2026-05-26 9:01 ` Mike Rapoport
2026-05-26 12:08 ` Sarthak Sharma
0 siblings, 1 reply; 14+ messages in thread
From: Mike Rapoport @ 2026-05-26 9:01 UTC (permalink / raw)
To: Sarthak Sharma
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On Mon, May 25, 2026 at 11:59:32AM +0530, Sarthak Sharma wrote:
> Hi Mike!
>
> On 5/24/26 10:36 PM, Mike Rapoport wrote:
> > On Thu, 21 May 2026 16:47:58 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
> >
> > Hi Sarthak,
> >
> >>
> >> diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
> >> new file mode 100644
> >> index 000000000000..0f9322f2cf41
> >> --- /dev/null
> >> +++ b/tools/lib/mm/file_utils.c
> >> @@ -0,0 +1,83 @@
> >> [ ... skip 48 lines ... ]
> >> + saved_errno = errno;
> >> + close(fd);
> >> + errno = saved_errno;
> >> + if (numwritten < 0) {
> >> + fprintf(stderr, "%s write(%.*s) failed: %s\n",
> >> + path, (int)(buflen - 1), buf, strerror(errno));
> >
> > This would break TAP formatting for selftests.
>
> Yes, thanks for pointing it out.
>
> >
> >> + exit(EXIT_FAILURE);
> >
> > and while EXIT_FAILURE == KSFT_FAIL I'm not sure it's robust enough.
>
> I used EXIT_FAILURE here because the helper is moving out of selftests
> and should not include kselftest.h anymore. The helper already
> terminated the process on these paths, so I tried to preserve that
> behavior while removing the ksft dependency.
In mm selftests a failure to update a /proc or /sysfs file meant there is
no point to continue the test. But if we make it a generic helper for
potentially broader use than mm selftests, exit() on failure is too harsh.
> We can change this to return errors instead of calling exit() and update
> the selftest callers to report failures through the ksft_* helpers. I
> agree this is cleaner, but it would grow the series a bit.
>
> If you feel strongly, I can include these changes in v4. Otherwise I
> feel we can handle it separately later to avoid growing this series.
There are not that many callers of write_file() and write_num().
I think a patch that makes them return an error rather than exit() can go
before moving these functions to lib.
> >>
> >> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
> >> index 2eab2110ac6a..5e947abb7425 100644
> >> --- a/tools/testing/selftests/mm/hugepage_settings.c
> >> +++ b/tools/testing/selftests/mm/hugepage_settings.c
> >> @@ -8,8 +8,9 @@
> >> #include <stdlib.h>
> >> #include <string.h>
> >> #include <unistd.h>
> >> +#include <mm/file_utils.h>
> >>
> >> -#include "vm_util.h"
> >
> > I think it would be fine to include file_utils.h in vm_utils.h and avoid
> > further churn.
>
> Okay, I'll change this.
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 1/4] tools/lib/mm: add shared file helpers
2026-05-26 9:01 ` Mike Rapoport
@ 2026-05-26 12:08 ` Sarthak Sharma
2026-05-26 12:34 ` Mike Rapoport
0 siblings, 1 reply; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-26 12:08 UTC (permalink / raw)
To: Mike Rapoport
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On 5/26/26 2:31 PM, Mike Rapoport wrote:
> On Mon, May 25, 2026 at 11:59:32AM +0530, Sarthak Sharma wrote:
>> Hi Mike!
>>
>> On 5/24/26 10:36 PM, Mike Rapoport wrote:
>>> On Thu, 21 May 2026 16:47:58 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
>>>
>>> Hi Sarthak,
>>>
>>>>
>>>> diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
>>>> new file mode 100644
>>>> index 000000000000..0f9322f2cf41
>>>> --- /dev/null
>>>> +++ b/tools/lib/mm/file_utils.c
>>>> @@ -0,0 +1,83 @@
>>>> [ ... skip 48 lines ... ]
>>>> + saved_errno = errno;
>>>> + close(fd);
>>>> + errno = saved_errno;
>>>> + if (numwritten < 0) {
>>>> + fprintf(stderr, "%s write(%.*s) failed: %s\n",
>>>> + path, (int)(buflen - 1), buf, strerror(errno));
>>>
>>> This would break TAP formatting for selftests.
>>
>> Yes, thanks for pointing it out.
>>
>>>
>>>> + exit(EXIT_FAILURE);
>>>
>>> and while EXIT_FAILURE == KSFT_FAIL I'm not sure it's robust enough.
>>
>> I used EXIT_FAILURE here because the helper is moving out of selftests
>> and should not include kselftest.h anymore. The helper already
>> terminated the process on these paths, so I tried to preserve that
>> behavior while removing the ksft dependency.
>
> In mm selftests a failure to update a /proc or /sysfs file meant there is
> no point to continue the test. But if we make it a generic helper for
> potentially broader use than mm selftests, exit() on failure is too harsh.
Okay yeah, this makes sense.
>
>> We can change this to return errors instead of calling exit() and update
>> the selftest callers to report failures through the ksft_* helpers. I
>> agree this is cleaner, but it would grow the series a bit.
>>
>> If you feel strongly, I can include these changes in v4. Otherwise I
>> feel we can handle it separately later to avoid growing this series.
>
> There are not that many callers of write_file() and write_num().
> I think a patch that makes them return an error rather than exit() can go
> before moving these functions to lib.
>
So I will add a patch before the move that makes read_file(),
write_file(), read_num() and write_num() return errors instead of
exiting and update the existing selftest callers to report those
failures via ksft_* helpers.
For hugepage_settings.c, I’d prefer to keep the existing fail fast
behaviour in this series. After this series, the users are still mm
selftests and the new tools/mm/gup_bench tool and for those users a
failure to read/write THP or HugeTLB state is fatal to the operation
being attempted.
Converting the full hugepage_settings API to return errors would be a
larger follow-up, because many of its helpers are used throughout mm
selftests. I can handle that in a separate series unless you think it
should be folded into this one as well.
>>>>
>>>> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
>>>> index 2eab2110ac6a..5e947abb7425 100644
>>>> --- a/tools/testing/selftests/mm/hugepage_settings.c
>>>> +++ b/tools/testing/selftests/mm/hugepage_settings.c
>>>> @@ -8,8 +8,9 @@
>>>> #include <stdlib.h>
>>>> #include <string.h>
>>>> #include <unistd.h>
>>>> +#include <mm/file_utils.h>
>>>>
>>>> -#include "vm_util.h"
>>>
>>> I think it would be fine to include file_utils.h in vm_utils.h and avoid
>>> further churn.
>>
>> Okay, I'll change this.
>
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 1/4] tools/lib/mm: add shared file helpers
2026-05-26 12:08 ` Sarthak Sharma
@ 2026-05-26 12:34 ` Mike Rapoport
0 siblings, 0 replies; 14+ messages in thread
From: Mike Rapoport @ 2026-05-26 12:34 UTC (permalink / raw)
To: Sarthak Sharma
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On Tue, May 26, 2026 at 05:38:29PM +0530, Sarthak Sharma wrote:
> On 5/26/26 2:31 PM, Mike Rapoport wrote:
> > On Mon, May 25, 2026 at 11:59:32AM +0530, Sarthak Sharma wrote:
> >> On 5/24/26 10:36 PM, Mike Rapoport wrote:
> >>>
> >>> and while EXIT_FAILURE == KSFT_FAIL I'm not sure it's robust enough.
> >>
> >> I used EXIT_FAILURE here because the helper is moving out of selftests
> >> and should not include kselftest.h anymore. The helper already
> >> terminated the process on these paths, so I tried to preserve that
> >> behavior while removing the ksft dependency.
> >
> > In mm selftests a failure to update a /proc or /sysfs file meant there is
> > no point to continue the test. But if we make it a generic helper for
> > potentially broader use than mm selftests, exit() on failure is too harsh.
>
> Okay yeah, this makes sense.
>
> >
> >> We can change this to return errors instead of calling exit() and update
> >> the selftest callers to report failures through the ksft_* helpers. I
> >> agree this is cleaner, but it would grow the series a bit.
> >>
> >> If you feel strongly, I can include these changes in v4. Otherwise I
> >> feel we can handle it separately later to avoid growing this series.
> >
> > There are not that many callers of write_file() and write_num().
> > I think a patch that makes them return an error rather than exit() can go
> > before moving these functions to lib.
> >
>
> So I will add a patch before the move that makes read_file(),
> write_file(), read_num() and write_num() return errors instead of
> exiting and update the existing selftest callers to report those
> failures via ksft_* helpers.
>
> For hugepage_settings.c, I’d prefer to keep the existing fail fast
> behaviour in this series. After this series, the users are still mm
> selftests and the new tools/mm/gup_bench tool and for those users a
> failure to read/write THP or HugeTLB state is fatal to the operation
> being attempted.
>
> Converting the full hugepage_settings API to return errors would be a
> larger follow-up, because many of its helpers are used throughout mm
> selftests. I can handle that in a separate series unless you think it
> should be folded into this one as well.
Keeping fail fast in hugepage_settings for now makes sense, they are not as
generic as write_file().
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests
2026-05-21 11:17 [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
2026-05-21 11:17 ` [PATCH v3 1/4] tools/lib/mm: add shared file helpers Sarthak Sharma
@ 2026-05-21 11:17 ` Sarthak Sharma
2026-05-24 17:06 ` Mike Rapoport
2026-05-21 11:18 ` [PATCH v3 3/4] tools/mm: add a standalone GUP microbenchmark Sarthak Sharma
` (2 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-21 11:17 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
Move hugepage_settings.[ch] from tools/testing/selftests/mm/ to
tools/lib/mm/ so the THP and HugeTLB helpers can be shared more easily
between selftests and other tools.
Update the mm selftest users to include the header from tools/lib/mm/
and adjust the selftests/mm build to compile the moved implementation
from its new location.
Remove the remaining kselftest dependency from hugepage_settings.c by
replacing ksft_print_msg() calls with plain fprintf() calls. Drop the
non-fatal hugetlb informational prints from the shared helper, so it
does not emit unwanted output during selftest runs.
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
.../selftests => lib}/mm/hugepage_settings.c | 15 +++++++++------
.../selftests => lib}/mm/hugepage_settings.h | 0
tools/testing/selftests/mm/Makefile | 6 ++++--
tools/testing/selftests/mm/compaction_test.c | 2 +-
tools/testing/selftests/mm/cow.c | 2 +-
.../testing/selftests/mm/folio_split_race_test.c | 3 ++-
tools/testing/selftests/mm/guard-regions.c | 3 ++-
tools/testing/selftests/mm/gup_longterm.c | 2 +-
tools/testing/selftests/mm/gup_test.c | 3 ++-
tools/testing/selftests/mm/hmm-tests.c | 6 +++---
tools/testing/selftests/mm/hugetlb-madvise.c | 3 ++-
tools/testing/selftests/mm/hugetlb-mmap.c | 3 ++-
tools/testing/selftests/mm/hugetlb-mremap.c | 3 ++-
tools/testing/selftests/mm/hugetlb-shm.c | 2 +-
tools/testing/selftests/mm/hugetlb-soft-offline.c | 2 +-
tools/testing/selftests/mm/hugetlb-vmemmap.c | 3 ++-
tools/testing/selftests/mm/hugetlb_dio.c | 3 ++-
.../selftests/mm/hugetlb_fault_after_madv.c | 2 +-
tools/testing/selftests/mm/hugetlb_madv_vs_map.c | 2 +-
tools/testing/selftests/mm/khugepaged.c | 2 +-
tools/testing/selftests/mm/ksm_tests.c | 2 +-
tools/testing/selftests/mm/migration.c | 6 +++---
tools/testing/selftests/mm/pagemap_ioctl.c | 2 +-
tools/testing/selftests/mm/prctl_thp_disable.c | 2 +-
tools/testing/selftests/mm/protection_keys.c | 2 +-
tools/testing/selftests/mm/soft-dirty.c | 2 +-
tools/testing/selftests/mm/split_huge_page_test.c | 2 +-
tools/testing/selftests/mm/thuge-gen.c | 3 ++-
tools/testing/selftests/mm/transhuge-stress.c | 3 ++-
tools/testing/selftests/mm/uffd-common.h | 2 +-
tools/testing/selftests/mm/uffd-wp-mremap.c | 3 ++-
tools/testing/selftests/mm/va_high_addr_switch.c | 2 +-
32 files changed, 57 insertions(+), 41 deletions(-)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.c (98%)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.h (100%)
diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/hugepage_settings.c
similarity index 98%
rename from tools/testing/selftests/mm/hugepage_settings.c
rename to tools/lib/mm/hugepage_settings.c
index 5e947abb7425..b08b27776fc5 100644
--- a/tools/testing/selftests/mm/hugepage_settings.c
+++ b/tools/lib/mm/hugepage_settings.c
@@ -8,13 +8,17 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <mm/file_utils.h>
-#include "kselftest.h"
+#include "file_utils.h"
#include "hugepage_settings.h"
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
#define MAX_SETTINGS_DEPTH 4
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
static int settings_index;
static struct thp_settings saved_settings;
@@ -383,8 +387,6 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
continue;
sizes[count++] = kb * 1024;
- ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
- kb);
}
closedir(dir);
return count;
@@ -503,7 +505,6 @@ unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
return 0;
if (nr_enabled > max) {
- ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max);
nr_enabled = max;
}
@@ -575,8 +576,10 @@ static void hugepage_restore_settings_atexit(void)
static void hugepage_restore_settings_sighandler(int sig)
{
+ (void)sig;
+
/* exit() will invoke the hugepage_restore_settings_atexit handler. */
- exit(KSFT_FAIL);
+ exit(EXIT_FAILURE);
}
void hugepage_save_settings(bool thp, bool hugetlb)
diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/lib/mm/hugepage_settings.h
similarity index 100%
rename from tools/testing/selftests/mm/hugepage_settings.h
rename to tools/lib/mm/hugepage_settings.h
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index b5fb4b6ab31b..8a307b777630 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -187,8 +187,10 @@ TEST_FILES += write_hugetlb_memory.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
-$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_PROGS): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.c \
+ $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_FILES): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.c \
+ $(top_srcdir)/tools/lib/mm/file_utils.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
index de0633f9a7e5..7c58506c0aa7 100644
--- a/tools/testing/selftests/mm/compaction_test.c
+++ b/tools/testing/selftests/mm/compaction_test.c
@@ -15,9 +15,9 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
-#include "hugepage_settings.h"
#define MAP_SIZE_MB 100
#define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024)
diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index 0c627ea89ff7..7c29d3d2d887 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -20,6 +20,7 @@
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <linux/memfd.h>
+#include <mm/hugepage_settings.h>
#include "local_config.h"
#ifdef LOCAL_CONFIG_HAVE_LIBURING
@@ -29,7 +30,6 @@
#include "../../../../mm/gup_test.h"
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
static size_t pagesize;
static int pagemap_fd;
diff --git a/tools/testing/selftests/mm/folio_split_race_test.c b/tools/testing/selftests/mm/folio_split_race_test.c
index 6329e37fff4c..ca9b95c39c16 100644
--- a/tools/testing/selftests/mm/folio_split_race_test.c
+++ b/tools/testing/selftests/mm/folio_split_race_test.c
@@ -23,9 +23,10 @@
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
uint64_t page_size;
uint64_t pmd_pagesize;
diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
index b21df3040b1c..ccca432c8802 100644
--- a/tools/testing/selftests/mm/guard-regions.c
+++ b/tools/testing/selftests/mm/guard-regions.c
@@ -20,8 +20,9 @@
#include <sys/syscall.h>
#include <sys/uio.h>
#include <unistd.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
-#include "hugepage_settings.h"
#include "../pidfd/pidfd.h"
diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c
index eb8963e9d98f..0cfc67fc5c8f 100644
--- a/tools/testing/selftests/mm/gup_longterm.c
+++ b/tools/testing/selftests/mm/gup_longterm.c
@@ -20,6 +20,7 @@
#include <sys/vfs.h>
#include <linux/magic.h>
#include <linux/memfd.h>
+#include <mm/hugepage_settings.h>
#include "local_config.h"
#ifdef LOCAL_CONFIG_HAVE_LIBURING
@@ -29,7 +30,6 @@
#include "../../../../mm/gup_test.h"
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
static size_t pagesize;
static int nr_hugetlbsizes;
diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c
index 3f841a96f870..803ab829a841 100644
--- a/tools/testing/selftests/mm/gup_test.c
+++ b/tools/testing/selftests/mm/gup_test.c
@@ -12,9 +12,10 @@
#include <pthread.h>
#include <assert.h>
#include <mm/gup_test.h>
+#include <mm/hugepage_settings.h>
+
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define MB (1UL << 20)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index e1c8a679a4cf..b7f85a144f03 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -10,9 +10,6 @@
* bugs.
*/
-#include "kselftest_harness.h"
-#include "hugepage_settings.h"
-
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -27,6 +24,9 @@
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
+#include <mm/hugepage_settings.h>
+
+#include "kselftest_harness.h"
/*
* This is a private UAPI to the kernel test module so it isn't exported
diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
index 555b4b3d1430..8e18b5027904 100644
--- a/tools/testing/selftests/mm/hugetlb-madvise.c
+++ b/tools/testing/selftests/mm/hugetlb-madvise.c
@@ -12,9 +12,10 @@
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define MIN_FREE_PAGES 20
#define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */
diff --git a/tools/testing/selftests/mm/hugetlb-mmap.c b/tools/testing/selftests/mm/hugetlb-mmap.c
index 0f2aad1b7dbd..12184c1889ad 100644
--- a/tools/testing/selftests/mm/hugetlb-mmap.c
+++ b/tools/testing/selftests/mm/hugetlb-mmap.c
@@ -16,9 +16,10 @@
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/memfd.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define LENGTH (256UL*1024*1024)
#define PROTECTION (PROT_READ | PROT_WRITE)
diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c
index d239905790dd..0b1a1fc9e766 100644
--- a/tools/testing/selftests/mm/hugetlb-mremap.c
+++ b/tools/testing/selftests/mm/hugetlb-mremap.c
@@ -24,9 +24,10 @@
#include <sys/ioctl.h>
#include <string.h>
#include <stdbool.h>
+#include <mm/hugepage_settings.h>
+
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define DEFAULT_LENGTH_MB 10UL
#define MB_TO_BYTES(x) (x * 1024 * 1024)
diff --git a/tools/testing/selftests/mm/hugetlb-shm.c b/tools/testing/selftests/mm/hugetlb-shm.c
index 3ff7f062b7eb..25988f97057a 100644
--- a/tools/testing/selftests/mm/hugetlb-shm.c
+++ b/tools/testing/selftests/mm/hugetlb-shm.c
@@ -27,9 +27,9 @@
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define LENGTH (256UL*1024*1024)
diff --git a/tools/testing/selftests/mm/hugetlb-soft-offline.c b/tools/testing/selftests/mm/hugetlb-soft-offline.c
index bc202e4ed2bd..20864e7d4e0c 100644
--- a/tools/testing/selftests/mm/hugetlb-soft-offline.c
+++ b/tools/testing/selftests/mm/hugetlb-soft-offline.c
@@ -21,9 +21,9 @@
#include <sys/mman.h>
#include <sys/statfs.h>
#include <sys/types.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
-#include "hugepage_settings.h"
#ifndef MADV_SOFT_OFFLINE
#define MADV_SOFT_OFFLINE 101
diff --git a/tools/testing/selftests/mm/hugetlb-vmemmap.c b/tools/testing/selftests/mm/hugetlb-vmemmap.c
index 507df78a158d..ffecea89ffa1 100644
--- a/tools/testing/selftests/mm/hugetlb-vmemmap.c
+++ b/tools/testing/selftests/mm/hugetlb-vmemmap.c
@@ -10,8 +10,9 @@
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
-#include "hugepage_settings.h"
#define PAGE_COMPOUND_HEAD (1UL << 15)
#define PAGE_COMPOUND_TAIL (1UL << 16)
diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c
index fb4600570e13..896c6e3c09da 100644
--- a/tools/testing/selftests/mm/hugetlb_dio.c
+++ b/tools/testing/selftests/mm/hugetlb_dio.c
@@ -18,9 +18,10 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#ifndef STATX_DIOALIGN
#define STATX_DIOALIGN 0x00002000U
diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
index 2dc158054f66..70e685f0b510 100644
--- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
+++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
@@ -7,10 +7,10 @@
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define INLOOP_ITER 100
diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
index f94549efcc6f..c8f6414e8a77 100644
--- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
+++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
@@ -23,9 +23,9 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define INLOOP_ITER 100
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index ae5945e2bfac..9bf1f8744431 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -19,11 +19,11 @@
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <mm/file_utils.h>
+#include <mm/hugepage_settings.h>
#include "linux/magic.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define BASE_ADDR ((void *)(1UL << 30))
static unsigned long hpage_pmd_size;
diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c
index a050f4840cfa..4fda799a148d 100644
--- a/tools/testing/selftests/mm/ksm_tests.c
+++ b/tools/testing/selftests/mm/ksm_tests.c
@@ -11,11 +11,11 @@
#include <fcntl.h>
#include <stdint.h>
#include <err.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include <include/vdso/time64.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
#define KSM_FP(s) (KSM_SYSFS_PATH s)
diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
index 29f7492453d4..c3e60ddcefa7 100644
--- a/tools/testing/selftests/mm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
@@ -4,9 +4,6 @@
* paths in the kernel.
*/
-#include "kselftest_harness.h"
-#include "hugepage_settings.h"
-
#include <strings.h>
#include <pthread.h>
#include <numa.h>
@@ -16,6 +13,9 @@
#include <sys/types.h>
#include <signal.h>
#include <time.h>
+#include <mm/hugepage_settings.h>
+
+#include "kselftest_harness.h"
#include "vm_util.h"
#define TWOMEG (2<<20)
diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
index 762306177ad8..924308ead037 100644
--- a/tools/testing/selftests/mm/pagemap_ioctl.c
+++ b/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -20,10 +20,10 @@
#include <assert.h>
#include <sys/ipc.h>
#include <sys/shm.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define PAGEMAP_BITS_ALL (PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | \
PAGE_IS_FILE | PAGE_IS_PRESENT | \
diff --git a/tools/testing/selftests/mm/prctl_thp_disable.c b/tools/testing/selftests/mm/prctl_thp_disable.c
index d8d9d1de57b8..db1807adf72e 100644
--- a/tools/testing/selftests/mm/prctl_thp_disable.c
+++ b/tools/testing/selftests/mm/prctl_thp_disable.c
@@ -12,9 +12,9 @@
#include <linux/mman.h>
#include <sys/prctl.h>
#include <sys/wait.h>
+#include <mm/hugepage_settings.h>
#include "kselftest_harness.h"
-#include "hugepage_settings.h"
#include "vm_util.h"
#ifndef PR_THP_DISABLE_EXCEPT_ADVISED
diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c
index 9a6d954ee371..5ba2033e8a09 100644
--- a/tools/testing/selftests/mm/protection_keys.c
+++ b/tools/testing/selftests/mm/protection_keys.c
@@ -45,8 +45,8 @@
#include <unistd.h>
#include <sys/ptrace.h>
#include <setjmp.h>
+#include <mm/hugepage_settings.h>
-#include "hugepage_settings.h"
#include "pkey-helpers.h"
int iteration_nr = 1;
diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c
index fb1864a68e1c..852cc0a9743f 100644
--- a/tools/testing/selftests/mm/soft-dirty.c
+++ b/tools/testing/selftests/mm/soft-dirty.c
@@ -6,10 +6,10 @@
#include <stdint.h>
#include <malloc.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define PAGEMAP_FILE_PATH "/proc/self/pagemap"
#define TEST_ITERATIONS 10000
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index 4a2ccc055c8b..b4ff8ed0b446 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -20,10 +20,10 @@
#include <stdbool.h>
#include <time.h>
#include <mm/file_utils.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
uint64_t pagesize;
unsigned int pageshift;
diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c
index 22b9c2f1c35d..5556a1b89393 100644
--- a/tools/testing/selftests/mm/thuge-gen.c
+++ b/tools/testing/selftests/mm/thuge-gen.c
@@ -12,9 +12,10 @@
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#if !defined(MAP_HUGETLB)
#define MAP_HUGETLB 0x40000
diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c
index 8eb0c5630e7e..ed78b2142ac1 100644
--- a/tools/testing/selftests/mm/transhuge-stress.c
+++ b/tools/testing/selftests/mm/transhuge-stress.c
@@ -15,9 +15,10 @@
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
+
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
int backing_fd = -1;
int mmap_flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE;
diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h
index 92a21b97f745..b8e25a96381d 100644
--- a/tools/testing/selftests/mm/uffd-common.h
+++ b/tools/testing/selftests/mm/uffd-common.h
@@ -34,10 +34,10 @@
#include <stdint.h>
#include <sys/random.h>
#include <stdatomic.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define UFFD_FLAGS (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)
diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c
index 90ac410c6c6f..dcd7ac35deba 100644
--- a/tools/testing/selftests/mm/uffd-wp-mremap.c
+++ b/tools/testing/selftests/mm/uffd-wp-mremap.c
@@ -7,8 +7,9 @@
#include <assert.h>
#include <linux/mman.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
+
#include "kselftest.h"
-#include "hugepage_settings.h"
#include "uffd-common.h"
static int pagemap_fd;
diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c
index e24d7ba00b44..07fcb03316cc 100644
--- a/tools/testing/selftests/mm/va_high_addr_switch.c
+++ b/tools/testing/selftests/mm/va_high_addr_switch.c
@@ -8,10 +8,10 @@
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
/*
* The hint addr value is used to allocate addresses
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests
2026-05-21 11:17 ` [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests Sarthak Sharma
@ 2026-05-24 17:06 ` Mike Rapoport
2026-05-25 6:13 ` Sarthak Sharma
0 siblings, 1 reply; 14+ messages in thread
From: Mike Rapoport @ 2026-05-24 17:06 UTC (permalink / raw)
To: Sarthak Sharma
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On Thu, 21 May 2026 16:47:59 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
Hi Sarthak,
>
> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/hugepage_settings.c
> similarity index 98%
> rename from tools/testing/selftests/mm/hugepage_settings.c
> rename to tools/lib/mm/hugepage_settings.c
> index 5e947abb7425..b08b27776fc5 100644
> --- a/tools/testing/selftests/mm/hugepage_settings.c
> +++ b/tools/lib/mm/hugepage_settings.c
> @@ -383,8 +387,6 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
> if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
> continue;
> sizes[count++] = kb * 1024;
> - ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
> - kb);
I believe this message is useful for debugging.
> @@ -503,7 +505,6 @@ unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
> return 0;
>
> if (nr_enabled > max) {
> - ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max);
And this one as well.
>
> diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
> index de0633f9a7e5..7c58506c0aa7 100644
> --- a/tools/testing/selftests/mm/compaction_test.c
> +++ b/tools/testing/selftests/mm/compaction_test.c
> @@ -15,9 +15,9 @@
> #include <errno.h>
> #include <unistd.h>
> #include <string.h>
> +#include <mm/hugepage_settings.h>
As with file_utils.h this can also go to vm_utils.h IMHO.
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests
2026-05-24 17:06 ` Mike Rapoport
@ 2026-05-25 6:13 ` Sarthak Sharma
2026-05-26 9:01 ` Mike Rapoport
0 siblings, 1 reply; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-25 6:13 UTC (permalink / raw)
To: Mike Rapoport
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
Hi Mike!
On 5/24/26 10:36 PM, Mike Rapoport wrote:
> On Thu, 21 May 2026 16:47:59 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
>
> Hi Sarthak,
>
>>
>> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/hugepage_settings.c
>> similarity index 98%
>> rename from tools/testing/selftests/mm/hugepage_settings.c
>> rename to tools/lib/mm/hugepage_settings.c
>> index 5e947abb7425..b08b27776fc5 100644
>> --- a/tools/testing/selftests/mm/hugepage_settings.c
>> +++ b/tools/lib/mm/hugepage_settings.c
>> @@ -383,8 +387,6 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
>> if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
>> continue;
>> sizes[count++] = kb * 1024;
>> - ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
>> - kb);
>
> I believe this message is useful for debugging.
I removed this because hugepage_settings.c is now moving out of
selftests, and converting the ksft_print_msg() calls to plain printf()
would break TAP output for selftests.
I can add these diagnostic lines back, but since this is now a shared
helper, in order to preserve TAP compatibility, I'll have to do a
fprintf(stderr, "# ..."). I feel this would look a bit odd from a
non-selftest invocation though.
Another option is to add a logging hook so selftests can wire this to
ksft_print_msg() while non-selftest users can use normal stderr, but
that feels too much for 2 print statements and would extend the scope of
this series.
Please let me know if you have a preference here.
>
>> @@ -503,7 +505,6 @@ unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
>> return 0;
>>
>> if (nr_enabled > max) {
>> - ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max);
>
> And this one as well.
>
>>
>> diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
>> index de0633f9a7e5..7c58506c0aa7 100644
>> --- a/tools/testing/selftests/mm/compaction_test.c
>> +++ b/tools/testing/selftests/mm/compaction_test.c
>> @@ -15,9 +15,9 @@
>> #include <errno.h>
>> #include <unistd.h>
>> #include <string.h>
>> +#include <mm/hugepage_settings.h>
>
> As with file_utils.h this can also go to vm_utils.h IMHO.
Yeah I can do this for files that already include vm_util.h.
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests
2026-05-25 6:13 ` Sarthak Sharma
@ 2026-05-26 9:01 ` Mike Rapoport
0 siblings, 0 replies; 14+ messages in thread
From: Mike Rapoport @ 2026-05-26 9:01 UTC (permalink / raw)
To: Sarthak Sharma
Cc: Andrew Morton, David Hildenbrand, Jonathan Corbet,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On Mon, May 25, 2026 at 11:43:37AM +0530, Sarthak Sharma wrote:
> Hi Mike!
>
> On 5/24/26 10:36 PM, Mike Rapoport wrote:
> > On Thu, 21 May 2026 16:47:59 +0530, Sarthak Sharma <sarthak.sharma@arm.com> wrote:
> >
> > Hi Sarthak,
> >
> >>
> >> diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/hugepage_settings.c
> >> similarity index 98%
> >> rename from tools/testing/selftests/mm/hugepage_settings.c
> >> rename to tools/lib/mm/hugepage_settings.c
> >> index 5e947abb7425..b08b27776fc5 100644
> >> --- a/tools/testing/selftests/mm/hugepage_settings.c
> >> +++ b/tools/lib/mm/hugepage_settings.c
> >> @@ -383,8 +387,6 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
> >> if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
> >> continue;
> >> sizes[count++] = kb * 1024;
> >> - ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
> >> - kb);
> >
> > I believe this message is useful for debugging.
>
> I removed this because hugepage_settings.c is now moving out of
> selftests, and converting the ksft_print_msg() calls to plain printf()
> would break TAP output for selftests.
>
> I can add these diagnostic lines back, but since this is now a shared
> helper, in order to preserve TAP compatibility, I'll have to do a
> fprintf(stderr, "# ..."). I feel this would look a bit odd from a
> non-selftest invocation though.
I think "# ... " is not that bad to begin with :)
> Another option is to add a logging hook so selftests can wire this to
> ksft_print_msg() while non-selftest users can use normal stderr, but
> that feels too much for 2 print statements and would extend the scope of
> this series.
>
> Please let me know if you have a preference here.
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 3/4] tools/mm: add a standalone GUP microbenchmark
2026-05-21 11:17 [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
2026-05-21 11:17 ` [PATCH v3 1/4] tools/lib/mm: add shared file helpers Sarthak Sharma
2026-05-21 11:17 ` [PATCH v3 2/4] tools/lib/mm: move hugepage_settings out of selftests Sarthak Sharma
@ 2026-05-21 11:18 ` Sarthak Sharma
2026-05-21 11:18 ` [PATCH v3 4/4] selftests/mm: rewrite gup_test as a standalone harness-based selftest Sarthak Sharma
2026-05-21 21:12 ` [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Andrew Morton
4 siblings, 0 replies; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-21 11:18 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
Add a command-line tool for benchmarking get_user_pages fast-path
(GUP_FAST), pin_user_pages fast-path (PIN_FAST), and pin_user_pages
longterm (PIN_LONGTERM) via the CONFIG_GUP_TEST debugfs interface.
When invoked without arguments, gup_bench runs the same matrix of
configurations as run_gup_matrix() in run_vmtests.sh: all three GUP
commands across read/write, private/shared mappings, and a range of
page counts, with THP on/off for regular mappings and hugetlb for huge
page mappings.
This tool is a mix of reused and new logic. The mapping/setup path comes
from selftests/mm/gup_test.c, while the default benchmark matrix matches
run_gup_matrix() in run_vmtests.sh. The standalone CLI and tools/mm
integration are added here and the HugeTLB setup code is shared via
tools/lib/mm.
Add gup_bench to BUILD_TARGETS and INSTALL_TARGETS in tools/mm/Makefile,
link it against the shared tools/lib/mm hugepage helpers, and ignore the
resulting binary in tools/mm/.gitignore. While here, also add the
missing thp_swap_allocator_test entry to .gitignore.
Add tools/mm/gup_bench.c to the GUP entry in MAINTAINERS.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
MAINTAINERS | 1 +
tools/mm/.gitignore | 2 +
tools/mm/Makefile | 10 +-
tools/mm/gup_bench.c | 390 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 400 insertions(+), 3 deletions(-)
create mode 100644 tools/mm/gup_bench.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 7887a3263373..0dbb90247a75 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16839,6 +16839,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
F: mm/gup.c
F: mm/gup_test.c
F: mm/gup_test.h
+F: tools/mm/gup_bench.c
F: tools/testing/selftests/mm/gup_longterm.c
F: tools/testing/selftests/mm/gup_test.c
diff --git a/tools/mm/.gitignore b/tools/mm/.gitignore
index 922879f93fc8..154d740be02e 100644
--- a/tools/mm/.gitignore
+++ b/tools/mm/.gitignore
@@ -2,3 +2,5 @@
slabinfo
page-types
page_owner_sort
+thp_swap_allocator_test
+gup_bench
diff --git a/tools/mm/Makefile b/tools/mm/Makefile
index f5725b5c23aa..d82cc8c43ee0 100644
--- a/tools/mm/Makefile
+++ b/tools/mm/Makefile
@@ -3,13 +3,14 @@
#
include ../scripts/Makefile.include
-BUILD_TARGETS=page-types slabinfo page_owner_sort thp_swap_allocator_test
+BUILD_TARGETS=page-types slabinfo page_owner_sort thp_swap_allocator_test gup_bench
INSTALL_TARGETS = $(BUILD_TARGETS) thpmaps
LIB_DIR = ../lib/api
LIBS = $(LIB_DIR)/libapi.a
+GUP_BENCH_OBJS = gup_bench.c ../lib/mm/hugepage_settings.c ../lib/mm/file_utils.c
-CFLAGS += -Wall -Wextra -I../lib/ -pthread
+CFLAGS += -Wall -Wextra -I../lib/ -I../.. -pthread
LDFLAGS += $(LIBS) -pthread
all: $(BUILD_TARGETS)
@@ -22,8 +23,11 @@ $(LIBS):
%: %.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+gup_bench: $(GUP_BENCH_OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(GUP_BENCH_OBJS) $(LDFLAGS)
+
clean:
- $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test
+ $(RM) page-types slabinfo page_owner_sort thp_swap_allocator_test gup_bench
make -C $(LIB_DIR) clean
sbindir ?= /usr/sbin
diff --git a/tools/mm/gup_bench.c b/tools/mm/gup_bench.c
new file mode 100644
index 000000000000..50faff4527e5
--- /dev/null
+++ b/tools/mm/gup_bench.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microbenchmark for get_user_pages (GUP) kernel interfaces.
+ *
+ * Exercises GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK, and
+ * PIN_LONGTERM_BENCHMARK via the CONFIG_GUP_TEST debugfs interface.
+ *
+ * Example use:
+ * # Run the full matrix (all commands, access modes, page counts):
+ * ./gup_bench
+ *
+ * # Single run: pin_user_pages_fast, 512 pages, write access, hugetlb:
+ * ./gup_bench -a -n 512 -w -H
+ *
+ * Requires CONFIG_GUP_TEST=y and debugfs mounted at /sys/kernel/debug.
+ * Must be run as root.
+ */
+
+#define __SANE_USERSPACE_TYPES__ // Use ll64
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <mm/gup_test.h>
+#include <mm/hugepage_settings.h>
+
+#define MB (1UL << 20)
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+/* Just the flags we need, copied from the kernel internals. */
+#define FOLL_WRITE 0x01 /* check pte is writable */
+
+#define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
+
+static unsigned int psize(void)
+{
+ static unsigned int __page_size;
+
+ if (!__page_size)
+ __page_size = sysconf(_SC_PAGESIZE);
+ return __page_size;
+}
+
+static unsigned long cmd;
+static const char *bench_label;
+static int gup_fd, repeats = 1;
+static unsigned long size = 128 * MB;
+static atomic_int bench_error;
+/* Serialize prints */
+static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static const unsigned long bench_cmds[] = {
+ GUP_FAST_BENCHMARK,
+ PIN_FAST_BENCHMARK,
+ PIN_LONGTERM_BENCHMARK,
+};
+static const int bench_thp_modes[] = { 1, 0 }; /* on, off */
+static const int bench_nr_pages_list[] = { 1, 512, 123, -1 };
+
+static const char *cmd_to_str(unsigned long cmd)
+{
+ switch (cmd) {
+ case GUP_FAST_BENCHMARK:
+ return "GUP_FAST_BENCHMARK";
+ case PIN_FAST_BENCHMARK:
+ return "PIN_FAST_BENCHMARK";
+ case PIN_LONGTERM_BENCHMARK:
+ return "PIN_LONGTERM_BENCHMARK";
+ }
+ return "Unknown command";
+}
+
+struct bench_run {
+ unsigned long cmd;
+ int thp; /* -1: default, 0: off, 1: on */
+ bool hugetlb;
+ bool write;
+ bool shared;
+ int nr_pages; /* -1 means all pages (size / psize()) */
+ unsigned long size;
+ char *file;
+ int nthreads;
+ unsigned int gup_flags;
+};
+
+void *gup_thread(void *data)
+{
+ struct gup_test gup = *(struct gup_test *)data;
+ int i, status;
+
+ for (i = 0; i < repeats; i++) {
+ gup.size = size;
+ status = ioctl(gup_fd, cmd, &gup);
+ if (status) {
+ bench_error = 1;
+ break;
+ }
+
+ pthread_mutex_lock(&print_mutex);
+ printf("%s time: get:%lld put:%lld us",
+ bench_label, gup.get_delta_usec,
+ gup.put_delta_usec);
+ if (gup.size != size)
+ printf(", truncated (size: %lld)", gup.size);
+ printf("\n");
+ pthread_mutex_unlock(&print_mutex);
+ }
+
+ return NULL;
+}
+
+static int run_bench(struct bench_run *run)
+{
+ struct gup_test gup = { 0 };
+ int zero_fd, i, ret, started_threads = 0;
+ int flags = MAP_PRIVATE;
+ pthread_t *tid;
+ char label[128];
+ char *p;
+
+ /* Set globals consumed by gup_thread */
+ cmd = run->cmd;
+ size = run->size;
+ bench_error = 0;
+
+ if (run->hugetlb) {
+ unsigned long hp_size = default_huge_page_size();
+
+ if (!hp_size) {
+ fprintf(stderr, "Could not determine huge page size\n");
+ return 1;
+ }
+ size = (size + hp_size - 1) & ~(hp_size - 1);
+ if (!hugetlb_setup_default(size / hp_size)) {
+ fprintf(stderr, "Not enough huge pages\n");
+ return 1;
+ }
+ flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
+ }
+
+ if (run->shared) {
+ flags &= ~MAP_PRIVATE;
+ flags |= MAP_SHARED;
+ }
+
+ gup.nr_pages_per_call = run->nr_pages < 0 ? size / psize() :
+ (unsigned long)run->nr_pages;
+
+ gup.gup_flags = run->gup_flags;
+ if (run->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ snprintf(label, sizeof(label), "%s (nr_pages=%-4u %s %s %s %s)",
+ cmd_to_str(run->cmd),
+ gup.nr_pages_per_call,
+ run->write ? "write" : "read",
+ run->shared ? "shared" : "private",
+ run->hugetlb ? "hugetlb=on" : "hugetlb=off",
+ run->hugetlb ? "thp=off" :
+ (run->thp == 1 ? "thp=on" :
+ (run->thp == 0 ? "thp=off" : "thp=default")));
+ bench_label = label;
+
+ zero_fd = open(run->file, O_RDWR);
+ if (zero_fd < 0) {
+ fprintf(stderr, "Unable to open %s: %s\n", run->file, strerror(errno));
+ return 1;
+ }
+
+ p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, zero_fd, 0);
+ close(zero_fd);
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "mmap: %s\n", strerror(errno));
+ return 1;
+ }
+ gup.addr = (unsigned long)p;
+
+ if (run->thp == 1)
+ madvise(p, size, MADV_HUGEPAGE);
+ else if (run->thp == 0)
+ madvise(p, size, MADV_NOHUGEPAGE);
+
+ /* Fault them in here, from user space. */
+ for (; (unsigned long)p < gup.addr + size; p += psize())
+ p[0] = 0;
+
+ tid = malloc(sizeof(pthread_t) * run->nthreads);
+ if (!tid) {
+ fprintf(stderr, "Failed to allocate %d threads: %s\n",
+ run->nthreads, strerror(errno));
+ munmap((void *)gup.addr, size);
+ return 1;
+ }
+
+ for (i = 0; i < run->nthreads; i++) {
+ ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
+ if (ret) {
+ fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));
+ bench_error = 1;
+ break;
+ }
+ started_threads++;
+ }
+ for (i = 0; i < started_threads; i++) {
+ ret = pthread_join(tid[i], NULL);
+ if (ret) {
+ fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
+ bench_error = 1;
+ }
+ }
+
+ free(tid);
+ munmap((void *)gup.addr, size);
+
+ return bench_error ? 1 : 0;
+}
+
+static int run_matrix(void)
+{
+ unsigned int c, t, w, s, n;
+ int ret = 0;
+
+ for (c = 0; c < ARRAY_SIZE(bench_cmds); c++) {
+ for (w = 0; w <= 1; w++) {
+ for (s = 0; s <= 1; s++) {
+ for (t = 0; t < ARRAY_SIZE(bench_thp_modes); t++) {
+ for (n = 0; n < ARRAY_SIZE(bench_nr_pages_list); n++) {
+ struct bench_run run = {
+ .cmd = bench_cmds[c],
+ .thp = bench_thp_modes[t],
+ .hugetlb = false,
+ .write = w,
+ .shared = s,
+ .nr_pages = bench_nr_pages_list[n],
+ .size = 128 * MB,
+ .file = "/dev/zero",
+ .nthreads = 1,
+ };
+ ret |= run_bench(&run);
+ }
+ }
+ /* hugetlb: 256M to match run_gup_matrix() in run_vmtests.sh */
+ for (n = 0; n < ARRAY_SIZE(bench_nr_pages_list); n++) {
+ struct bench_run run = {
+ .cmd = bench_cmds[c],
+ .thp = -1,
+ .hugetlb = true,
+ .write = w,
+ .shared = s,
+ .nr_pages = bench_nr_pages_list[n],
+ .size = 256 * MB,
+ .file = "/dev/zero",
+ .nthreads = 1,
+ };
+ ret |= run_bench(&run);
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ struct bench_run run = {
+ .cmd = GUP_FAST_BENCHMARK,
+ .thp = -1,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+ .nr_pages = 1,
+ .size = 128 * MB,
+ .file = "/dev/zero",
+ .nthreads = 1,
+ };
+ int opt, result;
+
+ while ((opt = getopt(argc, argv, "m:r:n:F:f:aj:tTLuwWSH")) != -1) {
+ switch (opt) {
+
+ /* Command selection */
+ case 'u':
+ run.cmd = GUP_FAST_BENCHMARK;
+ break;
+ case 'a':
+ run.cmd = PIN_FAST_BENCHMARK;
+ break;
+ case 'L':
+ run.cmd = PIN_LONGTERM_BENCHMARK;
+ break;
+
+ /* Memory type */
+ case 'H':
+ run.hugetlb = true;
+ break;
+ case 't':
+ run.thp = 1;
+ break;
+ case 'T':
+ run.thp = 0;
+ break;
+
+ /* Access mode */
+ case 'w':
+ run.write = true;
+ break;
+ case 'W':
+ run.write = false;
+ break;
+ case 'S':
+ run.shared = true;
+ break;
+
+ /* Mapping */
+ case 'f':
+ run.file = optarg;
+ break;
+
+ /* Sizing and iteration */
+ case 'm':
+ run.size = atoi(optarg) * MB;
+ break;
+ case 'n':
+ run.nr_pages = atoi(optarg);
+ break;
+ case 'r':
+ repeats = atoi(optarg);
+ break;
+ case 'j': {
+ char *end;
+ long val;
+
+ errno = 0;
+ val = strtol(optarg, &end, 10);
+ if (errno || end == optarg || *end != '\0' || val < 1 ||
+ val > INT_MAX ||
+ (size_t)val > SIZE_MAX / sizeof(pthread_t)) {
+ fprintf(stderr, "Invalid thread count '%s'\n", optarg);
+ exit(1);
+ }
+ run.nthreads = val;
+ break;
+ }
+
+ /* Advanced */
+ case 'F':
+ /* strtol, so you can pass flags in hex form */
+ run.gup_flags = strtol(optarg, 0, 0);
+ break;
+
+ default:
+ fprintf(stderr, "Wrong argument\n");
+ exit(1);
+ }
+ }
+
+ gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ if (gup_fd == -1) {
+ if (errno == EACCES) {
+ fprintf(stderr, "Please run as root\n");
+ } else if (errno == ENOENT) {
+ if (opendir("/sys/kernel/debug") == NULL)
+ fprintf(stderr, "Mount debugfs at /sys/kernel/debug\n");
+ else
+ fprintf(stderr, "Check CONFIG_GUP_TEST in kernel config\n");
+ } else {
+ fprintf(stderr, "Failed to open %s: %s\n", GUP_TEST_FILE,
+ strerror(errno));
+ }
+ exit(1);
+ }
+
+ result = (argc == 1) ? run_matrix() : run_bench(&run);
+ close(gup_fd);
+ return result;
+}
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 4/4] selftests/mm: rewrite gup_test as a standalone harness-based selftest
2026-05-21 11:17 [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (2 preceding siblings ...)
2026-05-21 11:18 ` [PATCH v3 3/4] tools/mm: add a standalone GUP microbenchmark Sarthak Sharma
@ 2026-05-21 11:18 ` Sarthak Sharma
2026-05-21 21:12 ` [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Andrew Morton
4 siblings, 0 replies; 14+ messages in thread
From: Sarthak Sharma @ 2026-05-21 11:18 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Jonathan Corbet, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jason Gunthorpe, John Hubbard, Peter Xu,
Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mark Brown, linux-kernel,
linux-mm, linux-kselftest, linux-doc, Sarthak Sharma
Rewrite gup_test.c using kselftest_harness.h. The new test covers 12
mapping configurations: THP on, THP off and hugetlb, each across
private/shared and read/write variants. It runs seven test cases per
variant: get_user_pages, get_user_pages_fast, pin_user_pages,
pin_user_pages_fast, pin_user_pages_longterm, and DUMP_USER_PAGES_TEST
via both get and pin.
Each test case sweeps four nr_pages_per_call values: 1, 512, 123, and
all pages. This preserves the old run_gup_matrix() sweep: 12 mapping
combinations x 5 GUP/PUP operations x 4 batch sizes = 240 ioctl sweeps.
It also expands DUMP_USER_PAGES_TEST coverage from one standalone
invocation to 12 variants x 2 dump modes x 4 batch sizes = 96
additional sweeps.
Preserve the old sparse dump coverage from run_vmtests.sh with one
standalone test that exercises DUMP_USER_PAGES_TEST with page indices
0, 19 and 0x1000. This brings the total to 85 TAP-reported cases
(12 x 7 + 1) and 337 ioctl sweeps (240 + 96 + 1).
On a Radxa Orion O6 board, ./gup_test completes in 5.39s on average
over 10 runs (range: 5.33s - 5.48s).
Update run_vmtests.sh: remove run_gup_matrix() and the multiple flagged
invocations of gup_test, replacing them with a single unconditional
invocation. Benchmark functionality is handled by tools/mm/gup_bench
introduced in the previous patch.
Update Documentation/core-api/pin_user_pages.rst to reflect the new
harness-based gup_test interface rather than command-line flag
invocations.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
Documentation/core-api/pin_user_pages.rst | 12 +-
tools/testing/selftests/mm/gup_test.c | 584 ++++++++++++++--------
tools/testing/selftests/mm/run_vmtests.sh | 37 +-
3 files changed, 376 insertions(+), 257 deletions(-)
diff --git a/Documentation/core-api/pin_user_pages.rst b/Documentation/core-api/pin_user_pages.rst
index c16ca163b55e..ea722adf22cc 100644
--- a/Documentation/core-api/pin_user_pages.rst
+++ b/Documentation/core-api/pin_user_pages.rst
@@ -230,10 +230,16 @@ This file::
tools/testing/selftests/mm/gup_test.c
-has the following new calls to exercise the new pin*() wrapper functions:
+contains the following test cases to exercise pin_user_pages*():
-* PIN_FAST_BENCHMARK (./gup_test -a)
-* PIN_BASIC_TEST (./gup_test -b)
+* pin_user_pages via PIN_BASIC_TEST
+* pin_user_pages_fast via PIN_FAST_BENCHMARK
+* pin_user_pages_longterm via PIN_LONGTERM_BENCHMARK
+
+Run with::
+
+ make -C tools/testing/selftests/mm
+ ./tools/testing/selftests/mm/gup_test
You can monitor how many total dma-pinned pages have been acquired and released
since the system was booted, via two new /proc/vmstat entries: ::
diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c
index 803ab829a841..3f6626fe94a2 100644
--- a/tools/testing/selftests/mm/gup_test.c
+++ b/tools/testing/selftests/mm/gup_test.c
@@ -9,268 +9,416 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <pthread.h>
-#include <assert.h>
#include <mm/gup_test.h>
#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
+#include "kselftest_harness.h"
#define MB (1UL << 20)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
/* Just the flags we need, copied from the kernel internals. */
#define FOLL_WRITE 0x01 /* check pte is writable */
+/* Page counts exercising single, THP-batch, partial, and full-mapping GUP. */
+static const int nr_pages_list[] = { 1, 512, 123, -1 };
+
#define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
-static unsigned long cmd = GUP_FAST_BENCHMARK;
-static int gup_fd, repeats = 1;
-static unsigned long size = 128 * MB;
-/* Serialize prints */
-static pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER;
+FIXTURE(gup_test)
+{
+ int gup_fd;
+ char *addr;
+ unsigned long size;
+};
+
+FIXTURE_VARIANT(gup_test)
+{
+ bool thp;
+ bool hugetlb;
+ bool write;
+ bool shared;
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_readonly)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_readonly_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_readonly_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_readonly)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = true,
+ .shared = true,
+};
-static char *cmd_to_str(unsigned long cmd)
+FIXTURE_VARIANT_ADD(gup_test, shared_readonly_thp)
{
- switch (cmd) {
- case GUP_FAST_BENCHMARK:
- return "GUP_FAST_BENCHMARK";
- case PIN_FAST_BENCHMARK:
- return "PIN_FAST_BENCHMARK";
- case PIN_LONGTERM_BENCHMARK:
- return "PIN_LONGTERM_BENCHMARK";
- case GUP_BASIC_TEST:
- return "GUP_BASIC_TEST";
- case PIN_BASIC_TEST:
- return "PIN_BASIC_TEST";
- case DUMP_USER_PAGES_TEST:
- return "DUMP_USER_PAGES_TEST";
+ .thp = true,
+ .hugetlb = false,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_readonly_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_SETUP(gup_test)
+{
+ int mmap_flags = MAP_PRIVATE;
+ int zero_fd;
+ char *p;
+
+ self->size = variant->hugetlb ? 256 * MB : 128 * MB;
+
+ /* Check for hugetlb */
+ if (variant->hugetlb) {
+ unsigned long hp_size = default_huge_page_size();
+
+ if (!hp_size)
+ SKIP(return, "HugeTLB not available\n");
+
+ self->size = (self->size + hp_size - 1) & ~(hp_size - 1);
+ if (!hugetlb_setup_default(self->size / hp_size)) {
+ hugetlb_restore_settings();
+ SKIP(return, "Not enough huge pages\n");
+ }
+
+ mmap_flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
}
- return "Unknown command";
+
+ /* zero_fd has to be >= 0. Already checked in main() */
+ zero_fd = open("/dev/zero", O_RDWR);
+ ASSERT_GE(zero_fd, 0);
+
+ /* gup_fd has to be >= 0. Already checked in main() */
+ self->gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ ASSERT_GE(self->gup_fd, 0);
+
+ if (variant->shared)
+ mmap_flags = (mmap_flags & ~MAP_PRIVATE) | MAP_SHARED;
+
+ self->addr = mmap(NULL, self->size, PROT_READ | PROT_WRITE,
+ mmap_flags, zero_fd, 0);
+ close(zero_fd);
+ ASSERT_NE(self->addr, MAP_FAILED);
+
+ if (variant->thp)
+ madvise(self->addr, self->size, MADV_HUGEPAGE);
+ else
+ madvise(self->addr, self->size, MADV_NOHUGEPAGE);
+
+ for (p = self->addr; (unsigned long)p < (unsigned long)self->addr
+ + self->size; p += psize())
+ p[0] = 0;
}
-void *gup_thread(void *data)
+FIXTURE_TEARDOWN(gup_test)
{
- struct gup_test gup = *(struct gup_test *)data;
- int i, status;
-
- /* Only report timing information on the *_BENCHMARK commands: */
- if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) ||
- (cmd == PIN_LONGTERM_BENCHMARK)) {
- for (i = 0; i < repeats; i++) {
- gup.size = size;
- status = ioctl(gup_fd, cmd, &gup);
- if (status)
- break;
-
- pthread_mutex_lock(&print_mutex);
- ksft_print_msg("%s: Time: get:%lld put:%lld us",
- cmd_to_str(cmd), gup.get_delta_usec,
- gup.put_delta_usec);
- if (gup.size != size)
- ksft_print_msg(", truncated (size: %lld)", gup.size);
- ksft_print_msg("\n");
- pthread_mutex_unlock(&print_mutex);
- }
- } else {
- gup.size = size;
- status = ioctl(gup_fd, cmd, &gup);
- if (status)
- goto return_;
-
- pthread_mutex_lock(&print_mutex);
- ksft_print_msg("%s: done\n", cmd_to_str(cmd));
- if (gup.size != size)
- ksft_print_msg("Truncated (size: %lld)\n", gup.size);
- pthread_mutex_unlock(&print_mutex);
+ munmap(self->addr, self->size);
+ close(self->gup_fd);
+
+ if (variant->hugetlb)
+ hugetlb_restore_settings();
+}
+
+TEST_F(gup_test, get_user_pages)
+{
+ /* Tests the get_user_pages path */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, GUP_BASIC_TEST, &gup), 0);
+ }
+}
+
+TEST_F(gup_test, pin_user_pages)
+{
+ /* Tests the pin_user_pages path */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, PIN_BASIC_TEST, &gup), 0);
}
+}
+
+TEST_F(gup_test, dump_user_pages_with_get)
+{
+ /* Tests DUMP_USER_PAGES_TEST using get_user_pages */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
-return_:
- ksft_test_result(!status, "ioctl status %d\n", status);
- return NULL;
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ gup.which_pages[0] = 1;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, DUMP_USER_PAGES_TEST, &gup), 0);
+ }
}
-int main(int argc, char **argv)
+TEST_F(gup_test, dump_user_pages_with_pin)
{
- struct gup_test gup = { 0 };
- int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret;
- int flags = MAP_PRIVATE;
- char *file = "/dev/zero";
- bool hugetlb = false;
- pthread_t *tid;
- char *p;
+ /* Tests DUMP_USER_PAGES_TEST using pin_user_pages */
+ int i;
- while ((opt = getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
- switch (opt) {
- case 'a':
- cmd = PIN_FAST_BENCHMARK;
- break;
- case 'b':
- cmd = PIN_BASIC_TEST;
- break;
- case 'L':
- cmd = PIN_LONGTERM_BENCHMARK;
- break;
- case 'c':
- cmd = DUMP_USER_PAGES_TEST;
- /*
- * Dump page 0 (index 1). May be overridden later, by
- * user's non-option arguments.
- *
- * .which_pages is zero-based, so that zero can mean "do
- * nothing".
- */
- gup.which_pages[0] = 1;
- break;
- case 'p':
- /* works only with DUMP_USER_PAGES_TEST */
- gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
- break;
- case 'F':
- /* strtol, so you can pass flags in hex form */
- gup.gup_flags = strtol(optarg, 0, 0);
- break;
- case 'j':
- nthreads = atoi(optarg);
- break;
- case 'm':
- size = atoi(optarg) * MB;
- break;
- case 'r':
- repeats = atoi(optarg);
- break;
- case 'n':
- nr_pages = atoi(optarg);
- if (nr_pages < 0)
- nr_pages = size / psize();
- break;
- case 't':
- thp = 1;
- break;
- case 'T':
- thp = 0;
- break;
- case 'U':
- cmd = GUP_BASIC_TEST;
- break;
- case 'u':
- cmd = GUP_FAST_BENCHMARK;
- break;
- case 'w':
- write = 1;
- break;
- case 'W':
- write = 0;
- break;
- case 'f':
- file = optarg;
- break;
- case 'S':
- flags &= ~MAP_PRIVATE;
- flags |= MAP_SHARED;
- break;
- case 'H':
- flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
- hugetlb = true;
- break;
- default:
- ksft_exit_fail_msg("Wrong argument\n");
- }
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ gup.which_pages[0] = 1;
+ gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, DUMP_USER_PAGES_TEST, &gup), 0);
}
+}
- if (optind < argc) {
- int extra_arg_count = 0;
- /*
- * For example:
- *
- * ./gup_test -c 0 1 0x1001
- *
- * ...to dump pages 0, 1, and 4097
- */
-
- while ((optind < argc) &&
- (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
- /*
- * Do the 1-based indexing here, so that the user can
- * use normal 0-based indexing on the command line.
- */
- long page_index = strtol(argv[optind], 0, 0) + 1;
-
- gup.which_pages[extra_arg_count] = page_index;
- extra_arg_count++;
- optind++;
- }
+TEST_F(gup_test, get_user_pages_fast)
+{
+ /* Tests the lockless get_user_pages_fast() path */
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
+
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, GUP_FAST_BENCHMARK, &gup), 0);
}
+}
- ksft_print_header();
+TEST_F(gup_test, pin_user_pages_fast)
+{
+ /* Tests the lockless pin_user_pages_fast() path */
+ int i;
- if (hugetlb) {
- unsigned long hp_size = default_huge_page_size();
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
- if (!hp_size)
- ksft_exit_skip("HugeTLB is unavailable\n");
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
+
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
- size = (size + hp_size - 1) & ~(hp_size - 1);
- if (!hugetlb_setup_default(size / hp_size))
- ksft_exit_skip("Not enough huge pages\n");
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, PIN_FAST_BENCHMARK, &gup), 0);
}
+}
- ksft_set_plan(nthreads);
+TEST_F(gup_test, pin_user_pages_longterm)
+{
+ /* Tests pin_user_pages() with FOLL_LONGTERM */
+ int i;
- filed = open(file, O_RDWR|O_CREAT, 0664);
- if (filed < 0)
- ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = { 0 };
- gup.nr_pages_per_call = nr_pages;
- if (write)
- gup.gup_flags |= FOLL_WRITE;
+ gup.addr = (unsigned long)self->addr;
+ gup.size = self->size;
+ gup.nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i];
- gup_fd = open(GUP_TEST_FILE, O_RDWR);
- if (gup_fd == -1) {
- switch (errno) {
- case EACCES:
- if (getuid())
- ksft_print_msg("Please run this test as root\n");
- break;
- case ENOENT:
- if (opendir("/sys/kernel/debug") == NULL)
- ksft_print_msg("mount debugfs at /sys/kernel/debug\n");
- ksft_print_msg("check if CONFIG_GUP_TEST is enabled in kernel config\n");
- break;
- default:
- ksft_print_msg("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
- break;
- }
- ksft_test_result_skip("Please run this test as root\n");
- ksft_exit_pass();
+ if (variant->write)
+ gup.gup_flags |= FOLL_WRITE;
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, PIN_LONGTERM_BENCHMARK, &gup), 0);
}
+}
- p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0);
- if (p == MAP_FAILED)
- ksft_exit_fail_msg("mmap: %s\n", strerror(errno));
- gup.addr = (unsigned long)p;
+TEST(dump_user_pages_sparse_indices)
+{
+ /* Tests sparse multi-index which_pages[] inputs. */
+ struct gup_test gup = { 0 };
+ unsigned long size = 128 * MB;
+ int zero_fd, gup_fd;
+ char *addr, *p;
+
+ zero_fd = open("/dev/zero", O_RDWR);
+ ASSERT_GE(zero_fd, 0);
+
+ gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ ASSERT_GE(gup_fd, 0);
- if (thp == 1)
- madvise(p, size, MADV_HUGEPAGE);
- else if (thp == 0)
- madvise(p, size, MADV_NOHUGEPAGE);
+ addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0);
+ close(zero_fd);
+ ASSERT_NE(addr, MAP_FAILED);
- /* Fault them in here, from user space. */
- for (; (unsigned long)p < gup.addr + size; p += psize())
+ ASSERT_EQ(madvise(addr, size, MADV_HUGEPAGE), 0);
+
+ for (p = addr; (unsigned long)p < (unsigned long)addr + size;
+ p += psize())
p[0] = 0;
- tid = malloc(sizeof(pthread_t) * nthreads);
- assert(tid);
- for (i = 0; i < nthreads; i++) {
- ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
- assert(ret == 0);
- }
- for (i = 0; i < nthreads; i++) {
- ret = pthread_join(tid[i], NULL);
- assert(ret == 0);
+ gup.addr = (unsigned long)addr;
+ gup.size = size;
+ gup.gup_flags = FOLL_WRITE;
+ gup.which_pages[0] = 1;
+ gup.which_pages[1] = 20;
+ gup.which_pages[2] = 0x1001;
+
+ /*
+ * Preserve the old "./gup_test -ct -F 0x1 0 19 0x1000" sparse dump
+ * coverage after removing command-line parsing from this binary.
+ */
+ ASSERT_EQ(ioctl(gup_fd, DUMP_USER_PAGES_TEST, &gup), 0);
+
+ munmap(addr, size);
+ close(gup_fd);
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char *file = "/dev/zero";
+
+ fd = open(file, O_RDWR);
+ if (fd < 0) {
+ ksft_print_header();
+ ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
}
+ close(fd);
- free(tid);
+ fd = open(GUP_TEST_FILE, O_RDWR);
+ if (fd == -1) {
+ ksft_print_header();
+ if (errno == EACCES)
+ ksft_exit_skip("Please run this test as root\n");
+ if (errno == ENOENT) {
+ if (opendir("/sys/kernel/debug") == NULL)
+ ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n");
+ else
+ ksft_exit_skip("Check CONFIG_GUP_TEST in kernel config\n");
+ }
+ ksft_exit_skip("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
+ }
+ close(fd);
- ksft_exit_pass();
+ return test_harness_run(argc, argv);
}
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 043aa3ed2596..65a4ef0f3748 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -130,30 +130,6 @@ test_selected() {
fi
}
-run_gup_matrix() {
- # -t: thp=on, -T: thp=off, -H: hugetlb=on
- local hugetlb_mb=256
-
- for huge in -t -T "-H -m $hugetlb_mb"; do
- # -u: gup-fast, -U: gup-basic, -a: pin-fast, -b: pin-basic, -L: pin-longterm
- for test_cmd in -u -U -a -b -L; do
- # -w: write=1, -W: write=0
- for write in -w -W; do
- # -S: shared
- for share in -S " "; do
- # -n: How many pages to fetch together? 512 is special
- # because it's default thp size (or 2M on x86), 123 to
- # just test partial gup when hit a huge in whatever form
- for num in "-n 1" "-n 512" "-n 123" "-n -1"; do
- CATEGORY="gup_test" run_test ./gup_test \
- $huge $test_cmd $write $share $num
- done
- done
- done
- done
- done
-}
-
# filter 64bit architectures
ARCH64STR="arm64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sparc64 x86_64"
if [ -z "$ARCH" ]; then
@@ -239,18 +215,7 @@ fi
CATEGORY="mmap" run_test ./map_fixed_noreplace
-if $RUN_ALL; then
- run_gup_matrix
-else
- # get_user_pages_fast() benchmark
- CATEGORY="gup_test" run_test ./gup_test -u -n 1
- CATEGORY="gup_test" run_test ./gup_test -u -n -1
- # pin_user_pages_fast() benchmark
- CATEGORY="gup_test" run_test ./gup_test -a -n 1
- CATEGORY="gup_test" run_test ./gup_test -a -n -1
-fi
-# Dump pages 0, 19, and 4096, using pin_user_pages:
-CATEGORY="gup_test" run_test ./gup_test -ct -F 0x1 0 19 0x1000
+CATEGORY="gup_test" run_test ./gup_test
CATEGORY="gup_test" run_test ./gup_longterm
CATEGORY="userfaultfd" run_test ./uffd-unit-tests
--
2.39.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing
2026-05-21 11:17 [PATCH v3 0/4] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (3 preceding siblings ...)
2026-05-21 11:18 ` [PATCH v3 4/4] selftests/mm: rewrite gup_test as a standalone harness-based selftest Sarthak Sharma
@ 2026-05-21 21:12 ` Andrew Morton
4 siblings, 0 replies; 14+ messages in thread
From: Andrew Morton @ 2026-05-21 21:12 UTC (permalink / raw)
To: Sarthak Sharma
Cc: David Hildenbrand, Jonathan Corbet, Lorenzo Stoakes,
Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Shuah Khan,
Jason Gunthorpe, John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan,
Baolin Wang, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mark Brown, linux-kernel, linux-mm, linux-kselftest,
linux-doc
On Thu, 21 May 2026 16:47:57 +0530 Sarthak Sharma <sarthak.sharma@arm.com> wrote:
> gup_test.c currently serves two distinct purposes: microbenchmarking
> (GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK, PIN_LONGTERM_BENCHMARK) and
> functional correctness testing (GUP_BASIC_TEST, PIN_BASIC_TEST,
> DUMP_USER_PAGES_TEST). Mixing these in a single binary means functional
> tests cannot be run or reported individually and run_vmtests.sh must
> invoke the binary multiple times with different flag combinations to
> cover all configurations.
>
> This patch series separates the two concerns: tools/mm/gup_bench for
> benchmarking and tools/testing/selftests/mm/gup_test for functional
> testing. To avoid duplicating HugeTLB and related file helpers, the
> series first moves the common helper code to tools/lib/mm/ so it can be
> shared by both selftests and tools/mm.
>
> ...
>
Thanks.
> These patches apply on top of mm/mm-new.
>
> Changes in v3:
> - Address v2 feedback from Sashiko
Sashiko has thoughts on v3:
https://sashiko.dev/#/patchset/20260521111801.173019-1-sarthak.sharma@arm.com
Probably that's incorrect/ignored material which also appeared against
the v2 series. But sometimes it finds new things so please scan the
output.
I'll hold off on this series for now, shall see what reviewers have to
say.
^ permalink raw reply [flat|nested] 14+ messages in thread