root/tools/testing/selftests/vm/mlock2-tests.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_vm_area
  2. is_vmflag_set
  3. get_value_for_name
  4. is_vma_lock_on_fault
  5. lock_check
  6. unlock_lock_check
  7. test_mlock_lock
  8. onfault_check
  9. unlock_onfault_check
  10. test_mlock_onfault
  11. test_lock_onfault_of_present
  12. test_munlockall
  13. test_vma_management
  14. test_mlockall
  15. main

   1 // SPDX-License-Identifier: GPL-2.0
   2 #define _GNU_SOURCE
   3 #include <sys/mman.h>
   4 #include <stdint.h>
   5 #include <unistd.h>
   6 #include <string.h>
   7 #include <sys/time.h>
   8 #include <sys/resource.h>
   9 #include <stdbool.h>
  10 #include "mlock2.h"
  11 
  12 #include "../kselftest.h"
  13 
  14 struct vm_boundaries {
  15         unsigned long start;
  16         unsigned long end;
  17 };
  18 
  19 static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
  20 {
  21         FILE *file;
  22         int ret = 1;
  23         char line[1024] = {0};
  24         char *end_addr;
  25         char *stop;
  26         unsigned long start;
  27         unsigned long end;
  28 
  29         if (!area)
  30                 return ret;
  31 
  32         file = fopen("/proc/self/maps", "r");
  33         if (!file) {
  34                 perror("fopen");
  35                 return ret;
  36         }
  37 
  38         memset(area, 0, sizeof(struct vm_boundaries));
  39 
  40         while(fgets(line, 1024, file)) {
  41                 end_addr = strchr(line, '-');
  42                 if (!end_addr) {
  43                         printf("cannot parse /proc/self/maps\n");
  44                         goto out;
  45                 }
  46                 *end_addr = '\0';
  47                 end_addr++;
  48                 stop = strchr(end_addr, ' ');
  49                 if (!stop) {
  50                         printf("cannot parse /proc/self/maps\n");
  51                         goto out;
  52                 }
  53                 stop = '\0';
  54 
  55                 sscanf(line, "%lx", &start);
  56                 sscanf(end_addr, "%lx", &end);
  57 
  58                 if (start <= addr && end > addr) {
  59                         area->start = start;
  60                         area->end = end;
  61                         ret = 0;
  62                         goto out;
  63                 }
  64         }
  65 out:
  66         fclose(file);
  67         return ret;
  68 }
  69 
  70 #define VMFLAGS "VmFlags:"
  71 
  72 static bool is_vmflag_set(unsigned long addr, const char *vmflag)
  73 {
  74         char *line = NULL;
  75         char *flags;
  76         size_t size = 0;
  77         bool ret = false;
  78         FILE *smaps;
  79 
  80         smaps = seek_to_smaps_entry(addr);
  81         if (!smaps) {
  82                 printf("Unable to parse /proc/self/smaps\n");
  83                 goto out;
  84         }
  85 
  86         while (getline(&line, &size, smaps) > 0) {
  87                 if (!strstr(line, VMFLAGS)) {
  88                         free(line);
  89                         line = NULL;
  90                         size = 0;
  91                         continue;
  92                 }
  93 
  94                 flags = line + strlen(VMFLAGS);
  95                 ret = (strstr(flags, vmflag) != NULL);
  96                 goto out;
  97         }
  98 
  99 out:
 100         free(line);
 101         fclose(smaps);
 102         return ret;
 103 }
 104 
 105 #define SIZE "Size:"
 106 #define RSS  "Rss:"
 107 #define LOCKED "lo"
 108 
 109 static unsigned long get_value_for_name(unsigned long addr, const char *name)
 110 {
 111         char *line = NULL;
 112         size_t size = 0;
 113         char *value_ptr;
 114         FILE *smaps = NULL;
 115         unsigned long value = -1UL;
 116 
 117         smaps = seek_to_smaps_entry(addr);
 118         if (!smaps) {
 119                 printf("Unable to parse /proc/self/smaps\n");
 120                 goto out;
 121         }
 122 
 123         while (getline(&line, &size, smaps) > 0) {
 124                 if (!strstr(line, name)) {
 125                         free(line);
 126                         line = NULL;
 127                         size = 0;
 128                         continue;
 129                 }
 130 
 131                 value_ptr = line + strlen(name);
 132                 if (sscanf(value_ptr, "%lu kB", &value) < 1) {
 133                         printf("Unable to parse smaps entry for Size\n");
 134                         goto out;
 135                 }
 136                 break;
 137         }
 138 
 139 out:
 140         if (smaps)
 141                 fclose(smaps);
 142         free(line);
 143         return value;
 144 }
 145 
 146 static bool is_vma_lock_on_fault(unsigned long addr)
 147 {
 148         bool locked;
 149         unsigned long vma_size, vma_rss;
 150 
 151         locked = is_vmflag_set(addr, LOCKED);
 152         if (!locked)
 153                 return false;
 154 
 155         vma_size = get_value_for_name(addr, SIZE);
 156         vma_rss = get_value_for_name(addr, RSS);
 157 
 158         /* only one page is faulted in */
 159         return (vma_rss < vma_size);
 160 }
 161 
 162 #define PRESENT_BIT     0x8000000000000000ULL
 163 #define PFN_MASK        0x007FFFFFFFFFFFFFULL
 164 #define UNEVICTABLE_BIT (1UL << 18)
 165 
 166 static int lock_check(unsigned long addr)
 167 {
 168         bool locked;
 169         unsigned long vma_size, vma_rss;
 170 
 171         locked = is_vmflag_set(addr, LOCKED);
 172         if (!locked)
 173                 return false;
 174 
 175         vma_size = get_value_for_name(addr, SIZE);
 176         vma_rss = get_value_for_name(addr, RSS);
 177 
 178         return (vma_rss == vma_size);
 179 }
 180 
 181 static int unlock_lock_check(char *map)
 182 {
 183         if (is_vmflag_set((unsigned long)map, LOCKED)) {
 184                 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
 185                 return 1;
 186         }
 187 
 188         return 0;
 189 }
 190 
 191 static int test_mlock_lock()
 192 {
 193         char *map;
 194         int ret = 1;
 195         unsigned long page_size = getpagesize();
 196 
 197         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
 198                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 199         if (map == MAP_FAILED) {
 200                 perror("test_mlock_locked mmap");
 201                 goto out;
 202         }
 203 
 204         if (mlock2_(map, 2 * page_size, 0)) {
 205                 if (errno == ENOSYS) {
 206                         printf("Cannot call new mlock family, skipping test\n");
 207                         _exit(KSFT_SKIP);
 208                 }
 209                 perror("mlock2(0)");
 210                 goto unmap;
 211         }
 212 
 213         if (!lock_check((unsigned long)map))
 214                 goto unmap;
 215 
 216         /* Now unlock and recheck attributes */
 217         if (munlock(map, 2 * page_size)) {
 218                 perror("munlock()");
 219                 goto unmap;
 220         }
 221 
 222         ret = unlock_lock_check(map);
 223 
 224 unmap:
 225         munmap(map, 2 * page_size);
 226 out:
 227         return ret;
 228 }
 229 
 230 static int onfault_check(char *map)
 231 {
 232         *map = 'a';
 233         if (!is_vma_lock_on_fault((unsigned long)map)) {
 234                 printf("VMA is not marked for lock on fault\n");
 235                 return 1;
 236         }
 237 
 238         return 0;
 239 }
 240 
 241 static int unlock_onfault_check(char *map)
 242 {
 243         unsigned long page_size = getpagesize();
 244 
 245         if (is_vma_lock_on_fault((unsigned long)map) ||
 246             is_vma_lock_on_fault((unsigned long)map + page_size)) {
 247                 printf("VMA is still lock on fault after unlock\n");
 248                 return 1;
 249         }
 250 
 251         return 0;
 252 }
 253 
 254 static int test_mlock_onfault()
 255 {
 256         char *map;
 257         int ret = 1;
 258         unsigned long page_size = getpagesize();
 259 
 260         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
 261                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 262         if (map == MAP_FAILED) {
 263                 perror("test_mlock_locked mmap");
 264                 goto out;
 265         }
 266 
 267         if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
 268                 if (errno == ENOSYS) {
 269                         printf("Cannot call new mlock family, skipping test\n");
 270                         _exit(KSFT_SKIP);
 271                 }
 272                 perror("mlock2(MLOCK_ONFAULT)");
 273                 goto unmap;
 274         }
 275 
 276         if (onfault_check(map))
 277                 goto unmap;
 278 
 279         /* Now unlock and recheck attributes */
 280         if (munlock(map, 2 * page_size)) {
 281                 if (errno == ENOSYS) {
 282                         printf("Cannot call new mlock family, skipping test\n");
 283                         _exit(KSFT_SKIP);
 284                 }
 285                 perror("munlock()");
 286                 goto unmap;
 287         }
 288 
 289         ret = unlock_onfault_check(map);
 290 unmap:
 291         munmap(map, 2 * page_size);
 292 out:
 293         return ret;
 294 }
 295 
 296 static int test_lock_onfault_of_present()
 297 {
 298         char *map;
 299         int ret = 1;
 300         unsigned long page_size = getpagesize();
 301 
 302         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
 303                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 304         if (map == MAP_FAILED) {
 305                 perror("test_mlock_locked mmap");
 306                 goto out;
 307         }
 308 
 309         *map = 'a';
 310 
 311         if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
 312                 if (errno == ENOSYS) {
 313                         printf("Cannot call new mlock family, skipping test\n");
 314                         _exit(KSFT_SKIP);
 315                 }
 316                 perror("mlock2(MLOCK_ONFAULT)");
 317                 goto unmap;
 318         }
 319 
 320         if (!is_vma_lock_on_fault((unsigned long)map) ||
 321             !is_vma_lock_on_fault((unsigned long)map + page_size)) {
 322                 printf("VMA with present pages is not marked lock on fault\n");
 323                 goto unmap;
 324         }
 325         ret = 0;
 326 unmap:
 327         munmap(map, 2 * page_size);
 328 out:
 329         return ret;
 330 }
 331 
 332 static int test_munlockall()
 333 {
 334         char *map;
 335         int ret = 1;
 336         unsigned long page_size = getpagesize();
 337 
 338         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
 339                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 340 
 341         if (map == MAP_FAILED) {
 342                 perror("test_munlockall mmap");
 343                 goto out;
 344         }
 345 
 346         if (mlockall(MCL_CURRENT)) {
 347                 perror("mlockall(MCL_CURRENT)");
 348                 goto out;
 349         }
 350 
 351         if (!lock_check((unsigned long)map))
 352                 goto unmap;
 353 
 354         if (munlockall()) {
 355                 perror("munlockall()");
 356                 goto unmap;
 357         }
 358 
 359         if (unlock_lock_check(map))
 360                 goto unmap;
 361 
 362         munmap(map, 2 * page_size);
 363 
 364         map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
 365                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 366 
 367         if (map == MAP_FAILED) {
 368                 perror("test_munlockall second mmap");
 369                 goto out;
 370         }
 371 
 372         if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
 373                 perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
 374                 goto unmap;
 375         }
 376 
 377         if (onfault_check(map))
 378                 goto unmap;
 379 
 380         if (munlockall()) {
 381                 perror("munlockall()");
 382                 goto unmap;
 383         }
 384 
 385         if (unlock_onfault_check(map))
 386                 goto unmap;
 387 
 388         if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
 389                 perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
 390                 goto out;
 391         }
 392 
 393         if (!lock_check((unsigned long)map))
 394                 goto unmap;
 395 
 396         if (munlockall()) {
 397                 perror("munlockall()");
 398                 goto unmap;
 399         }
 400 
 401         ret = unlock_lock_check(map);
 402 
 403 unmap:
 404         munmap(map, 2 * page_size);
 405 out:
 406         munlockall();
 407         return ret;
 408 }
 409 
 410 static int test_vma_management(bool call_mlock)
 411 {
 412         int ret = 1;
 413         void *map;
 414         unsigned long page_size = getpagesize();
 415         struct vm_boundaries page1;
 416         struct vm_boundaries page2;
 417         struct vm_boundaries page3;
 418 
 419         map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
 420                    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 421         if (map == MAP_FAILED) {
 422                 perror("mmap()");
 423                 return ret;
 424         }
 425 
 426         if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
 427                 if (errno == ENOSYS) {
 428                         printf("Cannot call new mlock family, skipping test\n");
 429                         _exit(KSFT_SKIP);
 430                 }
 431                 perror("mlock(ONFAULT)\n");
 432                 goto out;
 433         }
 434 
 435         if (get_vm_area((unsigned long)map, &page1) ||
 436             get_vm_area((unsigned long)map + page_size, &page2) ||
 437             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
 438                 printf("couldn't find mapping in /proc/self/maps\n");
 439                 goto out;
 440         }
 441 
 442         /*
 443          * Before we unlock a portion, we need to that all three pages are in
 444          * the same VMA.  If they are not we abort this test (Note that this is
 445          * not a failure)
 446          */
 447         if (page1.start != page2.start || page2.start != page3.start) {
 448                 printf("VMAs are not merged to start, aborting test\n");
 449                 ret = 0;
 450                 goto out;
 451         }
 452 
 453         if (munlock(map + page_size, page_size)) {
 454                 perror("munlock()");
 455                 goto out;
 456         }
 457 
 458         if (get_vm_area((unsigned long)map, &page1) ||
 459             get_vm_area((unsigned long)map + page_size, &page2) ||
 460             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
 461                 printf("couldn't find mapping in /proc/self/maps\n");
 462                 goto out;
 463         }
 464 
 465         /* All three VMAs should be different */
 466         if (page1.start == page2.start || page2.start == page3.start) {
 467                 printf("failed to split VMA for munlock\n");
 468                 goto out;
 469         }
 470 
 471         /* Now unlock the first and third page and check the VMAs again */
 472         if (munlock(map, page_size * 3)) {
 473                 perror("munlock()");
 474                 goto out;
 475         }
 476 
 477         if (get_vm_area((unsigned long)map, &page1) ||
 478             get_vm_area((unsigned long)map + page_size, &page2) ||
 479             get_vm_area((unsigned long)map + page_size * 2, &page3)) {
 480                 printf("couldn't find mapping in /proc/self/maps\n");
 481                 goto out;
 482         }
 483 
 484         /* Now all three VMAs should be the same */
 485         if (page1.start != page2.start || page2.start != page3.start) {
 486                 printf("failed to merge VMAs after munlock\n");
 487                 goto out;
 488         }
 489 
 490         ret = 0;
 491 out:
 492         munmap(map, 3 * page_size);
 493         return ret;
 494 }
 495 
 496 static int test_mlockall(int (test_function)(bool call_mlock))
 497 {
 498         int ret = 1;
 499 
 500         if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
 501                 perror("mlockall");
 502                 return ret;
 503         }
 504 
 505         ret = test_function(false);
 506         munlockall();
 507         return ret;
 508 }
 509 
 510 int main(int argc, char **argv)
 511 {
 512         int ret = 0;
 513         ret += test_mlock_lock();
 514         ret += test_mlock_onfault();
 515         ret += test_munlockall();
 516         ret += test_lock_onfault_of_present();
 517         ret += test_vma_management(true);
 518         ret += test_mlockall(test_vma_management);
 519         return ret;
 520 }

/* [<][>][^][v][top][bottom][index][help] */