diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 94b9f20..bad2506 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -414,6 +414,7 @@ Table 1-5: 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 @@ -770,6 +771,39 @@ Provides counts of softirq handlers serviced since boot time, for each cpu. HRTIMER: 0 0 0 0 RCU: 1678 1769 2178 2250 +.............................................................................. + +filecache: + +Provides access to the in-memory file cache. + +To list an index of all cached files: + + 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 + + + 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 1.3 IDE devices in /proc/ide ---------------------------- diff --git a/fs/dcache.c b/fs/dcache.c index ddd1986..fa1c3ce 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1927,7 +1927,11 @@ char *__d_path(const struct path *path, struct path *root, if (dentry == root->dentry && vfsmnt == root->mnt) break; - if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + + if (!vfsmnt) { + if (IS_ROOT(dentry)) + break; + } else if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { /* Global root? */ if (vfsmnt->mnt_parent == vfsmnt) { goto global_root; diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 11a7b5c..c532d59 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -19,6 +19,7 @@ proc-y += stat.o proc-y += uptime.o proc-y += version.o proc-y += softirqs.o +proc-y += filecache.o proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o proc-$(CONFIG_NET) += proc_net.o proc-$(CONFIG_PROC_KCORE) += kcore.o diff --git a/fs/proc/filecache.c b/fs/proc/filecache.c new file mode 100644 index 0000000..0ca121c --- /dev/null +++ b/fs/proc/filecache.c @@ -0,0 +1,334 @@ +/* + * fs/proc/filecache.c + * + * Copyright (C) 2006, 2007 Fengguang Wu + * Copyright (C) 2009 KOSAKI Motohiro + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Internal buffer sizes. The larger the more effcient. */ +#define SBUF_SIZE (128<<10) +#define IWIN_PAGE_ORDER 3 +#define IWIN_SIZE ((PAGE_SIZE<i_count)) + return 0; + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) + return 0; + if (!inode->i_mapping) + return 0; + + if (!inode->i_mapping->nrpages) + return 0; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode) || S_ISBLK(inode->i_mode))) + return 0; + + return 1; +} + +/* + * Full: there are more data following. + */ +static int iwin_full(struct fcache_read_state *s) +{ + return !s->iwin.cursor || + s->iwin.cursor > s->iwin.origin + s->iwin.size; +} + +static int iwin_push(struct fcache_read_state *s, struct inode *inode) +{ + if (!may_show_inode(s, inode)) + return 0; + + s->iwin.cursor++; + + if (s->iwin.size >= IWIN_SIZE) + return 1; + + if (s->iwin.cursor > s->iwin.origin) + s->iwin.inodes[s->iwin.size++] = inode; + return 0; +} + +/* + * Travease the inode lists in order - newest first. + * And fill @s->iwin.inodes with inodes positioned in [@pos, @pos+IWIN_SIZE). + */ +static int iwin_fill(struct fcache_read_state *s, unsigned long pos) +{ + struct inode *inode; + struct backing_dev_info *bdi; + + s->iwin.origin = pos; + s->iwin.cursor = 0; + s->iwin.size = 0; + + spin_lock_bh(&bdi_lock); + list_for_each_entry(bdi, &bdi_list, bdi_list) { + list_for_each_entry(inode, &bdi->wb.b_dirty, i_list) { + if (iwin_push(s, inode)) + goto out_full_unlock; + } + list_for_each_entry(inode, &bdi->wb.b_io, i_list) { + if (iwin_push(s, inode)) + goto out_full_unlock; + } + } + spin_unlock_bh(&bdi_lock); + + list_for_each_entry(inode, &inode_in_use, i_list) { + if (iwin_push(s, inode)) + goto out_full; + ; + } + + list_for_each_entry(inode, &inode_unused, i_list) { + if (iwin_push(s, inode)) + goto out_full; + } + + return 0; + +out_full_unlock: + spin_unlock_bh(&bdi_lock); +out_full: + return 1; +} + +static struct inode* iwin_inode(struct fcache_read_state *s, unsigned long pos) +{ + if ((iwin_full(s) && pos >= s->iwin.origin + s->iwin.size) + || pos < s->iwin.origin) + iwin_fill(s, pos); + + if (pos >= s->iwin.cursor) + return NULL; + + s->ipos.pos = pos; + s->ipos.inode = s->iwin.inodes[pos - s->iwin.origin]; + BUG_ON(!s->ipos.inode); + + return s->ipos.inode; +} + +static void show_inode(struct seq_file *m, struct inode *inode) +{ + char state[] = "--"; /* dirty, locked */ + struct dentry *dentry; + unsigned long size = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); + unsigned long nrpages; + int percent = 0; + int refcnt; + int len; + + if (inode->i_mapping) + nrpages = inode->i_mapping->nrpages; + else { + nrpages = 0; + WARN_ON(1); + } + + if (size) + percent = 100 * nrpages / size; + + 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[1] = 'L'; + + refcnt = 0; + list_for_each_entry(dentry, &inode->i_dentry, d_alias) { + refcnt += atomic_read(&dentry->d_count); + } + + seq_printf(m, "%10lu %10lu %8lu %7d %6d %5s %02x:%02x(%.5s)%n", + inode->i_ino, + size << (PAGE_CACHE_SHIFT - 10), + nrpages << (PAGE_CACHE_SHIFT - 10), + percent, + refcnt, + state, + MAJOR(inode->i_sb->s_dev), + MINOR(inode->i_sb->s_dev), + inode->i_sb->s_id, + &len); + + seq_printf(m, "%*c", 65 - len, ' '); + + seq_printf(m, "%.7o ", + inode->i_mode); + + 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 { + struct path path = { + .mnt = NULL, + .dentry = list_entry(inode->i_dentry.next, + struct dentry, d_alias) + }; + + seq_path(m, &path, "\t\n\\"); + seq_putc(m, '\n'); + } +} + +static int ii_show(struct seq_file *m, void *v) +{ + unsigned long index = *(loff_t *) v; + struct fcache_read_state *s = m->private; + struct inode *inode; + + if (index == 0) { + seq_puts(m, "# ino size cached cached% " + "refcnt state dev mode file\n"); + } + + inode = iwin_inode(s, index); + show_inode(m, inode); + + return 0; +} + +static void *ii_start(struct seq_file *m, loff_t *pos) +{ + struct fcache_read_state *s = m->private; + + s->iwin.size = 0; + s->iwin.inodes = (struct inode **) + __get_free_pages(GFP_KERNEL, IWIN_PAGE_ORDER); + if (!s->iwin.inodes) + return NULL; + + spin_lock(&inode_lock); + + return iwin_inode(s, *pos) ? pos : NULL; +} + +static void *ii_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct fcache_read_state *s = m->private; + + (*pos)++; + return iwin_inode(s, *pos) ? pos : NULL; +} + +static void ii_stop(struct seq_file *m, void *v) +{ + struct fcache_read_state *s = m->private; + + if (!s->iwin.inodes) + return; /* we don't have inode_lock */ + + spin_unlock(&inode_lock); + free_pages((unsigned long) s->iwin.inodes, IWIN_PAGE_ORDER); +} + +static struct seq_operations ii_op = { + .start = ii_start, + .next = ii_next, + .stop = ii_stop, + .show = ii_show, +}; + +/* + * Proc file operations. + */ +static int filecache_open(struct inode *inode, struct file *proc_file) +{ + struct seq_file *m; + struct fcache_read_state *s; + int ret; + + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto out; + } + + ret = seq_open(proc_file, &ii_op); + if (ret) { + kfree(s); + goto out; + } + + m = proc_file->private_data; + m->private = s; + +out: + return ret; +} + +static int filecache_release(struct inode *inode, struct file *proc_file) +{ + struct seq_file *seq = proc_file->private_data; + struct fcache_read_state *s = seq ? seq->private : NULL; + int ret; + + kfree(s); + ret = seq_release(inode, proc_file); + return ret; +} + +static struct file_operations proc_filecache_fops = { + .open = filecache_open, + .release = filecache_release, + .read = seq_read, + .llseek = seq_lseek, +}; + + +static __init int filecache_init(void) +{ + proc_create("filecache", 0600, NULL, &proc_filecache_fops); + return 0; +} +module_init(filecache_init);