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.c168
1 files changed, 112 insertions, 56 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index ea9120a830d8..83bb9d1f30aa 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}
@@ -490,7 +509,7 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
490 } 509 }
491 510
492 sd->s_flags |= SYSFS_FLAG_REMOVED; 511 sd->s_flags |= SYSFS_FLAG_REMOVED;
493 sd->s_sibling = acxt->removed; 512 sd->u.removed_list = acxt->removed;
494 acxt->removed = sd; 513 acxt->removed = sd;
495} 514}
496 515
@@ -514,8 +533,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
514 while (acxt->removed) { 533 while (acxt->removed) {
515 struct sysfs_dirent *sd = acxt->removed; 534 struct sysfs_dirent *sd = acxt->removed;
516 535
517 acxt->removed = sd->s_sibling; 536 acxt->removed = sd->u.removed_list;
518 sd->s_sibling = NULL;
519 537
520 sysfs_deactivate(sd); 538 sysfs_deactivate(sd);
521 unmap_bin_file(sd); 539 unmap_bin_file(sd);
@@ -540,15 +558,36 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
540 const void *ns, 558 const void *ns,
541 const unsigned char *name) 559 const unsigned char *name)
542{ 560{
543 struct sysfs_dirent *sd; 561 struct rb_node *p = parent_sd->s_dir.name_tree.rb_node;
562 struct sysfs_dirent *found = NULL;
563
564 while (p) {
565 int c;
566#define node rb_entry(p, struct sysfs_dirent, name_node)
567 c = strcmp(name, node->s_name);
568 if (c < 0) {
569 p = node->name_node.rb_left;
570 } else if (c > 0) {
571 p = node->name_node.rb_right;
572 } else {
573 found = node;
574 p = node->name_node.rb_left;
575 }
576#undef node
577 }
544 578
545 for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) { 579 if (found && ns) {
546 if (ns && sd->s_ns && (sd->s_ns != ns)) 580 while (found->s_ns && found->s_ns != ns) {
547 continue; 581 p = rb_next(&found->name_node);
548 if (!strcmp(sd->s_name, name)) 582 if (!p)
549 return sd; 583 return NULL;
584 found = rb_entry(p, struct sysfs_dirent, name_node);
585 if (strcmp(name, found->s_name))
586 return NULL;
587 }
550 } 588 }
551 return NULL; 589
590 return found;
552} 591}
553 592
554/** 593/**
@@ -744,21 +783,19 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd)
744static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) 783static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
745{ 784{
746 struct sysfs_addrm_cxt acxt; 785 struct sysfs_addrm_cxt acxt;
747 struct sysfs_dirent **pos; 786 struct rb_node *pos;
748 787
749 if (!dir_sd) 788 if (!dir_sd)
750 return; 789 return;
751 790
752 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); 791 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
753 sysfs_addrm_start(&acxt, dir_sd); 792 sysfs_addrm_start(&acxt, dir_sd);
754 pos = &dir_sd->s_dir.children; 793 pos = rb_first(&dir_sd->s_dir.inode_tree);
755 while (*pos) { 794 while (pos) {
756 struct sysfs_dirent *sd = *pos; 795 struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node);
757 796 pos = rb_next(pos);
758 if (sysfs_type(sd) != SYSFS_DIR) 797 if (sysfs_type(sd) != SYSFS_DIR)
759 sysfs_remove_one(&acxt, sd); 798 sysfs_remove_one(&acxt, sd);
760 else
761 pos = &(*pos)->s_sibling;
762 } 799 }
763 sysfs_addrm_finish(&acxt); 800 sysfs_addrm_finish(&acxt);
764 801
@@ -881,12 +918,28 @@ static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
881 pos = NULL; 918 pos = NULL;
882 } 919 }
883 if (!pos && (ino > 1) && (ino < INT_MAX)) { 920 if (!pos && (ino > 1) && (ino < INT_MAX)) {
884 pos = parent_sd->s_dir.children; 921 struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node;
885 while (pos && (ino > pos->s_ino)) 922 while (p) {
886 pos = pos->s_sibling; 923#define node rb_entry(p, struct sysfs_dirent, inode_node)
924 if (ino < node->s_ino) {
925 pos = node;
926 p = node->inode_node.rb_left;
927 } else if (ino > node->s_ino) {
928 p = node->inode_node.rb_right;
929 } else {
930 pos = node;
931 break;
932 }
933#undef node
934 }
935 }
936 while (pos && pos->s_ns && pos->s_ns != ns) {
937 struct rb_node *p = rb_next(&pos->inode_node);
938 if (!p)
939 pos = NULL;
940 else
941 pos = rb_entry(p, struct sysfs_dirent, inode_node);
887 } 942 }
888 while (pos && pos->s_ns && pos->s_ns != ns)
889 pos = pos->s_sibling;
890 return pos; 943 return pos;
891} 944}
892 945
@@ -894,10 +947,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) 947 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
895{ 948{
896 pos = sysfs_dir_pos(ns, parent_sd, ino, pos); 949 pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
897 if (pos) 950 if (pos) do {
898 pos = pos->s_sibling; 951 struct rb_node *p = rb_next(&pos->inode_node);
899 while (pos && pos->s_ns && pos->s_ns != ns) 952 if (!p)
900 pos = pos->s_sibling; 953 pos = NULL;
954 else
955 pos = rb_entry(p, struct sysfs_dirent, inode_node);
956 } while (pos && pos->s_ns && pos->s_ns != ns);
901 return pos; 957 return pos;
902} 958}
903 959