This patchset uses kpageflags to get after-split folio orders for a better split_huge_page_test result check[1]. The added gather_after_split_folio_orders() scans through a VPN range and collects the numbers of folios at different orders. check_after_split_folio_orders() compares the result of gather_after_split_folio_orders() to a given list of numbers of different orders.
This patchset also added new order and in folio offset to the split huge page debugfs's pr_debug()s;
Changelog === From V3[4]: 1. Renamed {gather,check}_folio_orders() to {gather,check}_after_split_folio_orders() and moved them to split_huge_page_test.c, since both functions are not general enough for arbitrary folio order checks in any virtual address range. Comments are added to clarify the purpose of these functions. 2. Fixed file descriptor error check. 3. Removed unnecessary statements. 4. Fixed is_backed_by_folio() tail PFN off-by-one check issue. 5. Added a check in is_backed_by_folio() to detect if the order of the backing large folio is larger than the given order. 6. Moved misplaced comments in is_backed_by_folio(). 7. Added a comment and a commit message to clarify why the split range of folio_split() tests is changed.
From V2[3]: 1. Added two missing free()s in check_folio_orders(). 2. Reimplemented is_backed_by_thp() to use kpageflags to get precise folio order information and renamed it to is_backed_by_folio() in new Patch 3. 3. Renamed *_file to *_fd in Patch 2. 4. Indentation fixes. 5. Fixed vaddr stepping issue in gather_folio_orders() when a compound tail page is encountered. 6. Used pmd_order in place of max_order in split_huge_page_test.c. 7. Documented gather_folio_orders().
From V1[2]: 1. Dropped split_huge_pages_pid() for loop step change to avoid messing up with PTE-mapped THP handling. split_huge_page_test.c is changed to perform split at [addr, addr + pagesize) range to limit one folio_split() per folio. 2. Moved pr_debug changes in Patch 2 to Patch 1. 3. Moved KPF_* to vm_util.h and used PAGEMAP_PFN instead of local PFN_MASK. 4. Used pagemap_get_pfn() helper. 5. Used char *vaddr and size_t len as inputs to gather_folio_orders() and check_folio_orders() instead of vpn and nr_pages. 6. Removed variable length variables and used malloc instead.
[1] https://lore.kernel.org/linux-mm/e2f32bdb-e4a4-447c-867c-31405cbba151@redhat... [2] https://lore.kernel.org/linux-mm/20250806022045.342824-1-ziy@nvidia.com/ [3] https://lore.kernel.org/linux-mm/20250808190144.797076-1-ziy@nvidia.com/ [4] https://lore.kernel.org/linux-mm/20250812155512.926011-1-ziy@nvidia.com/
Zi Yan (5): mm/huge_memory: add new_order and offset to split_huge_pages*() pr_debug. selftests/mm: mark all functions static in split_huge_page_test.c selftests/mm: reimplement is_backed_by_thp() with more precise check selftests/mm: add check_after_split_folio_orders() helper. selftests/mm: check after-split folio orders in split_huge_page_test.
mm/huge_memory.c | 8 +- .../selftests/mm/split_huge_page_test.c | 345 +++++++++++++++--- tools/testing/selftests/mm/vm_util.c | 13 + tools/testing/selftests/mm/vm_util.h | 4 + 4 files changed, 311 insertions(+), 59 deletions(-)
They are useful information for debugging split huge page tests.
Signed-off-by: Zi Yan ziy@nvidia.com Reviewed-by: Wei Yang richard.weiyang@gmail.com Reviewed-by: Donet Tom donettom@linux.ibm.com Reviewed-by: wang lian lianux.mm@gmail.com Reviewed-by: Baolin Wang baolin.wang@linux.alibaba.com Reviewed-by: Barry Song baohua@kernel.org Acked-by: David Hildenbrand david@redhat.com --- mm/huge_memory.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c index a2f476e7419a..6df1ed0cef5c 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4319,8 +4319,8 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start, goto out; }
- pr_debug("Split huge pages in pid: %d, vaddr: [0x%lx - 0x%lx]\n", - pid, vaddr_start, vaddr_end); + pr_debug("Split huge pages in pid: %d, vaddr: [0x%lx - 0x%lx], new_order: %u, in_folio_offset: %ld\n", + pid, vaddr_start, vaddr_end, new_order, in_folio_offset);
mmap_read_lock(mm); /* @@ -4430,8 +4430,8 @@ static int split_huge_pages_in_file(const char *file_path, pgoff_t off_start, if (IS_ERR(candidate)) goto out;
- pr_debug("split file-backed THPs in file: %s, page offset: [0x%lx - 0x%lx]\n", - file_path, off_start, off_end); + pr_debug("split file-backed THPs in file: %s, page offset: [0x%lx - 0x%lx], new_order: %u, in_folio_offset: %ld\n", + file_path, off_start, off_end, new_order, in_folio_offset);
mapping = candidate->f_mapping; min_order = mapping_min_folio_order(mapping);
All functions are only used within the file.
Signed-off-by: Zi Yan ziy@nvidia.com --- .../selftests/mm/split_huge_page_test.c | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 5d07b0b89226..89d3dc08fe4c 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -38,7 +38,7 @@ uint64_t pmd_pagesize; #define KPF_THP (1UL<<22) #define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages))
-int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) +static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) { uint64_t paddr; uint64_t page_flags; @@ -136,7 +136,7 @@ static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int nr_hp rss_anon_before, rss_anon_after); }
-void split_pmd_zero_pages(void) +static void split_pmd_zero_pages(void) { char *one_page; int nr_hpages = 4; @@ -148,7 +148,7 @@ void split_pmd_zero_pages(void) free(one_page); }
-void split_pmd_thp_to_order(int order) +static void split_pmd_thp_to_order(int order) { char *one_page; size_t len = 4 * pmd_pagesize; @@ -182,7 +182,7 @@ void split_pmd_thp_to_order(int order) free(one_page); }
-void split_pte_mapped_thp(void) +static void split_pte_mapped_thp(void) { char *one_page, *pte_mapped, *pte_mapped2; size_t len = 4 * pmd_pagesize; @@ -265,7 +265,7 @@ void split_pte_mapped_thp(void) close(kpageflags_fd); }
-void split_file_backed_thp(int order) +static void split_file_backed_thp(int order) { int status; int fd; @@ -365,7 +365,7 @@ void split_file_backed_thp(int order) ksft_exit_fail_msg("Error occurred\n"); }
-bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template, +static bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template, const char **thp_fs_loc) { if (xfs_path) { @@ -381,7 +381,7 @@ bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template, return true; }
-void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp) +static void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp) { int status;
@@ -394,8 +394,8 @@ void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp) strerror(errno)); }
-int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd, - char **addr) +static int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, + int *fd, char **addr) { size_t i; unsigned char buf[1024]; @@ -461,8 +461,8 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd, return -1; }
-void split_thp_in_pagecache_to_order_at(size_t fd_size, const char *fs_loc, - int order, int offset) +static void split_thp_in_pagecache_to_order_at(size_t fd_size, + const char *fs_loc, int order, int offset) { int fd; char *addr;
On Thu, Aug 14, 2025 at 10:39:11PM -0400, Zi Yan wrote:
All functions are only used within the file.
Signed-off-by: Zi Yan ziy@nvidia.com
Reviewed-by: Wei Yang richard.weiyang@gmail.com
On Aug 15, 2025, at 10:39, Zi Yan ziy@nvidia.com wrote:
All functions are only used within the file.
Signed-off-by: Zi Yan ziy@nvidia.com
.../selftests/mm/split_huge_page_test.c | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 5d07b0b89226..89d3dc08fe4c 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -38,7 +38,7 @@ uint64_t pmd_pagesize; #define KPF_THP (1UL<<22) #define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages))
-int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) +static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) { uint64_t paddr; uint64_t page_flags; @@ -136,7 +136,7 @@ static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int nr_hp rss_anon_before, rss_anon_after); }
-void split_pmd_zero_pages(void) +static void split_pmd_zero_pages(void) { char *one_page; int nr_hpages = 4; @@ -148,7 +148,7 @@ void split_pmd_zero_pages(void) free(one_page); }
-void split_pmd_thp_to_order(int order) +static void split_pmd_thp_to_order(int order) { char *one_page; size_t len = 4 * pmd_pagesize; @@ -182,7 +182,7 @@ void split_pmd_thp_to_order(int order) free(one_page); }
-void split_pte_mapped_thp(void) +static void split_pte_mapped_thp(void) { char *one_page, *pte_mapped, *pte_mapped2; size_t len = 4 * pmd_pagesize; @@ -265,7 +265,7 @@ void split_pte_mapped_thp(void) close(kpageflags_fd); }
-void split_file_backed_thp(int order) +static void split_file_backed_thp(int order) { int status; int fd; @@ -365,7 +365,7 @@ void split_file_backed_thp(int order) ksft_exit_fail_msg("Error occurred\n"); }
-bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template, +static bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template, const char **thp_fs_loc) { if (xfs_path) { @@ -381,7 +381,7 @@ bool prepare_thp_fs(const char *xfs_path, char *thp_fs_template, return true; }
-void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp) +static void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp) { int status;
@@ -394,8 +394,8 @@ void cleanup_thp_fs(const char *thp_fs_loc, bool created_tmp) strerror(errno)); }
-int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd,
char **addr)
+static int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size,
int *fd, char **addr)
{ size_t i; unsigned char buf[1024]; @@ -461,8 +461,8 @@ int create_pagecache_thp_and_fd(const char *testfile, size_t fd_size, int *fd, return -1; }
-void split_thp_in_pagecache_to_order_at(size_t fd_size, const char *fs_loc,
int order, int offset)
+static void split_thp_in_pagecache_to_order_at(size_t fd_size,
const char *fs_loc, int order, int offset)
{ int fd; char *addr; -- 2.50.1
LGTM. Reviewed-by: wang lian lianux.mm@gmail.com
On 15.08.25 04:39, Zi Yan wrote:
All functions are only used within the file.
Signed-off-by: Zi Yan ziy@nvidia.com
Acked-by: David Hildenbrand david@redhat.com
and rename it to is_backed_by_folio().
is_backed_by_folio() checks if the given vaddr is backed a folio with a given order. It does so by: 1. getting the pfn of the vaddr; 2. checking kpageflags of the pfn;
if order is greater than 0: 3. checking kpageflags of the head pfn; 4. checking kpageflags of all tail pfns.
pmd_order is added to split_huge_page_test.c and replaces max_order.
Signed-off-by: Zi Yan ziy@nvidia.com --- .../selftests/mm/split_huge_page_test.c | 90 ++++++++++++++----- tools/testing/selftests/mm/vm_util.c | 13 +++ tools/testing/selftests/mm/vm_util.h | 4 + 3 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 89d3dc08fe4c..80f718ca21c7 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -25,6 +25,7 @@ uint64_t pagesize; unsigned int pageshift; uint64_t pmd_pagesize; +unsigned int pmd_order;
#define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" #define SMAP_PATH "/proc/self/smaps" @@ -34,27 +35,71 @@ uint64_t pmd_pagesize; #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d" #define PATH_FMT "%s,0x%lx,0x%lx,%d"
-#define PFN_MASK ((1UL<<55)-1) -#define KPF_THP (1UL<<22) #define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages))
-static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd, + int kpageflags_fd) { - uint64_t paddr; - uint64_t page_flags; + unsigned long pfn_head; + uint64_t pfn_flags; + unsigned long pfn; + unsigned long i;
- if (pagemap_file) { - pread(pagemap_file, &paddr, sizeof(paddr), - ((long)vaddr >> pageshift) * sizeof(paddr)); + if (pagemap_fd == -1 || kpageflags_fd == -1) + goto fail;
- if (kpageflags_file) { - pread(kpageflags_file, &page_flags, sizeof(page_flags), - (paddr & PFN_MASK) * sizeof(page_flags)); + pfn = pagemap_get_pfn(pagemap_fd, vaddr);
- return !!(page_flags & KPF_THP); - } + /* non present page */ + if (pfn == -1UL) + return 0; + + if (get_pfn_flags(pfn, kpageflags_fd, &pfn_flags)) + goto fail; + + /* check for order-0 pages */ + if (!order) { + if (pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL)) + return 0; + return 1; } - return 0; + + /* non THP folio */ + if (!(pfn_flags & KPF_THP)) + return 0; + + pfn_head = pfn & ~((1 << order) - 1); + + if (get_pfn_flags(pfn_head, kpageflags_fd, &pfn_flags)) + goto fail; + + /* head PFN has no compound_head flag set */ + if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD))) + return 0; + + /* check all tail PFN flags */ + for (i = 1; i < 1UL << order; i++) { + if (get_pfn_flags(pfn_head + i, kpageflags_fd, &pfn_flags)) + goto fail; + if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL))) + return 0; + } + + /* + * check the PFN after this folio, but if its flags cannot be obtained, + * assume this folio has the expected order + */ + if (get_pfn_flags(pfn_head + (1UL << order), kpageflags_fd, &pfn_flags)) + return 1; + + /* this folio is bigger than the given order */ + if (pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL)) + return 0; + + return 1; +fail: + ksft_exit_fail_msg("Failed to get folio info\n"); + return -1; }
static void write_file(const char *path, const char *buf, size_t buflen) @@ -235,7 +280,7 @@ static void split_pte_mapped_thp(void) thp_size = 0; for (i = 0; i < pagesize * 4; i++) if (i % pagesize == 0 && - is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd)) + is_backed_by_folio(&pte_mapped[i], pmd_order, pagemap_fd, kpageflags_fd) == 1) thp_size++;
if (thp_size != 4) @@ -252,7 +297,7 @@ static void split_pte_mapped_thp(void) ksft_exit_fail_msg("%ld byte corrupted\n", i);
if (i % pagesize == 0 && - is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd)) + is_backed_by_folio(&pte_mapped[i], 0, pagemap_fd, kpageflags_fd) == 0) thp_size++; }
@@ -524,7 +569,6 @@ int main(int argc, char **argv) const char *fs_loc; bool created_tmp; int offset; - unsigned int max_order; unsigned int nr_pages; unsigned int tests;
@@ -545,28 +589,28 @@ int main(int argc, char **argv) ksft_exit_fail_msg("Reading PMD pagesize failed\n");
nr_pages = pmd_pagesize / pagesize; - max_order = GET_ORDER(nr_pages); - tests = 2 + (max_order - 1) + (2 * max_order) + (max_order - 1) * 4 + 2; + pmd_order = GET_ORDER(nr_pages); + tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2; ksft_set_plan(tests);
fd_size = 2 * pmd_pagesize;
split_pmd_zero_pages();
- for (i = 0; i < max_order; i++) + for (i = 0; i < pmd_order; i++) if (i != 1) split_pmd_thp_to_order(i);
split_pte_mapped_thp(); - for (i = 0; i < max_order; i++) + for (i = 0; i < pmd_order; i++) split_file_backed_thp(i);
created_tmp = prepare_thp_fs(optional_xfs_path, fs_loc_template, &fs_loc); - for (i = max_order - 1; i >= 0; i--) + for (i = pmd_order - 1; i >= 0; i--) split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, -1);
- for (i = 0; i < max_order; i++) + for (i = 0; i < pmd_order; i++) for (offset = 0; offset < nr_pages; offset += MAX(nr_pages / 4, 1 << i)) diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 6a239aa413e2..18b7cb51fc56 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -338,6 +338,19 @@ int detect_hugetlb_page_sizes(size_t sizes[], int max) return count; }
+int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags) +{ + size_t count; + + count = pread(kpageflags_fd, flags, sizeof(*flags), + pfn * sizeof(*flags)); + + if (count != sizeof(*flags)) + return -1; + + return 0; +} + /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */ int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor, uint64_t *ioctls) diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index 1843ad48d32b..03481ca0a1b4 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -18,6 +18,9 @@ #define PM_SWAP BIT_ULL(62) #define PM_PRESENT BIT_ULL(63)
+#define KPF_COMPOUND_HEAD BIT_ULL(15) +#define KPF_COMPOUND_TAIL BIT_ULL(16) +#define KPF_THP BIT_ULL(22) /* * Ignore the checkpatch warning, we must read from x but don't want to do * anything with it in order to trigger a read page fault. We therefore must use @@ -85,6 +88,7 @@ bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size); int64_t allocate_transhuge(void *ptr, int pagemap_fd); unsigned long default_huge_page_size(void); int detect_hugetlb_page_sizes(size_t sizes[], int max); +int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
int uffd_register(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor);
On Thu, Aug 14, 2025 at 10:39:12PM -0400, Zi Yan wrote:
and rename it to is_backed_by_folio().
is_backed_by_folio() checks if the given vaddr is backed a folio with a given order. It does so by:
- getting the pfn of the vaddr;
- checking kpageflags of the pfn;
if order is greater than 0: 3. checking kpageflags of the head pfn; 4. checking kpageflags of all tail pfns.
pmd_order is added to split_huge_page_test.c and replaces max_order.
Signed-off-by: Zi Yan ziy@nvidia.com
Reviewed-by: Wei Yang richard.weiyang@gmail.com
On Aug 15, 2025, at 10:39, Zi Yan ziy@nvidia.com wrote:
and rename it to is_backed_by_folio().
is_backed_by_folio() checks if the given vaddr is backed a folio with a given order. It does so by:
- getting the pfn of the vaddr;
- checking kpageflags of the pfn;
if order is greater than 0: 3. checking kpageflags of the head pfn; 4. checking kpageflags of all tail pfns.
pmd_order is added to split_huge_page_test.c and replaces max_order.
Signed-off-by: Zi Yan ziy@nvidia.com
.../selftests/mm/split_huge_page_test.c | 90 ++++++++++++++----- tools/testing/selftests/mm/vm_util.c | 13 +++ tools/testing/selftests/mm/vm_util.h | 4 + 3 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 89d3dc08fe4c..80f718ca21c7 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -25,6 +25,7 @@ uint64_t pagesize; unsigned int pageshift; uint64_t pmd_pagesize; +unsigned int pmd_order;
#define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" #define SMAP_PATH "/proc/self/smaps" @@ -34,27 +35,71 @@ uint64_t pmd_pagesize; #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d" #define PATH_FMT "%s,0x%lx,0x%lx,%d"
-#define PFN_MASK ((1UL<<55)-1) -#define KPF_THP (1UL<<22) #define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages))
-static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
int kpageflags_fd)
{
- uint64_t paddr;
- uint64_t page_flags;
- unsigned long pfn_head;
- uint64_t pfn_flags;
- unsigned long pfn;
- unsigned long i;
- if (pagemap_file) {
pread(pagemap_file, &paddr, sizeof(paddr),
((long)vaddr >> pageshift) * sizeof(paddr));
- if (pagemap_fd == -1 || kpageflags_fd == -1)
goto fail;
if (kpageflags_file) {
pread(kpageflags_file, &page_flags, sizeof(page_flags),
(paddr & PFN_MASK) * sizeof(page_flags));
- pfn = pagemap_get_pfn(pagemap_fd, vaddr);
return !!(page_flags & KPF_THP);
}
- /* non present page */
- if (pfn == -1UL)
return 0;
- if (get_pfn_flags(pfn, kpageflags_fd, &pfn_flags))
goto fail;
- /* check for order-0 pages */
- if (!order) {
if (pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))
return 0;
}return 1;
- return 0;
- /* non THP folio */
- if (!(pfn_flags & KPF_THP))
return 0;
- pfn_head = pfn & ~((1 << order) - 1);
- if (get_pfn_flags(pfn_head, kpageflags_fd, &pfn_flags))
goto fail;
- /* head PFN has no compound_head flag set */
- if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_HEAD)))
return 0;
- /* check all tail PFN flags */
- for (i = 1; i < 1UL << order; i++) {
if (get_pfn_flags(pfn_head + i, kpageflags_fd, &pfn_flags))
goto fail;
if (!(pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL)))
return 0;
- }
- /*
* check the PFN after this folio, but if its flags cannot be obtained,
* assume this folio has the expected order
*/
- if (get_pfn_flags(pfn_head + (1UL << order), kpageflags_fd, &pfn_flags))
return 1;
- /* this folio is bigger than the given order */
- if (pfn_flags & (KPF_THP | KPF_COMPOUND_TAIL))
return 0;
- return 1;
+fail:
- ksft_exit_fail_msg("Failed to get folio info\n");
- return -1;
}
static void write_file(const char *path, const char *buf, size_t buflen) @@ -235,7 +280,7 @@ static void split_pte_mapped_thp(void) thp_size = 0; for (i = 0; i < pagesize * 4; i++) if (i % pagesize == 0 &&
is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
is_backed_by_folio(&pte_mapped[i], pmd_order, pagemap_fd, kpageflags_fd) == 1) thp_size++;
if (thp_size != 4)
@@ -252,7 +297,7 @@ static void split_pte_mapped_thp(void) ksft_exit_fail_msg("%ld byte corrupted\n", i);
if (i % pagesize == 0 &&
is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
}is_backed_by_folio(&pte_mapped[i], 0, pagemap_fd, kpageflags_fd) == 0) thp_size++;
@@ -524,7 +569,6 @@ int main(int argc, char **argv) const char *fs_loc; bool created_tmp; int offset;
- unsigned int max_order; unsigned int nr_pages; unsigned int tests;
@@ -545,28 +589,28 @@ int main(int argc, char **argv) ksft_exit_fail_msg("Reading PMD pagesize failed\n");
nr_pages = pmd_pagesize / pagesize;
- max_order = GET_ORDER(nr_pages);
- tests = 2 + (max_order - 1) + (2 * max_order) + (max_order - 1) * 4 + 2;
pmd_order = GET_ORDER(nr_pages);
tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2; ksft_set_plan(tests);
fd_size = 2 * pmd_pagesize;
split_pmd_zero_pages();
- for (i = 0; i < max_order; i++)
for (i = 0; i < pmd_order; i++) if (i != 1) split_pmd_thp_to_order(i);
split_pte_mapped_thp();
- for (i = 0; i < max_order; i++)
for (i = 0; i < pmd_order; i++) split_file_backed_thp(i);
created_tmp = prepare_thp_fs(optional_xfs_path, fs_loc_template, &fs_loc);
- for (i = max_order - 1; i >= 0; i--)
- for (i = pmd_order - 1; i >= 0; i--) split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, -1);
- for (i = 0; i < max_order; i++)
- for (i = 0; i < pmd_order; i++) for (offset = 0; offset < nr_pages; offset += MAX(nr_pages / 4, 1 << i))
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c index 6a239aa413e2..18b7cb51fc56 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -338,6 +338,19 @@ int detect_hugetlb_page_sizes(size_t sizes[], int max) return count; }
+int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags) +{
- size_t count;
- count = pread(kpageflags_fd, flags, sizeof(*flags),
pfn * sizeof(*flags));
- if (count != sizeof(*flags))
return -1;
- return 0;
+}
/* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */ int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor, uint64_t *ioctls) diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h index 1843ad48d32b..03481ca0a1b4 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -18,6 +18,9 @@ #define PM_SWAP BIT_ULL(62) #define PM_PRESENT BIT_ULL(63)
+#define KPF_COMPOUND_HEAD BIT_ULL(15) +#define KPF_COMPOUND_TAIL BIT_ULL(16) +#define KPF_THP BIT_ULL(22) /*
- Ignore the checkpatch warning, we must read from x but don't want to do
- anything with it in order to trigger a read page fault. We therefore must use
@@ -85,6 +88,7 @@ bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size); int64_t allocate_transhuge(void *ptr, int pagemap_fd); unsigned long default_huge_page_size(void); int detect_hugetlb_page_sizes(size_t sizes[], int max); +int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags);
int uffd_register(int uffd, void *addr, uint64_t len, bool miss, bool wp, bool minor); -- 2.50.1
LGTM. Reviewed-by: wang lian lianux.mm@gmail.com
Best regards, wang lian
I hit a large reject. Can you please redo against next mm-new?
Thanks.
On 15.08.25 04:39, Zi Yan wrote:
and rename it to is_backed_by_folio().
is_backed_by_folio() checks if the given vaddr is backed a folio with a given order. It does so by:
- getting the pfn of the vaddr;
- checking kpageflags of the pfn;
if order is greater than 0: 3. checking kpageflags of the head pfn; 4. checking kpageflags of all tail pfns.
pmd_order is added to split_huge_page_test.c and replaces max_order.
Signed-off-by: Zi Yan ziy@nvidia.com
.../selftests/mm/split_huge_page_test.c | 90 ++++++++++++++----- tools/testing/selftests/mm/vm_util.c | 13 +++ tools/testing/selftests/mm/vm_util.h | 4 + 3 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 89d3dc08fe4c..80f718ca21c7 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -25,6 +25,7 @@ uint64_t pagesize; unsigned int pageshift; uint64_t pmd_pagesize; +unsigned int pmd_order; #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" #define SMAP_PATH "/proc/self/smaps" @@ -34,27 +35,71 @@ uint64_t pmd_pagesize; #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d" #define PATH_FMT "%s,0x%lx,0x%lx,%d" -#define PFN_MASK ((1UL<<55)-1) -#define KPF_THP (1UL<<22) #define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages)) -static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
int kpageflags_fd)
Could we convert this into a bool and simply return "false" on error instead of "-"? These tristate returns for a "is_*" function are a bit unfortunate.
{
- uint64_t paddr;
- uint64_t page_flags;
- unsigned long pfn_head;
- uint64_t pfn_flags;
- unsigned long pfn;
- unsigned long i;
- if (pagemap_file) {
pread(pagemap_file, &paddr, sizeof(paddr),
((long)vaddr >> pageshift) * sizeof(paddr));
- if (pagemap_fd == -1 || kpageflags_fd == -1)
goto fail;
Should we rather expect that callers make sure these are valid? In particular, because split_pte_mapped_thp() seems to ksft_exit_fail_msg() already.
if (kpageflags_file) {
pread(kpageflags_file, &page_flags, sizeof(page_flags),
(paddr & PFN_MASK) * sizeof(page_flags));
- pfn = pagemap_get_pfn(pagemap_fd, vaddr);
Hm, if it's swapped out we would get intermittent errors, but that just seems hard to avoid. The caller could mock to avoid swapout.
Memory migration is another possible problem ...
But this is nothing new regarding your patch, so no need to worry for now.
[...]
+int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags) +{
- size_t count;
- count = pread(kpageflags_fd, flags, sizeof(*flags),
pfn * sizeof(*flags));
- if (count != sizeof(*flags))
return -1;
- return 0;
+}
I would have called this function "pageflags_get()" to resemble "pagemap_get"
On 18 Aug 2025, at 4:33, David Hildenbrand wrote:
On 15.08.25 04:39, Zi Yan wrote:
and rename it to is_backed_by_folio().
is_backed_by_folio() checks if the given vaddr is backed a folio with a given order. It does so by:
- getting the pfn of the vaddr;
- checking kpageflags of the pfn;
if order is greater than 0: 3. checking kpageflags of the head pfn; 4. checking kpageflags of all tail pfns.
pmd_order is added to split_huge_page_test.c and replaces max_order.
Signed-off-by: Zi Yan ziy@nvidia.com
.../selftests/mm/split_huge_page_test.c | 90 ++++++++++++++----- tools/testing/selftests/mm/vm_util.c | 13 +++ tools/testing/selftests/mm/vm_util.h | 4 + 3 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 89d3dc08fe4c..80f718ca21c7 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -25,6 +25,7 @@ uint64_t pagesize; unsigned int pageshift; uint64_t pmd_pagesize; +unsigned int pmd_order; #define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" #define SMAP_PATH "/proc/self/smaps" @@ -34,27 +35,71 @@ uint64_t pmd_pagesize; #define PID_FMT_OFFSET "%d,0x%lx,0x%lx,%d,%d" #define PATH_FMT "%s,0x%lx,0x%lx,%d" -#define PFN_MASK ((1UL<<55)-1) -#define KPF_THP (1UL<<22) #define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages)) -static int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) +static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd,
int kpageflags_fd)
Could we convert this into a bool and simply return "false" on error instead of "-"? These tristate returns for a "is_*" function are a bit unfortunate.
OK.
{
- uint64_t paddr;
- uint64_t page_flags;
- unsigned long pfn_head;
- uint64_t pfn_flags;
- unsigned long pfn;
- unsigned long i;
- if (pagemap_file) {
pread(pagemap_file, &paddr, sizeof(paddr),
((long)vaddr >> pageshift) * sizeof(paddr));
- if (pagemap_fd == -1 || kpageflags_fd == -1)
goto fail;
Should we rather expect that callers make sure these are valid? In particular, because split_pte_mapped_thp() seems to ksft_exit_fail_msg() already.
Sure.
if (kpageflags_file) {
pread(kpageflags_file, &page_flags, sizeof(page_flags),
(paddr & PFN_MASK) * sizeof(page_flags));
- pfn = pagemap_get_pfn(pagemap_fd, vaddr);
Hm, if it's swapped out we would get intermittent errors, but that just seems hard to avoid. The caller could mock to avoid swapout.
Memory migration is another possible problem ...
But this is nothing new regarding your patch, so no need to worry for now.
Right. The function is only used by PTE-mapped THP split and I assume swapping and migration would not happen. If this function is used more broadly, it will need to take care of cases.
[...]
+int get_pfn_flags(unsigned long pfn, int kpageflags_fd, uint64_t *flags) +{
- size_t count;
- count = pread(kpageflags_fd, flags, sizeof(*flags),
pfn * sizeof(*flags));
- if (count != sizeof(*flags))
return -1;
- return 0;
+}
I would have called this function "pageflags_get()" to resemble "pagemap_get"
OK. Will rename it.
Thanks.
-- Best Regards, Yan, Zi
The helper gathers a folio order statistics of folios within a virtual address range and checks it against a given order list. It aims to provide a more precise folio order check instead of just checking the existence of PMD folios.
The helper will be used the upcoming commit.
Signed-off-by: Zi Yan ziy@nvidia.com --- .../selftests/mm/split_huge_page_test.c | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index 80f718ca21c7..d2467072e752 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -102,6 +102,156 @@ static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd, return -1; }
+static int get_page_flags(char *vaddr, int pagemap_fd, int kpageflags_fd, + uint64_t *flags) +{ + unsigned long pfn; + + pfn = pagemap_get_pfn(pagemap_fd, vaddr); + + /* non-present PFN */ + if (pfn == -1UL) + return 1; + + if (get_pfn_flags(pfn, kpageflags_fd, flags)) + return -1; + + return 0; +} + +/* + * gather_after_split_folio_orders - scan through [vaddr_start, len) and record + * folio orders + * + * @vaddr_start: start vaddr + * @len: range length + * @pagemap_fd: file descriptor to /proc/<pid>/pagemap + * @kpageflags_fd: file descriptor to /proc/kpageflags + * @orders: output folio order array + * @nr_orders: folio order array size + * + * gather_after_split_folio_orders() scan through [vaddr_start, len) and check + * all folios within the range and record their orders. All order-0 pages will + * be recorded. Non-present vaddr is skipped. + * + * NOTE: the function is used to check folio orders after a split is performed, + * so it assumes [vaddr_start, len) fully maps to folios within that range. + * + * Return: 0 - no error, -1 - unhandled cases + */ +static int gather_after_split_folio_orders(char *vaddr_start, size_t len, + int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders) +{ + uint64_t page_flags = 0; + int cur_order = -1; + char *vaddr; + + if (pagemap_fd == -1 || kpageflags_fd == -1) + return -1; + if (!orders) + return -1; + if (nr_orders <= 0) + return -1; + + for (vaddr = vaddr_start; vaddr < vaddr_start + len;) { + char *next_folio_vaddr; + int status; + + status = get_page_flags(vaddr, pagemap_fd, kpageflags_fd, + &page_flags); + if (status < 0) + return -1; + + /* skip non present vaddr */ + if (status == 1) { + vaddr += psize(); + continue; + } + + /* all order-0 pages with possible false postive (non folio) */ + if (!(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) { + orders[0]++; + vaddr += psize(); + continue; + } + + /* skip non thp compound pages */ + if (!(page_flags & KPF_THP)) { + vaddr += psize(); + continue; + } + + /* vpn points to part of a THP at this point */ + if (page_flags & KPF_COMPOUND_HEAD) + cur_order = 1; + else { + vaddr += psize(); + continue; + } + + next_folio_vaddr = vaddr + (1UL << (cur_order + pshift())); + + if (next_folio_vaddr >= vaddr_start + len) + break; + + while ((status = get_page_flags(next_folio_vaddr, pagemap_fd, + kpageflags_fd, &page_flags)) >= 0) { + /* + * non present vaddr, next compound head page, or + * order-0 page + */ + if (status == 1 || + (page_flags & KPF_COMPOUND_HEAD) || + !(page_flags & (KPF_COMPOUND_HEAD | KPF_COMPOUND_TAIL))) { + if (cur_order < nr_orders) { + orders[cur_order]++; + cur_order = -1; + vaddr = next_folio_vaddr; + } + break; + } + + cur_order++; + next_folio_vaddr = vaddr + (1UL << (cur_order + pshift())); + } + + if (status < 0) + return status; + } + if (cur_order > 0 && cur_order < nr_orders) + orders[cur_order]++; + return 0; +} + +static int check_after_split_folio_orders(char *vaddr_start, size_t len, + int pagemap_fd, int kpageflags_fd, int orders[], int nr_orders) +{ + int *vaddr_orders; + int status; + int i; + + vaddr_orders = (int *)malloc(sizeof(int) * nr_orders); + + if (!vaddr_orders) + ksft_exit_fail_msg("Cannot allocate memory for vaddr_orders"); + + memset(vaddr_orders, 0, sizeof(int) * nr_orders); + status = gather_after_split_folio_orders(vaddr_start, len, pagemap_fd, + kpageflags_fd, vaddr_orders, nr_orders); + if (status) + ksft_exit_fail_msg("gather folio info failed\n"); + + for (i = 0; i < nr_orders; i++) + if (vaddr_orders[i] != orders[i]) { + ksft_print_msg("order %d: expected: %d got %d\n", i, + orders[i], vaddr_orders[i]); + status = -1; + } + + free(vaddr_orders); + return status; +} + static void write_file(const char *path, const char *buf, size_t buflen) { int fd;
Instead of just checking the existence of PMD folios before and after folio split tests, use check_folio_orders() to check after-split folio orders.
The split ranges in split_thp_in_pagecache_to_order_at() are changed to [addr, addr + pagesize) for every pmd_pagesize. It prevents folios within the range being split multiple times due to debugfs split function always perform splits with a pagesize step for a given range.
The following tests are not changed: 1. split_pte_mapped_thp: the test already uses kpageflags to check; 2. split_file_backed_thp: no vaddr available.
Signed-off-by: Zi Yan ziy@nvidia.com --- .../selftests/mm/split_huge_page_test.c | 91 ++++++++++++++----- 1 file changed, 66 insertions(+), 25 deletions(-)
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c index d2467072e752..7d898c380236 100644 --- a/tools/testing/selftests/mm/split_huge_page_test.c +++ b/tools/testing/selftests/mm/split_huge_page_test.c @@ -26,6 +26,7 @@ uint64_t pagesize; unsigned int pageshift; uint64_t pmd_pagesize; unsigned int pmd_order; +int *expected_orders;
#define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" #define SMAP_PATH "/proc/self/smaps" @@ -37,6 +38,11 @@ unsigned int pmd_order;
#define GET_ORDER(nr_pages) (31 - __builtin_clz(nr_pages))
+const char *pagemap_proc = "/proc/self/pagemap"; +const char *kpageflags_proc = "/proc/kpageflags"; +int pagemap_fd; +int kpageflags_fd; + static int is_backed_by_folio(char *vaddr, int order, int pagemap_fd, int kpageflags_fd) { @@ -135,7 +141,8 @@ static int get_page_flags(char *vaddr, int pagemap_fd, int kpageflags_fd, * be recorded. Non-present vaddr is skipped. * * NOTE: the function is used to check folio orders after a split is performed, - * so it assumes [vaddr_start, len) fully maps to folios within that range. + * so it assumes [vaddr_start, len) fully maps to after-split folios within that + * range. * * Return: 0 - no error, -1 - unhandled cases */ @@ -369,6 +376,13 @@ static void split_pmd_thp_to_order(int order) if (one_page[i] != (char)i) ksft_exit_fail_msg("%ld byte corrupted\n", i);
+ memset(expected_orders, 0, sizeof(int) * (pmd_order + 1)); + expected_orders[order] = 4 << (pmd_order - order); + + if (check_after_split_folio_orders(one_page, len, pagemap_fd, + kpageflags_fd, expected_orders, + (pmd_order + 1))) + ksft_exit_fail_msg("Unexpected THP split\n");
if (!check_huge_anon(one_page, 0, pmd_pagesize)) ksft_exit_fail_msg("Still AnonHugePages not split\n"); @@ -383,22 +397,6 @@ static void split_pte_mapped_thp(void) size_t len = 4 * pmd_pagesize; uint64_t thp_size; size_t i; - const char *pagemap_template = "/proc/%d/pagemap"; - const char *kpageflags_proc = "/proc/kpageflags"; - char pagemap_proc[255]; - int pagemap_fd; - int kpageflags_fd; - - if (snprintf(pagemap_proc, 255, pagemap_template, getpid()) < 0) - ksft_exit_fail_msg("get pagemap proc error: %s\n", strerror(errno)); - - pagemap_fd = open(pagemap_proc, O_RDONLY); - if (pagemap_fd == -1) - ksft_exit_fail_msg("read pagemap: %s\n", strerror(errno)); - - kpageflags_fd = open(kpageflags_proc, O_RDONLY); - if (kpageflags_fd == -1) - ksft_exit_fail_msg("read kpageflags: %s\n", strerror(errno));
one_page = mmap((void *)(1UL << 30), len, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); @@ -456,8 +454,6 @@ static void split_pte_mapped_thp(void)
ksft_test_result_pass("Split PTE-mapped huge pages successful\n"); munmap(one_page, len); - close(pagemap_fd); - close(kpageflags_fd); }
static void split_file_backed_thp(int order) @@ -660,6 +656,7 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, const char *fs_loc, int order, int offset) { int fd; + char *split_addr; char *addr; size_t i; char testfile[INPUT_MAX]; @@ -673,14 +670,33 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, err = create_pagecache_thp_and_fd(testfile, fd_size, &fd, &addr); if (err) return; + err = 0;
- if (offset == -1) - write_debugfs(PID_FMT, getpid(), (uint64_t)addr, - (uint64_t)addr + fd_size, order); - else - write_debugfs(PID_FMT_OFFSET, getpid(), (uint64_t)addr, - (uint64_t)addr + fd_size, order, offset); + memset(expected_orders, 0, sizeof(int) * (pmd_order + 1)); + /* + * use [split_addr, split_addr + pagesize) range to split THPs, since + * the debugfs function always split a range with pagesize step and + * providing a full [addr, addr + fd_size) range can trigger multiple + * splits, complicating after-split result checking. + */ + if (offset == -1) { + for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize) + write_debugfs(PID_FMT, getpid(), (uint64_t)split_addr, + (uint64_t)split_addr + pagesize, order); + + expected_orders[order] = fd_size / (pagesize << order); + } else { + int times = fd_size / pmd_pagesize; + + for (split_addr = addr; split_addr < addr + fd_size; split_addr += pmd_pagesize) + write_debugfs(PID_FMT_OFFSET, getpid(), (uint64_t)split_addr, + (uint64_t)split_addr + pagesize, order, offset); + + for (i = order + 1; i < pmd_order; i++) + expected_orders[i] = times; + expected_orders[order] = 2 * times; + }
for (i = 0; i < fd_size; i++) if (*(addr + i) != (char)i) { @@ -689,6 +705,14 @@ static void split_thp_in_pagecache_to_order_at(size_t fd_size, goto out; }
+ if (check_after_split_folio_orders(addr, fd_size, pagemap_fd, + kpageflags_fd, expected_orders, + (pmd_order + 1))) { + ksft_print_msg("Unexpected THP split\n"); + err = 1; + goto out; + } + if (!check_huge_file(addr, 0, pmd_pagesize)) { ksft_print_msg("Still FilePmdMapped not split\n"); err = EXIT_FAILURE; @@ -740,9 +764,22 @@ int main(int argc, char **argv)
nr_pages = pmd_pagesize / pagesize; pmd_order = GET_ORDER(nr_pages); + + expected_orders = (int *)malloc(sizeof(int) * (pmd_order + 1)); + if (!expected_orders) + ksft_exit_fail_msg("Fail to allocate memory: %s\n", strerror(errno)); + tests = 2 + (pmd_order - 1) + (2 * pmd_order) + (pmd_order - 1) * 4 + 2; ksft_set_plan(tests);
+ pagemap_fd = open(pagemap_proc, O_RDONLY); + if (pagemap_fd == -1) + ksft_exit_fail_msg("read pagemap: %s\n", strerror(errno)); + + kpageflags_fd = open(kpageflags_proc, O_RDONLY); + if (kpageflags_fd == -1) + ksft_exit_fail_msg("read kpageflags: %s\n", strerror(errno)); + fd_size = 2 * pmd_pagesize;
split_pmd_zero_pages(); @@ -767,6 +804,10 @@ int main(int argc, char **argv) split_thp_in_pagecache_to_order_at(fd_size, fs_loc, i, offset); cleanup_thp_fs(fs_loc, created_tmp);
+ close(pagemap_fd); + close(kpageflags_fd); + free(expected_orders); + ksft_finished();
return 0;
linux-kselftest-mirror@lists.linaro.org