[RFC] the /proc/filecache interface

From: Fengguang Wu
Date: Mon Jun 12 2006 - 03:50:21 EST


Hello,

The /proc/filecache support has been half done. And I'd like to hear
early comments on the interface.

(The patch is not quite ready for code reviews. It is attached in case
someone are interested to have it a try.)


NOTES

- Devices are shown as MAJOR:MINOR numbers(03:00), rather than device
files(/dev/hda).

- The query is atomic in a single read() context. So if you need
consistent result, prepare a large enough read buffer.

- Parameters(only one for now: the file name) are stored in two kind of
places: one as the global default, and the others inside each opened
/proc/filecache file. They are set according to the following rules:
- on opening up a new /proc/filecache fd, it's associated
parameters are initialized by the global one;
- on writing to a /proc/filecache file to set new value for a
parameter, both the file's associated parameters and the
global one are updated.
The rules imply that:
- one can normally query the filecache use simple echo/cat
command sequences.
- for scripts that don't want to be disturbed by concurrent
queries, always reuse one single fd to expect a consistent
set of private parameters.


TODO

1) sort the inode list
a) dirty inodes for each partition
b) in use inodes(newest at top)
c) unused inodes
2) security concerns
3) split up the patch
4) documents

5) breakup the page attribute 'location' into section/node/zone
6) allow users to specify which attrs to show, examples:
file.attr = cached% dev file # show only the three attrs
file.attr = ALL -uid -gid # all attrs but uid/gid
page.attr = NONE # report only idx/len
page.state = referenced active # show only two page flags
page.state = RF AC # use acronyms

Note that (6) is necessary only if we want to do (5) and provide
support for the section/node/zone attributes. If ever these attrs
should be supported, they must be optional ones: For systems with
interleaved memory policies, the page location differs for every
successive pages, thus can make the output rather tedious and huge.

I guess the location attrs might be useful for someone. However I need strong
confirmation on this, to make sure that I'm not wasting time when doing (6).

If you thinks a lot of the page location attrs, please voice it now, thanks.


SAMPLE OUTPUT

root ~# echo -n ALL > /proc/filecache
root ~# head /proc/filecache
# file ALL
# ino size cached cached% state refcnt uid gid dev file
413605 23 24 100 -- 0 0 0 03:00(hda) /sbin/modprobe
381701 17 20 100 -- 1 0 0 03:00(hda) /bin/cat
398088 1 4 100 -- 0 0 0 03:00(hda) /root/bin/checkpath
381698 611 612 100 -- 1 0 0 03:00(hda) /bin/bash
159498 1 4 100 -- 0 0 0 03:00(hda) /etc/mtab
381719 30 32 100 -- 0 0 0 03:00(hda) /bin/rm
335993 4 4 100 -- 0 0 43 03:00(hda) /var/run/utmp
0 3687424 828 0 -- 0 0 0 00:02(bdev) 03:00
159701 5 8 100 -- 0 0 0 03:00(hda) /etc/modprobe.d/aliases
31866 1238 1240 100 -- 4 0 0 03:00(hda) /lib/tls/libc-2.3.5.so
381705 51 52 100 -- 0 0 0 03:00(hda) /bin/cp
31814 27 28 100 -- 0 0 0 03:00(hda) /lib/libblkid.so.1.0
159534 17 20 100 -- 0 0 0 03:00(hda) /etc/ld.so.cache
[...]


root ~# echo -n /lib/tls/libc-2.3.5.so > /proc/filecache
root ~# head /proc/filecache
# file /lib/tls/libc-2.3.5.so
# flags LK:locked ER:error RF:referenced UD:uptodate DT:dirty AC:active SL:slab CK:checked A1:arch_1 RS:reserved PV:private WB:writeback NS:nosave CP:compound SC:swapcache MD:mappedtodisk RC:reclaim ns:nosave_free RA:readahead MM:mmap
# idx len state refcnt location
0 21 ____RFUD__AC__________________MD______MM 4 0.0.Normal
21 1 ____RFUD__AC__________________MD______MM 3 0.0.Normal
22 3 ______UD______________________MD________ 1 0.0.Normal
25 1 ____RFUD__AC__________________MD______MM 2 0.0.Normal
26 2 ______UD______________________MD________ 1 0.0.Normal
28 1 ______UD______________________MD____RA__ 1 0.0.Normal
29 2 ______UD______________________MD________ 1 0.0.Normal


