aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r--fs/sysfs/dir.c182
1 files changed, 126 insertions, 56 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index ea9120a830d8..48ffbdf0d017 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -43,20 +43,48 @@ static DEFINE_IDA(sysfs_ino_ida);
43static void sysfs_link_sibling(struct sysfs_dirent *sd) 43static void sysfs_link_sibling(struct sysfs_dirent *sd)
44{ 44{
45 struct sysfs_dirent *parent_sd = sd->s_parent; 45 struct sysfs_dirent *parent_sd = sd->s_parent;
46 struct sysfs_dirent **pos;
47 46
48 BUG_ON(sd->s_sibling); 47 struct rb_node **p;
49 48 struct rb_node *parent;
50 /* Store directory entries in order by ino. This allows 49
51 * readdir to properly restart without having to add a 50 if (sysfs_type(sd) == SYSFS_DIR)
52 * cursor into the s_dir.children list. 51 parent_sd->s_dir.subdirs++;
53 */ 52
54 for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) { 53 p = &parent_sd->s_dir.inode_tree.rb_node;
55 if (sd->s_ino < (*pos)->s_ino) 54 parent = NULL;
56 break; 55 while (*p) {
56 parent = *p;
57#define node rb_entry(parent, struct sysfs_dirent, inode_node)
58 if (sd->s_ino < node->s_ino) {
59 p = &node->inode_node.rb_left;
60 } else if (sd->s_ino > node->s_ino) {
61 p = &node->inode_node.rb_right;
62 } else {
63 printk(KERN_CRIT "sysfs: inserting duplicate inode '%lx'\n",
64 (unsigned long) sd->s_ino);
65 BUG();
66 }
67#undef node
57 } 68 }
58 sd->s_sibling = *pos; 69 rb_link_node(&sd->inode_node, parent, p);
59 *pos = sd; 70 rb_insert_color(&sd->inode_node, &parent_sd->s_dir.inode_tree);
71
72 p = &parent_sd->s_dir.name_tree.rb_node;
73 parent = NULL;
74 while (*p) {
75 int c;
76 parent = *p;
77#define node rb_entry(parent, struct sysfs_dirent, name_node)
78 c = strcmp(sd->s_name, node->s_name);
79 if (c < 0) {
80 p = &node->name_node.rb_left;
81 } else {
82 p = &node->name_node.rb_right;
83 }
84#undef node
85 }
86 rb_link_node(&sd->name_node, parent, p);
87 rb_insert_color(&sd->name_node, &parent_sd->s_dir.name_tree);
60} 88}
61 89
62/** 90/**
@@ -71,16 +99,11 @@ static void sysfs_link_sibling(struct sysfs_dirent *sd)
71 */ 99 */
72static void sysfs_unlink_sibling(struct sysfs_dirent *sd) 100static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
73{ 101{
74 struct sysfs_dirent **pos; 102 if (sysfs_type(sd) == SYSFS_DIR)
103 sd->s_parent->s_dir.subdirs--;
75 104
76 for (pos = &sd->s_parent->s_dir.children; *pos; 105 rb_erase(&sd->inode_node, &sd->s_parent->s_dir.inode_tree);
77 pos = &(*pos)->s_sibling) { 106 rb_erase(&sd->name_node, &sd->s_parent->s_dir.name_tree);
78 if (*pos == sd) {
79 *pos = sd->s_sibling;
80 sd->s_sibling = NULL;
81 break;
82 }
83 }
84} 107}
85 108
86/** 109/**
@@ -126,7 +149,6 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
126 */ 149 */
127void sysfs_put_active(struct sysfs_dirent *sd) 150void sysfs_put_active(struct sysfs_dirent *sd)
128{ 151{
129 struct completion *cmpl;
130 int v; 152 int v;
131 153
132 if (unlikely(!sd)) 154 if (unlikely(!sd))
@@ -138,10 +160,9 @@ void sysfs_put_active(struct sysfs_dirent *sd)
138 return; 160 return;
139 161
140 /* atomic_dec_return() is a mb(), we'll always see the updated 162 /* atomic_dec_return() is a mb(), we'll always see the updated
141 * sd->s_sibling. 163 * sd->u.completion.
142 */ 164 */
143 cmpl = (void *)sd->s_sibling; 165 complete(sd->u.completion);
144 complete(cmpl);
145} 166}
146 167
147/** 168/**
@@ -155,16 +176,16 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
155 DECLARE_COMPLETION_ONSTACK(wait); 176 DECLARE_COMPLETION_ONSTACK(wait);
156 int v; 177 int v;
157 178
158 BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED)); 179 BUG_ON(!(sd->s_flags & SYSFS_FLAG_REMOVED));
159 180
160 if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF)) 181 if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF))
161 return; 182 return;
162 183
163 sd->s_sibling = (void *)&wait; 184 sd->u.completion = (void *)&wait;
164 185
165 rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); 186 rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_);
166 /* atomic_add_return() is a mb(), put_active() will always see 187 /* atomic_add_return() is a mb(), put_active() will always see
167 * the updated sd->s_sibling. 188 * the updated sd->u.completion.
168 */ 189 */
169 v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); 190 v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active);
170 191
@@ -173,8 +194,6 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
173 wait_for_completion(&wait); 194 wait_for_completion(&wait);
174 } 195 }
175 196
176 sd->s_sibling = NULL;
177
178 lock_acquired(&sd->dep_map, _RET_IP_); 197 lock_acquired(&sd->dep_map, _RET_IP_);
179 rwsem_release(&sd->dep_map, 1, _RET_IP_); 198 rwsem_release(&sd->dep_map, 1, _RET_IP_);
180} 199}
@@ -384,6 +403,13 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
384{ 403{
385 struct sysfs_inode_attrs *ps_iattr; 404 struct sysfs_inode_attrs *ps_iattr;
386 405
406 if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
407 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
408 sysfs_ns_type(acxt->parent_sd)? "required": "invalid",
409 acxt->parent_sd->s_name, sd->s_name);
410 return -EINVAL;
411 }
412
387 if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) 413 if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))
388 return -EEXIST; 414 return -EEXIST;
389 415
@@ -490,7 +516,7 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
490 } 516 }
491 517
492 sd->s_flags |= SYSFS_FLAG_REMOVED; 518 sd->s_flags |= SYSFS_FLAG_REMOVED;
493 sd->s_sibling = acxt->removed; 519 sd->u.removed_list = acxt->removed;
494 acxt->removed = sd; 520 acxt->removed = sd;
495} 521}
496 522
@@ -514,8 +540,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
514 while (acxt->removed) { 540 while (acxt->removed) {
515 struct sysfs_dirent *sd = acxt->removed; 541 struct sysfs_dirent *sd = acxt->removed;
516 542
517 acxt->removed = sd->s_sibling; 543 acxt->removed = sd->u.removed_list;
518 sd->s_sibling = NULL;
519 544
520 sysfs_deactivate(sd); 545 sysfs_deactivate(sd);
521 unmap_bin_file(sd); 546 unmap_bin_file(sd);
@@ -540,15 +565,43 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
540 const void *ns, 565 const void *ns,
541 const unsigned char *name) 566 const unsigned char *name)
542{ 567{
543 struct sysfs_dirent *sd; 568 struct rb_node *p = parent_sd->s_dir.name_tree.rb_node;
569 struct sysfs_dirent *found = NULL;
544 570
545 for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) { 571 if (!!sysfs_ns_type(parent_sd) != !!ns) {
546 if (ns && sd->s_ns && (sd->s_ns != ns)) 572 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
547 continue; 573 sysfs_ns_type(parent_sd)? "required": "invalid",
548 if (!strcmp(sd->s_name, name)) 574 parent_sd->s_name, name);
549 return sd; 575 return NULL;
550 } 576 }
551 return NULL; 577
578 while (p) {
579 int c;
580#define node rb_entry(p, struct sysfs_dirent, name_node)
581 c = strcmp(name, node->s_name);
582 if (c < 0) {
583 p = node->name_node.rb_left;
584 } else if (c > 0) {
585 p = node->name_node.rb_right;
586 } else {
587 found = node;
588 p = node->name_node.rb_left;
589 }
590#undef node
591 }
592
593 if (found) {
594 while (found->s_ns != ns) {
595 p = rb_next(&found->name_node);
596 if (!p)
597 return NULL;
598 found = rb_entry(p, struct sysfs_dirent, name_node);
599 if (strcmp(name, found->s_name))
600 return NULL;
601 }
602 }
603
604 return found;
552} 605}
553 606
554/** 607/**
@@ -744,21 +797,19 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd)
744static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) 797static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
745{ 798{
746 struct sysfs_addrm_cxt acxt; 799 struct sysfs_addrm_cxt acxt;
747 struct sysfs_dirent **pos; 800 struct rb_node *pos;
748 801
749 if (!dir_sd) 802 if (!dir_sd)
750 return; 803 return;
751 804
752 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); 805 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
753 sysfs_addrm_start(&acxt, dir_sd); 806 sysfs_addrm_start(&acxt, dir_sd);
754 pos = &dir_sd->s_dir.children; 807 pos = rb_first(&dir_sd->s_dir.inode_tree);
755 while (*pos) { 808 while (pos) {
756 struct sysfs_dirent *sd = *pos; 809 struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node);
757 810 pos = rb_next(pos);
758 if (sysfs_type(sd) != SYSFS_DIR) 811 if (sysfs_type(sd) != SYSFS_DIR)
759 sysfs_remove_one(&acxt, sd); 812 sysfs_remove_one(&acxt, sd);
760 else
761 pos = &(*pos)->s_sibling;
762 } 813 }
763 sysfs_addrm_finish(&acxt); 814 sysfs_addrm_finish(&acxt);
764 815
@@ -881,12 +932,28 @@ static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
881 pos = NULL; 932 pos = NULL;
882 } 933 }
883 if (!pos && (ino > 1) && (ino < INT_MAX)) { 934 if (!pos && (ino > 1) && (ino < INT_MAX)) {
884 pos = parent_sd->s_dir.children; 935 struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node;
885 while (pos && (ino > pos->s_ino)) 936 while (p) {
886 pos = pos->s_sibling; 937#define node rb_entry(p, struct sysfs_dirent, inode_node)
938 if (ino < node->s_ino) {
939 pos = node;
940 p = node->inode_node.rb_left;
941 } else if (ino > node->s_ino) {
942 p = node->inode_node.rb_right;
943 } else {
944 pos = node;
945 break;
946 }
947#undef node
948 }
949 }
950 while (pos && pos->s_ns != ns) {
951 struct rb_node *p = rb_next(&pos->inode_node);
952 if (!p)
953 pos = NULL;
954 else
955 pos = rb_entry(p, struct sysfs_dirent, inode_node);
887 } 956 }
888 while (pos && pos->s_ns && pos->s_ns != ns)
889 pos = pos->s_sibling;
890 return pos; 957 return pos;
891} 958}
892 959
@@ -894,10 +961,13 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
894 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) 961 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
895{ 962{
896 pos = sysfs_dir_pos(ns, parent_sd, ino, pos); 963 pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
897 if (pos) 964 if (pos) do {
898 pos = pos->s_sibling; 965 struct rb_node *p = rb_next(&pos->inode_node);
899 while (pos && pos->s_ns && pos->s_ns != ns) 966 if (!p)
900 pos = pos->s_sibling; 967 pos = NULL;
968 else
969 pos = rb_entry(p, struct sysfs_dirent, inode_node);
970 } while (pos && pos->s_ns != ns);
901 return pos; 971 return pos;
902} 972}
903 973