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 | |
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>
-rw-r--r-- | fs/sysfs/dir.c | 1 | ||||
-rw-r--r-- | fs/sysfs/file.c | 25 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 1 | ||||
-rw-r--r-- | include/linux/kobject.h | 1 | ||||
-rw-r--r-- | lib/kobject.c | 1 |
5 files changed, 19 insertions, 10 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 4ad9422566a8..e301a1207b60 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -318,7 +318,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) | |||
318 | 318 | ||
319 | atomic_set(&sd->s_count, 1); | 319 | atomic_set(&sd->s_count, 1); |
320 | atomic_set(&sd->s_active, 0); | 320 | atomic_set(&sd->s_active, 0); |
321 | atomic_set(&sd->s_event, 1); | ||
322 | 321 | ||
323 | sd->s_name = name; | 322 | sd->s_name = name; |
324 | sd->s_mode = mode; | 323 | sd->s_mode = mode; |
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); |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 3adce7d5e4f7..269c845c590f 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -46,7 +46,6 @@ struct sysfs_dirent { | |||
46 | ino_t s_ino; | 46 | ino_t s_ino; |
47 | umode_t s_mode; | 47 | umode_t s_mode; |
48 | struct iattr *s_iattr; | 48 | struct iattr *s_iattr; |
49 | atomic_t s_event; | ||
50 | }; | 49 | }; |
51 | 50 | ||
52 | #define SD_DEACTIVATED_BIAS INT_MIN | 51 | #define SD_DEACTIVATED_BIAS INT_MIN |
diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 0777b3f57ae6..a8a84fcccbc0 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h | |||
@@ -66,7 +66,6 @@ struct kobject { | |||
66 | struct kset * kset; | 66 | struct kset * kset; |
67 | struct kobj_type * ktype; | 67 | struct kobj_type * ktype; |
68 | struct sysfs_dirent * sd; | 68 | struct sysfs_dirent * sd; |
69 | wait_queue_head_t poll; | ||
70 | }; | 69 | }; |
71 | 70 | ||
72 | extern int kobject_set_name(struct kobject *, const char *, ...) | 71 | extern int kobject_set_name(struct kobject *, const char *, ...) |
diff --git a/lib/kobject.c b/lib/kobject.c index e8181d3cec34..fc6db6b4bfc5 100644 --- a/lib/kobject.c +++ b/lib/kobject.c | |||
@@ -131,7 +131,6 @@ void kobject_init(struct kobject * kobj) | |||
131 | return; | 131 | return; |
132 | kref_init(&kobj->kref); | 132 | kref_init(&kobj->kref); |
133 | INIT_LIST_HEAD(&kobj->entry); | 133 | INIT_LIST_HEAD(&kobj->entry); |
134 | init_waitqueue_head(&kobj->poll); | ||
135 | kobj->kset = kset_get(kobj->kset); | 134 | kobj->kset = kset_get(kobj->kset); |
136 | } | 135 | } |
137 | 136 | ||