root ~# echo -n /dev/hda > /proc/filecache
root ~# head /proc/filecache
# file /dev/hda
# flags LK:locked ER:error RF:referenced UD:uptodate DT:dirty AC:active SL:slab CK:checked A1:arch_1 RS:reserved PV:private WB:writeback NS:nosave CP:compound SC:swapcache MD:mappedtodisk RC:reclaim ns:nosave_free RA:readahead MM:mmap
# idx len state refcnt location
0 2 ______UDDTAC________PV__________________ 2 0.0.Normal
4 1 ____RF____AC________PV__________________ 2 0.0.Normal
501 1 ____RF____AC________PV__________________ 2 0.0.Normal
506 1 ____RFUD____________PV__________________ 2 0.0.Normal
507 1 ____RF______________PV__________________ 2 0.0.Normal
518 1 ____RF____AC________PV__________________ 2 0.0.Normal
571 2 ____RF______________PV__________________ 2 0.0.Normal


Signed-off-by: Wu Fengguang <wfg@xxxxxxxxxxxxxxxx>
---

fs/Kconfig | 10
fs/dcache.c | 17 +
fs/inode.c | 9
fs/proc/Makefile | 1
fs/proc/filecache.c | 535 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fs.h | 1
include/linux/mm.h | 1
mm/page_alloc.c | 2
8 files changed, 574 insertions(+), 2 deletions(-)

--- linux-2.6.17-rc6-mm1.orig/fs/Kconfig
+++ linux-2.6.17-rc6-mm1/fs/Kconfig
@@ -894,6 +894,16 @@ config CONFIGFS_FS
Both sysfs and configfs can and should exist together on the
same system. One is not a replacement for the other.

+config PROC_FILECACHE
+ tristate "/proc/filecache support"
+ default m
+ depends on PROC_FS
+ help
+ This option creates a file /proc/filecache which enables one to
+ get the listing of cached files/pages.
+
+ It can be a handy tool for sysadms and desktop users.
+
endmenu

