On Mon, Dec 04, 2006 at 09:20:21PM +1100, Nick Piggin wrote:
> Fengguang Wu wrote:
>
> >I'd like to move this sysctl interface to the upcoming /proc/filecache.
> >Being a module, it helps reduce the kernel size :)
>
> What's /proc/filecache?
It provides access to the in-memory file cache.
To list an index of all cached files:
echo -n index > /proc/filecache
cat /proc/filecache
The output looks like:
# filecache 1.0
# ino size cached cached% state refcnt dev file
1026334 91 92 100 -- 66 03:02(hda2) /lib/ld-2.3.6.so
233608 1242 972 78 -- 66 03:02(hda2) /lib/tls/libc-2.3.6.so
65203 651 476 73 -- 1 03:02(hda2) /bin/bash
1026445 261 160 61 -- 10 03:02(hda2) /lib/libncurses.so.5.5
235427 10 12 100 -- 44 03:02(hda2) /lib/tls/libdl-2.3.6.so
To list the cached pages of a perticular file:
echo -n /bin/bash > /proc/filecache
cat /proc/filecache
# file /bin/bash
# flags R:referenced A:active U:uptodate D:dirty W:writeback M:mmap
# idx len state refcnt
0 36 RAU__M 3
36 1 RAU__M 2
37 8 RAU__M 3
45 2 RAU___ 1
47 6 RAU__M 3
53 3 RAU__M 2
56 2 RAU__M 3
To instruct the kernel to drop clean caches, dentries and inodes from memory,
causing that memory to become free:
# drop clean file data cache (i.e. file backed pagecache)
echo drop data > /proc/filecache
# drop clean file metadata cache (i.e. dentries and inodes)
echo drop metadata > /proc/filecache
It's first introduced here: http://lkml.org/lkml/2006/6/12/29
And the latest working patch for -mm:
--- linux-2.6.19-rc6-mm2.orig/include/linux/mm.h
+++ linux-2.6.19-rc6-mm2/include/linux/mm.h
@@ -29,6 +29,7 @@ extern unsigned long num_physpages;
extern void * high_memory;
extern unsigned long vmalloc_earlyreserve;
extern int page_cluster;
+extern char *zone_names[];
#ifdef CONFIG_SYSCTL
extern int sysctl_legacy_va_layout;
--- linux-2.6.19-rc6-mm2.orig/mm/page_alloc.c
+++ linux-2.6.19-rc6-mm2/mm/page_alloc.c
@@ -86,7 +86,7 @@ int sysctl_lowmem_reserve_ratio[MAX_NR_Z
EXPORT_SYMBOL(totalram_pages);
-static char *zone_names[MAX_NR_ZONES] = {
+char *zone_names[MAX_NR_ZONES] = {
#ifdef CONFIG_ZONE_DMA
"DMA",
#endif
--- linux-2.6.19-rc6-mm2.orig/fs/dcache.c
+++ linux-2.6.19-rc6-mm2/fs/dcache.c
@@ -1776,7 +1776,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.19-rc6-mm2.orig/fs/seq_file.c
+++ linux-2.6.19-rc6-mm2/fs/seq_file.c
@@ -13,6 +13,8 @@
#include <asm/uaccess.h>
#include <asm/page.h>
+#define SEQFILE_SHOW_FROM_NEXT LONG_MAX
+
/**
* seq_open - initialize sequential file
* @file: file we initialize
@@ -93,6 +95,7 @@ ssize_t seq_read(struct file *file, char
/* if not empty - flush it first */
if (m->count) {
n = min(m->count, size);
+ BUG_ON(m->from == SEQFILE_SHOW_FROM_NEXT);
err = copy_to_user(buf, m->buf + m->from, n);
if (err)
goto Efault;
@@ -102,7 +105,7 @@ ssize_t seq_read(struct file *file, char
buf += n;
copied += n;
if (!m->count)
- m->index++;
+ m->from = SEQFILE_SHOW_FROM_NEXT;
if (!size)
goto Done;
}
@@ -113,9 +116,11 @@ ssize_t seq_read(struct file *file, char
err = PTR_ERR(p);
if (!p || IS_ERR(p))
break;
- err = m->op->show(m, p);
- if (err)
- break;
+ if (m->from != SEQFILE_SHOW_FROM_NEXT) {
+ err = m->op->show(m, p);
+ if (err)
+ break;
+ }
if (m->count < m->size)
goto Fill;
m->op->stop(m, p);
@@ -156,7 +161,7 @@ Fill:
if (m->count)
m->from = n;
else
- pos++;
+ m->from = SEQFILE_SHOW_FROM_NEXT;
m->index = pos;
Done:
if (!copied)
@@ -208,11 +213,9 @@ static int traverse(struct seq_file *m,
}
pos += m->count;
m->count = 0;
- if (pos == offset) {
- m->index++;
- break;
- }
p = m->op->next(m, p, &m->index);
+ if (pos == offset)
+ break;
}
m->op->stop(m, p);
return error;
--- linux-2.6.19-rc6-mm2.orig/Documentation/filesystems/proc.txt
+++ linux-2.6.19-rc6-mm2/Documentation/filesystems/proc.txt
@@ -209,6 +209,7 @@ Table 1-3: Kernel info in /proc
driver Various drivers grouped here, currently rtc (2.4)
execdomains Execdomains, related to security (2.4)
fb Frame Buffer devices (2.4)
+ filecache Query/drop in-memory file cache
fs File system parameters, currently nfs/exports (2.4)
ide Directory containing info about the IDE subsystem
interrupts Interrupt usage
@@ -453,6 +454,88 @@ VmallocTotal: total size of vmalloc memo
VmallocUsed: amount of vmalloc area which is used
VmallocChunk: largest contigious block of vmalloc area which is free
+..............................................................................
+
+filecache:
+
+Provides access to the in-memory file cache.
+
+To list an index of all cached files:
+
+ echo -n index > /proc/filecache
+ cat /proc/filecache
+
+The output looks like:
+
+ # filecache 1.0
+ # ino size cached cached% state refcnt dev file
+ 1026334 91 92 100 -- 66 03:02(hda2) /lib/ld-2.3.6.so
+ 233608 1242 972 78 -- 66 03:02(hda2) /lib/tls/libc-2.3.6.so
+ 65203 651 476 73 -- 1 03:02(hda2) /bin/bash
+ 1026445 261 160 61 -- 10 03:02(hda2) /lib/libncurses.so.5.5
+ 235427 10 12 100 -- 44 03:02(hda2) /lib/tls/libdl-2.3.6.so
+
+FIELD INTRO
+---------------------------------------------------------------------------
+ino inode number
+size inode size in KB
+cached cached size in KB
+cached% percent of file data cached
+state1 '-' clean; 'd' metadata dirty; 'D' data dirty
+state2 '-' unlocked; 'L' locked, normally indicates file being written out
+refcnt file reference count, it's an in-kernel one, not exactly open count
+dev major:minor numbers in hex, followed by a descriptive device name
+file file path _inside_ the filesystem. There are several special names:
+ '(noname)': the file name is not available
+ '(03:02)': the file is a block device file of major:minor
+ '...(deleted)': the named file has been deleted from the disk
+
+To list the cached pages of a perticular file:
+
+ echo -n /bin/bash > /proc/filecache
+ cat /proc/filecache
+
+ # file /bin/bash
+ # flags R:referenced A:active U:uptodate D:dirty W:writeback M:mmap
+ # idx len state refcnt
+ 0 36 RAU__M 3
+ 36 1 RAU__M 2
+ 37 8 RAU__M 3
+ 45 2 RAU___ 1
+ 47 6 RAU__M 3
+ 53 3 RAU__M 2
+ 56 2 RAU__M 3
+
+FIELD INTRO
+----------------------------------------------------------------------------
+idx page index
+len number of pages which are cached and share the same state
+state page state of the flags listed in line two
+refcnt page reference count
+
+Careful users may notice that the file name to be queried is remembered between
+commands. Internally, the module has a global variable to store the file name
+parameter, so that it can be inherited by newly opened /proc/filecache file.
+However it can lead to interference for multiple queriers. The solution here
+is to obey a rule: only root can interactively change the file name parameter;
+normal users must go for scripts to access the interface. Scripts should do it
+by following the code example below:
+
+ filecache = open("/proc/filecache", "rw");
+ # avoid polluting the global parameter filename
+ filecache.write("private session");
+
+To instruct the kernel to drop clean caches, dentries and inodes from memory,
+causing that memory to become free:
+
+ # drop clean file data cache (i.e. file backed pagecache)
+ echo drop data > /proc/filecache
+
+ # drop clean file metadata cache (i.e. dentries and inodes)
+ echo drop metadata > /proc/filecache
+
+Note that the drop commands are non-destructive operations and dirty objects
+are not freeable, the user should run `sync' first.
1.3 IDE devices in /proc/ide
----------------------------
--- /dev/null
+++ linux-2.6.19-rc6-mm2/fs/proc/filecache.c
@@ -0,0 +1,614 @@
+/*
+ * linux/fs/proc/filecache.c
+ *
+ * Copyright (C) 2006 Fengguang Wu <[email protected]>
+ *
+ * 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/vmalloc.h>
+#include <linux/writeback.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+/* Increase it whenever there are visible changes. */
+#define FILECACHE_VERSION "1.0"
+
+/*
+ * Session management.
+ *
+ * Each opened /proc/filecache file is assiocated with a session object.
+ *
+ * session.query_file is the file whose cache info is to be queried.
+ * Its value determines what we get on read():
+ * - NULL: call inode_index_*() to show the index of cached inodes
+ * - filp: call page_ranges_*() to show the cached pages of filp
+ *
+ * session.query_file is
+ * - initialized from global_name on open();
+ * - updated on write("filename");
+ * note that the new filename will also be saved in global_name if
+ * session.private_session is false.
+ */
+
+struct session {
+ int private_session;
+ struct file *query_file;
+ struct inode **ordered_inodes;
+ pgoff_t next_offset;
+};
+
+static char name_index[] = "index";
+static char *global_name = name_index;
+
+int name_session(struct session *s, char *name)
+{
+ static DEFINE_MUTEX(mutex);
+ int ret = 0;
+
+ mutex_lock(&mutex);
+
+ /* close old file */
+ if (s->query_file) {
+ if ((ret = filp_close(s->query_file, NULL)))
+ goto out;
+ s->query_file = NULL;
+ }
+
+ if (!name)
+ goto out;
+
+ /* open the named file */
+ if (strcmp(name, name_index)) {
+ if (IS_ERR(s->query_file =
+ filp_open(name, O_RDONLY|O_LARGEFILE, 0))) {
+ ret = (int)s->query_file;
+ s->query_file = NULL;
+ goto out;
+ }
+ }
+
+ /* set @name as new global default */
+ if (!s->private_session && name != global_name) {
+ /* non-root users are not allowed to modify global_name */
+ if (current->uid) {
+ ret = -EACCES;
+ goto out;
+ }
+
+ if (global_name != name_index)
+ __putname(global_name);
+
+ if (!strcmp(name, name_index))
+ global_name = name_index;
+ else {
+ global_name = __getname();
+ if (global_name)
+ strcpy(global_name, name);
+ else {
+ global_name = name_index;
+ ret = -ENOMEM;
+ }
+ }
+ }
+
+out:
+ mutex_unlock(&mutex);
+
+ return ret;
+}
+
+/*
+ * Session address is stored in proc_file->f_ra.flags:
+ * we assume that there will be no readahead for proc_file.
+ */
+struct session *get_session(struct file *proc_file)
+{
+ return (struct session *)proc_file->f_ra.flags;
+}
+
+int new_session(struct file* proc_file)
+{
+ struct session *s;
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ BUG_ON(proc_file->f_ra.flags);
+ proc_file->f_ra.flags = (unsigned long)s;
+
+ memset(s, 0, sizeof(*s));
+ return name_session(s, global_name);
+}
+
+int kill_session(struct file *proc_file)
+{
+ struct session *s = get_session(proc_file);
+ int ret;
+
+ if (!(ret = name_session(s, NULL)))
+ kfree(s);
+
+ return ret;
+}
+
+
+/*
+ * Listing of cached files.
+ *
+ * Usage:
+ * echo -n index > /proc/filecache
+ * cat /proc/filecache
+ */
+
+static int may_show_inode(struct inode *inode)
+{
+ if (!inode)
+ return 0;
+
+ if (!inode->i_mapping->nrpages && !MAJOR(inode->i_sb->s_dev))
+ return 0;
+
+ if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode) || S_ISBLK(inode->i_mode))
+ return 1;
+ else
+ return 0;
+
+#if 0 /* FIXME: permission() is not enough -
+ it applies to the file content, not the file path. */
+
+ if (!current->uid)
+ return 1;
+
+ return !permission(inode, MAY_READ, NULL);
+#endif
+}
+
+static void show_inode(struct seq_file *m, struct inode *inode)
+{
+ char state[] = "--"; /* dirty, locked */
+ struct dentry *dentry;
+ loff_t size = i_size_read(inode);
+ unsigned long nrpages = inode->i_mapping->nrpages;
+ int percent;
+ int refcnt;
+ int shift;
+
+ if (!size)
+ size++;
+
+ for (shift = 0; (size >> shift) > ULONG_MAX / 128; shift += 12)
+ ;
+ percent = min(100UL, (((100 * nrpages) >> shift) << PAGE_CACHE_SHIFT) /
+ (unsigned long)(size >> shift));
+
+ if (inode->i_state & (I_DIRTY_DATASYNC|I_DIRTY_PAGES))
+ state[0] = 'D';
+ else if (inode->i_state & I_DIRTY_SYNC)
+ state[0] = 'd';
+
+ if (inode->i_state & I_LOCK)
+ state[0] = 'L';
+
+ refcnt = 0;
+ list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+ refcnt += atomic_read(&dentry->d_count);
+ }
+
+ seq_printf(m, "%10lu %10llu %8lu %6d\t%s\t%d\t%02x:%02x(%s)\t",
+ inode->i_ino,
+ DIV_ROUND_UP(size, 1024),
+ nrpages << (PAGE_CACHE_SHIFT - 10),
+ percent,
+ state,
+ refcnt,
+ 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 {
+ dentry = list_entry(inode->i_dentry.next,
+ struct dentry, d_alias);
+ seq_path(m, NULL, dentry, " \t\n\\");
+ seq_putc(m, '\n');
+ }
+}
+
+static int inode_index_show(struct seq_file *m, void *v)
+{
+ unsigned long index = *(loff_t *) v;
+ struct session *s = m->private;
+ struct inode *inode;
+
+ if (index == 0) {
+ seq_puts(m, "# filecache " FILECACHE_VERSION "\n");
+ seq_puts(m, "# ino size cached cached%"
+ "\tstate\trefcnt\tdev\t\tfile\n");
+ }
+
+ BUG_ON(!s->ordered_inodes);
+ inode = s->ordered_inodes[index];
+
+ if (may_show_inode(inode))
+ show_inode(m, inode);
+
+ return 0;
+}
+
+static void *inode_index_start(struct seq_file *m, loff_t *pos)
+{
+ struct session *s = m->private;
+ struct super_block *sb;
+ struct inode *inode;
+ struct inode **ip;
+ unsigned long n;
+
+alloc:
+ n = inodes_stat.nr_inodes + 100;
+ ip = s->ordered_inodes = vmalloc(n * sizeof(*ip));
+ if (!s->ordered_inodes)
+ return NULL;
+ memset(ip, 0, n * sizeof(*ip));
+
+ spin_lock(&inode_lock);
+
+ if (inodes_stat.nr_inodes > n) {
+ spin_unlock(&inode_lock);
+ vfree(ip);
+ goto alloc;
+ }
+
+ /*
+ * Retrieve the inodes in order - newest one at bottom.
+ */
+
+ list_for_each_entry_reverse(inode, &inode_unused, i_list) {
+ *ip++ = inode;
+ }
+
+ list_for_each_entry_reverse(inode, &inode_in_use, i_list) {
+ *ip++ = inode;
+ }
+
+ spin_lock(&sb_lock);
+ list_for_each_entry(sb, &super_blocks, s_list) {
+ list_for_each_entry_reverse(inode, &sb->s_dirty, i_list) {
+ *ip++ = inode;
+ }
+ list_for_each_entry_reverse(inode, &sb->s_io, i_list) {
+ *ip++ = inode;
+ }
+ }
+ spin_unlock(&sb_lock);
+
+ return *pos < inodes_stat.nr_inodes ? pos : NULL;
+}
+
+static void *inode_index_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ (*pos)++;
+
+ return *pos < inodes_stat.nr_inodes ? pos : NULL;
+}
+
+static void inode_index_stop(struct seq_file *m, void *v)
+{
+ struct session *s = m->private;
+
+ spin_unlock(&inode_lock);
+ vfree(s->ordered_inodes);
+}
+
+
+/*
+ * Listing of cached page ranges of a file.
+ *
+ * Usage:
+ * echo -n 'file name' > /proc/filecache
+ * cat /proc/filecache
+ */
+
+unsigned long page_mask;
+#define PG_MMAP PG_lru /* reuse this never-relevant flag */
+#define PG_COUNT (sizeof(page_flag)/sizeof(page_flag[0]))
+
+/*
+ * Page state names, prefixed by their abbreviations.
+ */
+struct {
+ unsigned long mask;
+ const char *name;
+} page_flag [] = {
+ {1 << PG_referenced, "R:referenced"},
+ {1 << PG_active, "A:active"},
+
+ {1 << PG_uptodate, "U:uptodate"},
+ {1 << PG_dirty, "D:dirty"},
+ {1 << PG_writeback, "W:writeback"},
+
+ {1 << PG_MMAP, "M:mmap"},
+
+};
+
+static unsigned long page_flags(struct page* page)
+{
+ unsigned long flags;
+
+ flags = page->flags & page_mask;
+
+ if (page_mapped(page))
+ flags |= (1UL << 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;
+
+ return 1;
+}
+
+static void show_range(struct seq_file *m, struct page* page, unsigned long len)
+{
+ int i;
+ unsigned long flags;
+
+ if (!m || !page)
+ return;
+
+ seq_printf(m, "%lu\t%lu\t", page->index, len);
+
+ flags = page_flags(page);
+ for (i = 0; i < PG_COUNT; i++)
+ seq_putc(m, (flags & page_flag[i].mask) ?
+ page_flag[i].name[0] : '_');
+
+ seq_printf(m, "\t%d\n", page_count(page));
+}
+
+#define MAX_LINES 100
+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 page_ranges_show(struct seq_file *m, void *v)
+{
+ struct session *s = m->private;
+ struct file *file = s->query_file;
+ pgoff_t offset;
+
+ if (!file)
+ return inode_index_show(m, v);
+
+ offset = *(loff_t *) v;
+
+ 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 < PG_COUNT; i++)
+ seq_printf(m, " %s", page_flag[i].name);
+
+ seq_puts(m, "\n# idx\tlen\tstate\trefcnt\n");
+ }
+
+ s->next_offset = 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 *page_ranges_start(struct seq_file *m, loff_t *pos)
+{
+ struct session *s = m->private;
+ struct file *file = s->query_file;
+
+ if (!file)
+ return inode_index_start(m, pos);
+
+ read_lock_irq(&file->f_mapping->tree_lock);
+
+ return file_has_page(file, (pgoff_t)*pos) ? pos : NULL;
+}
+
+static void *page_ranges_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct session *s = m->private;
+ struct file *file = s->query_file;
+
+ if (!file)
+ return inode_index_next(m, v, pos);
+
+ *pos = s->next_offset;
+ /* *pos = show_file_cache(NULL, file->f_mapping, *pos); */
+
+ return file_has_page(file, (pgoff_t)*pos) ? pos : NULL;
+}
+
+static void page_ranges_stop(struct seq_file *m, void *v)
+{
+ struct session *s = m->private;
+ struct file *file = s->query_file;
+
+ if (!file)
+ return inode_index_stop(m, v);
+
+ read_unlock_irq(&file->f_mapping->tree_lock);
+}
+
+struct seq_operations seq_filecache_op = {
+ .start = page_ranges_start,
+ .next = page_ranges_next,
+ .stop = page_ranges_stop,
+ .show = page_ranges_show,
+};
+
+
+/*
+ * Proc file operations.
+ */
+
+static int filecache_open(struct inode *inode, struct file *proc_file)
+{
+ struct seq_file *m;
+ int ret;
+ if (!(ret = seq_open(proc_file, &seq_filecache_op))) {
+ ret = new_session(proc_file);
+ m = proc_file->private_data;
+ m->private = get_session(proc_file);
+ }
+ return ret;
+}
+
+static int filecache_release(struct inode *inode, struct file *proc_file)
+{
+ int ret;
+ if (!(ret = kill_session(proc_file)))
+ ret = seq_release(inode, proc_file);
+ return ret;
+}
+
+ssize_t filecache_write(struct file *proc_file, const char __user * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct session *s;
+ char *name;
+ int e = 0;
+
+ if (count >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ name = kmalloc(count+1, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ if (copy_from_user(name, buffer, count)) {
+ e = -EFAULT;
+ goto out;
+ }
+ name[count] = '\0';
+
+ s = get_session(proc_file);
+ if (!strcmp(name, "private session")) {
+ s->private_session = 1;
+ goto out;
+ }
+
+ e = name_session(s, name);
+
+out:
+ kfree(name);
+
+ return e ? e : 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 filecache_init(void)
+{
+ int i;
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_entry("filecache", 0600, NULL);
+ if (entry)
+ entry->proc_fops = &proc_filecache_fops;
+
+ /* Note: the faked flag PG_MMAP is not included. */
+ for (page_mask = i = 0; i < PG_COUNT - 1; i++)
+ page_mask |= page_flag[i].mask;
+
+ return 0;
+}
+
+static void filecache_exit(void)
+{
+ remove_proc_entry("filecache", NULL);
+}
+
+MODULE_AUTHOR("Fengguang Wu <[email protected]>");
+MODULE_LICENSE("GPL");
+
+module_init(filecache_init);
+module_exit(filecache_exit);
--- linux-2.6.19-rc6-mm2.orig/fs/Kconfig
+++ linux-2.6.19-rc6-mm2/fs/Kconfig
@@ -1047,6 +1047,29 @@ 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
+ query/drop the cached files in memory.
+
+ A quick start guide:
+
+ # echo -n index > /proc/filecache
+ # cat /proc/filecache
+
+ # echo -n /bin/bash > /proc/filecache
+ # cat /proc/filecache
+
+ # echo drop data > /proc/filecache
+ # echo drop metadata > /proc/filecache
+
+ For more details, please check Documentation/filesystems/proc.txt .
+
+ It can be a handy tool for sysadms and desktop users.
+
endmenu
menu "Miscellaneous filesystems"
--- linux-2.6.19-rc6-mm2.orig/fs/proc/Makefile
+++ linux-2.6.19-rc6-mm2/fs/proc/Makefile
@@ -12,5 +12,6 @@ proc-y += inode.o root.o base.o ge
proc-$(CONFIG_PROC_KCORE) += kcore.o
proc-$(CONFIG_PROC_VMCORE) += vmcore.o
+proc-$(CONFIG_PROC_FILECACHE) += filecache.o
proc-$(CONFIG_PROC_DEVICETREE) += proc_devtree.o
proc-$(CONFIG_PRINTK) += kmsg.o
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]