Dave,
Sorry, I can't reply the mail to orignial thread, as my vpn got
blocked and I can't access my gmail account.
This is the v2 version of files -M and -m support. Following issues got
fixed in this version,
1. Patch is based on current git HEAD
2. Fixed warnings during make warn
3. files -M show full file path
4. Change PGCACHE-PGS to PAGE-COUNT
Some background informtion: Each Linux fd points to a file struct.
And each file struct or inode had a address space. If the file is
a regular file, the address space defined a page tree, which are the
page caches for this file. Some of file types don't use page cache.
Page tree support was from 2.6 kernel, I couldn't access early Linux
commit log. But at least 2.6.8 already had the definition.
5. Fixied page tree dump bugs, make sure page dump matched page number
This is a bug in my radix tree dump code.
I will share a method to valid page dumps later.
6. Use common dump_mem_map api instead of SPARSEMEM version
Original dump_mem_map had a bug, I use SPARSEMEM version as workaround.
But forgot switch it back.
7. Fixed the page tree dump bugs on 32bit kernel
This is a bug in my radix tree dump code.
I had fixed and verified it on Fedora 20 32bit kernel.
8. Check address space page tree number, will print error kernel is old
I checked struct address_space.page_tree member.
9. Reused existing radix tree dump api, and extended it.
RADIX_TREE_DUMP has no users in crash, this patch is first user.
I add a call back to make it more flexible.
10. Page count is gotten from address_space.nrpages
Original way(loop in radix tree) is not efficient.
Let me know your comments, thanks.
files: support dump file pages from its address space
Added two options in files command,
1. -M option, which allows dump address space and page number for each files
2. -m option, which could dump each pages in given address mapping
The foreach command also could work with -M, so that we can easily
find which processes/files hold most page cache pages within the system.
Signed-off-by: Yong Yang <yangoliver(a)gmail.com>
---
defs.h | 11 ++++++-
filesys.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
kernel.c | 4 +--
memory.c | 73 +++++++++++++++++++++++++++++++++++++++++
task.c | 25 +++++++++++---
5 files changed, 197 insertions(+), 27 deletions(-)
diff --git a/defs.h b/defs.h
index b25b505..608e09f 100644
--- a/defs.h
+++ b/defs.h
@@ -1111,6 +1111,7 @@ extern struct machdep_table *machdep;
#define FOREACH_a_FLAG (0x4000000)
#define FOREACH_G_FLAG (0x8000000)
#define FOREACH_F_FLAG2 (0x10000000)
+#define FOREACH_M_FLAG (0x20000000)
#define FOREACH_PS_EXCLUSIVE \
(FOREACH_g_FLAG|FOREACH_a_FLAG|FOREACH_t_FLAG|FOREACH_c_FLAG|FOREACH_p_FLAG|FOREACH_l_FLAG|FOREACH_r_FLAG|FOREACH_m_FLAG)
@@ -1416,6 +1417,7 @@ struct offset_table { /* stash of commonly-used
offsets */
long inode_i_flock;
long inode_i_fop;
long inode_i_mapping;
+ long address_space_page_tree;
long address_space_nrpages;
long vfsmount_mnt_next;
long vfsmount_mnt_devname;
@@ -2286,11 +2288,13 @@ struct vm_table { /* kernel VM-related data */
#define PAGEFLAGS (0x4000000)
#define SLAB_OVERLOAD_PAGE (0x8000000)
#define SLAB_CPU_CACHE (0x10000000)
+#define AS_PAGE_TREE (0x20000000)
#define IS_FLATMEM() (vt->flags & FLATMEM)
#define IS_DISCONTIGMEM() (vt->flags & DISCONTIGMEM)
#define IS_SPARSEMEM() (vt->flags & SPARSEMEM)
#define IS_SPARSEMEM_EX() (vt->flags & SPARSEMEM_EX)
+#define IS_AS_PAGE_TREE() (vt->flags & AS_PAGE_TREE)
#define COMMON_VADDR_SPACE() (vt->flags & COMMON_VADDR)
#define PADDR_PRLEN (vt->paddr_prlen)
@@ -2598,6 +2602,7 @@ struct load_module {
#define PRINT_SINGLE_VMA (0x80)
#define PRINT_RADIX_10 (0x100)
#define PRINT_RADIX_16 (0x200)
+#define PRINT_PAGES (0x400)
#define MIN_PAGE_SIZE (4096)
@@ -4707,6 +4712,9 @@ void alter_stackbuf(struct bt_info *);
int vaddr_type(ulong, struct task_context *);
char *format_stack_entry(struct bt_info *bt, char *, ulong, ulong);
int in_user_stack(ulong, ulong);
+void dump_file_address_mappings(ulong);
+long get_page_tree_count(ulong i_mapping);
+int is_as_page_tree_supported(void);
/*
* filesys.c
@@ -4747,12 +4755,13 @@ struct radix_tree_pair {
ulong index;
void *value;
};
-ulong do_radix_tree(ulong, int, struct radix_tree_pair *);
+ulong do_radix_tree(ulong, int, struct radix_tree_pair *, int (*)(ulong));
int file_dump(ulong, ulong, ulong, int, int);
#define DUMP_FULL_NAME 1
#define DUMP_INODE_ONLY 2
#define DUMP_DENTRY_ONLY 4
#define DUMP_EMPTY_FILE 8
+#define DUMP_FILE_PAGE 16
#endif /* !GDB_COMMON */
int same_file(char *, char *);
#ifndef GDB_COMMON
diff --git a/filesys.c b/filesys.c
index 0573fe6..f0ec78b 100644
--- a/filesys.c
+++ b/filesys.c
@@ -2187,11 +2187,12 @@ cmd_files(void)
int subsequent;
struct reference reference, *ref;
char *refarg;
+ int open_flags = 0;
ref = NULL;
refarg = NULL;
- while ((c = getopt(argcnt, args, "d:R:")) != EOF) {
+ while ((c = getopt(argcnt, args, "d:R:m:M")) != EOF) {
switch(c)
{
case 'R':
@@ -2209,7 +2210,20 @@ cmd_files(void)
value = htol(optarg, FAULT_ON_ERROR, NULL);
display_dentry_info(value);
return;
-
+ case 'm':
+ if (is_as_page_tree_supported()) {
+ value = htol(optarg, FAULT_ON_ERROR, NULL);
+ dump_file_address_mappings(value);
+ } else {
+ option_not_supported('m');
+ }
+ return;
+ case 'M':
+ if (is_as_page_tree_supported())
+ open_flags |= PRINT_PAGES;
+ else
+ option_not_supported('M');
+ break;
default:
argerrs++;
break;
@@ -2222,7 +2236,9 @@ cmd_files(void)
if (!args[optind]) {
if (!ref)
print_task_header(fp, CURRENT_CONTEXT(), 0);
- open_files_dump(CURRENT_TASK(), 0, ref);
+
+ open_files_dump(CURRENT_TASK(), open_flags, ref);
+
return;
}
@@ -2241,7 +2257,7 @@ cmd_files(void)
for (tc = pid_to_context(value); tc; tc = tc->tc_next) {
if (!ref)
print_task_header(fp, tc, subsequent);
- open_files_dump(tc->task, 0, ref);
+ open_files_dump(tc->task, open_flags, ref);
fprintf(fp, "\n");
}
break;
@@ -2249,7 +2265,7 @@ cmd_files(void)
case STR_TASK:
if (!ref)
print_task_header(fp, tc, subsequent);
- open_files_dump(tc->task, 0, ref);
+ open_files_dump(tc->task, open_flags, ref);
break;
case STR_INVALID:
@@ -2321,6 +2337,7 @@ open_files_dump(ulong task, int flags, struct reference *ref)
char buf4[BUFSIZE];
char root_pwd[BUFSIZE];
int root_pwd_printed = 0;
+ int file_dump_flags = 0;
BZERO(root_pathname, BUFSIZE);
BZERO(pwd_pathname, BUFSIZE);
@@ -2329,15 +2346,27 @@ open_files_dump(ulong task, int flags, struct reference *ref)
fdtable_buf = GETBUF(SIZE(fdtable));
fill_task_struct(task);
- sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
- space(MINSPACE),
- mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "FILE"),
- space(MINSPACE),
- mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
- space(MINSPACE),
- mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "INODE"),
- space(MINSPACE),
- space(MINSPACE));
+ if (flags & PRINT_PAGES) {
+ sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
+ space(MINSPACE),
+ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "ADDR-SPACE"),
+ space(MINSPACE),
+ mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "PAGE-COUNT"),
+ space(MINSPACE),
+ mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "INODE"),
+ space(MINSPACE),
+ space(MINSPACE));
+ } else {
+ sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
+ space(MINSPACE),
+ mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "FILE"),
+ space(MINSPACE),
+ mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
+ space(MINSPACE),
+ mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "INODE"),
+ space(MINSPACE),
+ space(MINSPACE));
+ }
tc = task_to_context(task);
@@ -2523,6 +2552,10 @@ open_files_dump(ulong task, int flags, struct reference *ref)
return;
}
+ file_dump_flags = DUMP_FULL_NAME | DUMP_EMPTY_FILE;
+ if (flags & PRINT_PAGES)
+ file_dump_flags |= DUMP_FILE_PAGE;
+
j = 0;
for (;;) {
unsigned long set;
@@ -2539,8 +2572,7 @@ open_files_dump(ulong task, int flags, struct reference *ref)
if (ref && file) {
open_tmpfile();
- if (file_dump(file, 0, 0, i,
- DUMP_FULL_NAME|DUMP_EMPTY_FILE)) {
+ if (file_dump(file, 0, 0, i, file_dump_flags)) {
BZERO(buf4, BUFSIZE);
rewind(pc->tmpfile);
ret = fgets(buf4, BUFSIZE,
@@ -2558,8 +2590,7 @@ open_files_dump(ulong task, int flags, struct reference *ref)
fprintf(fp, "%s", files_header);
header_printed = 1;
}
- file_dump(file, 0, 0, i,
- DUMP_FULL_NAME|DUMP_EMPTY_FILE);
+ file_dump(file, 0, 0, i, file_dump_flags);
}
}
i++;
@@ -2754,6 +2785,8 @@ file_dump(ulong file, ulong dentry, ulong inode, int fd, int flags)
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
+ ulong i_mapping = 0;
+ ulong count = 0;
file_buf = NULL;
@@ -2863,6 +2896,28 @@ file_dump(ulong file, ulong dentry, ulong inode, int fd, int
flags)
type,
space(MINSPACE),
pathname+1);
+ } else if (flags & DUMP_FILE_PAGE) {
+ i_mapping = ULONG(inode_buf + OFFSET(inode_i_mapping));
+ count = get_page_tree_count(i_mapping);
+
+ fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
+ fd,
+ space(MINSPACE),
+ mkstring(buf1, VADDR_PRLEN,
+ CENTER|RJUST|LONG_HEX,
+ MKSTR(i_mapping)),
+ space(MINSPACE),
+ mkstring(buf2, VADDR_PRLEN,
+ CENTER|RJUST|LONG_DEC,
+ MKSTR(count)),
+ space(MINSPACE),
+ mkstring(buf3, VADDR_PRLEN,
+ CENTER|RJUST|LONG_HEX,
+ MKSTR(inode)),
+ space(MINSPACE),
+ type,
+ space(MINSPACE),
+ pathname);
} else {
fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
fd,
@@ -3877,9 +3932,13 @@ ulong RADIX_TREE_MAP_MASK = UNINITIALIZED;
* RADIX_TREE_GATHER; the dimension (max count) of the array may
* be stored in the index field of the first structure to avoid
* any chance of an overrun.
+ *
+ * entry_ops: The operation against each of returned entry value.
+ * Only used by RADIX_TREE_DUMP.
*/
ulong
-do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
+do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp,
+ int (*entry_ops)(ulong))
{
int i, ilen, height;
long nlen;
@@ -3970,7 +4029,19 @@ do_radix_tree(ulong root, int flag, struct radix_tree_pair *rtp)
for (index = count = 0; index <= maxindex; index++) {
if ((ret =
radix_tree_lookup(root_rnode, index, height))) {
- fprintf(fp, "[%ld] %lx\n", index, (ulong)ret);
+ if (entry_ops == NULL) {
+ /* Default operation */
+ fprintf(fp, "[%ld] %lx\n",
+ index, (ulong)ret);
+ } else {
+ /* Caller defined operation */
+ if (entry_ops((ulong)ret) != 0) {
+ error(FATAL, "do_radix_tree: "
+ "dump operation failed, "
+ "count: %ld\n", count);
+ return -EIO;
+ }
+ }
count++;
}
}
diff --git a/kernel.c b/kernel.c
index cb8084a..53d2e1d 100644
--- a/kernel.c
+++ b/kernel.c
@@ -5746,12 +5746,12 @@ get_irq_desc_addr(int irq)
return addr;
cnt = do_radix_tree(symbol_value("irq_desc_tree"),
- RADIX_TREE_COUNT, NULL);
+ RADIX_TREE_COUNT, NULL, NULL);
len = sizeof(struct radix_tree_pair) * (cnt+1);
rtp = (struct radix_tree_pair *)GETBUF(len);
rtp[0].index = cnt;
cnt = do_radix_tree(symbol_value("irq_desc_tree"),
- RADIX_TREE_GATHER, rtp);
+ RADIX_TREE_GATHER, rtp, NULL);
if (kt->highest_irq == 0)
kt->highest_irq = rtp[cnt-1].index;
diff --git a/memory.c b/memory.c
index 765732b..0102dbc 100644
--- a/memory.c
+++ b/memory.c
@@ -292,6 +292,7 @@ static void dump_per_cpu_offsets(void);
static void dump_page_flags(ulonglong);
static ulong kmem_cache_nodelists(ulong);
static void dump_hstates(void);
+static int dump_file_page(ulong);
/*
* Memory display modes specific to this file.
@@ -476,6 +477,9 @@ vm_init(void)
MEMBER_OFFSET_INIT(block_device_bd_list, "block_device",
"bd_list");
MEMBER_OFFSET_INIT(block_device_bd_disk, "block_device",
"bd_disk");
MEMBER_OFFSET_INIT(inode_i_mapping, "inode", "i_mapping");
+ MEMBER_OFFSET_INIT(address_space_page_tree, "address_space",
"page_tree");
+ if (VALID_MEMBER(address_space_page_tree))
+ vt->flags |= AS_PAGE_TREE;
MEMBER_OFFSET_INIT(address_space_nrpages, "address_space",
"nrpages");
if (INVALID_MEMBER(address_space_nrpages))
MEMBER_OFFSET_INIT(address_space_nrpages, "address_space",
"__nrpages");
@@ -6465,6 +6469,75 @@ translate_page_flags(char *buffer, ulong flags)
}
/*
+ * Page tree dump ops.
+ */
+static int
+dump_file_page(ulong page)
+{
+ struct meminfo meminfo;
+
+ BZERO(&meminfo, sizeof(struct meminfo));
+ meminfo.spec_addr = page;
+ meminfo.memtype = KVADDR;
+ meminfo.flags = ADDRESS_SPECIFIED;
+ dump_mem_map(&meminfo);
+
+ return 0;
+}
+
+/*
+ * The address space file mapping radix tree walker.
+ */
+void
+dump_file_address_mappings(ulong i_mapping)
+{
+ ulong root_rnode;
+ ulong count;
+
+ root_rnode = i_mapping + OFFSET(address_space_page_tree);
+ count = get_page_tree_count(i_mapping);
+ fprintf(fp, "Address Space %lx, page tree %lx, %ld pages\n\n",
+ i_mapping, root_rnode, count);
+
+ /* Dump each pages in radix tree */
+ (void) do_radix_tree(root_rnode, RADIX_TREE_DUMP,
+ NULL, &dump_file_page);
+
+ return;
+}
+
+/*
+ * Get the page count for the specific mapping
+ */
+long
+get_page_tree_count(ulong i_mapping)
+{
+ ulong address_space = i_mapping;
+ char *address_space_buf;
+ ulong nrpages = 0;
+
+ address_space_buf = GETBUF(SIZE(address_space));
+
+ readmem(address_space, KVADDR, address_space_buf,
+ SIZE(address_space), "address_space buffer",
+ FAULT_ON_ERROR);
+ nrpages = ULONG(address_space_buf + OFFSET(address_space_nrpages));
+
+ FREEBUF(address_space_buf);
+
+ return nrpages;
+}
+
+/*
+ * Check the availability of address space page tree
+ */
+int
+is_as_page_tree_supported(void)
+{
+ return (IS_AS_PAGE_TREE() ? TRUE : FALSE);
+}
+
+/*
* dump_page_hash_table() displays the entries in each page_hash_table.
*/
diff --git a/task.c b/task.c
index 45be68c..4c95259 100644
--- a/task.c
+++ b/task.c
@@ -5612,7 +5612,7 @@ cmd_foreach(void)
BZERO(&foreach_data, sizeof(struct foreach_data));
fd = &foreach_data;
- while ((c = getopt(argcnt, args, "R:vomlgersStTpukcfFxhdaG")) != EOF)
{
+ while ((c = getopt(argcnt, args, "R:vomMlgersStTpukcfFxhdaG")) != EOF)
{
switch(c)
{
case 'R':
@@ -5636,6 +5636,10 @@ cmd_foreach(void)
fd->flags |= FOREACH_m_FLAG;
break;
+ case 'M':
+ fd->flags |= FOREACH_M_FLAG;
+ break;
+
case 'l':
fd->flags |= FOREACH_l_FLAG;
break;
@@ -6140,6 +6144,13 @@ foreach(struct foreach_data *fd)
print_header = FALSE;
break;
+ case FOREACH_FILES:
+ if (fd->flags & FOREACH_m_FLAG)
+ error(FATAL,
+ "foreach files command does not "
+ "support -m option\n");
+ break;
+
case FOREACH_TEST:
break;
}
@@ -6366,9 +6377,15 @@ foreach(struct foreach_data *fd)
case FOREACH_FILES:
pc->curcmd = "files";
- open_files_dump(tc->task,
- fd->flags & FOREACH_i_FLAG ?
- PRINT_INODES : 0,
+ cmdflags = 0;
+
+ if (fd->flags & FOREACH_i_FLAG)
+ cmdflags |= PRINT_INODES;
+ if (fd->flags & FOREACH_M_FLAG)
+ cmdflags |= PRINT_PAGES;
+
+ open_files_dump(tc->task,
+ cmdflags,
fd->reference ? ref : NULL);
break;
--
1.9.1