diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/sysfs/dir.c | 1 | ||||
-rw-r--r-- | fs/sysfs/file.c | 76 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 1 |
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 | */ |
73 | static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) | 75 | static 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 | */ | ||
369 | static 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 | |||
387 | static 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 | |||
407 | void 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 | } | ||
424 | EXPORT_SYMBOL_GPL(sysfs_notify); | ||
425 | |||
351 | const struct file_operations sysfs_file_operations = { | 426 | const 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 | ||
12 | extern int sysfs_add_file(struct dentry *, const struct attribute *, int); | 12 | extern int sysfs_add_file(struct dentry *, const struct attribute *, int); |
13 | extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); | 13 | extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); |
14 | extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name); | ||
14 | 15 | ||
15 | extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); | 16 | extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); |
16 | extern void sysfs_remove_subdir(struct dentry *); | 17 | extern void sysfs_remove_subdir(struct dentry *); |