Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: selftest to verify zero-filled pages are mapped to zeropage

When a THP is split, any subpage that is zero-filled will be mapped to the
shared zeropage, hence saving memory. Add selftest to verify this by
allocating zero-filled THP and comparing RssAnon before and after split.

Link: https://lkml.kernel.org/r/20240830100438.3623486-4-usamaarif642@gmail.com
Signed-off-by: Alexander Zhu <alexlzhu@fb.com>
Signed-off-by: Usama Arif <usamaarif642@gmail.com>
Acked-by: Rik van Riel <riel@surriel.com>
Cc: Barry Song <baohua@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Domenico Cerasuolo <cerasuolodomenico@gmail.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kairui Song <ryncsn@gmail.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Nico Pache <npache@redhat.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Shuang Zhai <zhais@google.com>
Cc: Yu Zhao <yuzhao@google.com>
Cc: Shuang Zhai <szhai2@cs.rochester.edu>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Alexander Zhu and committed by
Andrew Morton
391e8697 b1f20206

+94
+71
tools/testing/selftests/mm/split_huge_page_test.c
··· 84 84 write_file(SPLIT_DEBUGFS, input, ret + 1); 85 85 } 86 86 87 + static char *allocate_zero_filled_hugepage(size_t len) 88 + { 89 + char *result; 90 + size_t i; 91 + 92 + result = memalign(pmd_pagesize, len); 93 + if (!result) { 94 + printf("Fail to allocate memory\n"); 95 + exit(EXIT_FAILURE); 96 + } 97 + 98 + madvise(result, len, MADV_HUGEPAGE); 99 + 100 + for (i = 0; i < len; i++) 101 + result[i] = (char)0; 102 + 103 + return result; 104 + } 105 + 106 + static void verify_rss_anon_split_huge_page_all_zeroes(char *one_page, int nr_hpages, size_t len) 107 + { 108 + unsigned long rss_anon_before, rss_anon_after; 109 + size_t i; 110 + 111 + if (!check_huge_anon(one_page, 4, pmd_pagesize)) { 112 + printf("No THP is allocated\n"); 113 + exit(EXIT_FAILURE); 114 + } 115 + 116 + rss_anon_before = rss_anon(); 117 + if (!rss_anon_before) { 118 + printf("No RssAnon is allocated before split\n"); 119 + exit(EXIT_FAILURE); 120 + } 121 + 122 + /* split all THPs */ 123 + write_debugfs(PID_FMT, getpid(), (uint64_t)one_page, 124 + (uint64_t)one_page + len, 0); 125 + 126 + for (i = 0; i < len; i++) 127 + if (one_page[i] != (char)0) { 128 + printf("%ld byte corrupted\n", i); 129 + exit(EXIT_FAILURE); 130 + } 131 + 132 + if (!check_huge_anon(one_page, 0, pmd_pagesize)) { 133 + printf("Still AnonHugePages not split\n"); 134 + exit(EXIT_FAILURE); 135 + } 136 + 137 + rss_anon_after = rss_anon(); 138 + if (rss_anon_after >= rss_anon_before) { 139 + printf("Incorrect RssAnon value. Before: %ld After: %ld\n", 140 + rss_anon_before, rss_anon_after); 141 + exit(EXIT_FAILURE); 142 + } 143 + } 144 + 145 + void split_pmd_zero_pages(void) 146 + { 147 + char *one_page; 148 + int nr_hpages = 4; 149 + size_t len = nr_hpages * pmd_pagesize; 150 + 151 + one_page = allocate_zero_filled_hugepage(len); 152 + verify_rss_anon_split_huge_page_all_zeroes(one_page, nr_hpages, len); 153 + printf("Split zero filled huge pages successful\n"); 154 + free(one_page); 155 + } 156 + 87 157 void split_pmd_thp(void) 88 158 { 89 159 char *one_page; ··· 501 431 502 432 fd_size = 2 * pmd_pagesize; 503 433 434 + split_pmd_zero_pages(); 504 435 split_pmd_thp(); 505 436 split_pte_mapped_thp(); 506 437 split_file_backed_thp();
+22
tools/testing/selftests/mm/vm_util.c
··· 12 12 13 13 #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" 14 14 #define SMAP_FILE_PATH "/proc/self/smaps" 15 + #define STATUS_FILE_PATH "/proc/self/status" 15 16 #define MAX_LINE_LENGTH 500 16 17 17 18 unsigned int __page_size; ··· 170 169 close(fd); 171 170 172 171 return strtoul(buf, NULL, 10); 172 + } 173 + 174 + unsigned long rss_anon(void) 175 + { 176 + unsigned long rss_anon = 0; 177 + FILE *fp; 178 + char buffer[MAX_LINE_LENGTH]; 179 + 180 + fp = fopen(STATUS_FILE_PATH, "r"); 181 + if (!fp) 182 + ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, STATUS_FILE_PATH); 183 + 184 + if (!check_for_pattern(fp, "RssAnon:", buffer, sizeof(buffer))) 185 + goto err_out; 186 + 187 + if (sscanf(buffer, "RssAnon:%10lu kB", &rss_anon) != 1) 188 + ksft_exit_fail_msg("Reading status error\n"); 189 + 190 + err_out: 191 + fclose(fp); 192 + return rss_anon; 173 193 } 174 194 175 195 bool __check_huge(void *addr, char *pattern, int nr_hpages,
+1
tools/testing/selftests/mm/vm_util.h
··· 39 39 void clear_softdirty(void); 40 40 bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len); 41 41 uint64_t read_pmd_pagesize(void); 42 + unsigned long rss_anon(void); 42 43 bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size); 43 44 bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size); 44 45 bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size);