1 /* 2 * page-types: Tool for querying page flags 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the Free 6 * Software Foundation; version 2. 7 * 8 * This program is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should find a copy of v2 of the GNU General Public License somewhere on 14 * your Linux system; if not, write to the Free Software Foundation, Inc., 59 15 * Temple Place, Suite 330, Boston, MA 02111-1307 USA. 16 * 17 * Copyright (C) 2009 Intel corporation 18 * 19 * Authors: Wu Fengguang <fengguang.wu@intel.com> 20 */ 21 22 #define _LARGEFILE64_SOURCE 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <unistd.h> 26 #include <stdint.h> 27 #include <stdarg.h> 28 #include <string.h> 29 #include <getopt.h> 30 #include <limits.h> 31 #include <assert.h> 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <sys/fcntl.h> 35 #include <sys/mount.h> 36 #include <sys/statfs.h> 37 #include "../../include/uapi/linux/magic.h" 38 #include "../../include/uapi/linux/kernel-page-flags.h" 39 #include <lk/debugfs.h> 40 41 #ifndef MAX_PATH 42 # define MAX_PATH 256 43 #endif 44 45 #ifndef STR 46 # define _STR(x) #x 47 # define STR(x) _STR(x) 48 #endif 49 50 /* 51 * pagemap kernel ABI bits 52 */ 53 54 #define PM_ENTRY_BYTES sizeof(uint64_t) 55 #define PM_STATUS_BITS 3 56 #define PM_STATUS_OFFSET (64 - PM_STATUS_BITS) 57 #define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET) 58 #define PM_STATUS(nr) (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK) 59 #define PM_PSHIFT_BITS 6 60 #define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS) 61 #define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET) 62 #define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK) 63 #define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1) 64 #define PM_PFRAME(x) ((x) & PM_PFRAME_MASK) 65 66 #define PM_PRESENT PM_STATUS(4LL) 67 #define PM_SWAP PM_STATUS(2LL) 68 69 70 /* 71 * kernel page flags 72 */ 73 74 #define KPF_BYTES 8 75 #define PROC_KPAGEFLAGS "/proc/kpageflags" 76 77 /* [32-] kernel hacking assistances */ 78 #define KPF_RESERVED 32 79 #define KPF_MLOCKED 33 80 #define KPF_MAPPEDTODISK 34 81 #define KPF_PRIVATE 35 82 #define KPF_PRIVATE_2 36 83 #define KPF_OWNER_PRIVATE 37 84 #define KPF_ARCH 38 85 #define KPF_UNCACHED 39 86 87 /* [48-] take some arbitrary free slots for expanding overloaded flags 88 * not part of kernel API 89 */ 90 #define KPF_READAHEAD 48 91 #define KPF_SLOB_FREE 49 92 #define KPF_SLUB_FROZEN 50 93 #define KPF_SLUB_DEBUG 51 94 95 #define KPF_ALL_BITS ((uint64_t)~0ULL) 96 #define KPF_HACKERS_BITS (0xffffULL << 32) 97 #define KPF_OVERLOADED_BITS (0xffffULL << 48) 98 #define BIT(name) (1ULL << KPF_##name) 99 #define BITS_COMPOUND (BIT(COMPOUND_HEAD) | BIT(COMPOUND_TAIL)) 100 101 static const char * const page_flag_names[] = { 102 [KPF_LOCKED] = "L:locked", 103 [KPF_ERROR] = "E:error", 104 [KPF_REFERENCED] = "R:referenced", 105 [KPF_UPTODATE] = "U:uptodate", 106 [KPF_DIRTY] = "D:dirty", 107 [KPF_LRU] = "l:lru", 108 [KPF_ACTIVE] = "A:active", 109 [KPF_SLAB] = "S:slab", 110 [KPF_WRITEBACK] = "W:writeback", 111 [KPF_RECLAIM] = "I:reclaim", 112 [KPF_BUDDY] = "B:buddy", 113 114 [KPF_MMAP] = "M:mmap", 115 [KPF_ANON] = "a:anonymous", 116 [KPF_SWAPCACHE] = "s:swapcache", 117 [KPF_SWAPBACKED] = "b:swapbacked", 118 [KPF_COMPOUND_HEAD] = "H:compound_head", 119 [KPF_COMPOUND_TAIL] = "T:compound_tail", 120 [KPF_HUGE] = "G:huge", 121 [KPF_UNEVICTABLE] = "u:unevictable", 122 [KPF_HWPOISON] = "X:hwpoison", 123 [KPF_NOPAGE] = "n:nopage", 124 [KPF_KSM] = "x:ksm", 125 [KPF_THP] = "t:thp", 126 127 [KPF_RESERVED] = "r:reserved", 128 [KPF_MLOCKED] = "m:mlocked", 129 [KPF_MAPPEDTODISK] = "d:mappedtodisk", 130 [KPF_PRIVATE] = "P:private", 131 [KPF_PRIVATE_2] = "p:private_2", 132 [KPF_OWNER_PRIVATE] = "O:owner_private", 133 [KPF_ARCH] = "h:arch", 134 [KPF_UNCACHED] = "c:uncached", 135 136 [KPF_READAHEAD] = "I:readahead", 137 [KPF_SLOB_FREE] = "P:slob_free", 138 [KPF_SLUB_FROZEN] = "A:slub_frozen", 139 [KPF_SLUB_DEBUG] = "E:slub_debug", 140 }; 141 142 143 static const char * const debugfs_known_mountpoints[] = { 144 "/sys/kernel/debug", 145 "/debug", 146 0, 147 }; 148 149 /* 150 * data structures 151 */ 152 153 static int opt_raw; /* for kernel developers */ 154 static int opt_list; /* list pages (in ranges) */ 155 static int opt_no_summary; /* don't show summary */ 156 static pid_t opt_pid; /* process to walk */ 157 158 #define MAX_ADDR_RANGES 1024 159 static int nr_addr_ranges; 160 static unsigned long opt_offset[MAX_ADDR_RANGES]; 161 static unsigned long opt_size[MAX_ADDR_RANGES]; 162 163 #define MAX_VMAS 10240 164 static int nr_vmas; 165 static unsigned long pg_start[MAX_VMAS]; 166 static unsigned long pg_end[MAX_VMAS]; 167 168 #define MAX_BIT_FILTERS 64 169 static int nr_bit_filters; 170 static uint64_t opt_mask[MAX_BIT_FILTERS]; 171 static uint64_t opt_bits[MAX_BIT_FILTERS]; 172 173 static int page_size; 174 175 static int pagemap_fd; 176 static int kpageflags_fd; 177 178 static int opt_hwpoison; 179 static int opt_unpoison; 180 181 static char *hwpoison_debug_fs; 182 static int hwpoison_inject_fd; 183 static int hwpoison_forget_fd; 184 185 #define HASH_SHIFT 13 186 #define HASH_SIZE (1 << HASH_SHIFT) 187 #define HASH_MASK (HASH_SIZE - 1) 188 #define HASH_KEY(flags) (flags & HASH_MASK) 189 190 static unsigned long total_pages; 191 static unsigned long nr_pages[HASH_SIZE]; 192 static uint64_t page_flags[HASH_SIZE]; 193 194 195 /* 196 * helper functions 197 */ 198 199 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 200 201 #define min_t(type, x, y) ({ \ 202 type __min1 = (x); \ 203 type __min2 = (y); \ 204 __min1 < __min2 ? __min1 : __min2; }) 205 206 #define max_t(type, x, y) ({ \ 207 type __max1 = (x); \ 208 type __max2 = (y); \ 209 __max1 > __max2 ? __max1 : __max2; }) 210 211 static unsigned long pages2mb(unsigned long pages) 212 { 213 return (pages * page_size) >> 20; 214 } 215 216 static void fatal(const char *x, ...) 217 { 218 va_list ap; 219 220 va_start(ap, x); 221 vfprintf(stderr, x, ap); 222 va_end(ap); 223 exit(EXIT_FAILURE); 224 } 225 226 static int checked_open(const char *pathname, int flags) 227 { 228 int fd = open(pathname, flags); 229 230 if (fd < 0) { 231 perror(pathname); 232 exit(EXIT_FAILURE); 233 } 234 235 return fd; 236 } 237 238 /* 239 * pagemap/kpageflags routines 240 */ 241 242 static unsigned long do_u64_read(int fd, char *name, 243 uint64_t *buf, 244 unsigned long index, 245 unsigned long count) 246 { 247 long bytes; 248 249 if (index > ULONG_MAX / 8) 250 fatal("index overflow: %lu\n", index); 251 252 if (lseek(fd, index * 8, SEEK_SET) < 0) { 253 perror(name); 254 exit(EXIT_FAILURE); 255 } 256 257 bytes = read(fd, buf, count * 8); 258 if (bytes < 0) { 259 perror(name); 260 exit(EXIT_FAILURE); 261 } 262 if (bytes % 8) 263 fatal("partial read: %lu bytes\n", bytes); 264 265 return bytes / 8; 266 } 267 268 static unsigned long kpageflags_read(uint64_t *buf, 269 unsigned long index, 270 unsigned long pages) 271 { 272 return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages); 273 } 274 275 static unsigned long pagemap_read(uint64_t *buf, 276 unsigned long index, 277 unsigned long pages) 278 { 279 return do_u64_read(pagemap_fd, "/proc/pid/pagemap", buf, index, pages); 280 } 281 282 static unsigned long pagemap_pfn(uint64_t val) 283 { 284 unsigned long pfn; 285 286 if (val & PM_PRESENT) 287 pfn = PM_PFRAME(val); 288 else 289 pfn = 0; 290 291 return pfn; 292 } 293 294 295 /* 296 * page flag names 297 */ 298 299 static char *page_flag_name(uint64_t flags) 300 { 301 static char buf[65]; 302 int present; 303 size_t i, j; 304 305 for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) { 306 present = (flags >> i) & 1; 307 if (!page_flag_names[i]) { 308 if (present) 309 fatal("unknown flag bit %d\n", i); 310 continue; 311 } 312 buf[j++] = present ? page_flag_names[i][0] : '_'; 313 } 314 315 return buf; 316 } 317 318 static char *page_flag_longname(uint64_t flags) 319 { 320 static char buf[1024]; 321 size_t i, n; 322 323 for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) { 324 if (!page_flag_names[i]) 325 continue; 326 if ((flags >> i) & 1) 327 n += snprintf(buf + n, sizeof(buf) - n, "%s,", 328 page_flag_names[i] + 2); 329 } 330 if (n) 331 n--; 332 buf[n] = '\0'; 333 334 return buf; 335 } 336 337 338 /* 339 * page list and summary 340 */ 341 342 static void show_page_range(unsigned long voffset, 343 unsigned long offset, uint64_t flags) 344 { 345 static uint64_t flags0; 346 static unsigned long voff; 347 static unsigned long index; 348 static unsigned long count; 349 350 if (flags == flags0 && offset == index + count && 351 (!opt_pid || voffset == voff + count)) { 352 count++; 353 return; 354 } 355 356 if (count) { 357 if (opt_pid) 358 printf("%lx\t", voff); 359 printf("%lx\t%lx\t%s\n", 360 index, count, page_flag_name(flags0)); 361 } 362 363 flags0 = flags; 364 index = offset; 365 voff = voffset; 366 count = 1; 367 } 368 369 static void show_page(unsigned long voffset, 370 unsigned long offset, uint64_t flags) 371 { 372 if (opt_pid) 373 printf("%lx\t", voffset); 374 printf("%lx\t%s\n", offset, page_flag_name(flags)); 375 } 376 377 static void show_summary(void) 378 { 379 size_t i; 380 381 printf(" flags\tpage-count MB" 382 " symbolic-flags\t\t\tlong-symbolic-flags\n"); 383 384 for (i = 0; i < ARRAY_SIZE(nr_pages); i++) { 385 if (nr_pages[i]) 386 printf("0x%016llx\t%10lu %8lu %s\t%s\n", 387 (unsigned long long)page_flags[i], 388 nr_pages[i], 389 pages2mb(nr_pages[i]), 390 page_flag_name(page_flags[i]), 391 page_flag_longname(page_flags[i])); 392 } 393 394 printf(" total\t%10lu %8lu\n", 395 total_pages, pages2mb(total_pages)); 396 } 397 398 399 /* 400 * page flag filters 401 */ 402 403 static int bit_mask_ok(uint64_t flags) 404 { 405 int i; 406 407 for (i = 0; i < nr_bit_filters; i++) { 408 if (opt_bits[i] == KPF_ALL_BITS) { 409 if ((flags & opt_mask[i]) == 0) 410 return 0; 411 } else { 412 if ((flags & opt_mask[i]) != opt_bits[i]) 413 return 0; 414 } 415 } 416 417 return 1; 418 } 419 420 static uint64_t expand_overloaded_flags(uint64_t flags) 421 { 422 /* SLOB/SLUB overload several page flags */ 423 if (flags & BIT(SLAB)) { 424 if (flags & BIT(PRIVATE)) 425 flags ^= BIT(PRIVATE) | BIT(SLOB_FREE); 426 if (flags & BIT(ACTIVE)) 427 flags ^= BIT(ACTIVE) | BIT(SLUB_FROZEN); 428 if (flags & BIT(ERROR)) 429 flags ^= BIT(ERROR) | BIT(SLUB_DEBUG); 430 } 431 432 /* PG_reclaim is overloaded as PG_readahead in the read path */ 433 if ((flags & (BIT(RECLAIM) | BIT(WRITEBACK))) == BIT(RECLAIM)) 434 flags ^= BIT(RECLAIM) | BIT(READAHEAD); 435 436 return flags; 437 } 438 439 static uint64_t well_known_flags(uint64_t flags) 440 { 441 /* hide flags intended only for kernel hacker */ 442 flags &= ~KPF_HACKERS_BITS; 443 444 /* hide non-hugeTLB compound pages */ 445 if ((flags & BITS_COMPOUND) && !(flags & BIT(HUGE))) 446 flags &= ~BITS_COMPOUND; 447 448 return flags; 449 } 450 451 static uint64_t kpageflags_flags(uint64_t flags) 452 { 453 flags = expand_overloaded_flags(flags); 454 455 if (!opt_raw) 456 flags = well_known_flags(flags); 457 458 return flags; 459 } 460 461 /* 462 * page actions 463 */ 464 465 static void prepare_hwpoison_fd(void) 466 { 467 char buf[MAX_PATH + 1]; 468 469 hwpoison_debug_fs = debugfs_mount(NULL); 470 if (!hwpoison_debug_fs) { 471 perror("mount debugfs"); 472 exit(EXIT_FAILURE); 473 } 474 475 if (opt_hwpoison && !hwpoison_inject_fd) { 476 snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn", 477 hwpoison_debug_fs); 478 hwpoison_inject_fd = checked_open(buf, O_WRONLY); 479 } 480 481 if (opt_unpoison && !hwpoison_forget_fd) { 482 snprintf(buf, MAX_PATH, "%s/hwpoison/unpoison-pfn", 483 hwpoison_debug_fs); 484 hwpoison_forget_fd = checked_open(buf, O_WRONLY); 485 } 486 } 487 488 static int hwpoison_page(unsigned long offset) 489 { 490 char buf[100]; 491 int len; 492 493 len = sprintf(buf, "0x%lx\n", offset); 494 len = write(hwpoison_inject_fd, buf, len); 495 if (len < 0) { 496 perror("hwpoison inject"); 497 return len; 498 } 499 return 0; 500 } 501 502 static int unpoison_page(unsigned long offset) 503 { 504 char buf[100]; 505 int len; 506 507 len = sprintf(buf, "0x%lx\n", offset); 508 len = write(hwpoison_forget_fd, buf, len); 509 if (len < 0) { 510 perror("hwpoison forget"); 511 return len; 512 } 513 return 0; 514 } 515 516 /* 517 * page frame walker 518 */ 519 520 static size_t hash_slot(uint64_t flags) 521 { 522 size_t k = HASH_KEY(flags); 523 size_t i; 524 525 /* Explicitly reserve slot 0 for flags 0: the following logic 526 * cannot distinguish an unoccupied slot from slot (flags==0). 527 */ 528 if (flags == 0) 529 return 0; 530 531 /* search through the remaining (HASH_SIZE-1) slots */ 532 for (i = 1; i < ARRAY_SIZE(page_flags); i++, k++) { 533 if (!k || k >= ARRAY_SIZE(page_flags)) 534 k = 1; 535 if (page_flags[k] == 0) { 536 page_flags[k] = flags; 537 return k; 538 } 539 if (page_flags[k] == flags) 540 return k; 541 } 542 543 fatal("hash table full: bump up HASH_SHIFT?\n"); 544 exit(EXIT_FAILURE); 545 } 546 547 static void add_page(unsigned long voffset, 548 unsigned long offset, uint64_t flags) 549 { 550 flags = kpageflags_flags(flags); 551 552 if (!bit_mask_ok(flags)) 553 return; 554 555 if (opt_hwpoison) 556 hwpoison_page(offset); 557 if (opt_unpoison) 558 unpoison_page(offset); 559 560 if (opt_list == 1) 561 show_page_range(voffset, offset, flags); 562 else if (opt_list == 2) 563 show_page(voffset, offset, flags); 564 565 nr_pages[hash_slot(flags)]++; 566 total_pages++; 567 } 568 569 #define KPAGEFLAGS_BATCH (64 << 10) /* 64k pages */ 570 static void walk_pfn(unsigned long voffset, 571 unsigned long index, 572 unsigned long count) 573 { 574 uint64_t buf[KPAGEFLAGS_BATCH]; 575 unsigned long batch; 576 unsigned long pages; 577 unsigned long i; 578 579 while (count) { 580 batch = min_t(unsigned long, count, KPAGEFLAGS_BATCH); 581 pages = kpageflags_read(buf, index, batch); 582 if (pages == 0) 583 break; 584 585 for (i = 0; i < pages; i++) 586 add_page(voffset + i, index + i, buf[i]); 587 588 index += pages; 589 count -= pages; 590 } 591 } 592 593 #define PAGEMAP_BATCH (64 << 10) 594 static void walk_vma(unsigned long index, unsigned long count) 595 { 596 uint64_t buf[PAGEMAP_BATCH]; 597 unsigned long batch; 598 unsigned long pages; 599 unsigned long pfn; 600 unsigned long i; 601 602 while (count) { 603 batch = min_t(unsigned long, count, PAGEMAP_BATCH); 604 pages = pagemap_read(buf, index, batch); 605 if (pages == 0) 606 break; 607 608 for (i = 0; i < pages; i++) { 609 pfn = pagemap_pfn(buf[i]); 610 if (pfn) 611 walk_pfn(index + i, pfn, 1); 612 } 613 614 index += pages; 615 count -= pages; 616 } 617 } 618 619 static void walk_task(unsigned long index, unsigned long count) 620 { 621 const unsigned long end = index + count; 622 unsigned long start; 623 int i = 0; 624 625 while (index < end) { 626 627 while (pg_end[i] <= index) 628 if (++i >= nr_vmas) 629 return; 630 if (pg_start[i] >= end) 631 return; 632 633 start = max_t(unsigned long, pg_start[i], index); 634 index = min_t(unsigned long, pg_end[i], end); 635 636 assert(start < index); 637 walk_vma(start, index - start); 638 } 639 } 640 641 static void add_addr_range(unsigned long offset, unsigned long size) 642 { 643 if (nr_addr_ranges >= MAX_ADDR_RANGES) 644 fatal("too many addr ranges\n"); 645 646 opt_offset[nr_addr_ranges] = offset; 647 opt_size[nr_addr_ranges] = min_t(unsigned long, size, ULONG_MAX-offset); 648 nr_addr_ranges++; 649 } 650 651 static void walk_addr_ranges(void) 652 { 653 int i; 654 655 kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY); 656 657 if (!nr_addr_ranges) 658 add_addr_range(0, ULONG_MAX); 659 660 for (i = 0; i < nr_addr_ranges; i++) 661 if (!opt_pid) 662 walk_pfn(0, opt_offset[i], opt_size[i]); 663 else 664 walk_task(opt_offset[i], opt_size[i]); 665 666 close(kpageflags_fd); 667 } 668 669 670 /* 671 * user interface 672 */ 673 674 static const char *page_flag_type(uint64_t flag) 675 { 676 if (flag & KPF_HACKERS_BITS) 677 return "(r)"; 678 if (flag & KPF_OVERLOADED_BITS) 679 return "(o)"; 680 return " "; 681 } 682 683 static void usage(void) 684 { 685 size_t i, j; 686 687 printf( 688 "page-types [options]\n" 689 " -r|--raw Raw mode, for kernel developers\n" 690 " -d|--describe flags Describe flags\n" 691 " -a|--addr addr-spec Walk a range of pages\n" 692 " -b|--bits bits-spec Walk pages with specified bits\n" 693 " -p|--pid pid Walk process address space\n" 694 #if 0 /* planned features */ 695 " -f|--file filename Walk file address space\n" 696 #endif 697 " -l|--list Show page details in ranges\n" 698 " -L|--list-each Show page details one by one\n" 699 " -N|--no-summary Don't show summary info\n" 700 " -X|--hwpoison hwpoison pages\n" 701 " -x|--unpoison unpoison pages\n" 702 " -h|--help Show this usage message\n" 703 "flags:\n" 704 " 0x10 bitfield format, e.g.\n" 705 " anon bit-name, e.g.\n" 706 " 0x10,anon comma-separated list, e.g.\n" 707 "addr-spec:\n" 708 " N one page at offset N (unit: pages)\n" 709 " N+M pages range from N to N+M-1\n" 710 " N,M pages range from N to M-1\n" 711 " N, pages range from N to end\n" 712 " ,M pages range from 0 to M-1\n" 713 "bits-spec:\n" 714 " bit1,bit2 (flags & (bit1|bit2)) != 0\n" 715 " bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1\n" 716 " bit1,~bit2 (flags & (bit1|bit2)) == bit1\n" 717 " =bit1,bit2 flags == (bit1|bit2)\n" 718 "bit-names:\n" 719 ); 720 721 for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) { 722 if (!page_flag_names[i]) 723 continue; 724 printf("%16s%s", page_flag_names[i] + 2, 725 page_flag_type(1ULL << i)); 726 if (++j > 3) { 727 j = 0; 728 putchar('\n'); 729 } 730 } 731 printf("\n " 732 "(r) raw mode bits (o) overloaded bits\n"); 733 } 734 735 static unsigned long long parse_number(const char *str) 736 { 737 unsigned long long n; 738 739 n = strtoll(str, NULL, 0); 740 741 if (n == 0 && str[0] != '') 742 fatal("invalid name or number: %s\n", str); 743 744 return n; 745 } 746 747 static void parse_pid(const char *str) 748 { 749 FILE *file; 750 char buf[5000]; 751 752 opt_pid = parse_number(str); 753 754 sprintf(buf, "/proc/%d/pagemap", opt_pid); 755 pagemap_fd = checked_open(buf, O_RDONLY); 756 757 sprintf(buf, "/proc/%d/maps", opt_pid); 758 file = fopen(buf, "r"); 759 if (!file) { 760 perror(buf); 761 exit(EXIT_FAILURE); 762 } 763 764 while (fgets(buf, sizeof(buf), file) != NULL) { 765 unsigned long vm_start; 766 unsigned long vm_end; 767 unsigned long long pgoff; 768 int major, minor; 769 char r, w, x, s; 770 unsigned long ino; 771 int n; 772 773 n = sscanf(buf, "%lx-%lx %c%c%c%c %llx %x:%x %lu", 774 &vm_start, 775 &vm_end, 776 &r, &w, &x, &s, 777 &pgoff, 778 &major, &minor, 779 &ino); 780 if (n < 10) { 781 fprintf(stderr, "unexpected line: %s\n", buf); 782 continue; 783 } 784 pg_start[nr_vmas] = vm_start / page_size; 785 pg_end[nr_vmas] = vm_end / page_size; 786 if (++nr_vmas >= MAX_VMAS) { 787 fprintf(stderr, "too many VMAs\n"); 788 break; 789 } 790 } 791 fclose(file); 792 } 793 794 static void parse_file(const char *name) 795 { 796 } 797 798 static void parse_addr_range(const char *optarg) 799 { 800 unsigned long offset; 801 unsigned long size; 802 char *p; 803 804 p = strchr(optarg, ','); 805 if (!p) 806 p = strchr(optarg, '+'); 807 808 if (p == optarg) { 809 offset = 0; 810 size = parse_number(p + 1); 811 } else if (p) { 812 offset = parse_number(optarg); 813 if (p[1] == '\0') 814 size = ULONG_MAX; 815 else { 816 size = parse_number(p + 1); 817 if (*p == ',') { 818 if (size < offset) 819 fatal("invalid range: %lu,%lu\n", 820 offset, size); 821 size -= offset; 822 } 823 } 824 } else { 825 offset = parse_number(optarg); 826 size = 1; 827 } 828 829 add_addr_range(offset, size); 830 } 831 832 static void add_bits_filter(uint64_t mask, uint64_t bits) 833 { 834 if (nr_bit_filters >= MAX_BIT_FILTERS) 835 fatal("too much bit filters\n"); 836 837 opt_mask[nr_bit_filters] = mask; 838 opt_bits[nr_bit_filters] = bits; 839 nr_bit_filters++; 840 } 841 842 static uint64_t parse_flag_name(const char *str, int len) 843 { 844 size_t i; 845 846 if (!*str || !len) 847 return 0; 848 849 if (len <= 8 && !strncmp(str, "compound", len)) 850 return BITS_COMPOUND; 851 852 for (i = 0; i < ARRAY_SIZE(page_flag_names); i++) { 853 if (!page_flag_names[i]) 854 continue; 855 if (!strncmp(str, page_flag_names[i] + 2, len)) 856 return 1ULL << i; 857 } 858 859 return parse_number(str); 860 } 861 862 static uint64_t parse_flag_names(const char *str, int all) 863 { 864 const char *p = str; 865 uint64_t flags = 0; 866 867 while (1) { 868 if (*p == ',' || *p == '=' || *p == '\0') { 869 if ((*str != '~') || (*str == '~' && all && *++str)) 870 flags |= parse_flag_name(str, p - str); 871 if (*p != ',') 872 break; 873 str = p + 1; 874 } 875 p++; 876 } 877 878 return flags; 879 } 880 881 static void parse_bits_mask(const char *optarg) 882 { 883 uint64_t mask; 884 uint64_t bits; 885 const char *p; 886 887 p = strchr(optarg, '='); 888 if (p == optarg) { 889 mask = KPF_ALL_BITS; 890 bits = parse_flag_names(p + 1, 0); 891 } else if (p) { 892 mask = parse_flag_names(optarg, 0); 893 bits = parse_flag_names(p + 1, 0); 894 } else if (strchr(optarg, '~')) { 895 mask = parse_flag_names(optarg, 1); 896 bits = parse_flag_names(optarg, 0); 897 } else { 898 mask = parse_flag_names(optarg, 0); 899 bits = KPF_ALL_BITS; 900 } 901 902 add_bits_filter(mask, bits); 903 } 904 905 static void describe_flags(const char *optarg) 906 { 907 uint64_t flags = parse_flag_names(optarg, 0); 908 909 printf("0x%016llx\t%s\t%s\n", 910 (unsigned long long)flags, 911 page_flag_name(flags), 912 page_flag_longname(flags)); 913 } 914 915 static const struct option opts[] = { 916 { "raw" , 0, NULL, 'r' }, 917 { "pid" , 1, NULL, 'p' }, 918 { "file" , 1, NULL, 'f' }, 919 { "addr" , 1, NULL, 'a' }, 920 { "bits" , 1, NULL, 'b' }, 921 { "describe" , 1, NULL, 'd' }, 922 { "list" , 0, NULL, 'l' }, 923 { "list-each" , 0, NULL, 'L' }, 924 { "no-summary", 0, NULL, 'N' }, 925 { "hwpoison" , 0, NULL, 'X' }, 926 { "unpoison" , 0, NULL, 'x' }, 927 { "help" , 0, NULL, 'h' }, 928 { NULL , 0, NULL, 0 } 929 }; 930 931 int main(int argc, char *argv[]) 932 { 933 int c; 934 935 page_size = getpagesize(); 936 937 while ((c = getopt_long(argc, argv, 938 "rp:f:a:b:d:lLNXxh", opts, NULL)) != -1) { 939 switch (c) { 940 case 'r': 941 opt_raw = 1; 942 break; 943 case 'p': 944 parse_pid(optarg); 945 break; 946 case 'f': 947 parse_file(optarg); 948 break; 949 case 'a': 950 parse_addr_range(optarg); 951 break; 952 case 'b': 953 parse_bits_mask(optarg); 954 break; 955 case 'd': 956 describe_flags(optarg); 957 exit(0); 958 case 'l': 959 opt_list = 1; 960 break; 961 case 'L': 962 opt_list = 2; 963 break; 964 case 'N': 965 opt_no_summary = 1; 966 break; 967 case 'X': 968 opt_hwpoison = 1; 969 prepare_hwpoison_fd(); 970 break; 971 case 'x': 972 opt_unpoison = 1; 973 prepare_hwpoison_fd(); 974 break; 975 case 'h': 976 usage(); 977 exit(0); 978 default: 979 usage(); 980 exit(1); 981 } 982 } 983 984 if (opt_list && opt_pid) 985 printf("voffset\t"); 986 if (opt_list == 1) 987 printf("offset\tlen\tflags\n"); 988 if (opt_list == 2) 989 printf("offset\tflags\n"); 990 991 walk_addr_ranges(); 992 993 if (opt_list == 1) 994 show_page_range(0, 0, 0); /* drain the buffer */ 995 996 if (opt_no_summary) 997 return 0; 998 999 if (opt_list) 1000 printf("\n\n"); 1001 1002 show_summary(); 1003 1004 return 0; 1005 } 1006
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.