menu "Miscellaneous filesystems"
--- /dev/null
+++ linux-2.6.17-rc6-mm1/fs/proc/filecache.c
@@ -0,0 +1,524 @@
+/*
+ * linux/fs/proc/filecache.c
+ *
+ * Copyright (C) 2006 Wu Fengguang <wfg@xxxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/radix-tree.h>
+#include <linux/page-flags.h>
+#include <linux/pagevec.h>
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+static char name_all[] = "ALL";
+static char *global_default_name = name_all;
+static DEFINE_MUTEX(fcache_target_mutex);
+
+#define PG_LAST PG_readahead
+#define PG_MASK ((2UL << PG_LAST) - 1)
+#define FCACHE_PG_MMAP (PG_LAST + 1)
+#define FCACHE_PG_COUNT (PG_LAST + 2)
+
+/*
+ * Page state names, prefixed by their abbreviations.
+ */
+const char *flag_names[FCACHE_PG_COUNT] = {
+ [PG_locked] = "LK:locked",
+ [PG_error] = "ER:error",
+ [PG_referenced] = "RF:referenced",
+ [PG_uptodate] = "UD:uptodate",
+
+ [PG_dirty] = "DT:dirty",
+ /* [PG_lru] = "RU:lru", */
+ [PG_active] = "AC:active",
+ [PG_slab] = "SL:slab",
+
+ [PG_checked] = "CK:checked",
+ [PG_arch_1] = "A1:arch_1",
+ [PG_reserved] = "RS:reserved",
+ [PG_private] = "PV:private",
+
+ [PG_writeback] = "WB:writeback",
+ [PG_nosave] = "NS:nosave",
+ [PG_compound] = "CP:compound",
+ [PG_swapcache] = "SC:swapcache",
+
+ [PG_mappedtodisk] = "MD:mappedtodisk",
+ [PG_reclaim] = "RC:reclaim",
+ [PG_nosave_free] = "ns:nosave_free",
+ /* [PG_buddy] = "BD:buddy", */
+
+ [PG_readahead] = "RA:readahead",
+
+ [FCACHE_PG_MMAP] = "MM:mmap",
+};
+
+/*
+ * Inode listing.
+ *
+ * Usage:
+ * echo -n ALL > /proc/filecache
+ * cat /proc/filecache
+ *
+ * TODO
+ * show the files in a sorted manner(newest ones at top):
+ * - list_for_each(super_blocks)/list_for_each_prev(s_dirty)
+ * - list_for_each(inode_in_use)
+ * - list_for_each(inode_unused)
+ */
+
+static void show_inode(struct seq_file *m, struct inode *inode)
+{
+ char state[] = "--"; /* dirty, locked */
+ struct dentry *alias;
+ loff_t size = i_size_read(inode);
+ unsigned long pgcached = inode->i_mapping->nrpages;
+ int percent;
+ int refcnt;
+
+ if (!size || !pgcached)
+ return;
+
+ percent = min(100UL, 100 * (pgcached << PAGE_CACHE_SHIFT) /
+ (unsigned long)size);
+
+ if (inode->i_state & (I_DIRTY_DATASYNC|I_DIRTY_PAGES))
+ state[0] = 'D';
+ if (inode->i_state & I_LOCK)
+ state[0] = 'L';
+
+ refcnt = 0;
+ list_for_each_entry(alias, &inode->i_dentry, d_alias) {
+ refcnt += atomic_read(&alias->d_count);
+ }
+
+ seq_printf(m, "%10lu %10llu %8lu %6d\t%s\t%d\t%u\t%u\t%02x:%02x(%s)\t",
+ inode->i_ino,
+ DIV_ROUND_UP(size, 1024),
+ pgcached << (PAGE_CACHE_SHIFT - 10),
+ percent,
+ state,
+ /* atomic_read(&inode->i_count), */
+ refcnt,
+ inode->i_uid,
+ inode->i_gid,
+ MAJOR(inode->i_sb->s_dev),
+ MINOR(inode->i_sb->s_dev),
+ inode->i_sb->s_id);
+
+ if (list_empty(&inode->i_dentry)) {
+ if (!atomic_read(&inode->i_count))
+ seq_puts(m, "NONAME\n");
+ else
+ seq_printf(m, "%02x:%02x\n",
+ imajor(inode), iminor(inode));
+ } else {
+ alias = list_entry(inode->i_dentry.next,
+ struct dentry, d_alias);
+ seq_path(m, NULL, alias, " \t\n\\");
+ seq_putc(m, '\n');
+ }
+}
+
+static int may_show_inode(struct inode *inode)
+{
+ if (!inode->i_mapping->nrpages)
+ return 0;
+
+ if (!current->uid)
+ return 1;
+
+ return !permission(inode, MAY_READ, NULL);
+}
+
+static int ftable_show(struct seq_file *m, void *v)
+{
+ unsigned long index = *(loff_t *) v;
+ struct hlist_head *head = get_inode_hash_budget(index);
+ struct hlist_node *node;
+ struct inode * inode;
+
+ if (index == 0) {
+ seq_printf(m, "# file %s\n" , name_all);
+ seq_puts(m, "# ino size cached cached%"
+ "\tstate\trefcnt\tuid\tgid\tdev\t\tfile\n");
+ }
+
+ BUG_ON (head == NULL);
+ hlist_for_each (node, head) {
+ inode = hlist_entry(node, struct inode, i_hash);
+
+ if (may_show_inode(inode))
+ show_inode(m, inode);
+ }
+
+ return 0;
+}
+
+static void *ftable_start(struct seq_file *m, loff_t *pos)
+{
+ spin_lock(&inode_lock);
+
+ return get_inode_hash_budget(*pos) ? pos : NULL;
+}
+
+static void *ftable_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ (*pos)++;
+
+ return get_inode_hash_budget(*pos) ? pos : NULL;
+}
+
+static void ftable_stop(struct seq_file *m, void *v)
+{
+ spin_unlock(&inode_lock);
+}
+
+
+/*
+ * Show cached pages of a file.
+ *
+ * Usage:
+ * echo -n 'file name' > /proc/filecache
+ * cat /proc/filecache
+ */
+
+static unsigned long page_flags(struct page* page)
+{
+ unsigned long flags;
+
+ flags = page->flags & PG_MASK;
+
+ if (page_mapped(page))
+ flags |= (1UL << FCACHE_PG_MMAP);
+
+ return flags;
+}
+
+static int pages_similiar(struct page* page0, struct page* page)
+{
+ if (page_count(page0) != page_count(page))
+ return 0;
+
+ if (page_flags(page0) != page_flags(page))
+ return 0;
+
+ if (page_zone_id(page0) != page_zone_id(page))
+ return 0;
+
+ return 1;
+}
+
+static void show_range(struct seq_file *m, struct page* page, unsigned long len)
+{
+ const char **s;
+ unsigned long flags;
+
+ if (!m || !page)
+ return;
+
+ seq_printf(m, "%lu\t%lu\t", page->index, len);
+
+ for ( s = flag_names, flags = page_flags(page);
+ s - flag_names < FCACHE_PG_COUNT;
+ s++, flags >>= 1)
+ if (*s)
+ seq_printf(m, "%.2s", (flags & 1UL) ? *s : "__");
+
+ BUG_ON(!PageLRU(page));
+ BUG_ON(PageBuddy(page));
+
+ seq_printf(m, "\t%d\t%lu.%lu.%s\n",
+ page_count(page),
+ page_to_section(page),
+ page_to_nid(page),
+ zone_names[page_zonenum(page)]);
+}
+
+#define MAX_LINES 50
+static pgoff_t show_file_cache(struct seq_file *m,
+ struct address_space *mapping, pgoff_t start)
+{
+ int i;
+ int lines = 0;
+ pgoff_t len = 0;
+ struct pagevec pvec;
+ struct page *page;
+ struct page *page0 = NULL;
+
+ for (;;) {
+ pagevec_init(&pvec, 0);
+ pvec.nr = radix_tree_gang_lookup(&mapping->page_tree,
+ (void **)pvec.pages, start + len, PAGEVEC_SIZE);
+
+ if (pvec.nr == 0) {
+ show_range(m, page0, len);
+ start = ULONG_MAX;
+ goto out;
+ }
+
+ if (!page0)
+ page0 = pvec.pages[0];
+
+ for (i = 0; i < pvec.nr; i++) {
+ page = pvec.pages[i];
+
+ if (page->index == start + len &&
+ pages_similiar(page0, page))
+ len++;
+ else {
+ show_range(m, page0, len);
+ page0 = page;
+ start = page->index;
+ len = 1;
+ if (++lines > MAX_LINES)
+ goto out;
+ }
+ }
+ }
+
+out:
+ return start;
+}
+
+static int fcache_show(struct seq_file *m, void *v)
+{
+ struct file *file = (struct file *)(unsigned long)(m->version);
+ pgoff_t offset;
+
+ if (!file)
+ return ftable_show(m, v);
+
+ offset = *(loff_t *) v;
+ BUG_ON (offset != (pgoff_t)m->private);
+
+ if (!offset) { /* print header */
+ int i;
+ seq_puts(m, "# file ");
+ seq_path(m, file->f_vfsmnt, file->f_dentry, " \t\n\\");
+ seq_puts(m, "\n# flags");
+ for (i = 0; i < FCACHE_PG_COUNT; i++)
+ if (flag_names[i])
+ seq_printf(m, " %s", flag_names[i]);
+ seq_puts(m, "\n# idx\tlen\t\t\tstate\t\t\t\trefcnt\tlocation\n");
+ }
+
+ m->private = (void *)show_file_cache(m, file->f_mapping, offset);
+
+ return 0;
+}
+
+static int file_has_page(struct file *file, pgoff_t offset)
+{
+ loff_t size = i_size_read(file->f_mapping->host);
+ pgoff_t pages = DIV_ROUND_UP(size, PAGE_CACHE_SIZE);
+
+ return offset < pages;
+}
+
+static void *fcache_start(struct seq_file *m, loff_t *pos)
+{
+ struct file *file = (struct file *)(unsigned long)(m->version);
+
+ if (!file)
+ return ftable_start(m, pos);
+
+ read_lock_irq(&file->f_mapping->tree_lock);
+
+ if (*pos)
+ return NULL;
+
+ m->private = NULL;
+ return pos;
+}
+
+static void *fcache_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct file *file = (struct file *)(unsigned long)(m->version);
+
+ if (!file)
+ return ftable_next(m, v, pos);
+
+ *pos = (unsigned long)m->private;
+ /* *pos = show_file_cache(NULL, file->f_mapping, *pos); */
+
+ return file_has_page(file, (pgoff_t)m->private) ? pos : NULL;
+}
+
+static void fcache_stop(struct seq_file *m, void *v)
+{
+ struct file *file = (struct file *)(unsigned long)(m->version);
+
+ if (!file)
+ return ftable_stop(m, v);
+
+ read_unlock_irq(&file->f_mapping->tree_lock);
+}
+
+struct seq_operations seq_filecache_op = {
+ .start = fcache_start,
+ .next = fcache_next,
+ .stop = fcache_stop,
+ .show = fcache_show,
+};
+
+
+/*
+ * Target file management.
+ *
+ * Target file is stored in proc_file->f_version:
+ * - NULL: show a comprehensive cached inode listing
+ * - filp: show cached pages of filp
+ */
+
+/*
+ * Close target file associated with @file.
+ */
+static void fcache_target_close(struct file *file)
+{
+ if (file->f_version) {
+ filp_close((struct file *)file->f_version, NULL);
+ file->f_version = 0;
+ }
+}
+
+/*
+ * Open target file specified by @name.
+ * And associated it with @file.
+ */
+static struct file* fcache_target_open(struct file *file, const char *name)
+{
+ struct file *tf;
+
+ if (name == name_all)
+ tf = NULL;
+ else
+ tf = filp_open(name, O_RDONLY|O_LARGEFILE, 0);
+
+ if (!IS_ERR(tf))
+ file->f_version = (unsigned long)tf;
+
+ return tf;
+}
+
+/*
+ * Associate @file with a default target file.
+ */
+static void fcache_target_init(struct file *file)
+{
+ mutex_lock(&fcache_target_mutex);
+ fcache_target_open(file, global_default_name);
+ mutex_unlock(&fcache_target_mutex);
+}
+
+/*
+ * - set @name as new global default.
+ * - renew target file of @file.
+ */
+static struct file* fcache_target_update(struct file *file, char *name)
+{
+ struct file *tf = NULL;
+
+ mutex_lock(&fcache_target_mutex);
+
+ if (global_default_name != name_all)
+ kfree(global_default_name);
+
+ if (!strcmp(name, name_all)) {
+ kfree(name);
+ name = name_all;
+ }
+
+ global_default_name = name;
+ fcache_target_close(file);
+ tf = fcache_target_open(file, name);
+
+ mutex_unlock(&fcache_target_mutex);
+
+ return tf;
+}
+
+static int filecache_open(struct inode *inode, struct file *file)
+{
+ int ret = seq_open(file, &seq_filecache_op);
+ fcache_target_init(file);
+ return ret;
+}
+
+static int filecache_release(struct inode *inode, struct file *file)
+{
+ fcache_target_close(file);
+ return seq_release(inode, file);
+}
+
+ssize_t filecache_write(struct file *file, const char __user * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct file *err;
+ char *name;
+
+ if (*ppos)
+ return -EINVAL;
+
+ if (count >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ name = kmalloc(count+1, GFP_KERNEL);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name, buffer, count)) {
+ kfree(name);
+ return -EFAULT;
+ }
+ name[count] = '\0';
+
+ err = fcache_target_update(file, name);
+
+ if (IS_ERR(err))
+ return (ssize_t)err;
+ else
+ return count;
+}
+
+static struct file_operations proc_filecache_fops = {
+ .owner = THIS_MODULE,
+ .open = filecache_open,
+ .release = filecache_release,
+ .write = filecache_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+
+static __init int file_cache_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_entry("filecache", 0, NULL);
+ if (entry)
+ entry->proc_fops = &proc_filecache_fops;
+
+ return 0;
+}
+
+static void file_cache_exit(void)
+{
+ remove_proc_entry("filecache", NULL);
+}
+
+MODULE_AUTHOR("Wu Fengguang <wfg@xxxxxxxxxxxxxxxx>");
+MODULE_LICENSE("GPL");
+
+module_init(file_cache_init);
+module_exit(file_cache_exit);
--- linux-2.6.17-rc6-mm1.orig/fs/inode.c
+++ linux-2.6.17-rc6-mm1/fs/inode.c
@@ -1328,6 +1328,15 @@ void wake_up_inode(struct inode *inode)
wake_up_bit(&inode->i_state, __I_LOCK);
}

