Merge: selftests/mm: resolve virtual_address_range test fail on rhel-9.6

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/6781

JIRA: https://issues.redhat.com/browse/RHEL-88165

Tested: by me, using RHEL-9.6 s390x and aarch64 arches.

This is a bunch of backports to resolve virtual_address_range test fail on rhel9:

```
3bd6137220bb selftests/mm: virtual_address_range: avoid reading from VM_IO mappings
3c479b5dc60b selftests/mm: vm_util: split up /proc/self/smaps parsing
391e86971161 mm: selftest to verify zero-filled pages are mapped to zeropage
b2a79f62133a selftests/mm: virtual_address_range: unmap chunks after validation
a005145b9c96 selftests/mm: virtual_address_range: mmap() without PROT_WRITE
e847f8cd96ae selftest/mm: fix typo in virtual_address_range
86483f8b4e8d selftests: add ksft_exit_fail_perror()
```

Considering the __get_smap_entry is called by check_vmflag_io() and new ksft_exit_fail_perror() is needed by mark_range(), we have to additionally backport a few more commits (3c479b5dc60b, 391e86971161, 86483f8b4e8d) to resolve the compiling error on RHEL9.

Conflicts:
- 86483f8b4e8d: Minor merge conflict of ksft_exit_fail_perror(), no functional changes.
- 3bd6137220bb: Minor merge conflict of check_vmflag_io(), no functional changes.
- 391e86971161: Dropped updates to split_huge_page_test.c (split_pmd_zero_pages() not needed).

Omitted-fix: 136c5b40e0ad ("selftests/mm: use selftests framework to print test result")

Signed-off-by: Li Wang <liwang@redhat.com>

Approved-by: Rafael Aquini <raquini@redhat.com>
Approved-by: Waiman Long <longman@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: Augusto Caringi <acaringi@redhat.com>
This commit is contained in:
Augusto Caringi 2025-06-11 12:50:22 -03:00
commit f1570787e4
5 changed files with 131 additions and 19 deletions

View File

