diff options
-rw-r--r-- | fs/sysfs/dir.c | 146 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 12 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 3 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 4 |
4 files changed, 111 insertions, 54 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 40596a0eee52..b4074adbab01 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -22,6 +22,48 @@ static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; | |||
22 | static DEFINE_IDA(sysfs_ino_ida); | 22 | static DEFINE_IDA(sysfs_ino_ida); |
23 | 23 | ||
24 | /** | 24 | /** |
25 | * sysfs_link_sibling - link sysfs_dirent into sibling list | ||
26 | * @sd: sysfs_dirent of interest | ||
27 | * | ||
28 | * Link @sd into its sibling list which starts from | ||
29 | * sd->s_parent->s_children. | ||
30 | * | ||
31 | * Locking: | ||
32 | * mutex_lock(sd->s_parent->dentry->d_inode->i_mutex) | ||
33 | */ | ||
34 | static void sysfs_link_sibling(struct sysfs_dirent *sd) | ||
35 | { | ||
36 | struct sysfs_dirent *parent_sd = sd->s_parent; | ||
37 | |||
38 | BUG_ON(sd->s_sibling); | ||
39 | sd->s_sibling = parent_sd->s_children; | ||
40 | parent_sd->s_children = sd; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list | ||
45 | * @sd: sysfs_dirent of interest | ||
46 | * | ||
47 | * Unlink @sd from its sibling list which starts from | ||
48 | * sd->s_parent->s_children. | ||
49 | * | ||
50 | * Locking: | ||
51 | * mutex_lock(sd->s_parent->dentry->d_inode->i_mutex) | ||
52 | */ | ||
53 | static void sysfs_unlink_sibling(struct sysfs_dirent *sd) | ||
54 | { | ||
55 | struct sysfs_dirent **pos; | ||
56 | |||
57 | for (pos = &sd->s_parent->s_children; *pos; pos = &(*pos)->s_sibling) { | ||
58 | if (*pos == sd) { | ||
59 | *pos = sd->s_sibling; | ||
60 | sd->s_sibling = NULL; | ||
61 | break; | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | /** | ||
25 | * sysfs_get_active - get an active reference to sysfs_dirent | 67 | * sysfs_get_active - get an active reference to sysfs_dirent |
26 | * @sd: sysfs_dirent to get an active reference to | 68 | * @sd: sysfs_dirent to get an active reference to |
27 | * | 69 | * |
@@ -73,9 +115,9 @@ void sysfs_put_active(struct sysfs_dirent *sd) | |||
73 | return; | 115 | return; |
74 | 116 | ||
75 | /* atomic_dec_return() is a mb(), we'll always see the updated | 117 | /* atomic_dec_return() is a mb(), we'll always see the updated |
76 | * sd->s_sibling.next. | 118 | * sd->s_sibling. |
77 | */ | 119 | */ |
78 | cmpl = (void *)sd->s_sibling.next; | 120 | cmpl = (void *)sd->s_sibling; |
79 | complete(cmpl); | 121 | complete(cmpl); |
80 | } | 122 | } |
81 | 123 | ||
@@ -129,18 +171,18 @@ void sysfs_deactivate(struct sysfs_dirent *sd) | |||
129 | DECLARE_COMPLETION_ONSTACK(wait); | 171 | DECLARE_COMPLETION_ONSTACK(wait); |
130 | int v; | 172 | int v; |
131 | 173 | ||
132 | BUG_ON(!list_empty(&sd->s_sibling)); | 174 | BUG_ON(sd->s_sibling); |
133 | sd->s_sibling.next = (void *)&wait; | 175 | sd->s_sibling = (void *)&wait; |
134 | 176 | ||
135 | /* atomic_add_return() is a mb(), put_active() will always see | 177 | /* atomic_add_return() is a mb(), put_active() will always see |
136 | * the updated sd->s_sibling.next. | 178 | * the updated sd->s_sibling. |
137 | */ | 179 | */ |
138 | v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); | 180 | v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); |
139 | 181 | ||
140 | if (v != SD_DEACTIVATED_BIAS) | 182 | if (v != SD_DEACTIVATED_BIAS) |
141 | wait_for_completion(&wait); | 183 | wait_for_completion(&wait); |
142 | 184 | ||
143 | INIT_LIST_HEAD(&sd->s_sibling); | 185 | sd->s_sibling = NULL; |
144 | } | 186 | } |
145 | 187 | ||
146 | static int sysfs_alloc_ino(ino_t *pino) | 188 | static int sysfs_alloc_ino(ino_t *pino) |
@@ -237,8 +279,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) | |||
237 | atomic_set(&sd->s_count, 1); | 279 | atomic_set(&sd->s_count, 1); |
238 | atomic_set(&sd->s_active, 0); | 280 | atomic_set(&sd->s_active, 0); |
239 | atomic_set(&sd->s_event, 1); | 281 | atomic_set(&sd->s_event, 1); |
240 | INIT_LIST_HEAD(&sd->s_children); | ||
241 | INIT_LIST_HEAD(&sd->s_sibling); | ||
242 | 282 | ||
243 | sd->s_name = name; | 283 | sd->s_name = name; |
244 | sd->s_mode = mode; | 284 | sd->s_mode = mode; |
@@ -273,7 +313,7 @@ void sysfs_attach_dirent(struct sysfs_dirent *sd, | |||
273 | 313 | ||
274 | if (parent_sd) { | 314 | if (parent_sd) { |
275 | sd->s_parent = sysfs_get(parent_sd); | 315 | sd->s_parent = sysfs_get(parent_sd); |
276 | list_add(&sd->s_sibling, &parent_sd->s_children); | 316 | sysfs_link_sibling(sd); |
277 | } | 317 | } |
278 | } | 318 | } |
279 | 319 | ||
@@ -289,7 +329,7 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, | |||
289 | { | 329 | { |
290 | struct sysfs_dirent * sd; | 330 | struct sysfs_dirent * sd; |
291 | 331 | ||
292 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | 332 | for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { |
293 | if (sd->s_type) { | 333 | if (sd->s_type) { |
294 | if (strcmp(sd->s_name, new)) | 334 | if (strcmp(sd->s_name, new)) |
295 | continue; | 335 | continue; |
@@ -409,7 +449,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, | |||
409 | struct inode *inode; | 449 | struct inode *inode; |
410 | int found = 0; | 450 | int found = 0; |
411 | 451 | ||
412 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | 452 | for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { |
413 | if ((sd->s_type & SYSFS_NOT_PINNED) && | 453 | if ((sd->s_type & SYSFS_NOT_PINNED) && |
414 | !strcmp(sd->s_name, dentry->d_name.name)) { | 454 | !strcmp(sd->s_name, dentry->d_name.name)) { |
415 | found = 1; | 455 | found = 1; |
@@ -458,7 +498,7 @@ static void remove_dir(struct dentry * d) | |||
458 | 498 | ||
459 | mutex_lock(&parent->d_inode->i_mutex); | 499 | mutex_lock(&parent->d_inode->i_mutex); |
460 | 500 | ||
461 | list_del_init(&sd->s_sibling); | 501 | sysfs_unlink_sibling(sd); |
462 | 502 | ||
463 | pr_debug(" o %s removing done (%d)\n",d->d_name.name, | 503 | pr_debug(" o %s removing done (%d)\n",d->d_name.name, |
464 | atomic_read(&d->d_count)); | 504 | atomic_read(&d->d_count)); |
@@ -478,9 +518,9 @@ void sysfs_remove_subdir(struct dentry * d) | |||
478 | 518 | ||
479 | static void __sysfs_remove_dir(struct dentry *dentry) | 519 | static void __sysfs_remove_dir(struct dentry *dentry) |
480 | { | 520 | { |
481 | LIST_HEAD(removed); | 521 | struct sysfs_dirent *removed = NULL; |
482 | struct sysfs_dirent * parent_sd; | 522 | struct sysfs_dirent *parent_sd; |
483 | struct sysfs_dirent * sd, * tmp; | 523 | struct sysfs_dirent **pos; |
484 | 524 | ||
485 | if (!dentry) | 525 | if (!dentry) |
486 | return; | 526 | return; |
@@ -488,15 +528,25 @@ static void __sysfs_remove_dir(struct dentry *dentry) | |||
488 | pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); | 528 | pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); |
489 | mutex_lock(&dentry->d_inode->i_mutex); | 529 | mutex_lock(&dentry->d_inode->i_mutex); |
490 | parent_sd = dentry->d_fsdata; | 530 | parent_sd = dentry->d_fsdata; |
491 | list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { | 531 | pos = &parent_sd->s_children; |
492 | if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) | 532 | while (*pos) { |
493 | continue; | 533 | struct sysfs_dirent *sd = *pos; |
494 | list_move(&sd->s_sibling, &removed); | 534 | |
535 | if (sd->s_type && (sd->s_type & SYSFS_NOT_PINNED)) { | ||
536 | *pos = sd->s_sibling; | ||
537 | sd->s_sibling = removed; | ||
538 | removed = sd; | ||
539 | } else | ||
540 | pos = &(*pos)->s_sibling; | ||
495 | } | 541 | } |
496 | mutex_unlock(&dentry->d_inode->i_mutex); | 542 | mutex_unlock(&dentry->d_inode->i_mutex); |
497 | 543 | ||
498 | list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { | 544 | while (removed) { |
499 | list_del_init(&sd->s_sibling); | 545 | struct sysfs_dirent *sd = removed; |
546 | |||
547 | removed = sd->s_sibling; | ||
548 | sd->s_sibling = NULL; | ||
549 | |||
500 | sysfs_drop_dentry(sd); | 550 | sysfs_drop_dentry(sd); |
501 | sysfs_deactivate(sd); | 551 | sysfs_deactivate(sd); |
502 | sysfs_put(sd); | 552 | sysfs_put(sd); |
@@ -577,11 +627,11 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, | |||
577 | d_add(new_dentry, NULL); | 627 | d_add(new_dentry, NULL); |
578 | d_move(kobj->dentry, new_dentry); | 628 | d_move(kobj->dentry, new_dentry); |
579 | 629 | ||
580 | list_del_init(&sd->s_sibling); | 630 | sysfs_unlink_sibling(sd); |
581 | sysfs_get(parent_sd); | 631 | sysfs_get(parent_sd); |
582 | sysfs_put(sd->s_parent); | 632 | sysfs_put(sd->s_parent); |
583 | sd->s_parent = parent_sd; | 633 | sd->s_parent = parent_sd; |
584 | list_add(&sd->s_sibling, &parent_sd->s_children); | 634 | sysfs_link_sibling(sd); |
585 | 635 | ||
586 | error = 0; | 636 | error = 0; |
587 | goto out_unlock; | 637 | goto out_unlock; |
@@ -633,11 +683,11 @@ again: | |||
633 | dput(new_dentry); | 683 | dput(new_dentry); |
634 | 684 | ||
635 | /* Remove from old parent's list and insert into new parent's list. */ | 685 | /* Remove from old parent's list and insert into new parent's list. */ |
636 | list_del_init(&sd->s_sibling); | 686 | sysfs_unlink_sibling(sd); |
637 | sysfs_get(new_parent_sd); | 687 | sysfs_get(new_parent_sd); |
638 | sysfs_put(sd->s_parent); | 688 | sysfs_put(sd->s_parent); |
639 | sd->s_parent = new_parent_sd; | 689 | sd->s_parent = new_parent_sd; |
640 | list_add(&sd->s_sibling, &new_parent_sd->s_children); | 690 | sysfs_link_sibling(sd); |
641 | 691 | ||
642 | out: | 692 | out: |
643 | mutex_unlock(&new_parent_dentry->d_inode->i_mutex); | 693 | mutex_unlock(&new_parent_dentry->d_inode->i_mutex); |
@@ -668,7 +718,7 @@ static int sysfs_dir_close(struct inode *inode, struct file *file) | |||
668 | struct sysfs_dirent * cursor = file->private_data; | 718 | struct sysfs_dirent * cursor = file->private_data; |
669 | 719 | ||
670 | mutex_lock(&dentry->d_inode->i_mutex); | 720 | mutex_lock(&dentry->d_inode->i_mutex); |
671 | list_del_init(&cursor->s_sibling); | 721 | sysfs_unlink_sibling(cursor); |
672 | mutex_unlock(&dentry->d_inode->i_mutex); | 722 | mutex_unlock(&dentry->d_inode->i_mutex); |
673 | 723 | ||
674 | release_sysfs_dirent(cursor); | 724 | release_sysfs_dirent(cursor); |
@@ -687,7 +737,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
687 | struct dentry *dentry = filp->f_path.dentry; | 737 | struct dentry *dentry = filp->f_path.dentry; |
688 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; | 738 | struct sysfs_dirent * parent_sd = dentry->d_fsdata; |
689 | struct sysfs_dirent *cursor = filp->private_data; | 739 | struct sysfs_dirent *cursor = filp->private_data; |
690 | struct list_head *p, *q = &cursor->s_sibling; | 740 | struct sysfs_dirent **pos; |
691 | ino_t ino; | 741 | ino_t ino; |
692 | int i = filp->f_pos; | 742 | int i = filp->f_pos; |
693 | 743 | ||
@@ -710,16 +760,21 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
710 | i++; | 760 | i++; |
711 | /* fallthrough */ | 761 | /* fallthrough */ |
712 | default: | 762 | default: |
763 | pos = &parent_sd->s_children; | ||
764 | while (*pos != cursor) | ||
765 | pos = &(*pos)->s_sibling; | ||
766 | |||
767 | /* unlink cursor */ | ||
768 | *pos = cursor->s_sibling; | ||
769 | |||
713 | if (filp->f_pos == 2) | 770 | if (filp->f_pos == 2) |
714 | list_move(q, &parent_sd->s_children); | 771 | pos = &parent_sd->s_children; |
715 | 772 | ||
716 | for (p=q->next; p!= &parent_sd->s_children; p=p->next) { | 773 | for ( ; *pos; pos = &(*pos)->s_sibling) { |
717 | struct sysfs_dirent *next; | 774 | struct sysfs_dirent *next = *pos; |
718 | const char * name; | 775 | const char * name; |
719 | int len; | 776 | int len; |
720 | 777 | ||
721 | next = list_entry(p, struct sysfs_dirent, | ||
722 | s_sibling); | ||
723 | if (!next->s_type) | 778 | if (!next->s_type) |
724 | continue; | 779 | continue; |
725 | 780 | ||
@@ -729,12 +784,14 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) | |||
729 | 784 | ||
730 | if (filldir(dirent, name, len, filp->f_pos, ino, | 785 | if (filldir(dirent, name, len, filp->f_pos, ino, |
731 | dt_type(next)) < 0) | 786 | dt_type(next)) < 0) |
732 | return 0; | 787 | break; |
733 | 788 | ||
734 | list_move(q, p); | ||
735 | p = q; | ||
736 | filp->f_pos++; | 789 | filp->f_pos++; |
737 | } | 790 | } |
791 | |||
792 | /* put cursor back in */ | ||
793 | cursor->s_sibling = *pos; | ||
794 | *pos = cursor; | ||
738 | } | 795 | } |
739 | return 0; | 796 | return 0; |
740 | } | 797 | } |
@@ -759,20 +816,21 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) | |||
759 | if (file->f_pos >= 2) { | 816 | if (file->f_pos >= 2) { |
760 | struct sysfs_dirent *sd = dentry->d_fsdata; | 817 | struct sysfs_dirent *sd = dentry->d_fsdata; |
761 | struct sysfs_dirent *cursor = file->private_data; | 818 | struct sysfs_dirent *cursor = file->private_data; |
762 | struct list_head *p; | 819 | struct sysfs_dirent **pos; |
763 | loff_t n = file->f_pos - 2; | 820 | loff_t n = file->f_pos - 2; |
764 | 821 | ||
765 | list_del(&cursor->s_sibling); | 822 | sysfs_unlink_sibling(cursor); |
766 | p = sd->s_children.next; | 823 | |
767 | while (n && p != &sd->s_children) { | 824 | pos = &sd->s_children; |
768 | struct sysfs_dirent *next; | 825 | while (n && *pos) { |
769 | next = list_entry(p, struct sysfs_dirent, | 826 | struct sysfs_dirent *next = *pos; |
770 | s_sibling); | ||
771 | if (next->s_type) | 827 | if (next->s_type) |
772 | n--; | 828 | n--; |
773 | p = p->next; | 829 | pos = &(*pos)->s_sibling; |
774 | } | 830 | } |
775 | list_add_tail(&cursor->s_sibling, p); | 831 | |
832 | cursor->s_sibling = *pos; | ||
833 | *pos = cursor; | ||
776 | } | 834 | } |
777 | } | 835 | } |
778 | mutex_unlock(&dentry->d_inode->i_mutex); | 836 | mutex_unlock(&dentry->d_inode->i_mutex); |
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 3eab9c46a71b..732fd7f371e0 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
@@ -284,8 +284,8 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) | |||
284 | 284 | ||
285 | int sysfs_hash_and_remove(struct dentry * dir, const char * name) | 285 | int sysfs_hash_and_remove(struct dentry * dir, const char * name) |
286 | { | 286 | { |
287 | struct sysfs_dirent * sd; | 287 | struct sysfs_dirent **pos, *sd; |
288 | struct sysfs_dirent * parent_sd; | 288 | struct sysfs_dirent *parent_sd = dir->d_fsdata; |
289 | int found = 0; | 289 | int found = 0; |
290 | 290 | ||
291 | if (!dir) | 291 | if (!dir) |
@@ -295,13 +295,15 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) | |||
295 | /* no inode means this hasn't been made visible yet */ | 295 | /* no inode means this hasn't been made visible yet */ |
296 | return -ENOENT; | 296 | return -ENOENT; |
297 | 297 | ||
298 | parent_sd = dir->d_fsdata; | ||
299 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); | 298 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); |
300 | list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { | 299 | for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { |
300 | sd = *pos; | ||
301 | |||
301 | if (!sd->s_type) | 302 | if (!sd->s_type) |
302 | continue; | 303 | continue; |
303 | if (!strcmp(sd->s_name, name)) { | 304 | if (!strcmp(sd->s_name, name)) { |
304 | list_del_init(&sd->s_sibling); | 305 | *pos = sd->s_sibling; |
306 | sd->s_sibling = NULL; | ||
305 | found = 1; | 307 | found = 1; |
306 | break; | 308 | break; |
307 | } | 309 | } |
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index d0e160307c5d..4be9593ea000 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c | |||
@@ -26,11 +26,8 @@ static const struct super_operations sysfs_ops = { | |||
26 | 26 | ||
27 | static struct sysfs_dirent sysfs_root = { | 27 | static struct sysfs_dirent sysfs_root = { |
28 | .s_count = ATOMIC_INIT(1), | 28 | .s_count = ATOMIC_INIT(1), |
29 | .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), | ||
30 | .s_children = LIST_HEAD_INIT(sysfs_root.s_children), | ||
31 | .s_type = SYSFS_ROOT, | 29 | .s_type = SYSFS_ROOT, |
32 | .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, | 30 | .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, |
33 | .s_iattr = NULL, | ||
34 | .s_ino = 1, | 31 | .s_ino = 1, |
35 | }; | 32 | }; |
36 | 33 | ||
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ae006b070bf0..6f8aaf3805d2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -23,8 +23,8 @@ struct sysfs_dirent { | |||
23 | atomic_t s_count; | 23 | atomic_t s_count; |
24 | atomic_t s_active; | 24 | atomic_t s_active; |
25 | struct sysfs_dirent * s_parent; | 25 | struct sysfs_dirent * s_parent; |
26 | struct list_head s_sibling; | 26 | struct sysfs_dirent * s_sibling; |
27 | struct list_head s_children; | 27 | struct sysfs_dirent * s_children; |
28 | const char * s_name; | 28 | const char * s_name; |
29 | 29 | ||
30 | union { | 30 | union { |