+struct hlist_head * get_inode_hash_budget(unsigned long index)
+{
+ if (index >= (1 << i_hash_shift))
+ return NULL;
+
+ return inode_hashtable + index;
+}
+EXPORT_SYMBOL_GPL(get_inode_hash_budget);
+
static __initdata unsigned long ihash_entries;
static int __init set_ihash_entries(char *str)
{
--- linux-2.6.17-rc6-mm1.orig/include/linux/fs.h
+++ linux-2.6.17-rc6-mm1/include/linux/fs.h
@@ -1674,6 +1674,7 @@ extern void remove_inode_hash(struct ino
static inline void insert_inode_hash(struct inode *inode) {
__insert_inode_hash(inode, inode->i_ino);
}
+struct hlist_head * get_inode_hash_budget(unsigned long index);

extern struct file * get_empty_filp(void);
extern void file_move(struct file *f, struct list_head *list);
--- linux-2.6.17-rc6-mm1.orig/fs/dcache.c
+++ linux-2.6.17-rc6-mm1/fs/dcache.c
@@ -1478,7 +1478,10 @@ static char * __d_path( struct dentry *d

if (dentry == root && vfsmnt == rootmnt)
break;
- if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ if (unlikely(!vfsmnt)) {
+ if (IS_ROOT(dentry))
+ break;
+ } else if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
/* Global root? */
spin_lock(&vfsmount_lock);
if (vfsmnt->mnt_parent == vfsmnt) {
--- linux-2.6.17-rc6-mm1.orig/fs/proc/Makefile
+++ linux-2.6.17-rc6-mm1/fs/proc/Makefile
@@ -13,3 +13,4 @@ proc-y += inode.o root.o base.o ge
proc-$(CONFIG_PROC_KCORE) += kcore.o
proc-$(CONFIG_PROC_VMCORE) += vmcore.o
proc-$(CONFIG_PROC_DEVICETREE) += proc_devtree.o
+proc-$(CONFIG_PROC_FILECACHE) += filecache.o
--- linux-2.6.17-rc6-mm1.orig/include/linux/mm.h
+++ linux-2.6.17-rc6-mm1/include/linux/mm.h
@@ -483,6 +483,7 @@ static inline unsigned long page_zonenum

struct zone;
extern struct zone *zone_table[];
+extern char *zone_names[];

static inline int page_zone_id(struct page *page)
{
--- linux-2.6.17-rc6-mm1.orig/mm/page_alloc.c
+++ linux-2.6.17-rc6-mm1/mm/page_alloc.c
@@ -81,7 +81,7 @@ EXPORT_SYMBOL(totalram_pages);
struct zone *zone_table[1 << ZONETABLE_SHIFT] __read_mostly;
EXPORT_SYMBOL(zone_table);

-static char *zone_names[MAX_NR_ZONES] = { "DMA", "DMA32", "Normal", "HighMem" };
+char *zone_names[MAX_NR_ZONES] = { "DMA", "DMA32", "Normal", "HighMem" };
int min_free_kbytes = 1024;

unsigned long __meminitdata nr_kernel_pages;
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/