diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2005-12-22 15:22:26 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-12-22 15:22:26 -0800 |
commit | d0e4f6d13b33c4e463a4fb91cfecc4d87fa48c94 (patch) | |
tree | b53ae35248e09c94cbd6c6831262625b58012700 /driver | |
parent | 87c5263ad6176dc271c0d2f03ffadfe215aa426b (diff) | |
download | patches-d0e4f6d13b33c4e463a4fb91cfecc4d87fa48c94.tar.gz |
usb and other stuff
Diffstat (limited to 'driver')
-rw-r--r-- | driver/allow-sysfs-attribute-files-to-be-pollable.patch | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/driver/allow-sysfs-attribute-files-to-be-pollable.patch b/driver/allow-sysfs-attribute-files-to-be-pollable.patch new file mode 100644 index 00000000000000..c0297d9b4bb767 --- /dev/null +++ b/driver/allow-sysfs-attribute-files-to-be-pollable.patch @@ -0,0 +1,238 @@ +From neilb@suse.de Tue Dec 20 15:19:48 2005 +From: Neil Brown <neilb@suse.de> +To: Greg KH <greg@kroah.com>, <maneesh@in.ibm.com> +Date: Wed, 21 Dec 2005 10:14:29 +1100 +Message-ID: <17320.36949.269788.520946@cse.unsw.edu.au> +Subject: [PATCH - 2.6.15-rc5-mm3] Allow sysfs attribute files to be pollable. + +This allows an attribute file in sysfs to be polled for activity. + +It works like this: + Open the file + Read all the contents. + Call poll requesting POLLERR or POLLPRI (so select/exceptfds works) + When poll returns, close the file, and go to top of loop. + +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). + +This has a cost of one int per attribute, one wait_queuehead per kobject, +one int per open file. + +The name "sysfs_notify" may be confused with the inotify +functionality. Maybe it would be nice to support inotify for sysfs +attributes as well? + +This patch also uses sysfs_notify to allow /sys/block/md*/md/sync_action +to be pollable + +Signed-off-by: Neil Brown <neilb@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + fs/sysfs/dir.c | 1 + + fs/sysfs/file.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + fs/sysfs/inode.c | 20 ++++++++++++++++++++ + fs/sysfs/sysfs.h | 1 + + include/linux/kobject.h | 2 ++ + include/linux/sysfs.h | 7 +++++++ + lib/kobject.c | 1 + + 7 files changed, 79 insertions(+) + +--- gregkh-2.6.orig/fs/sysfs/dir.c ++++ gregkh-2.6/fs/sysfs/dir.c +@@ -43,6 +43,7 @@ static struct sysfs_dirent * sysfs_new_d + + memset(sd, 0, sizeof(*sd)); + atomic_set(&sd->s_count, 1); ++ atomic_set(&sd->s_event, 0); + INIT_LIST_HEAD(&sd->s_children); + list_add(&sd->s_sibling, &parent_sd->s_children); + sd->s_element = element; +--- gregkh-2.6.orig/fs/sysfs/file.c ++++ gregkh-2.6/fs/sysfs/file.c +@@ -6,6 +6,7 @@ + #include <linux/fsnotify.h> + #include <linux/kobject.h> + #include <linux/namei.h> ++#include <linux/poll.h> + #include <asm/uaccess.h> + #include <asm/semaphore.h> + +@@ -57,6 +58,7 @@ struct sysfs_buffer { + struct sysfs_ops * ops; + struct semaphore sem; + int needs_read_fill; ++ int event; + }; + + +@@ -72,6 +74,7 @@ struct sysfs_buffer { + */ + static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) + { ++ struct sysfs_dirent * sd = dentry->d_fsdata; + struct attribute * attr = to_attr(dentry); + struct kobject * kobj = to_kobj(dentry->d_parent); + struct sysfs_ops * ops = buffer->ops; +@@ -83,6 +86,7 @@ static int fill_read_buffer(struct dentr + if (!buffer->page) + return -ENOMEM; + ++ buffer->event = atomic_read(&sd->s_event); + count = ops->show(kobj,attr,buffer->page); + buffer->needs_read_fill = 0; + BUG_ON(count > (ssize_t)PAGE_SIZE); +@@ -349,12 +353,55 @@ static int sysfs_release(struct inode * + return 0; + } + ++/* Sysfs attribute files are pollable. The idea is that you read ++ * the content and then you use 'poll' or 'select' to wait for ++ * the content to change. When the content changes (assuming the ++ * manager for the kobject supports notification), poll will ++ * return POLLERR|POLLPRI, and select will return the fd whether ++ * it is waiting for read, write, or exceptions. ++ * Once poll/select indicates that the value has changed, you ++ * need to close and re-open the file, as simply seeking and reading ++ * again will not get new data, or reset the state of 'poll'. ++ * Reminder: this only works for attributes which actively support ++ * it, and it is not possible to test an attribute from userspace ++ * to see if it supports poll. ++ */ ++static unsigned int sysfs_poll(struct file *filp, poll_table *wait) ++{ ++ struct sysfs_buffer * buffer = filp->private_data; ++ struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); ++ struct sysfs_dirent * sd = filp->f_dentry->d_fsdata; ++ int res = 0; ++ ++ poll_wait(filp, &kobj->poll, wait); ++ ++ if (buffer->event != atomic_read(&sd->s_event)) ++ res = POLLERR|POLLPRI; ++ ++ 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) { ++ atomic_inc(&sd->s_event); ++ wake_up_interruptible(&k->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, + }; + + +--- gregkh-2.6.orig/fs/sysfs/inode.c ++++ gregkh-2.6/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; ++} +--- gregkh-2.6.orig/fs/sysfs/sysfs.h ++++ gregkh-2.6/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 *); +--- gregkh-2.6.orig/include/linux/kobject.h ++++ gregkh-2.6/include/linux/kobject.h +@@ -24,6 +24,7 @@ + #include <linux/rwsem.h> + #include <linux/kref.h> + #include <linux/kernel.h> ++#include <linux/wait.h> + #include <asm/atomic.h> + + #define KOBJ_NAME_LEN 20 +@@ -54,6 +55,7 @@ struct kobject { + struct kset * kset; + struct kobj_type * ktype; + struct dentry * dentry; ++ wait_queue_head_t poll; + }; + + extern int kobject_set_name(struct kobject *, const char *, ...) +--- gregkh-2.6.orig/include/linux/sysfs.h ++++ gregkh-2.6/include/linux/sysfs.h +@@ -74,6 +74,7 @@ struct sysfs_dirent { + umode_t s_mode; + struct dentry * s_dentry; + struct iattr * s_iattr; ++ atomic_t s_event; + }; + + #define SYSFS_ROOT 0x0001 +@@ -118,6 +119,7 @@ int sysfs_remove_bin_file(struct kobject + int sysfs_create_group(struct kobject *, const struct attribute_group *); + void sysfs_remove_group(struct kobject *, const struct attribute_group *); + ++void sysfs_notify(struct kobject * k, char *dir, char *attr); + #else /* CONFIG_SYSFS */ + + static inline int sysfs_create_dir(struct kobject * k) +@@ -185,6 +187,11 @@ static inline void sysfs_remove_group(st + ; + } + ++static inline void sysfs_notify(struct kobject * k, char *dir, char *attr) ++{ ++ ; ++} ++ + #endif /* CONFIG_SYSFS */ + + #endif /* _SYSFS_H_ */ +--- gregkh-2.6.orig/lib/kobject.c ++++ gregkh-2.6/lib/kobject.c +@@ -125,6 +125,7 @@ void kobject_init(struct kobject * kobj) + WARN_ON(atomic_read(&kobj->kref.refcount)); + kref_init(&kobj->kref); + INIT_LIST_HEAD(&kobj->entry); ++ init_waitqueue_head(&kobj->poll); + kobj->kset = kset_get(kobj->kset); + } + |