selftests/mm: avoid false failures in hugetlb and KSM tests
Subsystems (3)
v2 cover selftests/mm: avoid false failures in hugetlb and KSM tests
Hi all,
This series fixes issues in the hugetlb and KSM MM selftest categories that can report failures when the prerequisites for the tests are not satisfied.
Patch 1 updates the hugetlb selftest helpers to handle -EINVAL when attempting to configure gigantic HugeTLB pages via nr_hugepages. PowerPC hash MMU pSeries systems expose gigantic hugepage sizes but do not allow runtime allocation of such pages, causing the sysfs write to fail. Handle this case gracefully and continue running the test instead of aborting.
Patch 2 fixes the KSM NUMA merge test on systems with memoryless NUMA nodes. The test currently relies on the number of configured NUMA nodes and may attempt allocations on nodes that have no memory, resulting in spurious failures. Use the existing helpers to identify
NUMA nodes that contain memory and skip the test when fewer than two such nodes are available.
Patch 3 fixes a pre-existing operator precedence issue in ksm_tests, where a ternary expression combined with logical OR operators could be evaluated differently than intended. Added parentheses to ensure the correct evaluation order.
These changes improve handling of unsupported test configurations and unmet test prerequisites, avoiding spurious failures.
Thanks,
Sayali
---
V1 -> V2:
- For "selftests/mm: handle EINVAL when configuring gigantic hugepages":
Added a dedicated hugetlb_write_num() helper to handle the expected EINVAL returned by gigantic hugepage configuration, in the
hugepage setup code rather than modifying the generic write_file() helper.
- For "selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes":
Updated implementation as per David's review comment to build upon existing helpers get_next_mem_node().
- Added new patch "selftests/mm: fix ternary operator precedence in ksm_tests" to address a pre-existing issue identified during Sahiko's review.
V1: https://lore.kernel.org/all/[email protected]/
---
Sayali Patil (3):
selftests/mm: handle EINVAL when configuring gigantic hugepages selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA
nodes
selftests/mm: fix ternary operator precedence in ksm_tests
.../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++- .../testing/selftests/mm/hugepage_settings.h | 1 + tools/testing/selftests/mm/ksm_tests.c | 28 ++++++++-------- 3 files changed, 47 insertions(+), 14 deletions(-)
Signature · 1 line
2.52.0
PATCH v2 1/3 selftests/mm: handle EINVAL when configuring gigantic hugepages
Some MM selftests attempt to configure the amount of HugeTLB pages of different sizes by writing to nr_hugepages.
PowerPC hash MMU pSeries systems advertise gigantic hugepage sizes but do not support runtime allocation of such pages, writes to the corresponding nr_hugepages file fail with -EINVAL. This causes the test to bail out even though the failure is due to a platform limitation rather than the
functionality being tested.
Treat -EINVAL from the sysfs write as a skipped configuration request and continue running the test instead of failing.
Before patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB
[INFO] detected hugetlb page size: 16384 KiB
ok 1 MADV_DONTNEED and MADV_REMOVE on hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Bail out! /sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepages write(0) failed: Invalid argument
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL]
After patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB [INFO] detected hugetlb page size: 16384 KiB
ok 1 MADV_DONTNEED and MADV_REMOVE on hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 /sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepages write(0) failed: Invalid argument
[PASS]
--- .../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++- .../testing/selftests/mm/hugepage_settings.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-)
tools/testing/selftests/mm/hugepage_settings.c +31 -1
@@ -422,6 +422,36 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen, size / 1024, attr); } +void hugetlb_write_num (const char *path,unsigned long num) +{ +int fd, saved_errno; +ssize_t numwritten; +char buf[21 ]; + +sprintf (buf,"%lu" , num); + + fd =open (path,O_WRONLY ); +if (fd ==-1 ) +ksft_exit_fail_msg ("%s open failed: %s\n" , path,strerror (errno)); + + numwritten =write (fd, buf,strlen (buf)); + saved_errno = errno; +close (fd); + errno = saved_errno; + +/* Treat EINVAL as a skipped configuration (e.g., unsupported gigantic pages) */ +if (numwritten <0 && errno ==EINVAL ) { +ksft_print_msg ("%s write(%s) failed: %s\n" , path, buf,strerror (errno)); +return ; + } + +if (numwritten <0 ) +ksft_exit_fail_msg ("%s write(%s) failed: %s\n" , path, buf,strerror (errno)); +if (numwritten !=strlen (buf)) +ksft_exit_fail_msg ("%s write(%s) is truncated, expected %zu bytes, got %zd bytes\n" , + path, buf,strlen (buf), numwritten); +} +unsigned long hugetlb_nr_pages (unsigned long size) {char path[PATH_MAX ];
@@ -437,7 +467,7 @@ void hugetlb_set_nr_pages(unsigned long size, unsigned long nr)hugetlb_sysfs_path (path,sizeof (path), size,"nr_hugepages" ); -write_num (path, nr); +hugetlb_write_num (path, nr); }unsigned long hugetlb_free_pages (unsigned long size)
tools/testing/selftests/mm/hugepage_settings.h +1 -0
@@ -95,6 +95,7 @@ bool thp_is_enabled(void);int detect_hugetlb_page_sizes (unsigned long sizes[],int max);unsigned long default_huge_page_size (void ); +void hugetlb_write_num (const char *path,unsigned long num);unsigned long hugetlb_nr_pages (unsigned long size);void hugetlb_set_nr_pages (unsigned long size,unsigned long nr);unsigned long hugetlb_free_pages (unsigned long size);
Signature · 1 line
2.52.0
David Hildenbrand (Arm)
maintainerRe: [PATCH v2 1/3] selftests/mm: handle EINVAL when configuring gigantic hugepages
Quoted 67 lines · Sayali Patil
Some MM selftests attempt to configure the amount of HugeTLB pages of different sizes by writing to
nr_hugepages.PowerPC hash MMU pSeries systems advertise gigantic hugepage sizes but do not support runtime allocation of such pages, writes to the corresponding
nr_hugepagesfile fail with-EINVAL. This causes the test to bail out even though the failure is due to a platform limitation rather than the
functionality being tested.Treat
-EINVALfrom the sysfs write as a skipped configuration request and continue running the test instead of failing.Before patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB
[INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Bail out!/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL]After patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB [INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
[PASS]Fixes 27477b28b74f ("selftests/mm: hugepage_settings: add APIs to get and set nr_hugepages")Signed-off-by Sayali Patil <[email protected]>--- .../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++- .../testing/selftests/mm/hugepage_settings.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-)tools/testing/selftests/mm/hugepage_settings.c +10
-0@@ -422,6 +422,36 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen, size / 1024, attr); } +void hugetlb_write_num (const char *path,unsigned long num) +{ +int fd, saved_errno; +ssize_t numwritten; +char buf[21 ]; + +sprintf (buf,"%lu" , num); + + fd =open (path,O_WRONLY ); +if (fd ==-1 )
tools/testing/selftests/mm/hugepage_settings.c static void hugetlb_sysfs_path(char *buf, size_t buflen, · line 444
Quoted 10 lines
tools/testing/selftests/mm/hugepage_settings.c +10
-0+ksft_exit_fail_msg ("%s open failed: %s\n" , path,strerror (errno)); + + numwritten =write (fd, buf,strlen (buf)); + saved_errno = errno; +close (fd); + errno = saved_errno; + +/* Treat EINVAL as a skipped configuration (e.g., unsupported gigantic pages) */ +if (numwritten <0 && errno ==EINVAL ) { +ksft_print_msg ("%s write(%s) failed: %s\n" , path, buf,strerror (errno));
Should we even print anything here? Rather confusing. It's just like we cannot allocate anything (no memory).
In general, you are copy-pasting a lot of write_num()+write_file() content, which is really suboptimal.
All you want is an option for write_num -> write_file to skip on -EINVAL, correct?
There are not that many write_num / write_file users ...
Signature · 3 lines
Cheers, David
Re: [PATCH v2 1/3] selftests/mm: handle EINVAL when configuring gigantic hugepages
Quoted 82 lines · David Hildenbrand (Arm)
Quoted 67 lines · Sayali Patil
Some MM selftests attempt to configure the amount of HugeTLB pages of different sizes by writing to
nr_hugepages.PowerPC hash MMU pSeries systems advertise gigantic hugepage sizes but do not support runtime allocation of such pages, writes to the corresponding
nr_hugepagesfile fail with-EINVAL. This causes the test to bail out even though the failure is due to a platform limitation rather than the
functionality being tested.Treat
-EINVALfrom the sysfs write as a skipped configuration request and continue running the test instead of failing.Before patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB
[INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Bail out!/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL]After patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB [INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
[PASS]Fixes 27477b28b74f ("selftests/mm: hugepage_settings: add APIs to get and set nr_hugepages")Signed-off-by Sayali Patil <[email protected]>--- .../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++- .../testing/selftests/mm/hugepage_settings.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-)tools/testing/selftests/mm/hugepage_settings.c +10
-0@@ -422,6 +422,36 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen, size / 1024, attr); } +void hugetlb_write_num (const char *path,unsigned long num) +{ +int fd, saved_errno; +ssize_t numwritten; +char buf[21 ]; + +sprintf (buf,"%lu" , num); + + fd =open (path,O_WRONLY ); +if (fd ==-1 )tools/testing/selftests/mm/hugepage_settings.c static void hugetlb_sysfs_path(char *buf, size_t buflen, · line 444
Quoted 10 lines
tools/testing/selftests/mm/hugepage_settings.c +10
-0+ksft_exit_fail_msg ("%s open failed: %s\n" , path,strerror (errno)); + + numwritten =write (fd, buf,strlen (buf)); + saved_errno = errno; +close (fd); + errno = saved_errno; + +/* Treat EINVAL as a skipped configuration (e.g., unsupported gigantic pages) */ +if (numwritten <0 && errno ==EINVAL ) { +ksft_print_msg ("%s write(%s) failed: %s\n" , path, buf,strerror (errno));Should we even print anything here? Rather confusing. It's just like we cannot allocate anything (no memory).
Quoted 7 lines
In general, you are copy-pasting a lot of write_num()+write_file() content, which is really suboptimal.
All you want is an option for
write_num->write_fileto skip on-EINVAL, correct?There are not that many
write_num/write_fileusers ...
Hi David,
Yes, all I need is to ignore the expected -EINVAL when attempting to configure gigantic hugepages via nr_hugepages.
I looked at extending write_num()/write_file() for this as in v1
(https://lore.kernel.org/all/8bfa921e30eb94072685103f6496784aa23bb166.1782365671.git.sayalip@linux.ibm.com/),
but these helpers are shared by several other selftests. For example, write_file() is used by split_huge_page_test setup and by khugepaged tests for drop_caches, and is also used for various THP and khugepaged settings where -EINVAL would indicate a genuine setup failure. This concern was also raised during the v1 review.
Because the expected -EINVAL is specific to gigantic hugepage runtime allocation, I kept the handling local to the hugetlb setup path rather than changing the semantics of the common helpers.
I also agree that printing a message is not particularly useful in this case, and we can simply return without emitting any output.
Please let me know if you would prefer a different approach.
Thanks,
Sayali
David Hildenbrand (Arm)
maintainerRe: [PATCH v2 1/3] selftests/mm: handle EINVAL when configuring gigantic hugepages
Quoted 111 lines · Sayali Patil
Quoted 85 lines · David Hildenbrand (Arm)
Quoted 76 lines · Sayali Patil
Some MM selftests attempt to configure the amount of HugeTLB pages of different sizes by writing to
nr_hugepages.PowerPC hash MMU pSeries systems advertise gigantic hugepage sizes but do not support runtime allocation of such pages, writes to the corresponding
nr_hugepagesfile fail with-EINVAL. This causes the test to bail out even though the failure is due to a platform limitation rather than the
functionality being tested.Treat
-EINVALfrom the sysfs write as a skipped configuration request and continue running the test instead of failing.Before patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB
[INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Bail out!/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL]After patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB [INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
[PASS]Fixes 27477b28b74f ("selftests/mm: hugepage_settings: add APIs to get andset
nr_hugepages")Signed-off-by Sayali Patil <[email protected]>---
.../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++- .../testing/selftests/mm/hugepage_settings.h | 1 +2 files changed, 32 insertions(+), 1 deletion(-)tools/testing/ renamed from tools/testing/selftests/mm/hugepage_settings.c +0
-0
selftests/mm/hugepage_settings.c
index2eab2110ac6a..ce38ae3da01a100644tools/testing/selftests/mm/hugepage_settings.c +17
-0@@ -422,6 +422,36 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen, size / 1024, attr); } +void hugetlb_write_num(const char *path, unsigned long num) +{ + int fd, saved_errno; + ssize_t numwritten; + char buf[21]; + + sprintf(buf, "%lu", num); + + fd = open(path, O_WRONLY); + if (fd == -1) + ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno)); + + numwritten = write(fd, buf, strlen(buf)); + saved_errno = errno; + close(fd); + errno = saved_errno; + + /* Treat EINVAL as a skipped configuration (e.g., unsupported giganticQuoted 4 lines
pages) */
+ if (numwritten < 0 && errno == EINVAL) {
+ksft_print_msg("%s write(%s) failed: %s\n", path, buf, strerror(errno));Should we even print anything here? Rather confusing. It's just like we cannot allocate anything (no memory).
Quoted 8 lines
In general, you are copy-pasting a lot of write_num()+write_file() content, which is really suboptimal.
All you want is an option for
write_num->write_fileto skip on-EINVAL, correct?There are not that many
write_num/write_fileusers ...Hi David,
Yes, all I need is to ignore the expected
-EINVALwhen attempting to configure gigantic hugepages vianr_hugepages.I looked at extending write_num()/write_file() for this as in v1
(https://lore.kernel.org/
all/8bfa921e30eb94072685103f6496784aa23bb166.1782365671.git.sayalip@linux.ibm.com/), but these helpers are shared by several other selftests.
For example,write_file()is used bysplit_huge_page_testsetup and by khugepaged tests fordrop_caches, and is also used for various THP and khugepaged settings where-EINVALwould indicate a genuine setup failure. This concern was also raised during the v1 review.
Quoted 6 lines
Because the expected
-EINVALis specific to gigantic hugepage runtime allocation, I kept the handling local to the hugetlb setup path rather than changing the semantics of the common helpers.I also agree that printing a message is not particularly useful in this case, and we can simply return without emitting any output.
We can either convert the functions to use flags, or hide it in some internal helpers, like the following:
From 1b1b8ad51f1f0be469cb191736300254b9521fe4 Mon Sep 17 00:00:00 2001
From: "David Hildenbrand (Arm)" <[email protected]>
Date: Wed, 1 Jul 2026 10:45:16 +0200
Subject: [PATCH] tmp
--- tools/testing/selftests/mm/vm_util.c | 28 ++++++++++++++++++++++++---- tools/testing/selftests/mm/vm_util.h | 1 + 2 files changed, 25 insertions(+), 4 deletions(-)
tools/testing/selftests/mm/vm_util.c +24 -4
@@ -719,7 +719,8 @@ int read_file(const char *path, char *buf, size_t buflen)return (unsigned int ) numread; } -void write_file (const char *path,const char *buf,size_t buflen) +static int __write_file (const char *path,const char *buf,size_t buflen, +bool ignore_einval) {int fd, saved_errno;ssize_t numwritten;
@@ -735,14 +736,23 @@ void write_file(const char *path, const char *buf, size_t buflen) saved_errno = errno;close (fd); errno = saved_errno; -if (numwritten <0 ) + +if (numwritten <0 ) { +if (ignore_einval && errno ==EINVAL ) +return ;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); } +static void write_file (const char *path,const char *buf,size_t buflen) +{ +__write_file (path, bug, buflen,/* ignore_einval = */ false); +} +unsigned long read_num (const char *path) {char buf[21 ];
@@ -753,12 +763,22 @@ unsigned long read_num(const char *path)return strtoul (buf,NULL ,10 ); } -void write_num (const char *path,unsigned long num) +static void __write_num (const char *path,unsigned long num,bool ignore_einval) {char buf[21 ];sprintf (buf,"%lu" , num); -write_file (path, buf,strlen (buf) +1 ); +write_file (path, buf,strlen (buf) +1 , ignore_einval); +} + +void write_num (const char *path,unsigned long num) +{ +return __write_num (path, num,/* ignore_einval = */ false); +} + +void write_num_ignore_einval (const char *path,unsigned long num) +{ +return __write_num (path, num,/* ignore_einval = */ true); }static unsigned long shmall, shmmax;
tools/testing/selftests/mm/vm_util.h +1 -0
@@ -168,6 +168,7 @@ 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 write_num_ignore_einval (const char *path,unsigned long num);void shm_limits_prepare (unsigned long length);void __shm_limits_restore (void );
Signature · 7 lines
2.43.0 -- Cheers, David
Re: [PATCH v2 1/3] selftests/mm: handle EINVAL when configuring gigantic hugepages
Quoted 201 lines · David Hildenbrand (Arm)
Quoted 111 lines · Sayali Patil
Quoted 85 lines · David Hildenbrand (Arm)
Quoted 76 lines · Sayali Patil
Some MM selftests attempt to configure the amount of HugeTLB pages of different sizes by writing to
nr_hugepages.PowerPC hash MMU pSeries systems advertise gigantic hugepage sizes but do not support runtime allocation of such pages, writes to the corresponding
nr_hugepagesfile fail with-EINVAL. This causes the test to bail out even though the failure is due to a platform limitation rather than the
functionality being tested.Treat
-EINVALfrom the sysfs write as a skipped configuration request and continue running the test instead of failing.Before patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB
[INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 Bail out!/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:0 error:0 [FAIL]After patch:
-------------------------
running ./hugetlb-madvise
-------------------------
TAP version 13
1..1
[INFO] detected hugetlb page size: 16777216 KiB [INFO] detected hugetlb page size: 16384 KiB
ok 1MADV_DONTNEEDandMADV_REMOVEon hugetlb
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0/sys/kernel/mm/hugepages/hugepages-16777216kB/nr_hugepageswrite(0) failed: Invalid argument
[PASS]Fixes 27477b28b74f ("selftests/mm: hugepage_settings: add APIs to get andset
nr_hugepages")Signed-off-by Sayali Patil <[email protected]>--- .../testing/selftests/mm/hugepage_settings.c | 32 ++++++++++++++++++- .../testing/selftests/mm/hugepage_settings.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-)tools/testing/ renamed from tools/testing/selftests/mm/hugepage_settings.c +0
-0
selftests/mm/hugepage_settings.c
index2eab2110ac6a..ce38ae3da01a100644tools/testing/selftests/mm/hugepage_settings.c +17
-0@@ -422,6 +422,36 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen, size / 1024, attr); } +void hugetlb_write_num(const char *path, unsigned long num) +{ + int fd, saved_errno; + ssize_t numwritten; + char buf[21]; + + sprintf(buf, "%lu", num); + + fd = open(path, O_WRONLY); + if (fd == -1) + ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno)); + + numwritten = write(fd, buf, strlen(buf)); + saved_errno = errno; + close(fd); + errno = saved_errno; + + /* Treat EINVAL as a skipped configuration (e.g., unsupported giganticQuoted 4 lines
pages) */
+ if (numwritten < 0 && errno == EINVAL) {
+ksft_print_msg("%s write(%s) failed: %s\n", path, buf, strerror(errno));Should we even print anything here? Rather confusing. It's just like we cannot allocate anything (no memory).
Quoted 8 lines
In general, you are copy-pasting a lot of write_num()+write_file() content, which is really suboptimal.
All you want is an option for
write_num->write_fileto skip on-EINVAL, correct?There are not that many
write_num/write_fileusers ...Hi David,
Yes, all I need is to ignore the expected
-EINVALwhen attempting to configure gigantic hugepages vianr_hugepages.I looked at extending write_num()/write_file() for this as in v1
(https://lore.kernel.org/
all/8bfa921e30eb94072685103f6496784aa23bb166.1782365671.git.sayalip@linux.ibm.com/), but these helpers are shared by several other selftests.
For example,write_file()is used bysplit_huge_page_testsetup and by khugepaged tests fordrop_caches, and is also used for various THP and khugepaged settings where-EINVALwould indicate a genuine setup failure. This concern was also raised during the v1 review.Quoted 6 lines
Because the expected
-EINVALis specific to gigantic hugepage runtime allocation, I kept the handling local to the hugetlb setup path rather than changing the semantics of the common helpers.I also agree that printing a message is not particularly useful in this case, and we can simply return without emitting any output.
We can either convert the functions to use flags, or hide it in some internal helpers, like the following:
From
1b1b8ad51f1f0be469cb191736300254b9521fe4Mon Sep 17 00:00:00 2001
From: "David Hildenbrand (Arm)" <[email protected]>
Date: Wed, 1 Jul 2026 10:45:16 +0200
Subject: [PATCH] tmpSigned-off-by David Hildenbrand (Arm) <[email protected]>--- tools/testing/selftests/mm/vm_util.c | 28 ++++++++++++++++++++++++---- tools/testing/selftests/mm/vm_util.h | 1 + 2 files changed, 25 insertions(+), 4 deletions(-)tools/testing/selftests/mm/vm_util.c +24
-4@@ -719,7 +719,8 @@ int read_file(const char *path, char *buf, size_t buflen)return (unsigned int ) numread; } -void write_file (const char *path,const char *buf,size_t buflen) +static int __write_file (const char *path,const char *buf,size_t buflen, +bool ignore_einval) {int fd, saved_errno;ssize_t numwritten;@@ -735,14 +736,23 @@ void write_file(const char *path, const char *buf, size_t buflen) saved_errno = errno;close (fd); errno = saved_errno; -if (numwritten <0 ) + +if (numwritten <0 ) { +if (ignore_einval && errno ==EINVAL ) +return ;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); } +static void write_file (const char *path,const char *buf,size_t buflen) +{ +__write_file (path, bug, buflen,/* ignore_einval = */ false); +} +unsigned long read_num (const char *path) {char buf[21 ];@@ -753,12 +763,22 @@ unsigned long read_num(const char *path)return strtoul (buf,NULL ,10 ); } -void write_num (const char *path,unsigned long num) +static void __write_num (const char *path,unsigned long num,bool ignore_einval) {char buf[21 ];sprintf (buf,"%lu" , num); -write_file (path, buf,strlen (buf) +1 ); +write_file (path, buf,strlen (buf) +1 , ignore_einval); +} + +void write_num (const char *path,unsigned long num) +{ +return __write_num (path, num,/* ignore_einval = */ false); +} + +void write_num_ignore_einval (const char *path,unsigned long num) +{ +return __write_num (path, num,/* ignore_einval = */ true); }static unsigned long shmall, shmmax;tools/testing/selftests/mm/vm_util.h +0
-0
tools/testing/selftests/mm/vm_util.h void write_file(const char *path, const char *buf, size_t buflen); · line 174
Quoted 7 lines
tools/testing/selftests/mm/vm_util.h +1
-0@@ -168,6 +168,7 @@ 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 write_num_ignore_einval (const char *path,unsigned long num);void shm_limits_prepare (unsigned long length);void __shm_limits_restore (void );
Thanks, David. I've sent out v3 incorporating the suggested changes. V3:https://lore.kernel.org/all/[email protected]/
PATCH v2 2/3 selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes
In reply to Sayali PatilThe KSM NUMA merge test allocates identical pages on different NUMA nodes and verifies KSM behavior with merge_across_nodes enabled and disabled.
On systems with memoryless NUMA nodes, for example:
#numactl -H
available: 2 nodes (0,4)
.....
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 14825 MB
node 0 free: 1382 MB
node 4 cpus:
node 4 size: 0 MB
node 4 free: 0 MBthe test may attempt to allocate memory on a node without memory, causing numa_alloc_onnode() to fail and resulting in a spurious test failure.
The test currently checks numa_num_configured_nodes() to determine whether sufficient NUMA nodes are available. However, configured nodes do not necessarily have memory.
Reuse the existing get_first_mem_node() and get_next_mem_node() helpers to locate NUMA nodes that actually contain memory, and skip the test when fewer than two such nodes are available.
Before patch:
---------------------------
running ./ksm_tests -N -m 1
---------------------------
mbind: Invalid argument
ok 1 KSM NUMA merging
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0 [PASS]
ok 1 ksm_tests -N -m 1
---------------------------
running ./ksm_tests -N -m 0
---------------------------
mbind: Invalid argument
not ok 1 KSM NUMA merging
Totals: pass:0 fail:1 xfail:0 xpass:0 skip:0 error:0 [FAIL]
not ok 2 ksm_tests -N -m 0 # exit=1
After patch:
---------------------------
running ./ksm_tests -N -m 1
---------------------------
At least 2 NUMA nodes with memory must be available ok 1
SKIP KSM NUMA merging
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0 [PASS]
ok 1 ksm_tests -N -m 1
---------------------------
running ./ksm_tests -N -m 0
---------------------------
At least 2 NUMA nodes with memory must be available ok 1
SKIP KSM NUMA merging
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0 [PASS]
ok 2 ksm_tests -N -m 0
--- tools/testing/selftests/mm/ksm_tests.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
tools/testing/selftests/mm/ksm_tests.c +9 -7
@@ -440,9 +440,9 @@ static int get_next_mem_node(int node) mem_node = i % (max_node +1 ); node_size =numa_node_size (mem_node,NULL );if (node_size >0 ) -break ; +return mem_node; } -return mem_node; +return -ENODEV ; }static int get_first_mem_node (void )
@@ -455,8 +455,8 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo {void *numa1_map_ptr, *numa2_map_ptr;struct timespec start_time; +int first_node, second_node;int page_count =2 ; -int first_node;if (clock_gettime (CLOCK_MONOTONIC_RAW , &start_time)) {ksft_perror ("clock_gettime" );
@@ -467,17 +467,19 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeoksft_print_msg ("NUMA support not enabled\n" );return KSFT_SKIP ; } -if (numa_num_configured_nodes () <=1 ) { -ksft_print_msg ("At least 2 NUMA nodes must be available\n" ); + first_node =get_first_mem_node (); + second_node =get_next_mem_node (first_node); + +if (second_node <0 ) { +ksft_print_msg ("At least 2 NUMA nodes with memory must be available\n" );return KSFT_SKIP ; }if (ksm_write_sysfs (KSM_FP ("merge_across_nodes" ), merge_across_nodes))return KSFT_FAIL ;/* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */ - first_node =get_first_mem_node (); numa1_map_ptr =numa_alloc_onnode (page_size, first_node); - numa2_map_ptr =numa_alloc_onnode (page_size,get_next_mem_node (first_node)); + numa2_map_ptr =numa_alloc_onnode (page_size, second_node);if (!numa1_map_ptr || !numa2_map_ptr) {ksft_perror ("numa_alloc_onnode" );return KSFT_FAIL ;
Signature · 1 line
2.52.0
Re: [PATCH v2 2/3] selftests/mm: fix ksm NUMA merge test for systems with memoryless NUMA nodes
Quoted 113 lines · Sayali Patil
The KSM NUMA merge test allocates identical pages on different NUMA nodes and verifies KSM behavior with
merge_across_nodesenabled and disabled.On systems with memoryless NUMA nodes, for example:
#numactl -Havailable: 2 nodes (0,4) ..... node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 node 0 size: 14825 MB node 0 free: 1382 MB node 4 cpus: node 4 size: 0 MB node 4 free: 0 MBthe test may attempt to allocate memory on a node without memory, causing
numa_alloc_onnode()to fail and resulting in a spurious test failure.The test currently checks
numa_num_configured_nodes()to determine whether sufficient NUMA nodes are available. However, configured nodes do not necessarily have memory.Reuse the existing
get_first_mem_node()andget_next_mem_node()helpers to locate NUMA nodes that actually contain memory, and skip the test when fewer than two such nodes are available.Before patch:
---------------------------
running ./ksm_tests -N -m 1
---------------------------
mbind: Invalid argument
ok 1 KSM NUMA merging
Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
[PASS]
ok 1ksm_tests-N -m 1
---------------------------
running ./ksm_tests -N -m 0
---------------------------
mbind: Invalid argument
not ok 1 KSM NUMA merging
Totals: pass:0 fail:1 xfail:0 xpass:0 skip:0 error:0
[FAIL]
not ok 2ksm_tests-N -m 0 # exit=1After patch:
---------------------------
running ./ksm_tests -N -m 1
---------------------------
At least 2 NUMA nodes with memory must be available
ok 1
SKIP KSM NUMA merging
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0
[PASS]
ok 1ksm_tests-N -m 1
---------------------------
running ./ksm_tests -N -m 0
---------------------------
At least 2 NUMA nodes with memory must be available
ok 1
SKIP KSM NUMA merging
Totals: pass:0 fail:0 xfail:0 xpass:0 skip:1 error:0
[PASS]
ok 2ksm_tests-N -m 0Fixes e3820ab252dd ("selftest/vm: fix ksm selftest to run with different NUMA topologies")Co-developed-by David Hildenbrand (Arm) <[email protected]>Signed-off-by David Hildenbrand (Arm) <[email protected]>Signed-off-by Sayali Patil <[email protected]>--- tools/testing/selftests/mm/ksm_tests.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)tools/testing/selftests/mm/ksm_tests.c +8
-5@@ -440,9 +440,9 @@ static int get_next_mem_node(int node) mem_node = i % (max_node +1 ); node_size =numa_node_size (mem_node,NULL );if (node_size >0 ) -break ; +return mem_node; } -return mem_node; +return -ENODEV ; }static int get_first_mem_node (void )@@ -455,8 +455,8 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo {void *numa1_map_ptr, *numa2_map_ptr;struct timespec start_time; +int first_node, second_node;int page_count =2 ; -int first_node;if (clock_gettime (CLOCK_MONOTONIC_RAW , &start_time)) {ksft_perror ("clock_gettime" );@@ -467,17 +467,19 @@ static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeoksft_print_msg ("NUMA support not enabled\n" );return KSFT_SKIP ; } -if (numa_num_configured_nodes () <=1 ) { -ksft_print_msg ("At least 2 NUMA nodes must be available\n" ); + first_node =get_first_mem_node (); + second_node =get_next_mem_node (first_node); + +if (second_node <0 ) { +ksft_print_msg ("At least 2 NUMA nodes with memory must be available\n" );return KSFT_SKIP ; }if (ksm_write_sysfs (KSM_FP ("merge_across_nodes" ), merge_across_nodes))
tools/testing/selftests/mm/ksm_tests.c static int check_ksm_numa_merge(int merge_type, int mapping, int prot, int timeo · line 485
Quoted 10 lines
tools/testing/selftests/mm/ksm_tests.c +1
-2return KSFT_FAIL ;/* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */ - first_node =get_first_mem_node (); numa1_map_ptr =numa_alloc_onnode (page_size, first_node); - numa2_map_ptr =numa_alloc_onnode (page_size,get_next_mem_node (first_node)); + numa2_map_ptr =numa_alloc_onnode (page_size, second_node);if (!numa1_map_ptr || !numa2_map_ptr) {ksft_perror ("numa_alloc_onnode" );return KSFT_FAIL ;
AI review comment:
Quoted 10 lines
If
get_first_mem_node()doesn't find a node with memory, will it return-ENODEVand pass it directly intoget_next_mem_node()? Looking atget_next_mem_node()with a negative node parameter:tools/testing/selftests/mm/ksm_tests.c:get_next_mem_node() { ... for (i = node + 1; i <= max_node + node; i++) { mem_node = i % (max_node + 1); node_size = numa_node_size(mem_node, NULL); ... }
Quoted 6 lines
Because the modulo operator preserves the sign of a negative dividend, starting the loop with a negative node value causes
mem_nodeto become negative. This means a negative node ID is then passed into libnuma'snuma_node_size()function.
Could we check iffirst_nodeis valid before attempting to find the second node?
The additional check for get_first_mem_node() is not needed. After numa_available() succeeds, a running system should always have at least one NUMA node with memory, so get_first_mem_node() is expected to return a valid node ID. The actual prerequisite for this test is the presence of two NUMA nodes with memory, which is already validated by checking the return value of get_next_mem_node().
Thanks,
Sayali
PATCH v2 3/3 selftests/mm: fix ternary operator precedence in ksm_tests
In reply to Sayali PatilThe KSM selftest uses conditional expressions to skip accesses to merge_across_nodes on systems without NUMA support. However, the ternary operator is combined with logical OR without parentheses:
a || numa_available() ? 0 : b || c
Due to operator precedence rules, this is parsed as:
(a || numa_available()) ? 0 : (b || c)
instead of the intended:
a || (numa_available() ? 0 : b) || c
Add parentheses around the conditional expressions to ensure the correct evaluation order.
--- tools/testing/selftests/mm/ksm_tests.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
tools/testing/selftests/mm/ksm_tests.c +6 -6
@@ -288,8 +288,8 @@ static bool assert_ksm_pages_count(long dupl_page_count)static int ksm_save_def (struct ksm_sysfs *ksm_sysfs) { if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) || - numa_available() ? 0 : - ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) || + (numa_available() ? 0 : + ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes)) || ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) || ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) || ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) ||
@@ -304,8 +304,8 @@ static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)static int ksm_restore (struct ksm_sysfs *ksm_sysfs) { if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) || - numa_available() ? 0 : - ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) || + (numa_available() ? 0 : + ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes)) || ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) || ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) || ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) ||
@@ -846,8 +846,8 @@ int main(int argc, char *argv[])if (ksm_write_sysfs (KSM_FP ("run" ),2 ) ||ksm_write_sysfs (KSM_FP ("sleep_millisecs" ),0 ) || -numa_available () ?0 : -ksm_write_sysfs (KSM_FP ("merge_across_nodes" ),1 ) || + (numa_available () ?0 : +ksm_write_sysfs (KSM_FP ("merge_across_nodes" ),1 )) ||ksm_write_sysfs (KSM_FP ("pages_to_scan" ), page_count))ksft_exit_fail_msg ("Cannot set up KSM tunables\n" );
Signature · 1 line
2.52.0
Re: [PATCH v2 3/3] selftests/mm: fix ternary operator precedence in ksm_tests
Quoted 12 lines · Sayali Patil
The KSM selftest uses conditional expressions to skip accesses to
merge_across_nodeson systems without NUMA support. However, the ternary operator is combined with logical OR without parentheses:a ||
numa_available()? 0 : b || cDue to operator precedence rules, this is parsed as:
(a ||
numa_available()) ? 0 : (b || c)instead of the intended:
Quoted 8 lines
a || (
numa_available()? 0 : b) || cAdd parentheses around the conditional expressions to ensure the correct evaluation order.
Fixes 9aa1af954db0 ("selftests: vm: check numa_available() before operating "merge_across_nodes" in ksm_tests")Signed-off-by Sayali Patil <[email protected]>---
LGTM, although the code is a bit ugly (already before your changes).
Signature · 3 lines
Cheers, David