diff options
author | Chuck Lever <cel@netapp.com> | 2006-03-20 13:44:12 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-20 13:44:12 -0500 |
commit | b4629fe2f094b719847f31be1ee5ab38300038b2 (patch) | |
tree | 158b3aabf291ef9462e3e02493fb4c45265f9e8e | |
parent | 1356b8c28d67cafd74f7e7dcfb39bf53681790a5 (diff) |
VFS: New /proc file /proc/self/mountstats
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.
Thanks to Mike Waychison for his review and comments.
Test-plan:
Test concurrent mount/unmount operations while cat'ing /proc/self/mountstats.
Signed-off-by: Chuck Lever <cel@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/namespace.c | 38 | ||||
-rw-r--r-- | fs/proc/base.c | 39 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
3 files changed, 78 insertions, 0 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 39c81a8d6316..71e75bcf4d28 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -399,6 +399,44 @@ struct seq_operations mounts_op = { | |||
399 | .show = show_vfsmnt | 399 | .show = show_vfsmnt |
400 | }; | 400 | }; |
401 | 401 | ||
402 | static int show_vfsstat(struct seq_file *m, void *v) | ||
403 | { | ||
404 | struct vfsmount *mnt = v; | ||
405 | int err = 0; | ||
406 | |||
407 | /* device */ | ||
408 | if (mnt->mnt_devname) { | ||
409 | seq_puts(m, "device "); | ||
410 | mangle(m, mnt->mnt_devname); | ||
411 | } else | ||
412 | seq_puts(m, "no device"); | ||
413 | |||
414 | /* mount point */ | ||
415 | seq_puts(m, " mounted on "); | ||
416 | seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); | ||
417 | seq_putc(m, ' '); | ||
418 | |||
419 | /* file system type */ | ||
420 | seq_puts(m, "with fstype "); | ||
421 | mangle(m, mnt->mnt_sb->s_type->name); | ||
422 | |||
423 | /* optional statistics */ | ||
424 | if (mnt->mnt_sb->s_op->show_stats) { | ||
425 | seq_putc(m, ' '); | ||
426 | err = mnt->mnt_sb->s_op->show_stats(m, mnt); | ||
427 | } | ||
428 | |||
429 | seq_putc(m, '\n'); | ||
430 | return err; | ||
431 | } | ||
432 | |||
433 | struct seq_operations mountstats_op = { | ||
434 | .start = m_start, | ||
435 | .next = m_next, | ||
436 | .stop = m_stop, | ||
437 | .show = show_vfsstat, | ||
438 | }; | ||
439 | |||
402 | /** | 440 | /** |
403 | * may_umount_tree - check if a mount tree is busy | 441 | * may_umount_tree - check if a mount tree is busy |
404 | * @mnt: root of mount tree | 442 | * @mnt: root of mount tree |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 20feb7568deb..8f1f49ceebec 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -104,6 +104,7 @@ enum pid_directory_inos { | |||
104 | PROC_TGID_MAPS, | 104 | PROC_TGID_MAPS, |
105 | PROC_TGID_NUMA_MAPS, | 105 | PROC_TGID_NUMA_MAPS, |
106 | PROC_TGID_MOUNTS, | 106 | PROC_TGID_MOUNTS, |
107 | PROC_TGID_MOUNTSTATS, | ||
107 | PROC_TGID_WCHAN, | 108 | PROC_TGID_WCHAN, |
108 | #ifdef CONFIG_MMU | 109 | #ifdef CONFIG_MMU |
109 | PROC_TGID_SMAPS, | 110 | PROC_TGID_SMAPS, |
@@ -144,6 +145,7 @@ enum pid_directory_inos { | |||
144 | PROC_TID_MAPS, | 145 | PROC_TID_MAPS, |
145 | PROC_TID_NUMA_MAPS, | 146 | PROC_TID_NUMA_MAPS, |
146 | PROC_TID_MOUNTS, | 147 | PROC_TID_MOUNTS, |
148 | PROC_TID_MOUNTSTATS, | ||
147 | PROC_TID_WCHAN, | 149 | PROC_TID_WCHAN, |
148 | #ifdef CONFIG_MMU | 150 | #ifdef CONFIG_MMU |
149 | PROC_TID_SMAPS, | 151 | PROC_TID_SMAPS, |
@@ -201,6 +203,7 @@ static struct pid_entry tgid_base_stuff[] = { | |||
201 | E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO), | 203 | E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO), |
202 | E(PROC_TGID_EXE, "exe", S_IFLNK|S_IRWXUGO), | 204 | E(PROC_TGID_EXE, "exe", S_IFLNK|S_IRWXUGO), |
203 | E(PROC_TGID_MOUNTS, "mounts", S_IFREG|S_IRUGO), | 205 | E(PROC_TGID_MOUNTS, "mounts", S_IFREG|S_IRUGO), |
206 | E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUSR), | ||
204 | #ifdef CONFIG_MMU | 207 | #ifdef CONFIG_MMU |
205 | E(PROC_TGID_SMAPS, "smaps", S_IFREG|S_IRUGO), | 208 | E(PROC_TGID_SMAPS, "smaps", S_IFREG|S_IRUGO), |
206 | #endif | 209 | #endif |
@@ -732,6 +735,38 @@ static struct file_operations proc_mounts_operations = { | |||
732 | .poll = mounts_poll, | 735 | .poll = mounts_poll, |
733 | }; | 736 | }; |
734 | 737 | ||
738 | extern struct seq_operations mountstats_op; | ||
739 | static int mountstats_open(struct inode *inode, struct file *file) | ||
740 | { | ||
741 | struct task_struct *task = proc_task(inode); | ||
742 | int ret = seq_open(file, &mountstats_op); | ||
743 | |||
744 | if (!ret) { | ||
745 | struct seq_file *m = file->private_data; | ||
746 | struct namespace *namespace; | ||
747 | task_lock(task); | ||
748 | namespace = task->namespace; | ||
749 | if (namespace) | ||
750 | get_namespace(namespace); | ||
751 | task_unlock(task); | ||
752 | |||
753 | if (namespace) | ||
754 | m->private = namespace; | ||
755 | else { | ||
756 | seq_release(inode, file); | ||
757 | ret = -EINVAL; | ||
758 | } | ||
759 | } | ||
760 | return ret; | ||
761 | } | ||
762 | |||
763 | static struct file_operations proc_mountstats_operations = { | ||
764 | .open = mountstats_open, | ||
765 | .read = seq_read, | ||
766 | .llseek = seq_lseek, | ||
767 | .release = mounts_release, | ||
768 | }; | ||
769 | |||
735 | #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ | 770 | #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ |
736 | 771 | ||
737 | static ssize_t proc_info_read(struct file * file, char __user * buf, | 772 | static ssize_t proc_info_read(struct file * file, char __user * buf, |
@@ -1730,6 +1765,10 @@ static struct dentry *proc_pident_lookup(struct inode *dir, | |||
1730 | inode->i_fop = &proc_smaps_operations; | 1765 | inode->i_fop = &proc_smaps_operations; |
1731 | break; | 1766 | break; |
1732 | #endif | 1767 | #endif |
1768 | case PROC_TID_MOUNTSTATS: | ||
1769 | case PROC_TGID_MOUNTSTATS: | ||
1770 | inode->i_fop = &proc_mountstats_operations; | ||
1771 | break; | ||
1733 | #ifdef CONFIG_SECURITY | 1772 | #ifdef CONFIG_SECURITY |
1734 | case PROC_TID_ATTR: | 1773 | case PROC_TID_ATTR: |
1735 | inode->i_nlink = 2; | 1774 | inode->i_nlink = 2; |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 128d0082522c..be21e860a9f2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1086,6 +1086,7 @@ struct super_operations { | |||
1086 | void (*umount_begin) (struct super_block *); | 1086 | void (*umount_begin) (struct super_block *); |
1087 | 1087 | ||
1088 | int (*show_options)(struct seq_file *, struct vfsmount *); | 1088 | int (*show_options)(struct seq_file *, struct vfsmount *); |
1089 | int (*show_stats)(struct seq_file *, struct vfsmount *); | ||
1089 | 1090 | ||
1090 | ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); | 1091 | ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); |
1091 | ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); | 1092 | ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); |