diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-02-13 01:43:25 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-02-13 01:43:25 -0500 |
commit | d9bc125caf592b7d081021f32ce5b717efdf70c8 (patch) | |
tree | 263b7066ba22ddce21db610c0300f6eaac6f2064 /fs/sysfs | |
parent | 43d78ef2ba5bec26d0315859e8324bfc0be23766 (diff) | |
parent | ec2f9d1331f658433411c58077871e1eef4ee1b4 (diff) |
Merge branch 'master' of /home/trondmy/kernel/linux-2.6/
Conflicts:
net/sunrpc/auth_gss/gss_krb5_crypto.c
net/sunrpc/auth_gss/gss_spkm3_token.c
net/sunrpc/clnt.c
Merge with mainline and fix conflicts.
Diffstat (limited to 'fs/sysfs')
-rw-r--r-- | fs/sysfs/bin.c | 6 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 219 | ||||
-rw-r--r-- | fs/sysfs/file.c | 82 | ||||
-rw-r--r-- | fs/sysfs/group.c | 2 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 38 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 13 | ||||
-rw-r--r-- | fs/sysfs/symlink.c | 3 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 25 |
8 files changed, 312 insertions, 76 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index e8f540d38d48..d3b9f5f07db1 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 | ||
@@ -146,7 +147,7 @@ static int open(struct inode * inode, struct file * file) | |||
146 | Error: | 147 | Error: |
147 | module_put(attr->attr.owner); | 148 | module_put(attr->attr.owner); |
148 | Done: | 149 | Done: |
149 | if (error && kobj) | 150 | if (error) |
150 | kobject_put(kobj); | 151 | kobject_put(kobj); |
151 | return error; | 152 | return error; |
152 | } | 153 | } |
@@ -157,8 +158,7 @@ static int release(struct inode * inode, struct file * file) | |||
157 | struct bin_attribute * attr = to_bin_attr(file->f_path.dentry); | 158 | struct bin_attribute * attr = to_bin_attr(file->f_path.dentry); |
158 | u8 * buffer = file->private_data; | 159 | u8 * buffer = file->private_data; |
159 | 160 | ||
160 | if (kobj) | 161 | kobject_put(kobj); |
161 | kobject_put(kobj); | ||
162 | module_put(attr->attr.owner); | 162 | module_put(attr->attr.owner); |
163 | kfree(buffer); | 163 | kfree(buffer); |
164 | return 0; | 164 | return 0; |
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 511edef8b321..8813990304fe 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 | ||
14 | DECLARE_RWSEM(sysfs_rename_sem); | 15 | DECLARE_RWSEM(sysfs_rename_sem); |
@@ -32,25 +33,39 @@ static struct dentry_operations sysfs_dentry_ops = { | |||
32 | /* | 33 | /* |
33 | * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent | 34 | * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent |
34 | */ | 35 | */ |
35 | static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, | 36 | static struct sysfs_dirent * __sysfs_new_dirent(void * element) |
36 | void * element) | ||
37 | { | 37 | { |
38 | struct sysfs_dirent * sd; | 38 | struct sysfs_dirent * sd; |
39 | 39 | ||
40 | sd = kmem_cache_alloc(sysfs_dir_cachep, GFP_KERNEL); | 40 | sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); |
41 | if (!sd) | 41 | if (!sd) |
42 | return NULL; | 42 | return NULL; |
43 | 43 | ||
44 | memset(sd, 0, sizeof(*sd)); | ||
45 | atomic_set(&sd->s_count, 1); | 44 | atomic_set(&sd->s_count, 1); |
46 | atomic_set(&sd->s_event, 1); | 45 | atomic_set(&sd->s_event, 1); |
47 | INIT_LIST_HEAD(&sd->s_children); | 46 | INIT_LIST_HEAD(&sd->s_children); |
48 | list_add(&sd->s_sibling, &parent_sd->s_children); | 47 | INIT_LIST_HEAD(&sd->s_sibling); |
49 | sd->s_element = element; | 48 | sd->s_element = element; |
50 | 49 | ||
51 | return sd; | 50 | return sd; |
52 | } | 51 | } |
53 | 52 | ||
53 | static void __sysfs_list_dirent(struct sysfs_dirent *parent_sd, | ||
54 | struct sysfs_dirent *sd) | ||
55 | { | ||
56 | if (sd) | ||
57 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
58 | } | ||
59 | |||
60 | static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent *parent_sd, | ||
61 | void * element) | ||
62 | { | ||
63 | struct sysfs_dirent *sd; | ||
64 | sd = __sysfs_new_dirent(element); | ||
65 | __sysfs_list_dirent(parent_sd, sd); | ||
66 | return sd; | ||
67 | } | ||
68 | |||
54 | /* | 69 | /* |
55 | * | 70 | * |
56 | * Return -EEXIST if there is already a sysfs element with the same name for | 71 | * Return -EEXIST if there is already a sysfs element with the same name for |
@@ -77,14 +92,14 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, | |||
77 | } | 92 | } |
78 | 93 | ||
79 | 94 | ||
80 | int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | 95 | static struct sysfs_dirent * |
81 | void * element, umode_t mode, int type) | 96 | __sysfs_make_dirent(struct dentry *dentry, void *element, mode_t mode, int type) |
82 | { | 97 | { |
83 | struct sysfs_dirent * sd; | 98 | struct sysfs_dirent * sd; |
84 | 99 | ||
85 | sd = sysfs_new_dirent(parent_sd, element); | 100 | sd = __sysfs_new_dirent(element); |
86 | if (!sd) | 101 | if (!sd) |
87 | return -ENOMEM; | 102 | goto out; |
88 | 103 | ||
89 | sd->s_mode = mode; | 104 | sd->s_mode = mode; |
90 | sd->s_type = type; | 105 | sd->s_type = type; |
@@ -94,7 +109,19 @@ int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | |||
94 | dentry->d_op = &sysfs_dentry_ops; | 109 | dentry->d_op = &sysfs_dentry_ops; |
95 | } | 110 | } |
96 | 111 | ||
97 | return 0; | 112 | out: |
113 | return sd; | ||
114 | } | ||
115 | |||
116 | int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, | ||
117 | void * element, umode_t mode, int type) | ||
118 | { | ||
119 | struct sysfs_dirent *sd; | ||
120 | |||
121 | sd = __sysfs_make_dirent(dentry, element, mode, type); | ||
122 | __sysfs_list_dirent(parent_sd, sd); | ||
123 | |||
124 | return sd ? 0 : -ENOMEM; | ||
98 | } | 125 | } |
99 | 126 | ||
100 | static int init_dir(struct inode * inode) | 127 | static int init_dir(struct inode * inode) |
@@ -165,11 +192,11 @@ int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d) | |||
165 | 192 | ||
166 | /** | 193 | /** |
167 | * sysfs_create_dir - create a directory for an object. | 194 | * sysfs_create_dir - create a directory for an object. |
168 | * @parent: parent parent object. | ||
169 | * @kobj: object we're creating directory for. | 195 | * @kobj: object we're creating directory for. |
196 | * @shadow_parent: parent parent object. | ||
170 | */ | 197 | */ |
171 | 198 | ||
172 | int sysfs_create_dir(struct kobject * kobj) | 199 | int sysfs_create_dir(struct kobject * kobj, struct dentry *shadow_parent) |
173 | { | 200 | { |
174 | struct dentry * dentry = NULL; | 201 | struct dentry * dentry = NULL; |
175 | struct dentry * parent; | 202 | struct dentry * parent; |
@@ -177,7 +204,9 @@ int sysfs_create_dir(struct kobject * kobj) | |||
177 | 204 | ||
178 | BUG_ON(!kobj); | 205 | BUG_ON(!kobj); |
179 | 206 | ||
180 | if (kobj->parent) | 207 | if (shadow_parent) |
208 | parent = shadow_parent; | ||
209 | else if (kobj->parent) | ||
181 | parent = kobj->parent->dentry; | 210 | parent = kobj->parent->dentry; |
182 | else if (sysfs_mount && sysfs_mount->mnt_sb) | 211 | else if (sysfs_mount && sysfs_mount->mnt_sb) |
183 | parent = sysfs_mount->mnt_sb->s_root; | 212 | parent = sysfs_mount->mnt_sb->s_root; |
@@ -267,7 +296,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, | |||
267 | return ERR_PTR(err); | 296 | return ERR_PTR(err); |
268 | } | 297 | } |
269 | 298 | ||
270 | struct inode_operations sysfs_dir_inode_operations = { | 299 | const struct inode_operations sysfs_dir_inode_operations = { |
271 | .lookup = sysfs_lookup, | 300 | .lookup = sysfs_lookup, |
272 | .setattr = sysfs_setattr, | 301 | .setattr = sysfs_setattr, |
273 | }; | 302 | }; |
@@ -298,21 +327,12 @@ void sysfs_remove_subdir(struct dentry * d) | |||
298 | } | 327 | } |
299 | 328 | ||
300 | 329 | ||
301 | /** | 330 | static void __sysfs_remove_dir(struct dentry *dentry) |
302 | * sysfs_remove_dir - remove an object's directory. | ||
303 | * @kobj: object. | ||
304 | * | ||
305 | * The only thing special about this is that we remove any files in | ||
306 | * the directory before we remove the directory, and we've inlined | ||
307 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
308 | */ | ||
309 | |||
310 | void sysfs_remove_dir(struct kobject * kobj) | ||
311 | { | 331 | { |
312 | struct dentry * dentry = dget(kobj->dentry); | ||
313 | struct sysfs_dirent * parent_sd; | 332 | struct sysfs_dirent * parent_sd; |
314 | struct sysfs_dirent * sd, * tmp; | 333 | struct sysfs_dirent * sd, * tmp; |
315 | 334 | ||
335 | dget(dentry); | ||
316 | if (!dentry) | 336 | if (!dentry) |
317 | return; | 337 | return; |
318 | 338 | ||
@@ -333,32 +353,60 @@ void sysfs_remove_dir(struct kobject * kobj) | |||
333 | * Drop reference from dget() on entrance. | 353 | * Drop reference from dget() on entrance. |
334 | */ | 354 | */ |
335 | dput(dentry); | 355 | dput(dentry); |
356 | } | ||
357 | |||
358 | /** | ||
359 | * sysfs_remove_dir - remove an object's directory. | ||
360 | * @kobj: object. | ||
361 | * | ||
362 | * The only thing special about this is that we remove any files in | ||
363 | * the directory before we remove the directory, and we've inlined | ||
364 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
365 | */ | ||
366 | |||
367 | void sysfs_remove_dir(struct kobject * kobj) | ||
368 | { | ||
369 | __sysfs_remove_dir(kobj->dentry); | ||
336 | kobj->dentry = NULL; | 370 | kobj->dentry = NULL; |
337 | } | 371 | } |
338 | 372 | ||
339 | int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | 373 | int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, |
374 | const char *new_name) | ||
340 | { | 375 | { |
341 | int error = 0; | 376 | int error = 0; |
342 | struct dentry * new_dentry, * parent; | 377 | struct dentry * new_dentry; |
343 | |||
344 | if (!strcmp(kobject_name(kobj), new_name)) | ||
345 | return -EINVAL; | ||
346 | 378 | ||
347 | if (!kobj->parent) | 379 | if (!new_parent) |
348 | return -EINVAL; | 380 | return -EFAULT; |
349 | 381 | ||
350 | down_write(&sysfs_rename_sem); | 382 | down_write(&sysfs_rename_sem); |
351 | parent = kobj->parent->dentry; | 383 | mutex_lock(&new_parent->d_inode->i_mutex); |
352 | |||
353 | mutex_lock(&parent->d_inode->i_mutex); | ||
354 | 384 | ||
355 | new_dentry = lookup_one_len(new_name, parent, strlen(new_name)); | 385 | new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); |
356 | if (!IS_ERR(new_dentry)) { | 386 | if (!IS_ERR(new_dentry)) { |
357 | if (!new_dentry->d_inode) { | 387 | /* By allowing two different directories with the |
388 | * same d_parent we allow this routine to move | ||
389 | * between different shadows of the same directory | ||
390 | */ | ||
391 | if (kobj->dentry->d_parent->d_inode != new_parent->d_inode) | ||
392 | return -EINVAL; | ||
393 | else if (new_dentry->d_parent->d_inode != new_parent->d_inode) | ||
394 | error = -EINVAL; | ||
395 | else if (new_dentry == kobj->dentry) | ||
396 | error = -EINVAL; | ||
397 | else if (!new_dentry->d_inode) { | ||
358 | error = kobject_set_name(kobj, "%s", new_name); | 398 | error = kobject_set_name(kobj, "%s", new_name); |
359 | if (!error) { | 399 | if (!error) { |
400 | struct sysfs_dirent *sd, *parent_sd; | ||
401 | |||
360 | d_add(new_dentry, NULL); | 402 | d_add(new_dentry, NULL); |
361 | d_move(kobj->dentry, new_dentry); | 403 | d_move(kobj->dentry, new_dentry); |
404 | |||
405 | sd = kobj->dentry->d_fsdata; | ||
406 | parent_sd = new_parent->d_fsdata; | ||
407 | |||
408 | list_del_init(&sd->s_sibling); | ||
409 | list_add(&sd->s_sibling, &parent_sd->s_children); | ||
362 | } | 410 | } |
363 | else | 411 | else |
364 | d_drop(new_dentry); | 412 | d_drop(new_dentry); |
@@ -366,7 +414,7 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | |||
366 | error = -EEXIST; | 414 | error = -EEXIST; |
367 | dput(new_dentry); | 415 | dput(new_dentry); |
368 | } | 416 | } |
369 | mutex_unlock(&parent->d_inode->i_mutex); | 417 | mutex_unlock(&new_parent->d_inode->i_mutex); |
370 | up_write(&sysfs_rename_sem); | 418 | up_write(&sysfs_rename_sem); |
371 | 419 | ||
372 | return error; | 420 | return error; |
@@ -378,12 +426,10 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) | |||
378 | struct sysfs_dirent *new_parent_sd, *sd; | 426 | struct sysfs_dirent *new_parent_sd, *sd; |
379 | int error; | 427 | int error; |
380 | 428 | ||
381 | if (!new_parent) | ||
382 | return -EINVAL; | ||
383 | |||
384 | old_parent_dentry = kobj->parent ? | 429 | old_parent_dentry = kobj->parent ? |
385 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; | 430 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; |
386 | new_parent_dentry = new_parent->dentry; | 431 | new_parent_dentry = new_parent ? |
432 | new_parent->dentry : sysfs_mount->mnt_sb->s_root; | ||
387 | 433 | ||
388 | again: | 434 | again: |
389 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); | 435 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); |
@@ -547,6 +593,95 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
547 | return offset; | 593 | return offset; |
548 | } | 594 | } |
549 | 595 | ||
596 | |||
597 | /** | ||
598 | * sysfs_make_shadowed_dir - Setup so a directory can be shadowed | ||
599 | * @kobj: object we're creating shadow of. | ||
600 | */ | ||
601 | |||
602 | int sysfs_make_shadowed_dir(struct kobject *kobj, | ||
603 | void * (*follow_link)(struct dentry *, struct nameidata *)) | ||
604 | { | ||
605 | struct inode *inode; | ||
606 | struct inode_operations *i_op; | ||
607 | |||
608 | inode = kobj->dentry->d_inode; | ||
609 | if (inode->i_op != &sysfs_dir_inode_operations) | ||
610 | return -EINVAL; | ||
611 | |||
612 | i_op = kmalloc(sizeof(*i_op), GFP_KERNEL); | ||
613 | if (!i_op) | ||
614 | return -ENOMEM; | ||
615 | |||
616 | memcpy(i_op, &sysfs_dir_inode_operations, sizeof(*i_op)); | ||
617 | i_op->follow_link = follow_link; | ||
618 | |||
619 | /* Locking of inode->i_op? | ||
620 | * Since setting i_op is a single word write and they | ||
621 | * are atomic we should be ok here. | ||
622 | */ | ||
623 | inode->i_op = i_op; | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * sysfs_create_shadow_dir - create a shadow directory for an object. | ||
629 | * @kobj: object we're creating directory for. | ||
630 | * | ||
631 | * sysfs_make_shadowed_dir must already have been called on this | ||
632 | * directory. | ||
633 | */ | ||
634 | |||
635 | struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) | ||
636 | { | ||
637 | struct sysfs_dirent *sd; | ||
638 | struct dentry *parent, *dir, *shadow; | ||
639 | struct inode *inode; | ||
640 | |||
641 | dir = kobj->dentry; | ||
642 | inode = dir->d_inode; | ||
643 | parent = dir->d_parent; | ||
644 | shadow = ERR_PTR(-EINVAL); | ||
645 | if (!sysfs_is_shadowed_inode(inode)) | ||
646 | goto out; | ||
647 | |||
648 | shadow = d_alloc(parent, &dir->d_name); | ||
649 | if (!shadow) | ||
650 | goto nomem; | ||
651 | |||
652 | sd = __sysfs_make_dirent(shadow, kobj, inode->i_mode, SYSFS_DIR); | ||
653 | if (!sd) | ||
654 | goto nomem; | ||
655 | |||
656 | d_instantiate(shadow, igrab(inode)); | ||
657 | inc_nlink(inode); | ||
658 | inc_nlink(parent->d_inode); | ||
659 | shadow->d_op = &sysfs_dentry_ops; | ||
660 | |||
661 | dget(shadow); /* Extra count - pin the dentry in core */ | ||
662 | |||
663 | out: | ||
664 | return shadow; | ||
665 | nomem: | ||
666 | dput(shadow); | ||
667 | shadow = ERR_PTR(-ENOMEM); | ||
668 | goto out; | ||
669 | } | ||
670 | |||
671 | /** | ||
672 | * sysfs_remove_shadow_dir - remove an object's directory. | ||
673 | * @shadow: dentry of shadow directory | ||
674 | * | ||
675 | * The only thing special about this is that we remove any files in | ||
676 | * the directory before we remove the directory, and we've inlined | ||
677 | * what used to be sysfs_rmdir() below, instead of calling separately. | ||
678 | */ | ||
679 | |||
680 | void sysfs_remove_shadow_dir(struct dentry *shadow) | ||
681 | { | ||
682 | __sysfs_remove_dir(shadow); | ||
683 | } | ||
684 | |||
550 | const struct file_operations sysfs_dir_operations = { | 685 | const struct file_operations sysfs_dir_operations = { |
551 | .open = sysfs_dir_open, | 686 | .open = sysfs_dir_open, |
552 | .release = sysfs_dir_close, | 687 | .release = sysfs_dir_close, |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 9cfe53e1e00d..c0e117649a4d 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. |
@@ -70,7 +83,8 @@ struct sysfs_buffer { | |||
70 | * Allocate @buffer->page, if it hasn't been already, then call the | 83 | * Allocate @buffer->page, if it hasn't been already, then call the |
71 | * kobject's show() method to fill the buffer with this attribute's | 84 | * kobject's show() method to fill the buffer with this attribute's |
72 | * data. | 85 | * data. |
73 | * This is called only once, on the file's first read. | 86 | * This is called only once, on the file's first read unless an error |
87 | * is returned. | ||
74 | */ | 88 | */ |
75 | static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) | 89 | static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) |
76 | { | 90 | { |
@@ -88,12 +102,13 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer | |||
88 | 102 | ||
89 | buffer->event = atomic_read(&sd->s_event); | 103 | buffer->event = atomic_read(&sd->s_event); |
90 | count = ops->show(kobj,attr,buffer->page); | 104 | count = ops->show(kobj,attr,buffer->page); |
91 | buffer->needs_read_fill = 0; | ||
92 | BUG_ON(count > (ssize_t)PAGE_SIZE); | 105 | BUG_ON(count > (ssize_t)PAGE_SIZE); |
93 | if (count >= 0) | 106 | if (count >= 0) { |
107 | buffer->needs_read_fill = 0; | ||
94 | buffer->count = count; | 108 | buffer->count = count; |
95 | else | 109 | } else { |
96 | ret = count; | 110 | ret = count; |
111 | } | ||
97 | return ret; | 112 | return ret; |
98 | } | 113 | } |
99 | 114 | ||
@@ -153,6 +168,10 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
153 | ssize_t retval = 0; | 168 | ssize_t retval = 0; |
154 | 169 | ||
155 | down(&buffer->sem); | 170 | down(&buffer->sem); |
171 | if (buffer->orphaned) { | ||
172 | retval = -ENODEV; | ||
173 | goto out; | ||
174 | } | ||
156 | if (buffer->needs_read_fill) { | 175 | if (buffer->needs_read_fill) { |
157 | if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) | 176 | if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) |
158 | goto out; | 177 | goto out; |
@@ -165,7 +184,6 @@ out: | |||
165 | return retval; | 184 | return retval; |
166 | } | 185 | } |
167 | 186 | ||
168 | |||
169 | /** | 187 | /** |
170 | * fill_write_buffer - copy buffer from userspace. | 188 | * fill_write_buffer - copy buffer from userspace. |
171 | * @buffer: data buffer for file. | 189 | * @buffer: data buffer for file. |
@@ -243,19 +261,25 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t | |||
243 | ssize_t len; | 261 | ssize_t len; |
244 | 262 | ||
245 | down(&buffer->sem); | 263 | down(&buffer->sem); |
264 | if (buffer->orphaned) { | ||
265 | len = -ENODEV; | ||
266 | goto out; | ||
267 | } | ||
246 | len = fill_write_buffer(buffer, buf, count); | 268 | len = fill_write_buffer(buffer, buf, count); |
247 | if (len > 0) | 269 | if (len > 0) |
248 | len = flush_write_buffer(file->f_path.dentry, buffer, len); | 270 | len = flush_write_buffer(file->f_path.dentry, buffer, len); |
249 | if (len > 0) | 271 | if (len > 0) |
250 | *ppos += len; | 272 | *ppos += len; |
273 | out: | ||
251 | up(&buffer->sem); | 274 | up(&buffer->sem); |
252 | return len; | 275 | return len; |
253 | } | 276 | } |
254 | 277 | ||
255 | static int check_perm(struct inode * inode, struct file * file) | 278 | static int sysfs_open_file(struct inode *inode, struct file *file) |
256 | { | 279 | { |
257 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); | 280 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); |
258 | struct attribute * attr = to_attr(file->f_path.dentry); | 281 | struct attribute * attr = to_attr(file->f_path.dentry); |
282 | struct sysfs_buffer_collection *set; | ||
259 | struct sysfs_buffer * buffer; | 283 | struct sysfs_buffer * buffer; |
260 | struct sysfs_ops * ops = NULL; | 284 | struct sysfs_ops * ops = NULL; |
261 | int error = 0; | 285 | int error = 0; |
@@ -285,6 +309,18 @@ static int check_perm(struct inode * inode, struct file * file) | |||
285 | if (!ops) | 309 | if (!ops) |
286 | goto Eaccess; | 310 | goto Eaccess; |
287 | 311 | ||
312 | /* make sure we have a collection to add our buffers to */ | ||
313 | mutex_lock(&inode->i_mutex); | ||
314 | if (!(set = inode->i_private)) { | ||
315 | if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) { | ||
316 | error = -ENOMEM; | ||
317 | goto Done; | ||
318 | } else { | ||
319 | INIT_LIST_HEAD(&set->associates); | ||
320 | } | ||
321 | } | ||
322 | mutex_unlock(&inode->i_mutex); | ||
323 | |||
288 | /* File needs write support. | 324 | /* File needs write support. |
289 | * The inode's perms must say it's ok, | 325 | * The inode's perms must say it's ok, |
290 | * and we must have a store method. | 326 | * and we must have a store method. |
@@ -310,9 +346,11 @@ static int check_perm(struct inode * inode, struct file * file) | |||
310 | */ | 346 | */ |
311 | buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); | 347 | buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); |
312 | if (buffer) { | 348 | if (buffer) { |
349 | INIT_LIST_HEAD(&buffer->associates); | ||
313 | init_MUTEX(&buffer->sem); | 350 | init_MUTEX(&buffer->sem); |
314 | buffer->needs_read_fill = 1; | 351 | buffer->needs_read_fill = 1; |
315 | buffer->ops = ops; | 352 | buffer->ops = ops; |
353 | add_to_collection(buffer, inode); | ||
316 | file->private_data = buffer; | 354 | file->private_data = buffer; |
317 | } else | 355 | } else |
318 | error = -ENOMEM; | 356 | error = -ENOMEM; |
@@ -325,16 +363,11 @@ static int check_perm(struct inode * inode, struct file * file) | |||
325 | error = -EACCES; | 363 | error = -EACCES; |
326 | module_put(attr->owner); | 364 | module_put(attr->owner); |
327 | Done: | 365 | Done: |
328 | if (error && kobj) | 366 | if (error) |
329 | kobject_put(kobj); | 367 | kobject_put(kobj); |
330 | return error; | 368 | return error; |
331 | } | 369 | } |
332 | 370 | ||
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) | 371 | static int sysfs_release(struct inode * inode, struct file * filp) |
339 | { | 372 | { |
340 | struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); | 373 | struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); |
@@ -342,8 +375,9 @@ static int sysfs_release(struct inode * inode, struct file * filp) | |||
342 | struct module * owner = attr->owner; | 375 | struct module * owner = attr->owner; |
343 | struct sysfs_buffer * buffer = filp->private_data; | 376 | struct sysfs_buffer * buffer = filp->private_data; |
344 | 377 | ||
345 | if (kobj) | 378 | if (buffer) |
346 | kobject_put(kobj); | 379 | remove_from_collection(buffer, inode); |
380 | kobject_put(kobj); | ||
347 | /* After this point, attr should not be accessed. */ | 381 | /* After this point, attr should not be accessed. */ |
348 | module_put(owner); | 382 | module_put(owner); |
349 | 383 | ||
@@ -548,7 +582,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); | |||
548 | 582 | ||
549 | void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) | 583 | void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) |
550 | { | 584 | { |
551 | sysfs_hash_and_remove(kobj->dentry,attr->name); | 585 | sysfs_hash_and_remove(kobj->dentry, attr->name); |
552 | } | 586 | } |
553 | 587 | ||
554 | 588 | ||
diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 122145b0895c..b20951c93761 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c | |||
@@ -13,6 +13,8 @@ | |||
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 <linux/fs.h> | ||
17 | #include <asm/semaphore.h> | ||
16 | #include "sysfs.h" | 18 | #include "sysfs.h" |
17 | 19 | ||
18 | 20 | ||
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index e79e38d52c00..dd1344b007f5 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 | ||
18 | extern struct super_block * sysfs_sb; | 19 | extern struct super_block * sysfs_sb; |
@@ -28,10 +29,20 @@ static struct backing_dev_info sysfs_backing_dev_info = { | |||
28 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, | 29 | .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK, |
29 | }; | 30 | }; |
30 | 31 | ||
31 | static struct inode_operations sysfs_inode_operations ={ | 32 | static const struct inode_operations sysfs_inode_operations ={ |
32 | .setattr = sysfs_setattr, | 33 | .setattr = sysfs_setattr, |
33 | }; | 34 | }; |
34 | 35 | ||
36 | void sysfs_delete_inode(struct inode *inode) | ||
37 | { | ||
38 | /* Free the shadowed directory inode operations */ | ||
39 | if (sysfs_is_shadowed_inode(inode)) { | ||
40 | kfree(inode->i_op); | ||
41 | inode->i_op = NULL; | ||
42 | } | ||
43 | return generic_delete_inode(inode); | ||
44 | } | ||
45 | |||
35 | int sysfs_setattr(struct dentry * dentry, struct iattr * iattr) | 46 | int sysfs_setattr(struct dentry * dentry, struct iattr * iattr) |
36 | { | 47 | { |
37 | struct inode * inode = dentry->d_inode; | 48 | struct inode * inode = dentry->d_inode; |
@@ -209,6 +220,22 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) | |||
209 | return NULL; | 220 | return NULL; |
210 | } | 221 | } |
211 | 222 | ||
223 | static inline void orphan_all_buffers(struct inode *node) | ||
224 | { | ||
225 | struct sysfs_buffer_collection *set = node->i_private; | ||
226 | struct sysfs_buffer *buf; | ||
227 | |||
228 | mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); | ||
229 | if (node->i_private) { | ||
230 | list_for_each_entry(buf, &set->associates, associates) { | ||
231 | down(&buf->sem); | ||
232 | buf->orphaned = 1; | ||
233 | up(&buf->sem); | ||
234 | } | ||
235 | } | ||
236 | mutex_unlock(&node->i_mutex); | ||
237 | } | ||
238 | |||
212 | 239 | ||
213 | /* | 240 | /* |
214 | * Unhashes the dentry corresponding to given sysfs_dirent | 241 | * Unhashes the dentry corresponding to given sysfs_dirent |
@@ -217,16 +244,23 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) | |||
217 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | 244 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) |
218 | { | 245 | { |
219 | struct dentry * dentry = sd->s_dentry; | 246 | struct dentry * dentry = sd->s_dentry; |
247 | struct inode *inode; | ||
220 | 248 | ||
221 | if (dentry) { | 249 | if (dentry) { |
222 | spin_lock(&dcache_lock); | 250 | spin_lock(&dcache_lock); |
223 | spin_lock(&dentry->d_lock); | 251 | spin_lock(&dentry->d_lock); |
224 | if (!(d_unhashed(dentry) && dentry->d_inode)) { | 252 | if (!(d_unhashed(dentry) && dentry->d_inode)) { |
253 | inode = dentry->d_inode; | ||
254 | spin_lock(&inode->i_lock); | ||
255 | __iget(inode); | ||
256 | spin_unlock(&inode->i_lock); | ||
225 | dget_locked(dentry); | 257 | dget_locked(dentry); |
226 | __d_drop(dentry); | 258 | __d_drop(dentry); |
227 | spin_unlock(&dentry->d_lock); | 259 | spin_unlock(&dentry->d_lock); |
228 | spin_unlock(&dcache_lock); | 260 | spin_unlock(&dcache_lock); |
229 | simple_unlink(parent->d_inode, dentry); | 261 | simple_unlink(parent->d_inode, dentry); |
262 | orphan_all_buffers(inode); | ||
263 | iput(inode); | ||
230 | } else { | 264 | } else { |
231 | spin_unlock(&dentry->d_lock); | 265 | spin_unlock(&dentry->d_lock); |
232 | spin_unlock(&dcache_lock); | 266 | spin_unlock(&dcache_lock); |
@@ -248,7 +282,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) | |||
248 | return -ENOENT; | 282 | return -ENOENT; |
249 | 283 | ||
250 | parent_sd = dir->d_fsdata; | 284 | parent_sd = dir->d_fsdata; |
251 | mutex_lock(&dir->d_inode->i_mutex); | 285 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); |
252 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | 286 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { |
253 | if (!sd->s_element) | 287 | if (!sd->s_element) |
254 | continue; | 288 | continue; |
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index e503f858fba8..23a48a38e6af 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; | |||
18 | struct super_block * sysfs_sb = NULL; | 19 | struct super_block * sysfs_sb = NULL; |
19 | struct kmem_cache *sysfs_dir_cachep; | 20 | struct kmem_cache *sysfs_dir_cachep; |
20 | 21 | ||
21 | static struct super_operations sysfs_ops = { | 22 | static void sysfs_clear_inode(struct inode *inode); |
23 | |||
24 | static const struct super_operations sysfs_ops = { | ||
22 | .statfs = simple_statfs, | 25 | .statfs = simple_statfs, |
23 | .drop_inode = generic_delete_inode, | 26 | .drop_inode = sysfs_delete_inode, |
27 | .clear_inode = sysfs_clear_inode, | ||
24 | }; | 28 | }; |
25 | 29 | ||
26 | static struct sysfs_dirent sysfs_root = { | 30 | static 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 | ||
38 | static void sysfs_clear_inode(struct inode *inode) | ||
39 | { | ||
40 | kfree(inode->i_private); | ||
41 | } | ||
42 | |||
34 | static int sysfs_fill_super(struct super_block *sb, void *data, int silent) | 43 | static 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..7b9c5bfde920 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 | ||
@@ -180,7 +181,7 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *co | |||
180 | free_page((unsigned long)page); | 181 | free_page((unsigned long)page); |
181 | } | 182 | } |
182 | 183 | ||
183 | struct inode_operations sysfs_symlink_inode_operations = { | 184 | const struct inode_operations sysfs_symlink_inode_operations = { |
184 | .readlink = generic_readlink, | 185 | .readlink = generic_readlink, |
185 | .follow_link = sysfs_follow_link, | 186 | .follow_link = sysfs_follow_link, |
186 | .put_link = sysfs_put_link, | 187 | .put_link = sysfs_put_link, |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index bd7cec295dab..d976b0005549 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -2,6 +2,7 @@ | |||
2 | extern struct vfsmount * sysfs_mount; | 2 | extern struct vfsmount * sysfs_mount; |
3 | extern struct kmem_cache *sysfs_dir_cachep; | 3 | extern struct kmem_cache *sysfs_dir_cachep; |
4 | 4 | ||
5 | extern void sysfs_delete_inode(struct inode *inode); | ||
5 | extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); | 6 | extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); |
6 | extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); | 7 | extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); |
7 | 8 | ||
@@ -25,14 +26,30 @@ extern struct super_block * sysfs_sb; | |||
25 | extern const struct file_operations sysfs_dir_operations; | 26 | extern const struct file_operations sysfs_dir_operations; |
26 | extern const struct file_operations sysfs_file_operations; | 27 | extern const struct file_operations sysfs_file_operations; |
27 | extern const struct file_operations bin_fops; | 28 | extern const struct file_operations bin_fops; |
28 | extern struct inode_operations sysfs_dir_inode_operations; | 29 | extern const struct inode_operations sysfs_dir_inode_operations; |
29 | extern struct inode_operations sysfs_symlink_inode_operations; | 30 | extern const struct inode_operations sysfs_symlink_inode_operations; |
30 | 31 | ||
31 | struct sysfs_symlink { | 32 | struct sysfs_symlink { |
32 | char * link_name; | 33 | char * link_name; |
33 | struct kobject * target_kobj; | 34 | struct kobject * target_kobj; |
34 | }; | 35 | }; |
35 | 36 | ||
37 | struct sysfs_buffer { | ||
38 | struct list_head associates; | ||
39 | size_t count; | ||
40 | loff_t pos; | ||
41 | char * page; | ||
42 | struct sysfs_ops * ops; | ||
43 | struct semaphore sem; | ||
44 | int orphaned; | ||
45 | int needs_read_fill; | ||
46 | int event; | ||
47 | }; | ||
48 | |||
49 | struct sysfs_buffer_collection { | ||
50 | struct list_head associates; | ||
51 | }; | ||
52 | |||
36 | static inline struct kobject * to_kobj(struct dentry * dentry) | 53 | static inline struct kobject * to_kobj(struct dentry * dentry) |
37 | { | 54 | { |
38 | struct sysfs_dirent * sd = dentry->d_fsdata; | 55 | struct sysfs_dirent * sd = dentry->d_fsdata; |
@@ -96,3 +113,7 @@ static inline void sysfs_put(struct sysfs_dirent * sd) | |||
96 | release_sysfs_dirent(sd); | 113 | release_sysfs_dirent(sd); |
97 | } | 114 | } |
98 | 115 | ||
116 | static inline int sysfs_is_shadowed_inode(struct inode *inode) | ||
117 | { | ||
118 | return S_ISDIR(inode->i_mode) && inode->i_op->follow_link; | ||
119 | } | ||