make /proc/mounts pollable
Subsystems (3)
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 vfsmoreturn 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 vfsmountfor (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 *mndown_write (¤t->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 *mlist_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 nameidatdetach_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 vfsmountstruct 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 tasatomic_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 chadetach_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 *);
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
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
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
@@ -45,6 +45,7 @@ static struct sysfs_dirent * sysfs_new_datomic_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
@@ -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
@@ -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
@@ -10,6 +10,7 @@ extern int sysfs_make_dirent(struct sysfextern 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
@@ -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