aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/file.c
diff options
context:
space:
mode:
authorOliver Neukum <oliver@neukum.org>2006-12-20 04:52:44 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 13:37:13 -0500
commit94bebf4d1b8e7719f0f3944c037a21cfd99a4af7 (patch)
tree25c6ba1836e74f608b4e434b4f0f4d5c28b11de1 /fs/sysfs/file.c
parentcb986b749c7178422bfbc982cd30e04d5db54bbc (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.c67
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
54struct sysfs_buffer { 60static inline void
55 size_t count; 61add_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
70static inline void
71remove_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;
271out:
251 up(&buffer->sem); 272 up(&buffer->sem);
252 return len; 273 return len;
253} 274}
254 275
255static int check_perm(struct inode * inode, struct file * file) 276static 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
333static int sysfs_open_file(struct inode * inode, struct file * filp)
334{
335 return check_perm(inode,filp);
336}
337
338static int sysfs_release(struct inode * inode, struct file * filp) 369static 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
549void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) 582void 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