Commit b4629fe2 authored by Chuck Lever's avatar Chuck Lever Committed by Trond Myklebust
Browse files

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: default avatarChuck Lever <cel@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 1356b8c2
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -399,6 +399,44 @@ struct seq_operations mounts_op = {
	.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
 * @mnt: root of mount tree
+39 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ enum pid_directory_inos {
	PROC_TGID_MAPS,
	PROC_TGID_NUMA_MAPS,
	PROC_TGID_MOUNTS,
	PROC_TGID_MOUNTSTATS,
	PROC_TGID_WCHAN,
#ifdef CONFIG_MMU
	PROC_TGID_SMAPS,
@@ -144,6 +145,7 @@ enum pid_directory_inos {
	PROC_TID_MAPS,
	PROC_TID_NUMA_MAPS,
	PROC_TID_MOUNTS,
	PROC_TID_MOUNTSTATS,
	PROC_TID_WCHAN,
#ifdef CONFIG_MMU
	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_EXE,       "exe",     S_IFLNK|S_IRWXUGO),
	E(PROC_TGID_MOUNTS,    "mounts",  S_IFREG|S_IRUGO),
	E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUSR),
#ifdef CONFIG_MMU
	E(PROC_TGID_SMAPS,     "smaps",   S_IFREG|S_IRUGO),
#endif
@@ -732,6 +735,38 @@ static struct file_operations proc_mounts_operations = {
	.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 */

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;
			break;
#endif
		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;
+1 −0
Original line number Diff line number Diff line
@@ -1086,6 +1086,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);