1076 lines · 22.7 KB · C
RawDownload
/* * page-types: Tool for querying page flags * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for * more details. * * You should find a copy of v2 of the GNU General Public License somewhere on * your Linux system; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * Copyright (C) 2009 Intel corporation * * Authors: Wu Fengguang <fengguang.wu@intel.com> */ #define _LARGEFILE64_SOURCE#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <stdint.h>#include <stdarg.h>#include <string.h>#include <getopt.h>#include <limits.h>#include <assert.h>#include <sys/types.h>#include <sys/errno.h>#include <sys/fcntl.h>#include <sys/mount.h>#include <sys/statfs.h>#include "../../include/linux/magic.h"#include "../../include/linux/kernel-page-flags.h"  #ifndef MAX_PATH# define MAX_PATH 256#endif #ifndef STR# define _STR(x) #x# define STR(x) _STR(x)#endif /* * pagemap kernel ABI bits */ #define PM_ENTRY_BYTES      sizeof(uint64_t)#define PM_STATUS_BITS      3#define PM_STATUS_OFFSET    (64 - PM_STATUS_BITS)#define PM_STATUS_MASK      (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET)#define PM_STATUS(nr)       (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK)#define PM_PSHIFT_BITS      6#define PM_PSHIFT_OFFSET    (PM_STATUS_OFFSET - PM_PSHIFT_BITS)#define PM_PSHIFT_MASK      (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET)#define PM_PSHIFT(x)        (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK)#define PM_PFRAME_MASK      ((1LL << PM_PSHIFT_OFFSET) - 1)#define PM_PFRAME(x)        ((x) & PM_PFRAME_MASK) #define PM_PRESENT          PM_STATUS(4LL)#define PM_SWAP             PM_STATUS(2LL)  /* * kernel page flags */ #define KPF_BYTES		8#define PROC_KPAGEFLAGS		"/proc/kpageflags" /* [32-] kernel hacking assistances */#define KPF_RESERVED		32#define KPF_MLOCKED		33#define KPF_MAPPEDTODISK	34#define KPF_PRIVATE		35#define KPF_PRIVATE_2		36#define KPF_OWNER_PRIVATE	37#define KPF_ARCH		38#define KPF_UNCACHED		39 /* [48-] take some arbitrary free slots for expanding overloaded flags * not part of kernel API */#define KPF_READAHEAD		48#define KPF_SLOB_FREE		49#define KPF_SLUB_FROZEN		50#define KPF_SLUB_DEBUG		51 #define KPF_ALL_BITS		((uint64_t)~0ULL)#define KPF_HACKERS_BITS	(0xffffULL << 32)#define KPF_OVERLOADED_BITS	(0xffffULL << 48)#define BIT(name)		(1ULL << KPF_##name)#define BITS_COMPOUND		(BIT(COMPOUND_HEAD) | BIT(COMPOUND_TAIL)) static const char * const page_flag_names[] = {	[KPF_LOCKED]		= "L:locked",	[KPF_ERROR]		= "E:error",	[KPF_REFERENCED]	= "R:referenced",	[KPF_UPTODATE]		= "U:uptodate",	[KPF_DIRTY]		= "D:dirty",	[KPF_LRU]		= "l:lru",	[KPF_ACTIVE]		= "A:active",	[KPF_SLAB]		= "S:slab",	[KPF_WRITEBACK]		= "W:writeback",	[KPF_RECLAIM]		= "I:reclaim",	[KPF_BUDDY]		= "B:buddy", 	[KPF_MMAP]		= "M:mmap",	[KPF_ANON]		= "a:anonymous",	[KPF_SWAPCACHE]		= "s:swapcache",	[KPF_SWAPBACKED]	= "b:swapbacked",	[KPF_COMPOUND_HEAD]	= "H:compound_head",	[KPF_COMPOUND_TAIL]	= "T:compound_tail",	[KPF_HUGE]		= "G:huge",	[KPF_UNEVICTABLE]	= "u:unevictable",	[KPF_HWPOISON]		= "X:hwpoison",	[KPF_NOPAGE]		= "n:nopage",	[KPF_KSM]		= "x:ksm",	[KPF_THP]		= "t:thp", 	[KPF_RESERVED]		= "r:reserved",	[KPF_MLOCKED]		= "m:mlocked",	[KPF_MAPPEDTODISK]	= "d:mappedtodisk",	[KPF_PRIVATE]		= "P:private",	[KPF_PRIVATE_2]		= "p:private_2",	[KPF_OWNER_PRIVATE]	= "O:owner_private",	[KPF_ARCH]		= "h:arch",	[KPF_UNCACHED]		= "c:uncached", 	[KPF_READAHEAD]		= "I:readahead",	[KPF_SLOB_FREE]		= "P:slob_free",	[KPF_SLUB_FROZEN]	= "A:slub_frozen",	[KPF_SLUB_DEBUG]	= "E:slub_debug",};  static const char * const debugfs_known_mountpoints[] = {	"/sys/kernel/debug",	"/debug",	0,}; /* * data structures */ static int		opt_raw;	/* for kernel developers */static int		opt_list;	/* list pages (in ranges) */static int		opt_no_summary;	/* don't show summary */static pid_t		opt_pid;	/* process to walk */ #define MAX_ADDR_RANGES	1024static int		nr_addr_ranges;static unsigned long	opt_offset[MAX_ADDR_RANGES];static unsigned long	opt_size[MAX_ADDR_RANGES]; #define MAX_VMAS	10240static int		nr_vmas;static unsigned long	pg_start[MAX_VMAS];static unsigned long	pg_end[MAX_VMAS]; #define MAX_BIT_FILTERS	64static int		nr_bit_filters;static uint64_t		opt_mask[MAX_BIT_FILTERS];static uint64_t		opt_bits[MAX_BIT_FILTERS]; static int		page_size; static int		pagemap_fd;static int		kpageflags_fd; static int		opt_hwpoison;static int		opt_unpoison; static char		hwpoison_debug_fs[MAX_PATH+1];static int		hwpoison_inject_fd;static int		hwpoison_forget_fd; #define HASH_SHIFT	13#define HASH_SIZE	(1 << HASH_SHIFT)#define HASH_MASK	(HASH_SIZE - 1)#define HASH_KEY(flags)	(flags & HASH_MASK) static unsigned long	total_pages;static unsigned long	nr_pages[HASH_SIZE];static uint64_t		page_flags[HASH_SIZE];  /* * helper functions */ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define min_t(type, x, y) ({			\	type __min1 = (x);			\	type __min2 = (y);			\	__min1 < __min2 ? __min1 : __min2; }) #define max_t(type, x, y) ({			\	type __max1 = (x);			\	type __max2 = (y);			\	__max1 > __max2 ? __max1 : __max2; }) static unsigned long pages2mb(unsigned long pages){	return (pages * page_size) >> 20;} static void fatal(const char *x, ...){	va_list ap; 	va_start(ap, x);	vfprintf(stderr, x, ap);	va_end(ap);	exit(EXIT_FAILURE);} static int checked_open(const char *pathname, int flags){	int fd = open(pathname, flags); 	if (fd < 0) {		perror(pathname);		exit(EXIT_FAILURE);	} 	return fd;} /* * pagemap/kpageflags routines */ static unsigned long do_u64_read(int fd, char *name,				 uint64_t *buf,				 unsigned long index,				 unsigned long count){	long bytes; 	if (index > ULONG_MAX / 8)		fatal("index overflow: %lu\n", index); 	if (lseek(fd, index * 8, SEEK_SET) < 0) {		perror(name);		exit(EXIT_FAILURE);	} 	bytes = read(fd, buf, count * 8);	if (bytes < 0) {		perror(name);		exit(EXIT_FAILURE);	}	if (bytes % 8)		fatal("partial read: %lu bytes\n", bytes); 	return bytes / 8;} static unsigned long kpageflags_read(uint64_t *buf,				     unsigned long index,				     unsigned long pages){	return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages);} static unsigned long pagemap_read(uint64_t *buf,				  unsigned long index,				  unsigned long pages){	return do_u64_read(pagemap_fd, "/proc/pid/pagemap", buf, index, pages);} static unsigned long pagemap_pfn(uint64_t val){	unsigned long pfn; 	if (val & PM_PRESENT)		pfn = PM_PFRAME(val);	else		pfn = 0; 	return pfn;}  /* * page flag names */ static char *page_flag_name(uint64_t flags){	static char buf[65];	int present;	size_t i, j; 	for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) {		present = (flags >> i) & 1;		if (!page_flag_names[i]) {			if (present)				fatal("unknown flag bit %d\n", i);			continue;		}		buf[j++] = present ? page_flag_names[i][0] : '_';	} 	return buf;} static char *page_flag_longname(uint64_t flags){	static char buf[1024];	size_t i, n; 	for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) {		if (!page_flag_names[i])			continue;		if ((flags >> i) & 1)			n += snprintf(buf + n, sizeof(buf) - n, "%s,",					page_flag_names[i] + 2);	}	if (n)		n--;	buf[n] = '\0'; 	return buf;}  /* * page list and summary */ static void show_page_range(unsigned long voffset,			    unsigned long offset, uint64_t flags){	static uint64_t      flags0;	static unsigned long voff;	static unsigned long index;	static unsigned long count; 	if (flags == flags0 && offset == index + count &&	    (!opt_pid || voffset == voff + count)) {		count++;		return;	} 	if (count) {		if (opt_pid)			printf("%lx\t", voff);		printf("%lx\t%lx\t%s\n",				index, count, page_flag_name(flags0));	} 	flags0 = flags;	index  = offset;	voff   = voffset;	count  = 1;} static void show_page(unsigned long voffset,		      unsigned long offset, uint64_t flags){	if (opt_pid)		printf("%lx\t", voffset);	printf("%lx\t%s\n", offset, page_flag_name(flags));} static void show_summary(void){	size_t i; 	printf("             flags\tpage-count       MB"		"  symbolic-flags\t\t\tlong-symbolic-flags\n"); 	for (i = 0; i < ARRAY_SIZE(nr_pages); i++) {		if (nr_pages[i])			printf("0x%016llx\t%10lu %8lu  %s\t%s\n",				(unsigned long long)page_flags[i],				nr_pages[i],				pages2mb(nr_pages[i]),				page_flag_name(page_flags[i]),				page_flag_longname(page_flags[i]));	} 	printf("             total\t%10lu %8lu\n",			total_pages, pages2mb(total_pages));}  /* * page flag filters */ static int bit_mask_ok(uint64_t flags){	int i; 	for (i = 0; i < nr_bit_filters; i++) {		if (opt_bits[i] == KPF_ALL_BITS) {			if ((flags & opt_mask[i]) == 0)				return 0;		} else {			if ((flags & opt_mask[i]) != opt_bits[i])				return 0;		}	} 	return 1;} static uint64_t expand_overloaded_flags(uint64_t flags){	/* SLOB/SLUB overload several page flags */	if (flags & BIT(SLAB)) {		if (flags & BIT(PRIVATE))			flags ^= BIT(PRIVATE) | BIT(SLOB_FREE);		if (flags & BIT(ACTIVE))			flags ^= BIT(ACTIVE) | BIT(SLUB_FROZEN);		if (flags & BIT(ERROR))			flags ^= BIT(ERROR) | BIT(SLUB_DEBUG);	} 	/* PG_reclaim is overloaded as PG_readahead in the read path */	if ((flags & (BIT(RECLAIM) | BIT(WRITEBACK))) == BIT(RECLAIM))		flags ^= BIT(RECLAIM) | BIT(READAHEAD); 	return flags;} static uint64_t well_known_flags(uint64_t flags){	/* hide flags intended only for kernel hacker */	flags &= ~KPF_HACKERS_BITS; 	/* hide non-hugeTLB compound pages */	if ((flags & BITS_COMPOUND) && !(flags & BIT(HUGE)))		flags &= ~BITS_COMPOUND; 	return flags;} static uint64_t kpageflags_flags(uint64_t flags){	flags = expand_overloaded_flags(flags); 	if (!opt_raw)		flags = well_known_flags(flags); 	return flags;} /* verify that a mountpoint is actually a debugfs instance */static int debugfs_valid_mountpoint(const char *debugfs){	struct statfs st_fs; 	if (statfs(debugfs, &st_fs) < 0)		return -ENOENT;	else if (st_fs.f_type != (long) DEBUGFS_MAGIC)		return -ENOENT; 	return 0;} /* find the path to the mounted debugfs */static const char *debugfs_find_mountpoint(void){	const char *const *ptr;	char type[100];	FILE *fp; 	ptr = debugfs_known_mountpoints;	while (*ptr) {		if (debugfs_valid_mountpoint(*ptr) == 0) {			strcpy(hwpoison_debug_fs, *ptr);			return hwpoison_debug_fs;		}		ptr++;	} 	/* give up and parse /proc/mounts */	fp = fopen("/proc/mounts", "r");	if (fp == NULL)		perror("Can't open /proc/mounts for read"); 	while (fscanf(fp, "%*s %"		      STR(MAX_PATH)		      "s %99s %*s %*d %*d\n",		      hwpoison_debug_fs, type) == 2) {		if (strcmp(type, "debugfs") == 0)			break;

Lines 1–500 of 1076.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t