[PATCH 1/2] VFS: New /proc file /proc/self/mountstats

From: Chuck Lever
Date: Thu Mar 17 2005 - 11:30:34 EST


Create a new file under /proc/self, called mountstats, where mounted file
systems can export information (configuration options, performance counters,
and so on). Use a mechanism similar to /proc/mounts and s_ops->show_options.

This mechanism does not violate namespace security, and is safe to use while
other processes are unmounting file systems.

Version: Mon, 14 Mar 2005 17:06:04 -0500

Signed-off-by: Chuck Lever <cel@xxxxxxxxxx>
---

fs/namespace.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
fs/proc/base.c | 40 +++++++++++++++++++++++++++
include/linux/fs.h | 1
3 files changed, 107 insertions(+)


diff -X /home/cel/src/linux/dont-diff -Naurp 00-stock/fs/namespace.c 01-mountstats/fs/namespace.c
--- 00-stock/fs/namespace.c 2005-03-02 02:38:13.000000000 -0500
+++ 01-mountstats/fs/namespace.c 2005-03-14 15:24:51.565085000 -0500
@@ -265,6 +265,72 @@ struct seq_operations mounts_op = {
.show = show_vfsmnt
};

+/* iterator */
+static void *ms_start(struct seq_file *m, loff_t *pos)
+{
+ struct namespace *n = m->private;
+ struct list_head *p;
+ loff_t l = *pos;
+
+ down_read(&n->sem);
+ list_for_each(p, &n->list)
+ if (!l--)
+ return list_entry(p, struct vfsmount, mnt_list);
+ return NULL;
+}
+
+static void *ms_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct namespace *n = m->private;
+ struct list_head *p = ((struct vfsmount *)v)->mnt_list.next;
+ (*pos)++;
+ return p==&n->list ? NULL : list_entry(p, struct vfsmount, mnt_list);
+}
+
+static void ms_stop(struct seq_file *m, void *v)
+{
+ struct namespace *n = m->private;
+ up_read(&n->sem);
+}
+
+static int show_vfsstat(struct seq_file *m, void *v)
+{
+ struct vfsmount *mnt = v;
+ int err = 0;
+
+ /* device */
+ if (mnt->mnt_devname) {
+ seq_puts(m, "device ");
+ mangle(m, mnt->mnt_devname);
+ } else
+ seq_puts(m, "no device");
+
+ /* mount point */
+ seq_puts(m, " mounted on ");
+ seq_path(m, mnt, mnt->mnt_root, " \t\n\\");
+ seq_putc(m, ' ');
+
+ /* file system type */
+ seq_puts(m, "with fstype ");
+ mangle(m, mnt->mnt_sb->s_type->name);
+
+ /* optional statistics */
+ if (mnt->mnt_sb->s_op->show_stats) {
+ seq_putc(m, ' ');
+ err = mnt->mnt_sb->s_op->show_stats(m, mnt);
+ }
+
+ seq_putc(m, '\n');
+ return err;
+}
+
+struct seq_operations mountstats_op = {
+ .start = ms_start,
+ .next = ms_next,
+ .stop = ms_stop,
+ .show = show_vfsstat,
+};
+
/**
* may_umount_tree - check if a mount tree is busy
* @mnt: root of mount tree
diff -X /home/cel/src/linux/dont-diff -Naurp 00-stock/fs/proc/base.c 01-mountstats/fs/proc/base.c
--- 00-stock/fs/proc/base.c 2005-03-02 02:38:12.000000000 -0500
+++ 01-mountstats/fs/proc/base.c 2005-03-14 15:24:51.571085000 -0500
@@ -60,6 +60,7 @@ enum pid_directory_inos {
PROC_TGID_STATM,
PROC_TGID_MAPS,
PROC_TGID_MOUNTS,
+ PROC_TGID_MOUNTSTATS,
PROC_TGID_WCHAN,
#ifdef CONFIG_SCHEDSTATS
PROC_TGID_SCHEDSTAT,
@@ -91,6 +92,7 @@ enum pid_directory_inos {
PROC_TID_STATM,
PROC_TID_MAPS,
PROC_TID_MOUNTS,
+ PROC_TID_MOUNTSTATS,
PROC_TID_WCHAN,
#ifdef CONFIG_SCHEDSTATS
PROC_TID_SCHEDSTAT,
@@ -134,6 +136,7 @@ static struct pid_entry tgid_base_stuff[
E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO),
E(PROC_TGID_EXE, "exe", S_IFLNK|S_IRWXUGO),
E(PROC_TGID_MOUNTS, "mounts", S_IFREG|S_IRUGO),
+ E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUGO),
#ifdef CONFIG_SECURITY
E(PROC_TGID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO),
#endif
@@ -164,6 +167,7 @@ static struct pid_entry tid_base_stuff[]
E(PROC_TID_ROOT, "root", S_IFLNK|S_IRWXUGO),
E(PROC_TID_EXE, "exe", S_IFLNK|S_IRWXUGO),
E(PROC_TID_MOUNTS, "mounts", S_IFREG|S_IRUGO),
+ E(PROC_TID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUGO),
#ifdef CONFIG_SECURITY
E(PROC_TID_ATTR, "attr", S_IFDIR|S_IRUGO|S_IXUGO),
#endif
@@ -528,6 +532,38 @@ static struct file_operations proc_mount
.release = mounts_release,
};

+extern struct seq_operations mountstats_op;
+static int mountstats_open(struct inode *inode, struct file *file)
+{
+ struct task_struct *task = proc_task(inode);
+ int ret = seq_open(file, &mountstats_op);
+
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+ struct namespace *namespace;
+ task_lock(task);
+ namespace = task->namespace;
+ if (namespace)
+ get_namespace(namespace);
+ task_unlock(task);
+
+ if (namespace)
+ m->private = namespace;
+ else {
+ seq_release(inode, file);
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static struct file_operations proc_mountstats_operations = {
+ .open = mountstats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = mounts_release,
+};
+
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */

static ssize_t proc_info_read(struct file * file, char __user * buf,
@@ -1447,6 +1483,10 @@ static struct dentry *proc_pident_lookup
case PROC_TGID_MOUNTS:
inode->i_fop = &proc_mounts_operations;
break;
+ case PROC_TID_MOUNTSTATS:
+ case PROC_TGID_MOUNTSTATS:
+ inode->i_fop = &proc_mountstats_operations;
+ break;
#ifdef CONFIG_SECURITY
case PROC_TID_ATTR:
inode->i_nlink = 2;
diff -X /home/cel/src/linux/dont-diff -Naurp 00-stock/include/linux/fs.h 01-mountstats/include/linux/fs.h
--- 00-stock/include/linux/fs.h 2005-03-02 02:37:50.000000000 -0500
+++ 01-mountstats/include/linux/fs.h 2005-03-14 15:24:51.598084000 -0500
@@ -1008,6 +1008,7 @@ struct super_operations {
void (*umount_begin) (struct super_block *);

int (*show_options)(struct seq_file *, struct vfsmount *);
+ int (*show_stats)(struct seq_file *, struct vfsmount *);

ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
-
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/