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

selftests: vm: drop dependencies on page flags from mlock2 tests

It was noticed that mlock2 tests are failing after 9c4e6b1a7027f ("mm,
mlock, vmscan: no more skipping pagevecs") because the patch has changed
the timing on when the page is added to the unevictable LRU list and thus
gains the unevictable page flag.

The test was just too dependent on the implementation details which were
true at the time when it was introduced. Page flags and the timing when
they are set is something no userspace should ever depend on. The test
should be testing only for the user observable contract of the tested
syscalls. Those are defined pretty well for the mlock and there are other
means for testing them. In fact this is already done and testing for page
flags can be safely dropped to achieve the aimed purpose. Present bits
can be checked by /proc/<pid>/smaps RSS field and the locking state by
VmFlags although I would argue that Locked: field would be more
appropriate.

Drop all the page flag machinery and considerably simplify the test. This
should be more robust for future kernel changes while checking the
promised contract is still valid.

Fixes: 9c4e6b1a7027f ("mm, mlock, vmscan: no more skipping pagevecs")
Reported-by: Rafael Aquini <aquini@redhat.com>
Signed-off-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: Rafael Aquini <aquini@redhat.com>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Eric B Munson <emunson@akamai.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: <stable@vger.kernel.org>
Link: http://lkml.kernel.org/r/20200324154218.GS19542@dhcp22.suse.cz
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Michal Hocko and committed by
Linus Torvalds
eea274d6 c4ecddff

+37 -196
+37 -196
tools/testing/selftests/vm/mlock2-tests.c
··· 67 67 return ret; 68 68 } 69 69 70 - static uint64_t get_pageflags(unsigned long addr) 71 - { 72 - FILE *file; 73 - uint64_t pfn; 74 - unsigned long offset; 75 - 76 - file = fopen("/proc/self/pagemap", "r"); 77 - if (!file) { 78 - perror("fopen pagemap"); 79 - _exit(1); 80 - } 81 - 82 - offset = addr / getpagesize() * sizeof(pfn); 83 - 84 - if (fseek(file, offset, SEEK_SET)) { 85 - perror("fseek pagemap"); 86 - _exit(1); 87 - } 88 - 89 - if (fread(&pfn, sizeof(pfn), 1, file) != 1) { 90 - perror("fread pagemap"); 91 - _exit(1); 92 - } 93 - 94 - fclose(file); 95 - return pfn; 96 - } 97 - 98 - static uint64_t get_kpageflags(unsigned long pfn) 99 - { 100 - uint64_t flags; 101 - FILE *file; 102 - 103 - file = fopen("/proc/kpageflags", "r"); 104 - if (!file) { 105 - perror("fopen kpageflags"); 106 - _exit(1); 107 - } 108 - 109 - if (fseek(file, pfn * sizeof(flags), SEEK_SET)) { 110 - perror("fseek kpageflags"); 111 - _exit(1); 112 - } 113 - 114 - if (fread(&flags, sizeof(flags), 1, file) != 1) { 115 - perror("fread kpageflags"); 116 - _exit(1); 117 - } 118 - 119 - fclose(file); 120 - return flags; 121 - } 122 - 123 70 #define VMFLAGS "VmFlags:" 124 71 125 72 static bool is_vmflag_set(unsigned long addr, const char *vmflag) ··· 106 159 #define RSS "Rss:" 107 160 #define LOCKED "lo" 108 161 109 - static bool is_vma_lock_on_fault(unsigned long addr) 162 + static unsigned long get_value_for_name(unsigned long addr, const char *name) 110 163 { 111 - bool ret = false; 112 - bool locked; 113 - FILE *smaps = NULL; 114 - unsigned long vma_size, vma_rss; 115 164 char *line = NULL; 116 - char *value; 117 165 size_t size = 0; 118 - 119 - locked = is_vmflag_set(addr, LOCKED); 120 - if (!locked) 121 - goto out; 166 + char *value_ptr; 167 + FILE *smaps = NULL; 168 + unsigned long value = -1UL; 122 169 123 170 smaps = seek_to_smaps_entry(addr); 124 171 if (!smaps) { ··· 121 180 } 122 181 123 182 while (getline(&line, &size, smaps) > 0) { 124 - if (!strstr(line, SIZE)) { 183 + if (!strstr(line, name)) { 125 184 free(line); 126 185 line = NULL; 127 186 size = 0; 128 187 continue; 129 188 } 130 189 131 - value = line + strlen(SIZE); 132 - if (sscanf(value, "%lu kB", &vma_size) < 1) { 190 + value_ptr = line + strlen(name); 191 + if (sscanf(value_ptr, "%lu kB", &value) < 1) { 133 192 printf("Unable to parse smaps entry for Size\n"); 134 193 goto out; 135 194 } 136 195 break; 137 196 } 138 197 139 - while (getline(&line, &size, smaps) > 0) { 140 - if (!strstr(line, RSS)) { 141 - free(line); 142 - line = NULL; 143 - size = 0; 144 - continue; 145 - } 146 - 147 - value = line + strlen(RSS); 148 - if (sscanf(value, "%lu kB", &vma_rss) < 1) { 149 - printf("Unable to parse smaps entry for Rss\n"); 150 - goto out; 151 - } 152 - break; 153 - } 154 - 155 - ret = locked && (vma_rss < vma_size); 156 198 out: 157 - free(line); 158 199 if (smaps) 159 200 fclose(smaps); 160 - return ret; 201 + free(line); 202 + return value; 203 + } 204 + 205 + static bool is_vma_lock_on_fault(unsigned long addr) 206 + { 207 + bool locked; 208 + unsigned long vma_size, vma_rss; 209 + 210 + locked = is_vmflag_set(addr, LOCKED); 211 + if (!locked) 212 + return false; 213 + 214 + vma_size = get_value_for_name(addr, SIZE); 215 + vma_rss = get_value_for_name(addr, RSS); 216 + 217 + /* only one page is faulted in */ 218 + return (vma_rss < vma_size); 161 219 } 162 220 163 221 #define PRESENT_BIT 0x8000000000000000ULL 164 222 #define PFN_MASK 0x007FFFFFFFFFFFFFULL 165 223 #define UNEVICTABLE_BIT (1UL << 18) 166 224 167 - static int lock_check(char *map) 225 + static int lock_check(unsigned long addr) 168 226 { 169 - unsigned long page_size = getpagesize(); 170 - uint64_t page1_flags, page2_flags; 227 + bool locked; 228 + unsigned long vma_size, vma_rss; 171 229 172 - page1_flags = get_pageflags((unsigned long)map); 173 - page2_flags = get_pageflags((unsigned long)map + page_size); 230 + locked = is_vmflag_set(addr, LOCKED); 231 + if (!locked) 232 + return false; 174 233 175 - /* Both pages should be present */ 176 - if (((page1_flags & PRESENT_BIT) == 0) || 177 - ((page2_flags & PRESENT_BIT) == 0)) { 178 - printf("Failed to make both pages present\n"); 179 - return 1; 180 - } 234 + vma_size = get_value_for_name(addr, SIZE); 235 + vma_rss = get_value_for_name(addr, RSS); 181 236 182 - page1_flags = get_kpageflags(page1_flags & PFN_MASK); 183 - page2_flags = get_kpageflags(page2_flags & PFN_MASK); 184 - 185 - /* Both pages should be unevictable */ 186 - if (((page1_flags & UNEVICTABLE_BIT) == 0) || 187 - ((page2_flags & UNEVICTABLE_BIT) == 0)) { 188 - printf("Failed to make both pages unevictable\n"); 189 - return 1; 190 - } 191 - 192 - if (!is_vmflag_set((unsigned long)map, LOCKED)) { 193 - printf("VMA flag %s is missing on page 1\n", LOCKED); 194 - return 1; 195 - } 196 - 197 - if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) { 198 - printf("VMA flag %s is missing on page 2\n", LOCKED); 199 - return 1; 200 - } 201 - 202 - return 0; 237 + return (vma_rss == vma_size); 203 238 } 204 239 205 240 static int unlock_lock_check(char *map) 206 241 { 207 - unsigned long page_size = getpagesize(); 208 - uint64_t page1_flags, page2_flags; 209 - 210 - page1_flags = get_pageflags((unsigned long)map); 211 - page2_flags = get_pageflags((unsigned long)map + page_size); 212 - page1_flags = get_kpageflags(page1_flags & PFN_MASK); 213 - page2_flags = get_kpageflags(page2_flags & PFN_MASK); 214 - 215 - if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) { 216 - printf("A page is still marked unevictable after unlock\n"); 217 - return 1; 218 - } 219 - 220 242 if (is_vmflag_set((unsigned long)map, LOCKED)) { 221 243 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED); 222 - return 1; 223 - } 224 - 225 - if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) { 226 - printf("VMA flag %s is present on page 2 after unlock\n", LOCKED); 227 244 return 1; 228 245 } 229 246 ··· 210 311 goto unmap; 211 312 } 212 313 213 - if (lock_check(map)) 314 + if (!lock_check((unsigned long)map)) 214 315 goto unmap; 215 316 216 317 /* Now unlock and recheck attributes */ ··· 229 330 230 331 static int onfault_check(char *map) 231 332 { 232 - unsigned long page_size = getpagesize(); 233 - uint64_t page1_flags, page2_flags; 234 - 235 - page1_flags = get_pageflags((unsigned long)map); 236 - page2_flags = get_pageflags((unsigned long)map + page_size); 237 - 238 - /* Neither page should be present */ 239 - if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) { 240 - printf("Pages were made present by MLOCK_ONFAULT\n"); 241 - return 1; 242 - } 243 - 244 333 *map = 'a'; 245 - page1_flags = get_pageflags((unsigned long)map); 246 - page2_flags = get_pageflags((unsigned long)map + page_size); 247 - 248 - /* Only page 1 should be present */ 249 - if ((page1_flags & PRESENT_BIT) == 0) { 250 - printf("Page 1 is not present after fault\n"); 251 - return 1; 252 - } else if (page2_flags & PRESENT_BIT) { 253 - printf("Page 2 was made present\n"); 254 - return 1; 255 - } 256 - 257 - page1_flags = get_kpageflags(page1_flags & PFN_MASK); 258 - 259 - /* Page 1 should be unevictable */ 260 - if ((page1_flags & UNEVICTABLE_BIT) == 0) { 261 - printf("Failed to make faulted page unevictable\n"); 262 - return 1; 263 - } 264 - 265 334 if (!is_vma_lock_on_fault((unsigned long)map)) { 266 - printf("VMA is not marked for lock on fault\n"); 267 - return 1; 268 - } 269 - 270 - if (!is_vma_lock_on_fault((unsigned long)map + page_size)) { 271 335 printf("VMA is not marked for lock on fault\n"); 272 336 return 1; 273 337 } ··· 241 379 static int unlock_onfault_check(char *map) 242 380 { 243 381 unsigned long page_size = getpagesize(); 244 - uint64_t page1_flags; 245 - 246 - page1_flags = get_pageflags((unsigned long)map); 247 - page1_flags = get_kpageflags(page1_flags & PFN_MASK); 248 - 249 - if (page1_flags & UNEVICTABLE_BIT) { 250 - printf("Page 1 is still marked unevictable after unlock\n"); 251 - return 1; 252 - } 253 382 254 383 if (is_vma_lock_on_fault((unsigned long)map) || 255 384 is_vma_lock_on_fault((unsigned long)map + page_size)) { ··· 298 445 char *map; 299 446 int ret = 1; 300 447 unsigned long page_size = getpagesize(); 301 - uint64_t page1_flags, page2_flags; 302 448 303 449 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, 304 450 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ··· 314 462 _exit(KSFT_SKIP); 315 463 } 316 464 perror("mlock2(MLOCK_ONFAULT)"); 317 - goto unmap; 318 - } 319 - 320 - page1_flags = get_pageflags((unsigned long)map); 321 - page2_flags = get_pageflags((unsigned long)map + page_size); 322 - page1_flags = get_kpageflags(page1_flags & PFN_MASK); 323 - page2_flags = get_kpageflags(page2_flags & PFN_MASK); 324 - 325 - /* Page 1 should be unevictable */ 326 - if ((page1_flags & UNEVICTABLE_BIT) == 0) { 327 - printf("Failed to make present page unevictable\n"); 328 465 goto unmap; 329 466 } 330 467 ··· 348 507 goto out; 349 508 } 350 509 351 - if (lock_check(map)) 510 + if (!lock_check((unsigned long)map)) 352 511 goto unmap; 353 512 354 513 if (munlockall()) { ··· 390 549 goto out; 391 550 } 392 551 393 - if (lock_check(map)) 552 + if (!lock_check((unsigned long)map)) 394 553 goto unmap; 395 554 396 555 if (munlockall()) {