aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2006-03-20 01:53:53 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-04-14 14:41:24 -0400
commit4508a7a734b111b8b7e39986237d84acb1168dd0 (patch)
tree8fe535fbb97d619c9069da26367ead03d1a294aa /fs
parentf043ca43c1ae354346f72dc5826d820d5619f0b2 (diff)
[PATCH] sysfs: Allow sysfs attribute files to be pollable
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. or lseek to start of file and go back to the 'read'. 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>
Diffstat (limited to 'fs')
-rw-r--r--fs/sysfs/dir.c1
-rw-r--r--fs/sysfs/file.c76
-rw-r--r--fs/sysfs/sysfs.h1
3 files changed, 78 insertions, 0 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 6cfdc9a87772..610b5bdbe75b 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -43,6 +43,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd,
43 43
44 memset(sd, 0, sizeof(*sd)); 44 memset(sd, 0, sizeof(*sd));
45 atomic_set(&sd->s_count, 1); 45 atomic_set(&sd->s_count, 1);
46 atomic_set(&sd->s_event, 0);
46 INIT_LIST_HEAD(&sd->s_children); 47 INIT_LIST_HEAD(&sd->s_children);
47 list_add(&sd->s_sibling, &parent_sd->s_children); 48 list_add(&sd->s_sibling, &parent_sd->s_children);
48 sd->s_element = element; 49 sd->s_element = element;
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index f1cb1ddde511..cf3786625bfa 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -6,6 +6,7 @@
6#include <linux/fsnotify.h> 6#include <linux/fsnotify.h>
7#include <linux/kobject.h> 7#include <linux/kobject.h>
8#include <linux/namei.h> 8#include <linux/namei.h>
9#include <linux/poll.h>
9#include <asm/uaccess.h> 10#include <asm/uaccess.h>
10#include <asm/semaphore.h> 11#include <asm/semaphore.h>
11 12
@@ -57,6 +58,7 @@ struct sysfs_buffer {
57 struct sysfs_ops * ops; 58 struct sysfs_ops * ops;
58 struct semaphore sem; 59 struct semaphore sem;
59 int needs_read_fill; 60 int needs_read_fill;
61 int event;
60}; 62};
61 63
62 64
@@ -72,6 +74,7 @@ struct sysfs_buffer {
72 */ 74 */
73static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) 75static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
74{ 76{
77 struct sysfs_dirent * sd = dentry->d_fsdata;
75 struct attribute * attr = to_attr(dentry); 78 struct attribute * attr = to_attr(dentry);
76 struct kobject * kobj = to_kobj(dentry->d_parent); 79 struct kobject * kobj = to_kobj(dentry->d_parent);
77 struct sysfs_ops * ops = buffer->ops; 80 struct sysfs_ops * ops = buffer->ops;
@@ -83,6 +86,7 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer
83 if (!buffer->page) 86 if (!buffer->page)
84 return -ENOMEM; 87 return -ENOMEM;
85 88
89 buffer->event = atomic_read(&sd->s_event);
86 count = ops->show(kobj,attr,buffer->page); 90 count = ops->show(kobj,attr,buffer->page);
87 buffer->needs_read_fill = 0; 91 buffer->needs_read_fill = 0;
88 BUG_ON(count > (ssize_t)PAGE_SIZE); 92 BUG_ON(count > (ssize_t)PAGE_SIZE);
@@ -348,12 +352,84 @@ static int sysfs_release(struct inode * inode, struct file * filp)
348 return 0; 352 return 0;
349} 353}
350 354
355/* Sysfs attribute files are pollable. The idea is that you read
356 * the content and then you use 'poll' or 'select' to wait for
357 * the content to change. When the content changes (assuming the
358 * manager for the kobject supports notification), poll will
359 * return POLLERR|POLLPRI, and select will return the fd whether
360 * it is waiting for read, write, or exceptions.
361 * Once poll/select indicates that the value has changed, you
362 * need to close and re-open the file, as simply seeking and reading
363 * again will not get new data, or reset the state of 'poll'.
364 * Reminder: this only works for attributes which actively support
365 * it, and it is not possible to test an attribute from userspace
366 * to see if it supports poll (Nether 'poll' or 'select' return
367 * an appropriate error code). When in doubt, set a suitable timeout value.
368 */
369static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
370{
371 struct sysfs_buffer * buffer = filp->private_data;
372 struct kobject * kobj = to_kobj(filp->f_dentry->d_parent);
373 struct sysfs_dirent * sd = filp->f_dentry->d_fsdata;
374 int res = 0;
375
376 poll_wait(filp, &kobj->poll, wait);
377
378 if (buffer->event != atomic_read(&sd->s_event)) {
379 res = POLLERR|POLLPRI;
380 buffer->needs_read_fill = 1;
381 }
382
383 return res;
384}
385
386
387static struct dentry *step_down(struct dentry *dir, const char * name)
388{
389 struct dentry * de;
390
391 if (dir == NULL || dir->d_inode == NULL)
392 return NULL;
393
394 mutex_lock(&dir->d_inode->i_mutex);
395 de = lookup_one_len(name, dir, strlen(name));
396 mutex_unlock(&dir->d_inode->i_mutex);
397 dput(dir);
398 if (IS_ERR(de))
399 return NULL;
400 if (de->d_inode == NULL) {
401 dput(de);
402 return NULL;
403 }
404 return de;
405}
406
407void sysfs_notify(struct kobject * k, char *dir, char *attr)
408{
409 struct dentry *de = k->dentry;
410 if (de)
411 dget(de);
412 if (de && dir)
413 de = step_down(de, dir);
414 if (de && attr)
415 de = step_down(de, attr);
416 if (de) {
417 struct sysfs_dirent * sd = de->d_fsdata;
418 if (sd)
419 atomic_inc(&sd->s_event);
420 wake_up_interruptible(&k->poll);
421 dput(de);
422 }
423}
424EXPORT_SYMBOL_GPL(sysfs_notify);
425
351const struct file_operations sysfs_file_operations = { 426const struct file_operations sysfs_file_operations = {
352 .read = sysfs_read_file, 427 .read = sysfs_read_file,
353 .write = sysfs_write_file, 428 .write = sysfs_write_file,
354 .llseek = generic_file_llseek, 429 .llseek = generic_file_llseek,
355 .open = sysfs_open_file, 430 .open = sysfs_open_file,
356 .release = sysfs_release, 431 .release = sysfs_release,
432 .poll = sysfs_poll,
357}; 433};
358 434
359 435
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 32958a7c50e9..3651ffb5ec09 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -11,6 +11,7 @@ extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *,
11 11
12extern int sysfs_add_file(struct dentry *, const struct attribute *, int); 12extern int sysfs_add_file(struct dentry *, const struct attribute *, int);
13extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); 13extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
14extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name);
14 15
15extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); 16extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
16extern void sysfs_remove_subdir(struct dentry *); 17extern void sysfs_remove_subdir(struct dentry *);