aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
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
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')
-rw-r--r--fs/sysfs/bin.c1
-rw-r--r--fs/sysfs/dir.c1
-rw-r--r--fs/sysfs/file.c67
-rw-r--r--fs/sysfs/group.c1
-rw-r--r--fs/sysfs/inode.c24
-rw-r--r--fs/sysfs/mount.c9
-rw-r--r--fs/sysfs/symlink.c1
-rw-r--r--fs/sysfs/sysfs.h16
8 files changed, 103 insertions, 17 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
index e8f540d38d48..9ec1c8539a5d 100644
--- a/fs/sysfs/bin.c
+++ b/fs/sysfs/bin.c
@@ -16,6 +16,7 @@
16#include <linux/slab.h> 16#include <linux/slab.h>
17 17
18#include <asm/uaccess.h> 18#include <asm/uaccess.h>
19#include <asm/semaphore.h>
19 20
20#include "sysfs.h" 21#include "sysfs.h"
21 22
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 2bab1b4ddf5a..9ff04491e3ea 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -9,6 +9,7 @@
9#include <linux/module.h> 9#include <linux/module.h>
10#include <linux/kobject.h> 10#include <linux/kobject.h>
11#include <linux/namei.h> 11#include <linux/namei.h>
12#include <asm/semaphore.h>
12#include "sysfs.h" 13#include "sysfs.h"
13 14
14DECLARE_RWSEM(sysfs_rename_sem); 15DECLARE_RWSEM(sysfs_rename_sem);
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
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
index 122145b0895c..46a277b0838e 100644
--- a/fs/sysfs/group.c
+++ b/fs/sysfs/group.c
@@ -13,6 +13,7 @@
13#include <linux/dcache.h> 13#include <linux/dcache.h>
14#include <linux/namei.h> 14#include <linux/namei.h>
15#include <linux/err.h> 15#include <linux/err.h>
16#include <asm/semaphore.h>
16#include "sysfs.h" 17#include "sysfs.h"
17 18
18 19
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index e79e38d52c00..1a81ebce20e9 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -13,6 +13,7 @@
13#include <linux/backing-dev.h> 13#include <linux/backing-dev.h>
14#include <linux/capability.h> 14#include <linux/capability.h>
15#include <linux/errno.h> 15#include <linux/errno.h>
16#include <asm/semaphore.h>
16#include "sysfs.h" 17#include "sysfs.h"
17 18
18extern struct super_block * sysfs_sb; 19extern struct super_block * sysfs_sb;
@@ -209,6 +210,22 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd)
209 return NULL; 210 return NULL;
210} 211}
211 212
213static inline void orphan_all_buffers(struct inode *node)
214{
215 struct sysfs_buffer_collection *set = node->i_private;
216 struct sysfs_buffer *buf;
217
218 mutex_lock(&node->i_mutex);
219 if (node->i_private) {
220 list_for_each_entry(buf, &set->associates, associates) {
221 down(&buf->sem);
222 buf->orphaned = 1;
223 up(&buf->sem);
224 }
225 }
226 mutex_unlock(&node->i_mutex);
227}
228
212 229
213/* 230/*
214 * Unhashes the dentry corresponding to given sysfs_dirent 231 * Unhashes the dentry corresponding to given sysfs_dirent
@@ -217,16 +234,23 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd)
217void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) 234void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
218{ 235{
219 struct dentry * dentry = sd->s_dentry; 236 struct dentry * dentry = sd->s_dentry;
237 struct inode *inode;
220 238
221 if (dentry) { 239 if (dentry) {
222 spin_lock(&dcache_lock); 240 spin_lock(&dcache_lock);
223 spin_lock(&dentry->d_lock); 241 spin_lock(&dentry->d_lock);
224 if (!(d_unhashed(dentry) && dentry->d_inode)) { 242 if (!(d_unhashed(dentry) && dentry->d_inode)) {
243 inode = dentry->d_inode;
244 spin_lock(&inode->i_lock);
245 __iget(inode);
246 spin_unlock(&inode->i_lock);
225 dget_locked(dentry); 247 dget_locked(dentry);
226 __d_drop(dentry); 248 __d_drop(dentry);
227 spin_unlock(&dentry->d_lock); 249 spin_unlock(&dentry->d_lock);
228 spin_unlock(&dcache_lock); 250 spin_unlock(&dcache_lock);
229 simple_unlink(parent->d_inode, dentry); 251 simple_unlink(parent->d_inode, dentry);
252 orphan_all_buffers(inode);
253 iput(inode);
230 } else { 254 } else {
231 spin_unlock(&dentry->d_lock); 255 spin_unlock(&dentry->d_lock);
232 spin_unlock(&dcache_lock); 256 spin_unlock(&dcache_lock);
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index e503f858fba8..a1a58b97f322 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -8,6 +8,7 @@
8#include <linux/mount.h> 8#include <linux/mount.h>
9#include <linux/pagemap.h> 9#include <linux/pagemap.h>
10#include <linux/init.h> 10#include <linux/init.h>
11#include <asm/semaphore.h>
11 12
12#include "sysfs.h" 13#include "sysfs.h"
13 14
@@ -18,9 +19,12 @@ struct vfsmount *sysfs_mount;
18struct super_block * sysfs_sb = NULL; 19struct super_block * sysfs_sb = NULL;
19struct kmem_cache *sysfs_dir_cachep; 20struct kmem_cache *sysfs_dir_cachep;
20 21
22static void sysfs_clear_inode(struct inode *inode);
23
21static struct super_operations sysfs_ops = { 24static struct super_operations sysfs_ops = {
22 .statfs = simple_statfs, 25 .statfs = simple_statfs,
23 .drop_inode = generic_delete_inode, 26 .drop_inode = generic_delete_inode,
27 .clear_inode = sysfs_clear_inode,
24}; 28};
25 29
26static struct sysfs_dirent sysfs_root = { 30static struct sysfs_dirent sysfs_root = {
@@ -31,6 +35,11 @@ static struct sysfs_dirent sysfs_root = {
31 .s_iattr = NULL, 35 .s_iattr = NULL,
32}; 36};
33 37
38static void sysfs_clear_inode(struct inode *inode)
39{
40 kfree(inode->i_private);
41}
42
34static int sysfs_fill_super(struct super_block *sb, void *data, int silent) 43static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
35{ 44{
36 struct inode *inode; 45 struct inode *inode;
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index f50e3cc2ded8..4869f611192f 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -7,6 +7,7 @@
7#include <linux/module.h> 7#include <linux/module.h>
8#include <linux/kobject.h> 8#include <linux/kobject.h>
9#include <linux/namei.h> 9#include <linux/namei.h>
10#include <asm/semaphore.h>
10 11
11#include "sysfs.h" 12#include "sysfs.h"
12 13
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index bd7cec295dab..39c623fdc277 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -33,6 +33,22 @@ struct sysfs_symlink {
33 struct kobject * target_kobj; 33 struct kobject * target_kobj;
34}; 34};
35 35
36struct sysfs_buffer {
37 struct list_head associates;
38 size_t count;
39 loff_t pos;
40 char * page;
41 struct sysfs_ops * ops;
42 struct semaphore sem;
43 int orphaned;
44 int needs_read_fill;
45 int event;
46};
47
48struct sysfs_buffer_collection {
49 struct list_head associates;
50};
51
36static inline struct kobject * to_kobj(struct dentry * dentry) 52static inline struct kobject * to_kobj(struct dentry * dentry)
37{ 53{
38 struct sysfs_dirent * sd = dentry->d_fsdata; 54 struct sysfs_dirent * sd = dentry->d_fsdata;