diff options
author | Oliver Neukum <oliver@neukum.org> | 2006-12-20 04:52:44 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 13:37:13 -0500 |
commit | 94bebf4d1b8e7719f0f3944c037a21cfd99a4af7 (patch) | |
tree | 25c6ba1836e74f608b4e434b4f0f4d5c28b11de1 /fs/sysfs/file.c | |
parent | cb986b749c7178422bfbc982cd30e04d5db54bbc (diff) |
Driver core: fix race in sysfs between sysfs_remove_file() and read()/write()
This patch prevents a race between IO and removing a file from sysfs.
It introduces a list of sysfs_buffers associated with a file at the inode.
Upon removal of a file the list is walked and the buffers marked orphaned.
IO to orphaned buffers fails with -ENODEV. The driver can safely free
associated data structures or be unloaded.
Signed-off-by: Oliver Neukum <oliver@neukum.name>
Acked-by: Maneesh Soni <maneesh@in.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 | 67 |
1 files changed, 50 insertions, 17 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 9cfe53e1e00d..cba4c1c7383c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -7,6 +7,7 @@ | |||
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 <linux/poll.h> |
10 | #include <linux/list.h> | ||
10 | #include <asm/uaccess.h> | 11 | #include <asm/uaccess.h> |
11 | #include <asm/semaphore.h> | 12 | #include <asm/semaphore.h> |
12 | 13 | ||
@@ -50,17 +51,29 @@ static struct sysfs_ops subsys_sysfs_ops = { | |||
50 | .store = subsys_attr_store, | 51 | .store = subsys_attr_store, |
51 | }; | 52 | }; |
52 | 53 | ||
54 | /** | ||
55 | * add_to_collection - add buffer to a collection | ||
56 | * @buffer: buffer to be added | ||
57 | * @node inode of set to add to | ||
58 | */ | ||
53 | 59 | ||
54 | struct sysfs_buffer { | 60 | static inline void |
55 | size_t count; | 61 | add_to_collection(struct sysfs_buffer *buffer, struct inode *node) |
56 | loff_t pos; | 62 | { |
57 | char * page; | 63 | struct sysfs_buffer_collection *set = node->i_private; |
58 | struct sysfs_ops * ops; | ||
59 | struct semaphore sem; | ||
60 | int needs_read_fill; | ||
61 | int event; | ||
62 | }; | ||
63 | 64 | ||
65 | mutex_lock(&node->i_mutex); | ||
66 | list_add(&buffer->associates, &set->associates); | ||
67 | mutex_unlock(&node->i_mutex); | ||
68 | } | ||
69 | |||
70 | static inline void | ||
71 | remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) | ||
72 | { | ||
73 | mutex_lock(&node->i_mutex); | ||
74 | list_del(&buffer->associates); | ||
75 | mutex_unlock(&node->i_mutex); | ||
76 | } | ||
64 | 77 | ||
65 | /** | 78 | /** |
66 | * fill_read_buffer - allocate and fill buffer from object. | 79 | * fill_read_buffer - allocate and fill buffer from object. |
@@ -153,6 +166,10 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
153 | ssize_t retval = 0; | 166 | ssize_t retval = 0; |
154 | 167 | ||
155 | down(&buffer->sem); | 168 | down(&buffer->sem); |
169 | if (buffer->orphaned) { | ||
170 | retval = -ENODEV; | ||
171 | goto out; | ||
172 | } | ||
156 | if (buffer->needs_read_fill) { | 173 | if (buffer->needs_read_fill) { |
157 | if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) | 174 | if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) |
158 | goto out; | 175 | goto out; |
@@ -165,7 +182,6 @@ out: | |||
165 | return retval; | 182 | return retval; |
166 | } | 183 | } |
167 | 184 | ||
168 | |||
169 | /** | 185 | /** |
170 | * fill_write_buffer - copy buffer from userspace. | 186 | * fill_write_buffer - copy buffer from userspace. |
171 | * @buffer: data buffer for file. | 187 | * @buffer: data buffer for file. |
@@ -243,19 +259,25 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t | |||
243 | ssize_t len; | 259 | ssize_t len; |
244 | 260 | ||
245 | down(&buffer->sem); | 261 | down(&buffer->sem); |
262 | if (buffer->orphaned) { | ||
263 | len = -ENODEV; | ||
264 | goto out; | ||
265 | } | ||
246 | len = fill_write_buffer(buffer, buf, count); | 266 | len = fill_write_buffer(buffer, buf, count); |
247 | if (len > 0) | 267 | if (len > 0) |
248 | len = flush_write_buffer(file->f_path.dentry, buffer, len); | 268 | len = flush_write_buffer(file->f_path.dentry, buffer, len); |
249 | if (len > 0) | 269 | if (len > 0) |
250 | *ppos += len; | 270 | *ppos += len; |
271 | out: | ||
251 | up(&buffer->sem); | 272 | up(&buffer->sem); |
252 | return len; | 273 | return len; |
253 | } | 274 | } |
254 | 275 | ||
255 | static int check_perm(struct inode * inode, struct file * file) | 276 | static int sysfs_open_file(struct inode *inode, struct file *file) |
256 | { | 277 | { |
257 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); | 278 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); |
258 | struct attribute * attr = to_attr(file->f_path.dentry); | 279 | struct attribute * attr = to_attr(file->f_path.dentry); |
280 | struct sysfs_buffer_collection *set; | ||
259 | struct sysfs_buffer * buffer; | 281 | struct sysfs_buffer * buffer; |
260 | struct sysfs_ops * ops = NULL; | 282 | struct sysfs_ops * ops = NULL; |
261 | int error = 0; | 283 | int error = 0; |
@@ -285,6 +307,18 @@ static int check_perm(struct inode * inode, struct file * file) | |||
285 | if (!ops) | 307 | if (!ops) |
286 | goto Eaccess; | 308 | goto Eaccess; |
287 | 309 | ||
310 | /* make sure we have a collection to add our buffers to */ | ||
311 | mutex_lock(&inode->i_mutex); | ||
312 | if (!(set = inode->i_private)) { | ||
313 | if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) { | ||
314 | error = -ENOMEM; | ||
315 | goto Done; | ||
316 | } else { | ||
317 | INIT_LIST_HEAD(&set->associates); | ||
318 | } | ||
319 | } | ||
320 | mutex_unlock(&inode->i_mutex); | ||
321 | |||
288 | /* File needs write support. | 322 | /* File needs write support. |
289 | * The inode's perms must say it's ok, | 323 | * The inode's perms must say it's ok, |
290 | * and we must have a store method. | 324 | * and we must have a store method. |
@@ -310,9 +344,11 @@ static int check_perm(struct inode * inode, struct file * file) | |||
310 | */ | 344 | */ |
311 | buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); | 345 | buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); |
312 | if (buffer) { | 346 | if (buffer) { |
347 | INIT_LIST_HEAD(&buffer->associates); | ||
313 | init_MUTEX(&buffer->sem); | 348 | init_MUTEX(&buffer->sem); |
314 | buffer->needs_read_fill = 1; | 349 | buffer->needs_read_fill = 1; |
315 | buffer->ops = ops; | 350 | buffer->ops = ops; |
351 | add_to_collection(buffer, inode); | ||
316 | file->private_data = buffer; | 352 | file->private_data = buffer; |
317 | } else | 353 | } else |
318 | error = -ENOMEM; | 354 | error = -ENOMEM; |
@@ -330,11 +366,6 @@ static int check_perm(struct inode * inode, struct file * file) | |||
330 | return error; | 366 | return error; |
331 | } | 367 | } |
332 | 368 | ||
333 | static int sysfs_open_file(struct inode * inode, struct file * filp) | ||
334 | { | ||
335 | return check_perm(inode,filp); | ||
336 | } | ||
337 | |||
338 | static int sysfs_release(struct inode * inode, struct file * filp) | 369 | static int sysfs_release(struct inode * inode, struct file * filp) |
339 | { | 370 | { |
340 | struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); | 371 | struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); |
@@ -342,6 +373,8 @@ static int sysfs_release(struct inode * inode, struct file * filp) | |||
342 | struct module * owner = attr->owner; | 373 | struct module * owner = attr->owner; |
343 | struct sysfs_buffer * buffer = filp->private_data; | 374 | struct sysfs_buffer * buffer = filp->private_data; |
344 | 375 | ||
376 | if (buffer) | ||
377 | remove_from_collection(buffer, inode); | ||
345 | if (kobj) | 378 | if (kobj) |
346 | kobject_put(kobj); | 379 | kobject_put(kobj); |
347 | /* After this point, attr should not be accessed. */ | 380 | /* After this point, attr should not be accessed. */ |
@@ -548,7 +581,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); | |||
548 | 581 | ||
549 | void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) | 582 | void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) |
550 | { | 583 | { |
551 | sysfs_hash_and_remove(kobj->dentry,attr->name); | 584 | sysfs_hash_and_remove(kobj->dentry, attr->name); |
552 | } | 585 | } |
553 | 586 | ||
554 | 587 | ||