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>
This commit is contained in:
committed by
Trond Myklebust
parent
1356b8c28d
commit
b4629fe2f0
@@ -399,6 +399,44 @@ struct seq_operations mounts_op = {
|
|||||||
.show = show_vfsmnt
|
.show = show_vfsmnt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 = m_start,
|
||||||
|
.next = m_next,
|
||||||
|
.stop = m_stop,
|
||||||
|
.show = show_vfsstat,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* may_umount_tree - check if a mount tree is busy
|
* may_umount_tree - check if a mount tree is busy
|
||||||
* @mnt: root of mount tree
|
* @mnt: root of mount tree
|
||||||
|
@@ -104,6 +104,7 @@ enum pid_directory_inos {
|
|||||||
PROC_TGID_MAPS,
|
PROC_TGID_MAPS,
|
||||||
PROC_TGID_NUMA_MAPS,
|
PROC_TGID_NUMA_MAPS,
|
||||||
PROC_TGID_MOUNTS,
|
PROC_TGID_MOUNTS,
|
||||||
|
PROC_TGID_MOUNTSTATS,
|
||||||
PROC_TGID_WCHAN,
|
PROC_TGID_WCHAN,
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
PROC_TGID_SMAPS,
|
PROC_TGID_SMAPS,
|
||||||
@@ -144,6 +145,7 @@ enum pid_directory_inos {
|
|||||||
PROC_TID_MAPS,
|
PROC_TID_MAPS,
|
||||||
PROC_TID_NUMA_MAPS,
|
PROC_TID_NUMA_MAPS,
|
||||||
PROC_TID_MOUNTS,
|
PROC_TID_MOUNTS,
|
||||||
|
PROC_TID_MOUNTSTATS,
|
||||||
PROC_TID_WCHAN,
|
PROC_TID_WCHAN,
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
PROC_TID_SMAPS,
|
PROC_TID_SMAPS,
|
||||||
@@ -201,6 +203,7 @@ static struct pid_entry tgid_base_stuff[] = {
|
|||||||
E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO),
|
E(PROC_TGID_ROOT, "root", S_IFLNK|S_IRWXUGO),
|
||||||
E(PROC_TGID_EXE, "exe", 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_MOUNTS, "mounts", S_IFREG|S_IRUGO),
|
||||||
|
E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUSR),
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
E(PROC_TGID_SMAPS, "smaps", S_IFREG|S_IRUGO),
|
E(PROC_TGID_SMAPS, "smaps", S_IFREG|S_IRUGO),
|
||||||
#endif
|
#endif
|
||||||
@@ -732,6 +735,38 @@ static struct file_operations proc_mounts_operations = {
|
|||||||
.poll = mounts_poll,
|
.poll = mounts_poll,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 */
|
#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,
|
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,
|
|||||||
inode->i_fop = &proc_smaps_operations;
|
inode->i_fop = &proc_smaps_operations;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case PROC_TID_MOUNTSTATS:
|
||||||
|
case PROC_TGID_MOUNTSTATS:
|
||||||
|
inode->i_fop = &proc_mountstats_operations;
|
||||||
|
break;
|
||||||
#ifdef CONFIG_SECURITY
|
#ifdef CONFIG_SECURITY
|
||||||
case PROC_TID_ATTR:
|
case PROC_TID_ATTR:
|
||||||
inode->i_nlink = 2;
|
inode->i_nlink = 2;
|
||||||
|
@@ -1086,6 +1086,7 @@ struct super_operations {
|
|||||||
void (*umount_begin) (struct super_block *);
|
void (*umount_begin) (struct super_block *);
|
||||||
|
|
||||||
int (*show_options)(struct seq_file *, struct vfsmount *);
|
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_read)(struct super_block *, int, char *, size_t, loff_t);
|
||||||
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
|
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
|
||||||
|
Reference in New Issue
Block a user