aboutsummaryrefslogtreecommitdiffstats
path: root/driver
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2005-12-22 15:22:26 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2005-12-22 15:22:26 -0800
commitd0e4f6d13b33c4e463a4fb91cfecc4d87fa48c94 (patch)
treeb53ae35248e09c94cbd6c6831262625b58012700 /driver
parent87c5263ad6176dc271c0d2f03ffadfe215aa426b (diff)
downloadpatches-d0e4f6d13b33c4e463a4fb91cfecc4d87fa48c94.tar.gz
usb and other stuff
Diffstat (limited to 'driver')
-rw-r--r--driver/allow-sysfs-attribute-files-to-be-pollable.patch238
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);
+ }
+