@ -38,6 +38,7 @@
* the program is aborting before finishing all tests):
*
* ksft_exit_fail_msg(fmt, ...);
* ksft_exit_fail_perror(msg);
*
*/
#ifndef __KSELFTEST_H
@ -302,6 +303,19 @@ static inline __printf(1, 2) int ksft_exit_fail_msg(const char *msg, ...)
exit(KSFT_FAIL);
}
static inline void ksft_exit_fail_perror(const char *msg)
{
#ifndef NOLIBC
ksft_exit_fail_msg("%s: %s (%d)\n", msg, strerror(errno), errno);
#else
/*
* nolibc doesn't provide strerror() and it seems
* inappropriate to add one, just print the errno.
*/
ksft_exit_fail_msg("%s: %d)\n", msg, errno);
#endif
}
static inline int ksft_exit_xfail(void)
{
ksft_print_cnts();

View File

@ -6,3 +6,4 @@ CONFIG_TEST_HMM=m
CONFIG_GUP_TEST=y
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_MEM_SOFT_DIRTY=y
CONFIG_ANON_VMA_NAME=y

View File

@ -10,10 +10,12 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <fcntl.h>
#include "vm_util.h"
#include "../kselftest.h"
/*
@ -64,7 +66,7 @@
#define NR_CHUNKS_HIGH NR_CHUNKS_384TB
#endif
static char *hind_addr(void)
static char *hint_addr(void)
{
int bits = HIGH_ADDR_SHIFT + rand() % (63 - HIGH_ADDR_SHIFT);
@ -82,6 +84,24 @@ static void validate_addr(char *ptr, int high_addr)
ksft_exit_fail_msg("Bad address %lx\n", addr);
}
static void mark_range(char *ptr, size_t size)
{
if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, size, "virtual_address_range") == -1) {
if (errno == EINVAL) {
/* Depends on CONFIG_ANON_VMA_NAME */
ksft_test_result_skip("prctl(PR_SET_VMA_ANON_NAME) not supported\n");
ksft_finished();
} else {
ksft_exit_fail_perror("prctl(PR_SET_VMA_ANON_NAME) failed\n");
}
}
}
static int is_marked_vma(const char *vma_name)
{
return vma_name && !strcmp(vma_name, "[anon:virtual_address_range]\n");
}
static int validate_lower_address_hint(void)
{
char *ptr;
@ -116,12 +136,17 @@ static int validate_complete_va_space(void)
prev_end_addr = 0;
while (fgets(line, sizeof(line), file)) {
const char *vma_name = NULL;
int vma_name_start = 0;
unsigned long hop;
if (sscanf(line, "%lx-%lx %s[rwxp-]",
&start_addr, &end_addr, prot) != 3)
if (sscanf(line, "%lx-%lx %4s %*s %*s %*s %n",
&start_addr, &end_addr, prot, &vma_name_start) != 3)
ksft_exit_fail_msg("cannot parse /proc/self/maps\n");
if (vma_name_start)
vma_name = line + vma_name_start;
/* end of userspace mappings; ignore vsyscall mapping */
if (start_addr & (1UL << 63))
return 0;
@ -135,6 +160,9 @@ static int validate_complete_va_space(void)
if (prot[0] != 'r')
continue;
if (check_vmflag_io((void *)start_addr))
continue;
/*
* Confirm whether MAP_CHUNK_SIZE chunk can be found or not.
* If write succeeds, no need to check MAP_CHUNK_SIZE - 1
@ -149,6 +177,9 @@ static int validate_complete_va_space(void)
return 1;
lseek(fd, 0, SEEK_SET);
if (is_marked_vma(vma_name))
munmap((char *)(start_addr + hop), MAP_CHUNK_SIZE);
hop += MAP_CHUNK_SIZE;
}
}
@ -166,7 +197,7 @@ int main(int argc, char *argv[])
ksft_set_plan(1);
for (i = 0; i < NR_CHUNKS_LOW; i++) {
ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,
ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr[i] == MAP_FAILED) {
@ -175,6 +206,7 @@ int main(int argc, char *argv[])
break;
}
mark_range(ptr[i], MAP_CHUNK_SIZE);
validate_addr(ptr[i], 0);
}
lchunks = i;
@ -185,13 +217,14 @@ int main(int argc, char *argv[])
}
for (i = 0; i < NR_CHUNKS_HIGH; i++) {
hint = hind_addr();
hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,
hint = hint_addr();
hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (hptr[i] == MAP_FAILED)
break;
mark_range(ptr[i], MAP_CHUNK_SIZE);
validate_addr(hptr[i], 1);
}
hchunks = i;

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <linux/userfaultfd.h>
#include <sys/syscall.h>
@ -11,6 +12,7 @@
#define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
#define SMAP_FILE_PATH "/proc/self/smaps"
#define STATUS_FILE_PATH "/proc/self/status"
#define MAX_LINE_LENGTH 500
unsigned int __page_size;
@ -97,13 +99,32 @@ uint64_t read_pmd_pagesize(void)
return strtoul(buf, NULL, 10);
}
bool __check_huge(void *addr, char *pattern, int nr_hpages,
uint64_t hpage_size)
unsigned long rss_anon(void)
{
uint64_t thp = -1;
int ret;
unsigned long rss_anon = 0;
FILE *fp;
char buffer[MAX_LINE_LENGTH];
fp = fopen(STATUS_FILE_PATH, "r");
if (!fp)
ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, STATUS_FILE_PATH);
if (!check_for_pattern(fp, "RssAnon:", buffer, sizeof(buffer)))
goto err_out;
if (sscanf(buffer, "RssAnon:%10lu kB", &rss_anon) != 1)
ksft_exit_fail_msg("Reading status error\n");
err_out:
fclose(fp);
return rss_anon;
}
char *__get_smap_entry(void *addr, const char *pattern, char *buf, size_t len)
{
int ret;
FILE *fp;
char *entry = NULL;
char addr_pattern[MAX_LINE_LENGTH];
ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
@ -115,23 +136,40 @@ bool __check_huge(void *addr, char *pattern, int nr_hpages,
if (!fp)
ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer)))
if (!check_for_pattern(fp, addr_pattern, buf, len))
goto err_out;
/*
* Fetch the pattern in the same block and check the number of
* hugepages.
*/
if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer)))
/* Fetch the pattern in the same block */
if (!check_for_pattern(fp, pattern, buf, len))
goto err_out;
snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern);
/* Trim trailing newline */
entry = strchr(buf, '\n');
if (entry)
*entry = '\0';
if (sscanf(buffer, addr_pattern, &thp) != 1)
ksft_exit_fail_msg("Reading smap error\n");
entry = buf + strlen(pattern);
err_out:
fclose(fp);
return entry;
}
bool __check_huge(void *addr, char *pattern, int nr_hpages,
uint64_t hpage_size)
{
char buffer[MAX_LINE_LENGTH];
uint64_t thp = -1;
char *entry;
entry = __get_smap_entry(addr, pattern, buffer, sizeof(buffer));
if (!entry)
goto err_out;
if (sscanf(entry, "%9" SCNu64 " kB", &thp) != 1)
ksft_exit_fail_msg("Reading smap error\n");
err_out:
return thp == (nr_hpages * (hpage_size >> 10));
}
@ -218,6 +256,30 @@ unsigned long get_free_hugepages(void)
return fhp;
}
bool check_vmflag_io(void *addr)
{
char buffer[MAX_LINE_LENGTH];
const char *flags;
size_t flaglen;
flags = __get_smap_entry(addr, "VmFlags:", buffer, sizeof(buffer));
if (!flags)
ksft_exit_fail_msg("%s: No VmFlags for %p\n", __func__, addr);
while (true) {
flags += strspn(flags, " ");
flaglen = strcspn(flags, " ");
if (!flaglen)
return false;
if (flaglen == strlen("io") && !memcmp(flags, "io", flaglen))
return true;
flags += flaglen;
}
}
int detect_hugetlb_page_sizes(size_t sizes[], int max)
{
DIR *dir = opendir("/sys/kernel/mm/hugepages/");

View File

@ -39,6 +39,7 @@ unsigned long pagemap_get_pfn(int fd, char *start);
void clear_softdirty(void);
bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len);
uint64_t read_pmd_pagesize(void);
unsigned long rss_anon(void);
bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size);
bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size);
bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);
@ -52,6 +53,7 @@ int uffd_register(int uffd, void *addr, uint64_t len,
int uffd_unregister(int uffd, void *addr, uint64_t len);
int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
bool miss, bool wp, bool minor, uint64_t *ioctls);
bool check_vmflag_io(void *addr);
/*
* On ppc64 this will only work with radix 2M hugepage size