Skip to contentSkip to conversation

make /proc/mounts pollable

PATCH In review Al Viro 3 participants 4 messages Nov 8, 2005

Subsystems (3)
lore
Share & tools
lore

Al Viro

author
#0 lore raw

PATCH 4/18 make /proc/mounts pollable

From: Al Viro <[email protected]>
Date: 1131401749 -0500

---

 fs/namespace.c            |   30 +++++++++++++++++++++-
 fs/proc/base.c            |   62 ++++++++++++++++++++++++++++++++++-----------
 include/linux/namespace.h |    2 +
 3 files changed, 78 insertions(+), 16 deletions(-)

c98fca13440a0bbf547987f418e36e2e486e842c

fs/namespace.c +29 -1
@@ -37,7 +37,9 @@ static inline int sysfs_init(void)
 #endif
 
 /* spinlock for vfsmount related operations, inplace of dcache_lock */
- __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
+__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
+
+static int event;
 
 static struct list_head *mount_hashtable;
 static int hash_mask __read_mostly, hash_bits __read_mostly;
@@ -111,6 +113,22 @@ static inline int check_mnt(struct vfsmo
 	return mnt->mnt_namespace == current->namespace;
 }
 
+static void touch_namespace(struct namespace *ns)
+{
+	if (ns) {
+		ns->event = ++event;
+		wake_up_interruptible(&ns->poll);
+	}
+}
+
+static void __touch_namespace(struct namespace *ns)
+{
+	if (ns && ns->event != event) {
+		ns->event = event;
+		wake_up_interruptible(&ns->poll);
+	}
+}
+
 static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
 {
 	old_nd->dentry = mnt->mnt_mountpoint;
@@ -384,6 +402,7 @@ static void umount_tree(struct vfsmount 
 	for (p = mnt; p; p = next_mnt(p, mnt)) {
 		list_del(&p->mnt_list);
 		list_add(&p->mnt_list, &kill);
+		__touch_namespace(p->mnt_namespace);
 		p->mnt_namespace = NULL;
 	}
 
@@ -473,6 +492,7 @@ static int do_umount(struct vfsmount *mn
 
 	down_write(&current->namespace->sem);
 	spin_lock(&vfsmount_lock);
+	event++;
 
 	retval = -EBUSY;
 	if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
@@ -634,6 +654,7 @@ static int graft_tree(struct vfsmount *m
 		list_splice(&head, current->namespace->list.prev);
 		mntget(mnt);
 		err = 0;
+		touch_namespace(current->namespace);
 	}
 	spin_unlock(&vfsmount_lock);
 out_unlock:
@@ -771,6 +792,7 @@ static int do_move_mount(struct nameidat
 
 	detach_mnt(old_nd.mnt, &parent_nd);
 	attach_mnt(old_nd.mnt, nd);
+	touch_namespace(current->namespace);
 
 	/* if the mount is moved, it should no longer be expire
 	 * automatically */
@@ -877,6 +899,7 @@ static void expire_mount(struct vfsmount
 		struct nameidata old_nd;
 
 		/* delete from the namespace */
+		touch_namespace(mnt->mnt_namespace);
 		list_del_init(&mnt->mnt_list);
 		mnt->mnt_namespace = NULL;
 		detach_mnt(mnt, &old_nd);
@@ -1114,6 +1137,8 @@ int copy_namespace(int flags, struct tas
 	atomic_set(&new_ns->count, 1);
 	init_rwsem(&new_ns->sem);
 	INIT_LIST_HEAD(&new_ns->list);
+	init_waitqueue_head(&new_ns->poll);
+	new_ns->event = 0;
 
 	down_write(&tsk->namespace->sem);
 	/* First pass: copy the tree topology */
@@ -1377,6 +1402,7 @@ asmlinkage long sys_pivot_root(const cha
 	detach_mnt(user_nd.mnt, &root_parent);
 	attach_mnt(user_nd.mnt, &old_nd);     /* mount old root on put_old */
 	attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */
+	touch_namespace(current->namespace);
 	spin_unlock(&vfsmount_lock);
 	chroot_fs_refs(&user_nd, &new_nd);
 	security_sb_post_pivotroot(&user_nd, &new_nd);
@@ -1413,6 +1439,8 @@ static void __init init_mount_tree(void)
 	atomic_set(&namespace->count, 1);
 	INIT_LIST_HEAD(&namespace->list);
 	init_rwsem(&namespace->sem);
+	init_waitqueue_head(&namespace->poll);
+	namespace->event = 0;
 	list_add(&mnt->mnt_list, &namespace->list);
 	namespace->root = mnt;
 	mnt->mnt_namespace = namespace;
fs/proc/base.c +47 -15

90 lines not on this page · raw message

include/linux/namespace.h +2 -0
@@ -10,6 +10,8 @@ struct namespace {
 	struct vfsmount *	root;
 	struct list_head	list;
 	struct rw_semaphore	sem;
+	wait_queue_head_t poll;
+	int event;
 };
 
 extern int copy_namespace(int, struct task_struct *);
#1 lore raw

Ahh, now this is interesting.
I tried to make /proc/mdstat pollable some time ago and go howled down as it was said to be the Wrong Thing(TM). (It was not, I hasten to add, Al who howled me down).

I look forward to seeing the progress of this patch.

I wonder if there is any chance of attributes in sysfs being pollable too??

NeilBrown

#2 lore raw
Quoted 2 lines · Neil Brown

Ahh, now this is interesting.

Yeah, if we do this, we get rid of the "mount"-like kernel uevents, as they were in the wrong place.

Quoted 2 lines

I wonder if there is any chance of attributes in sysfs being pollable too??

I haven't had anyone ask for this yet. It might be a bit harder, as we would need to have a hook back to sysfs to let userspace know it had changed. As long as it was optional and didn't cause any overhead for everyone that does not need it, I don't see why it could not be added.

All we need now is a patch :)

thanks,

greg k-h

#3 lore raw
Quoted 10 lines · On Monday November 7, [email protected] wrote:
Quoted 2 lines · Neil Brown

Ahh, now this is interesting.

Yeah, if we do this, we get rid of the "mount"-like kernel uevents, as they were in the wrong place.

Quoted 2 lines

I wonder if there is any chance of attributes in sysfs being pollable too??

Quoted 6 lines

I haven't had anyone ask for this yet. It might be a bit harder, as we would need to have a hook back to sysfs to let userspace know it had changed. As long as it was optional and didn't cause any overhead for everyone that does not need it, I don't see why it could not be added.

All we need now is a patch :)

Here's one. Untested, but it compiles.
I don't actually have a need for this now, but I might soon, and would like to know if it is an option.

Comments welcome.

NeilBrown

-------------------------------
Allow sysfs attribute files to be pollable.

This is untested 'strawman' code.

It allows an attribute file in sysfs to be polled for activity. The 'poll' interface if based on that for /proc/mounts.
I think it works like this:
Open the file
Read all the contents.
Call poll requesting POLLERR
When poll returns, read from the file again (maybe you need to close
and reopen, may you need to lseek to the start ???)

Events are signaled by an object manager calling
sysfs_notify(kobj, dir, attr);

If the dir is non-NULL, it is used to find a subdirectory which contains the attribute (presumably created by sysfs_create_group).

I have no idea if there is adequate locking anywhere here. s_event should possibly be atomic_t.

This has a cost of one int and one wait_queue_head per attribute, and one int per file.

We could probably reduce this cost by having only one wait_queue_head per kobject.

The name "sysfs_notify" may be confused with the inotify functionality. Maybe it would be nice to support inotify for sysfs attributes as well?

Comments welcome.

### Diffstat output

 ./fs/sysfs/dir.c        |    1 +
 ./fs/sysfs/file.c       |   34 ++++++++++++++++++++++++++++++++++
 ./fs/sysfs/inode.c      |   20 ++++++++++++++++++++
 ./fs/sysfs/sysfs.h      |    1 +
 ./include/linux/sysfs.h |    3 +++
 5 files changed, 59 insertions(+)
fs/sysfs/dir.c renamed from fs/sysfs/dir.c~current~ +1 -0
diff ./fs/sysfs/dir.c~current~ ./fs/sysfs/dir.c
@@ -45,6 +45,7 @@ static struct sysfs_dirent * sysfs_new_d
 	atomic_set(&sd->s_count, 1);
 	INIT_LIST_HEAD(&sd->s_children);
 	list_add(&sd->s_sibling, &parent_sd->s_children);
+	init_waitqueue_head(&sd->s_poll);
 	sd->s_element = element;
 
 	return sd;
fs/sysfs/file.c renamed from fs/sysfs/file.c~current~ +34 -0
diff ./fs/sysfs/file.c~current~ ./fs/sysfs/file.c
@@ -7,6 +7,7 @@
 #include <linux/kobject.h>
 #include <linux/namei.h>
 #include <linux/limits.h>
+#include <linux/poll.h>
 
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
@@ -59,6 +60,7 @@ struct sysfs_buffer {
 	struct sysfs_ops	* ops;
 	struct semaphore	sem;
 	int			needs_read_fill;
+	int			event;
 };
 
 
@@ -305,10 +307,12 @@ static int check_perm(struct inode * ino
 	 */
 	buffer = kmalloc(sizeof(struct sysfs_buffer),GFP_KERNEL);
 	if (buffer) {
+		struct sysfs_dirent * sd = file->f_dentry->d_fsdata;
 		memset(buffer,0,sizeof(struct sysfs_buffer));
 		init_MUTEX(&buffer->sem);
 		buffer->needs_read_fill = 1;
 		buffer->ops = ops;
+		buffer->event = sd->s_event;
 		file->private_data = buffer;
 	} else
 		error = -ENOMEM;
@@ -357,12 +361,42 @@ static int sysfs_release(struct inode * 
 	return 0;
 }
 
+static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
+{
+	struct sysfs_buffer * buffer = filp->private_data;
+	struct sysfs_dirent * sd = filp->f_dentry->d_fsdata;
+	int res = 0;
+
+	poll_wait(filp, &sd->s_poll, wait);
+
+	if (buffer->event != sd->s_event) {
+		sd->s_event = buffer->event;
+		res = POLLERR;
+	}
+	return res;
+}
+
+void sysfs_notify(struct kobject * k, char *dir, char *attr)
+{
+	struct sysfs_dirent *sd = k->dentry->d_fsdata;
+	if (sd && dir)
+		sd = sysfs_find(sd, dir);
+	if (sd && attr)
+		sd = sysfs_find(sd, attr);
+	if (sd) {
+		sd->s_event++;
+		wake_up_interruptible(&sd->s_poll);
+	}
+}
+EXPORT_SYMBOL_GPL(sysfs_notify);
+
 struct file_operations sysfs_file_operations = {
 	.read		= sysfs_read_file,
 	.write		= sysfs_write_file,
 	.llseek		= generic_file_llseek,
 	.open		= sysfs_open_file,
 	.release	= sysfs_release,
+	.poll		= sysfs_poll,
 };
 
 
fs/sysfs/inode.c renamed from fs/sysfs/inode.c~current~ +20 -0
diff ./fs/sysfs/inode.c~current~ ./fs/sysfs/inode.c
@@ -247,3 +247,23 @@ void sysfs_hash_and_remove(struct dentry
 }
 
 
+struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name)
+{
+	struct sysfs_dirent * sd, * rv = NULL;
+
+	if (dir->s_dentry == NULL ||
+	    dir->s_dentry->d_inode == NULL)
+		return NULL;
+
+	down(&dir->s_dentry->d_inode->i_sem);
+	list_for_each_entry(sd, &dir->s_children, s_sibling) {
+		if (!sd->s_element)
+			continue;
+		if (!strcmp(sysfs_get_name(sd), name)) {
+			rv = sd;
+			break;
+		}
+	}
+	up(&dir->s_dentry->d_inode->i_sem);
+	return rv;
+}
fs/sysfs/sysfs.h renamed from fs/sysfs/sysfs.h~current~ +1 -0
diff ./fs/sysfs/sysfs.h~current~ ./fs/sysfs/sysfs.h
@@ -10,6 +10,7 @@ extern int sysfs_make_dirent(struct sysf
 
 extern int sysfs_add_file(struct dentry *, const struct attribute *, int);
 extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
+extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name);
 
 extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
 extern void sysfs_remove_subdir(struct dentry *);
include/linux/sysfs.h renamed from include/linux/sysfs.h~current~ +3 -0
diff ./include/linux/sysfs.h~current~ ./include/linux/sysfs.h
@@ -11,6 +11,7 @@
 #define _SYSFS_H_
 
 #include <asm/atomic.h>
+#include <linux/wait.h>
 
 struct kobject;
 struct module;
@@ -74,6 +75,8 @@ struct sysfs_dirent {
 	umode_t			s_mode;
 	struct dentry		* s_dentry;
 	struct iattr		* s_iattr;
+	int			s_event;
+	wait_queue_head_t	s_poll;
 };
 
 #define SYSFS_ROOT		0x0001