aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysfs')
-rw-r--r--fs/sysfs/dir.c228
-rw-r--r--fs/sysfs/inode.c3
-rw-r--r--fs/sysfs/mount.c2
-rw-r--r--fs/sysfs/sysfs.h18
4 files changed, 127 insertions, 124 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 7fdf6a7b7436..dd3779cf3a3b 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -22,76 +22,100 @@
22#include <linux/mutex.h> 22#include <linux/mutex.h>
23#include <linux/slab.h> 23#include <linux/slab.h>
24#include <linux/security.h> 24#include <linux/security.h>
25#include <linux/hash.h>
25#include "sysfs.h" 26#include "sysfs.h"
26 27
27DEFINE_MUTEX(sysfs_mutex); 28DEFINE_MUTEX(sysfs_mutex);
28DEFINE_SPINLOCK(sysfs_assoc_lock); 29DEFINE_SPINLOCK(sysfs_assoc_lock);
29 30
31#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb);
32
30static DEFINE_SPINLOCK(sysfs_ino_lock); 33static DEFINE_SPINLOCK(sysfs_ino_lock);
31static DEFINE_IDA(sysfs_ino_ida); 34static DEFINE_IDA(sysfs_ino_ida);
32 35
33/** 36/**
34 * sysfs_link_sibling - link sysfs_dirent into sibling list 37 * sysfs_name_hash
38 * @ns: Namespace tag to hash
39 * @name: Null terminated string to hash
40 *
41 * Returns 31 bit hash of ns + name (so it fits in an off_t )
42 */
43static unsigned int sysfs_name_hash(const void *ns, const char *name)
44{
45 unsigned long hash = init_name_hash();
46 unsigned int len = strlen(name);
47 while (len--)
48 hash = partial_name_hash(*name++, hash);
49 hash = ( end_name_hash(hash) ^ hash_ptr( (void *)ns, 31 ) );
50 hash &= 0x7fffffffU;
51 /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */
52 if (hash < 1)
53 hash += 2;
54 if (hash >= INT_MAX)
55 hash = INT_MAX - 1;
56 return hash;
57}
58
59static int sysfs_name_compare(unsigned int hash, const void *ns,
60 const char *name, const struct sysfs_dirent *sd)
61{
62 if (hash != sd->s_hash)
63 return hash - sd->s_hash;
64 if (ns != sd->s_ns)
65 return ns - sd->s_ns;
66 return strcmp(name, sd->s_name);
67}
68
69static int sysfs_sd_compare(const struct sysfs_dirent *left,
70 const struct sysfs_dirent *right)
71{
72 return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name,
73 right);
74}
75
76/**
77 * sysfs_link_subling - link sysfs_dirent into sibling rbtree
35 * @sd: sysfs_dirent of interest 78 * @sd: sysfs_dirent of interest
36 * 79 *
37 * Link @sd into its sibling list which starts from 80 * Link @sd into its sibling rbtree which starts from
38 * sd->s_parent->s_dir.children. 81 * sd->s_parent->s_dir.children.
39 * 82 *
40 * Locking: 83 * Locking:
41 * mutex_lock(sysfs_mutex) 84 * mutex_lock(sysfs_mutex)
85 *
86 * RETURNS:
87 * 0 on susccess -EEXIST on failure.
42 */ 88 */
43static void sysfs_link_sibling(struct sysfs_dirent *sd) 89static int sysfs_link_sibling(struct sysfs_dirent *sd)
44{ 90{
45 struct sysfs_dirent *parent_sd = sd->s_parent; 91 struct rb_node **node = &sd->s_parent->s_dir.children.rb_node;
46 92 struct rb_node *parent = NULL;
47 struct rb_node **p; 93
48 struct rb_node *parent; 94 while (*node) {
49 95 struct sysfs_dirent *pos;
50 if (sysfs_type(sd) == SYSFS_DIR) 96 int result;
51 parent_sd->s_dir.subdirs++; 97
52 98 pos = to_sysfs_dirent(*node);
53 p = &parent_sd->s_dir.inode_tree.rb_node; 99 parent = *node;
54 parent = NULL; 100 result = sysfs_sd_compare(sd, pos);
55 while (*p) { 101 if (result < 0)
56 parent = *p; 102 node = &pos->s_rb.rb_left;
57#define node rb_entry(parent, struct sysfs_dirent, inode_node) 103 else if (result > 0)
58 if (sd->s_ino < node->s_ino) { 104 node = &pos->s_rb.rb_right;
59 p = &node->inode_node.rb_left; 105 else
60 } else if (sd->s_ino > node->s_ino) { 106 return -EEXIST;
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
68 }
69 rb_link_node(&sd->inode_node, parent, p);
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 } 107 }
86 rb_link_node(&sd->name_node, parent, p); 108 /* add new node and rebalance the tree */
87 rb_insert_color(&sd->name_node, &parent_sd->s_dir.name_tree); 109 rb_link_node(&sd->s_rb, parent, node);
110 rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
111 return 0;
88} 112}
89 113
90/** 114/**
91 * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list 115 * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree
92 * @sd: sysfs_dirent of interest 116 * @sd: sysfs_dirent of interest
93 * 117 *
94 * Unlink @sd from its sibling list which starts from 118 * Unlink @sd from its sibling rbtree which starts from
95 * sd->s_parent->s_dir.children. 119 * sd->s_parent->s_dir.children.
96 * 120 *
97 * Locking: 121 * Locking:
@@ -99,11 +123,7 @@ static void sysfs_link_sibling(struct sysfs_dirent *sd)
99 */ 123 */
100static void sysfs_unlink_sibling(struct sysfs_dirent *sd) 124static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
101{ 125{
102 if (sysfs_type(sd) == SYSFS_DIR) 126 rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
103 sd->s_parent->s_dir.subdirs--;
104
105 rb_erase(&sd->inode_node, &sd->s_parent->s_dir.inode_tree);
106 rb_erase(&sd->name_node, &sd->s_parent->s_dir.name_tree);
107} 127}
108 128
109/** 129/**
@@ -198,7 +218,7 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
198 rwsem_release(&sd->dep_map, 1, _RET_IP_); 218 rwsem_release(&sd->dep_map, 1, _RET_IP_);
199} 219}
200 220
201static int sysfs_alloc_ino(ino_t *pino) 221static int sysfs_alloc_ino(unsigned int *pino)
202{ 222{
203 int ino, rc; 223 int ino, rc;
204 224
@@ -217,7 +237,7 @@ static int sysfs_alloc_ino(ino_t *pino)
217 return rc; 237 return rc;
218} 238}
219 239
220static void sysfs_free_ino(ino_t ino) 240static void sysfs_free_ino(unsigned int ino)
221{ 241{
222 spin_lock(&sysfs_ino_lock); 242 spin_lock(&sysfs_ino_lock);
223 ida_remove(&sysfs_ino_ida, ino); 243 ida_remove(&sysfs_ino_ida, ino);
@@ -402,6 +422,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
402int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) 422int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
403{ 423{
404 struct sysfs_inode_attrs *ps_iattr; 424 struct sysfs_inode_attrs *ps_iattr;
425 int ret;
405 426
406 if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) { 427 if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
407 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 428 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
@@ -410,12 +431,12 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
410 return -EINVAL; 431 return -EINVAL;
411 } 432 }
412 433
413 if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) 434 sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
414 return -EEXIST;
415
416 sd->s_parent = sysfs_get(acxt->parent_sd); 435 sd->s_parent = sysfs_get(acxt->parent_sd);
417 436
418 sysfs_link_sibling(sd); 437 ret = sysfs_link_sibling(sd);
438 if (ret)
439 return ret;
419 440
420 /* Update timestamps on the parent */ 441 /* Update timestamps on the parent */
421 ps_iattr = acxt->parent_sd->s_iattr; 442 ps_iattr = acxt->parent_sd->s_iattr;
@@ -565,8 +586,8 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
565 const void *ns, 586 const void *ns,
566 const unsigned char *name) 587 const unsigned char *name)
567{ 588{
568 struct rb_node *p = parent_sd->s_dir.name_tree.rb_node; 589 struct rb_node *node = parent_sd->s_dir.children.rb_node;
569 struct sysfs_dirent *found = NULL; 590 unsigned int hash;
570 591
571 if (!!sysfs_ns_type(parent_sd) != !!ns) { 592 if (!!sysfs_ns_type(parent_sd) != !!ns) {
572 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 593 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
@@ -575,33 +596,21 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
575 return NULL; 596 return NULL;
576 } 597 }
577 598
578 while (p) { 599 hash = sysfs_name_hash(ns, name);
579 int c; 600 while (node) {
580#define node rb_entry(p, struct sysfs_dirent, name_node) 601 struct sysfs_dirent *sd;
581 c = strcmp(name, node->s_name); 602 int result;
582 if (c < 0) { 603
583 p = node->name_node.rb_left; 604 sd = to_sysfs_dirent(node);
584 } else if (c > 0) { 605 result = sysfs_name_compare(hash, ns, name, sd);
585 p = node->name_node.rb_right; 606 if (result < 0)
586 } else { 607 node = node->rb_left;
587 found = node; 608 else if (result > 0)
588 p = node->name_node.rb_left; 609 node = node->rb_right;
589 } 610 else
590#undef node 611 return sd;
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 } 612 }
603 613 return NULL;
604 return found;
605} 614}
606 615
607/** 616/**
@@ -804,9 +813,9 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
804 813
805 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); 814 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
806 sysfs_addrm_start(&acxt, dir_sd); 815 sysfs_addrm_start(&acxt, dir_sd);
807 pos = rb_first(&dir_sd->s_dir.inode_tree); 816 pos = rb_first(&dir_sd->s_dir.children);
808 while (pos) { 817 while (pos) {
809 struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node); 818 struct sysfs_dirent *sd = to_sysfs_dirent(pos);
810 pos = rb_next(pos); 819 pos = rb_next(pos);
811 if (sysfs_type(sd) != SYSFS_DIR) 820 if (sysfs_type(sd) != SYSFS_DIR)
812 sysfs_remove_one(&acxt, sd); 821 sysfs_remove_one(&acxt, sd);
@@ -863,6 +872,7 @@ int sysfs_rename(struct sysfs_dirent *sd,
863 872
864 dup_name = sd->s_name; 873 dup_name = sd->s_name;
865 sd->s_name = new_name; 874 sd->s_name = new_name;
875 sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name);
866 } 876 }
867 877
868 /* Move to the appropriate place in the appropriate directories rbtree. */ 878 /* Move to the appropriate place in the appropriate directories rbtree. */
@@ -919,38 +929,36 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp)
919} 929}
920 930
921static struct sysfs_dirent *sysfs_dir_pos(const void *ns, 931static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
922 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) 932 struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos)
923{ 933{
924 if (pos) { 934 if (pos) {
925 int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && 935 int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
926 pos->s_parent == parent_sd && 936 pos->s_parent == parent_sd &&
927 ino == pos->s_ino; 937 hash == pos->s_hash;
928 sysfs_put(pos); 938 sysfs_put(pos);
929 if (!valid) 939 if (!valid)
930 pos = NULL; 940 pos = NULL;
931 } 941 }
932 if (!pos && (ino > 1) && (ino < INT_MAX)) { 942 if (!pos && (hash > 1) && (hash < INT_MAX)) {
933 struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node; 943 struct rb_node *node = parent_sd->s_dir.children.rb_node;
934 while (p) { 944 while (node) {
935#define node rb_entry(p, struct sysfs_dirent, inode_node) 945 pos = to_sysfs_dirent(node);
936 if (ino < node->s_ino) { 946
937 pos = node; 947 if (hash < pos->s_hash)
938 p = node->inode_node.rb_left; 948 node = node->rb_left;
939 } else if (ino > node->s_ino) { 949 else if (hash > pos->s_hash)
940 p = node->inode_node.rb_right; 950 node = node->rb_right;
941 } else { 951 else
942 pos = node;
943 break; 952 break;
944 }
945#undef node
946 } 953 }
947 } 954 }
955 /* Skip over entries in the wrong namespace */
948 while (pos && pos->s_ns != ns) { 956 while (pos && pos->s_ns != ns) {
949 struct rb_node *p = rb_next(&pos->inode_node); 957 struct rb_node *node = rb_next(&pos->s_rb);
950 if (!p) 958 if (!node)
951 pos = NULL; 959 pos = NULL;
952 else 960 else
953 pos = rb_entry(p, struct sysfs_dirent, inode_node); 961 pos = to_sysfs_dirent(node);
954 } 962 }
955 return pos; 963 return pos;
956} 964}
@@ -960,11 +968,11 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
960{ 968{
961 pos = sysfs_dir_pos(ns, parent_sd, ino, pos); 969 pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
962 if (pos) do { 970 if (pos) do {
963 struct rb_node *p = rb_next(&pos->inode_node); 971 struct rb_node *node = rb_next(&pos->s_rb);
964 if (!p) 972 if (!node)
965 pos = NULL; 973 pos = NULL;
966 else 974 else
967 pos = rb_entry(p, struct sysfs_dirent, inode_node); 975 pos = to_sysfs_dirent(node);
968 } while (pos && pos->s_ns != ns); 976 } while (pos && pos->s_ns != ns);
969 return pos; 977 return pos;
970} 978}
@@ -1006,7 +1014,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
1006 len = strlen(name); 1014 len = strlen(name);
1007 ino = pos->s_ino; 1015 ino = pos->s_ino;
1008 type = dt_type(pos); 1016 type = dt_type(pos);
1009 filp->f_pos = ino; 1017 filp->f_pos = pos->s_hash;
1010 filp->private_data = sysfs_get(pos); 1018 filp->private_data = sysfs_get(pos);
1011 1019
1012 mutex_unlock(&sysfs_mutex); 1020 mutex_unlock(&sysfs_mutex);
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 85eb81683a29..4291fd1617ab 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -216,9 +216,6 @@ static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)
216 iattrs->ia_secdata, 216 iattrs->ia_secdata,
217 iattrs->ia_secdata_len); 217 iattrs->ia_secdata_len);
218 } 218 }
219
220 if (sysfs_type(sd) == SYSFS_DIR)
221 set_nlink(inode, sd->s_dir.subdirs + 2);
222} 219}
223 220
224int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) 221int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index e34f0d99ea4e..140f26a34288 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -36,7 +36,7 @@ struct sysfs_dirent sysfs_root = {
36 .s_name = "", 36 .s_name = "",
37 .s_count = ATOMIC_INIT(1), 37 .s_count = ATOMIC_INIT(1),
38 .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), 38 .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
39 .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, 39 .s_mode = S_IFDIR | S_IRUGO | S_IXUGO,
40 .s_ino = 1, 40 .s_ino = 1,
41}; 41};
42 42
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 7484a36ee678..6289a00287db 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -19,10 +19,8 @@ struct sysfs_open_dirent;
19struct sysfs_elem_dir { 19struct sysfs_elem_dir {
20 struct kobject *kobj; 20 struct kobject *kobj;
21 21
22 unsigned long subdirs; 22 /* children rbtree starts here and goes through sd->s_rb */
23 23 struct rb_root children;
24 struct rb_root inode_tree;
25 struct rb_root name_tree;
26}; 24};
27 25
28struct sysfs_elem_symlink { 26struct sysfs_elem_symlink {
@@ -62,8 +60,7 @@ struct sysfs_dirent {
62 struct sysfs_dirent *s_parent; 60 struct sysfs_dirent *s_parent;
63 const char *s_name; 61 const char *s_name;
64 62
65 struct rb_node inode_node; 63 struct rb_node s_rb;
66 struct rb_node name_node;
67 64
68 union { 65 union {
69 struct completion *completion; 66 struct completion *completion;
@@ -71,6 +68,7 @@ struct sysfs_dirent {
71 } u; 68 } u;
72 69
73 const void *s_ns; /* namespace tag */ 70 const void *s_ns; /* namespace tag */
71 unsigned int s_hash; /* ns + name hash */
74 union { 72 union {
75 struct sysfs_elem_dir s_dir; 73 struct sysfs_elem_dir s_dir;
76 struct sysfs_elem_symlink s_symlink; 74 struct sysfs_elem_symlink s_symlink;
@@ -78,9 +76,9 @@ struct sysfs_dirent {
78 struct sysfs_elem_bin_attr s_bin_attr; 76 struct sysfs_elem_bin_attr s_bin_attr;
79 }; 77 };
80 78
81 unsigned int s_flags; 79 unsigned short s_flags;
82 umode_t s_mode; 80 umode_t s_mode;
83 ino_t s_ino; 81 unsigned int s_ino;
84 struct sysfs_inode_attrs *s_iattr; 82 struct sysfs_inode_attrs *s_iattr;
85}; 83};
86 84
@@ -95,11 +93,11 @@ struct sysfs_dirent {
95#define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) 93#define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR)
96 94
97/* identify any namespace tag on sysfs_dirents */ 95/* identify any namespace tag on sysfs_dirents */
98#define SYSFS_NS_TYPE_MASK 0xff00 96#define SYSFS_NS_TYPE_MASK 0xf00
99#define SYSFS_NS_TYPE_SHIFT 8 97#define SYSFS_NS_TYPE_SHIFT 8
100 98
101#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK) 99#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK)
102#define SYSFS_FLAG_REMOVED 0x020000 100#define SYSFS_FLAG_REMOVED 0x02000
103 101
104static inline unsigned int sysfs_type(struct sysfs_dirent *sd) 102static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
105{ 103{