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