diff options
author | Tejun Heo <htejun@gmail.com> | 2007-09-20 03:05:12 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-10-12 17:51:11 -0400 |
commit | a4e8b912541d5372ae049a3b7c1979968e52c40b (patch) | |
tree | a95937748b6216f914abe55ed47c2627e7856bf7 /fs/sysfs/file.c | |
parent | 85a4ffad3de77177591f7c2c18c26c3c8dd28bff (diff) |
sysfs: move sysfs file poll implementation to sysfs_open_dirent
Sysfs file poll implementation is scattered over sysfs and kobject.
Event numbering is done in sysfs_dirent but wait itself is done on
kobject. This not only unecessarily bloats both kobject and
sysfs_dirent but is also buggy - if a sysfs_dirent is removed while
there still are pollers, the associaton betwen the kobject and
sysfs_dirent breaks and kobject may be freed with the pollers still
sleeping on it.
This patch moves whole poll implementation into sysfs_open_dirent.
Each time a sysfs_open_dirent is created, event number restarts from 1
and pollers sleep on sysfs_open_dirent. As event sequence number is
meaningless without any open file and pollers should have open file
and thus sysfs_open_dirent, this ephemeral event counting works and is
a saner implementation.
This patch fixes the dnagling sleepers bug and reduces the sizes of
kobject and sysfs_dirent by one pointer.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs/file.c')
-rw-r--r-- | fs/sysfs/file.c | 25 |
1 files changed, 19 insertions, 6 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index b13ba94cf8ac..c05f9618b2dc 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -62,6 +62,8 @@ static spinlock_t sysfs_open_dirent_lock = SPIN_LOCK_UNLOCKED; | |||
62 | 62 | ||
63 | struct sysfs_open_dirent { | 63 | struct sysfs_open_dirent { |
64 | atomic_t refcnt; | 64 | atomic_t refcnt; |
65 | atomic_t event; | ||
66 | wait_queue_head_t poll; | ||
65 | struct list_head buffers; /* goes through sysfs_buffer.list */ | 67 | struct list_head buffers; /* goes through sysfs_buffer.list */ |
66 | }; | 68 | }; |
67 | 69 | ||
@@ -104,7 +106,7 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer | |||
104 | if (!sysfs_get_active_two(attr_sd)) | 106 | if (!sysfs_get_active_two(attr_sd)) |
105 | return -ENODEV; | 107 | return -ENODEV; |
106 | 108 | ||
107 | buffer->event = atomic_read(&attr_sd->s_event); | 109 | buffer->event = atomic_read(&attr_sd->s_attr.open->event); |
108 | count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); | 110 | count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); |
109 | 111 | ||
110 | sysfs_put_active_two(attr_sd); | 112 | sysfs_put_active_two(attr_sd); |
@@ -301,6 +303,8 @@ static int sysfs_get_open_dirent(struct sysfs_dirent *sd, | |||
301 | return -ENOMEM; | 303 | return -ENOMEM; |
302 | 304 | ||
303 | atomic_set(&new_od->refcnt, 0); | 305 | atomic_set(&new_od->refcnt, 0); |
306 | atomic_set(&new_od->event, 1); | ||
307 | init_waitqueue_head(&new_od->poll); | ||
304 | INIT_LIST_HEAD(&new_od->buffers); | 308 | INIT_LIST_HEAD(&new_od->buffers); |
305 | goto retry; | 309 | goto retry; |
306 | } | 310 | } |
@@ -443,17 +447,17 @@ static unsigned int sysfs_poll(struct file *filp, poll_table *wait) | |||
443 | { | 447 | { |
444 | struct sysfs_buffer * buffer = filp->private_data; | 448 | struct sysfs_buffer * buffer = filp->private_data; |
445 | struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; | 449 | struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; |
446 | struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; | 450 | struct sysfs_open_dirent *od = attr_sd->s_attr.open; |
447 | 451 | ||
448 | /* need parent for the kobj, grab both */ | 452 | /* need parent for the kobj, grab both */ |
449 | if (!sysfs_get_active_two(attr_sd)) | 453 | if (!sysfs_get_active_two(attr_sd)) |
450 | goto trigger; | 454 | goto trigger; |
451 | 455 | ||
452 | poll_wait(filp, &kobj->poll, wait); | 456 | poll_wait(filp, &od->poll, wait); |
453 | 457 | ||
454 | sysfs_put_active_two(attr_sd); | 458 | sysfs_put_active_two(attr_sd); |
455 | 459 | ||
456 | if (buffer->event != atomic_read(&attr_sd->s_event)) | 460 | if (buffer->event != atomic_read(&od->event)) |
457 | goto trigger; | 461 | goto trigger; |
458 | 462 | ||
459 | return 0; | 463 | return 0; |
@@ -474,8 +478,17 @@ void sysfs_notify(struct kobject *k, char *dir, char *attr) | |||
474 | if (sd && attr) | 478 | if (sd && attr) |
475 | sd = sysfs_find_dirent(sd, attr); | 479 | sd = sysfs_find_dirent(sd, attr); |
476 | if (sd) { | 480 | if (sd) { |
477 | atomic_inc(&sd->s_event); | 481 | struct sysfs_open_dirent *od; |
478 | wake_up_interruptible(&k->poll); | 482 | |
483 | spin_lock(&sysfs_open_dirent_lock); | ||
484 | |||
485 | od = sd->s_attr.open; | ||
486 | if (od) { | ||
487 | atomic_inc(&od->event); | ||
488 | wake_up_interruptible(&od->poll); | ||
489 | } | ||
490 | |||
491 | spin_unlock(&sysfs_open_dirent_lock); | ||
479 | } | 492 | } |
480 | 493 | ||
481 | mutex_unlock(&sysfs_mutex); | 494 | mutex_unlock(&sysfs_mutex); |