• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/Documentation/vm/
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