aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2011-12-18 23:05:43 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2012-01-24 15:36:17 -0500
commit4e4d6d860b9393c5395ba5920edb5b4c5d43a3a3 (patch)
tree245addd82a018bd6a9483b01decfada9a13ffc3e /fs/sysfs/dir.c
parentdcd6c92267155e70a94b3927bce681ce74b80d1f (diff)
sysfs: Add s_hash to sysfs_dirent and order directory entries by hash
Compute a 31 bit hash of directory entries (that can fit in a signed 32bit off_t) and index the sysfs directory entries by that hash, replacing the per directory indexes by name and by inode. Because we now only use a single rbtree this reduces the size of sysfs_dirent by 2 pointers. Because we have fewer cases to deal with the code is now simpler. For now I use the simple hash that the dcache uses as that is easy to use and seems simple enough. In addition to makeing the code simpler using a hash for the file position in readdir brings sysfs in line with other filesystems that have non-trivial directory structures. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r--fs/sysfs/dir.c219
1 files changed, 116 insertions, 103 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 7fdf6a7b7436..0daf255b7bf9 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -22,76 +22,103 @@
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;
48 struct rb_node *parent;
49 93
50 if (sysfs_type(sd) == SYSFS_DIR) 94 if (sysfs_type(sd) == SYSFS_DIR)
51 parent_sd->s_dir.subdirs++; 95 sd->s_parent->s_dir.subdirs++;
52 96
53 p = &parent_sd->s_dir.inode_tree.rb_node; 97 while (*node) {
54 parent = NULL; 98 struct sysfs_dirent *pos;
55 while (*p) { 99 int result;
56 parent = *p; 100
57#define node rb_entry(parent, struct sysfs_dirent, inode_node) 101 pos = to_sysfs_dirent(*node);
58 if (sd->s_ino < node->s_ino) { 102 parent = *node;
59 p = &node->inode_node.rb_left; 103 result = sysfs_sd_compare(sd, pos);
60 } else if (sd->s_ino > node->s_ino) { 104 if (result < 0)
61 p = &node->inode_node.rb_right; 105 node = &pos->s_rb.rb_left;
62 } else { 106 else if (result > 0)
63 printk(KERN_CRIT "sysfs: inserting duplicate inode '%lx'\n", 107 node = &pos->s_rb.rb_right;
64 (unsigned long) sd->s_ino); 108 else
65 BUG(); 109 return -EEXIST;
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 } 110 }
86 rb_link_node(&sd->name_node, parent, p); 111 /* add new node and rebalance the tree */
87 rb_insert_color(&sd->name_node, &parent_sd->s_dir.name_tree); 112 rb_link_node(&sd->s_rb, parent, node);
113 rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
114 return 0;
88} 115}
89 116
90/** 117/**
91 * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list 118 * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree
92 * @sd: sysfs_dirent of interest 119 * @sd: sysfs_dirent of interest
93 * 120 *
94 * Unlink @sd from its sibling list which starts from 121 * Unlink @sd from its sibling rbtree which starts from
95 * sd->s_parent->s_dir.children. 122 * sd->s_parent->s_dir.children.
96 * 123 *
97 * Locking: 124 * Locking:
@@ -102,8 +129,7 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
102 if (sysfs_type(sd) == SYSFS_DIR) 129 if (sysfs_type(sd) == SYSFS_DIR)
103 sd->s_parent->s_dir.subdirs--; 130 sd->s_parent->s_dir.subdirs--;
104 131
105 rb_erase(&sd->inode_node, &sd->s_parent->s_dir.inode_tree); 132 rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
106 rb_erase(&sd->name_node, &sd->s_parent->s_dir.name_tree);
107} 133}
108 134
109/** 135/**
@@ -402,6 +428,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
402int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) 428int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
403{ 429{
404 struct sysfs_inode_attrs *ps_iattr; 430 struct sysfs_inode_attrs *ps_iattr;
431 int ret;
405 432
406 if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) { 433 if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {
407 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 434 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
@@ -410,12 +437,12 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
410 return -EINVAL; 437 return -EINVAL;
411 } 438 }
412 439
413 if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) 440 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); 441 sd->s_parent = sysfs_get(acxt->parent_sd);
417 442
418 sysfs_link_sibling(sd); 443 ret = sysfs_link_sibling(sd);
444 if (ret)
445 return ret;
419 446
420 /* Update timestamps on the parent */ 447 /* Update timestamps on the parent */
421 ps_iattr = acxt->parent_sd->s_iattr; 448 ps_iattr = acxt->parent_sd->s_iattr;
@@ -565,8 +592,8 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
565 const void *ns, 592 const void *ns,
566 const unsigned char *name) 593 const unsigned char *name)
567{ 594{
568 struct rb_node *p = parent_sd->s_dir.name_tree.rb_node; 595 struct rb_node *node = parent_sd->s_dir.children.rb_node;
569 struct sysfs_dirent *found = NULL; 596 unsigned int hash;
570 597
571 if (!!sysfs_ns_type(parent_sd) != !!ns) { 598 if (!!sysfs_ns_type(parent_sd) != !!ns) {
572 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", 599 WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
@@ -575,33 +602,21 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
575 return NULL; 602 return NULL;
576 } 603 }
577 604
578 while (p) { 605 hash = sysfs_name_hash(ns, name);
579 int c; 606 while (node) {
580#define node rb_entry(p, struct sysfs_dirent, name_node) 607 struct sysfs_dirent *sd;
581 c = strcmp(name, node->s_name); 608 int result;
582 if (c < 0) { 609
583 p = node->name_node.rb_left; 610 sd = to_sysfs_dirent(node);
584 } else if (c > 0) { 611 result = sysfs_name_compare(hash, ns, name, sd);
585 p = node->name_node.rb_right; 612 if (result < 0)
586 } else { 613 node = node->rb_left;
587 found = node; 614 else if (result > 0)
588 p = node->name_node.rb_left; 615 node = node->rb_right;
589 } 616 else
590#undef node 617 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 } 618 }
603 619 return NULL;
604 return found;
605} 620}
606 621
607/** 622/**
@@ -804,9 +819,9 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
804 819
805 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); 820 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
806 sysfs_addrm_start(&acxt, dir_sd); 821 sysfs_addrm_start(&acxt, dir_sd);
807 pos = rb_first(&dir_sd->s_dir.inode_tree); 822 pos = rb_first(&dir_sd->s_dir.children);
808 while (pos) { 823 while (pos) {
809 struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node); 824 struct sysfs_dirent *sd = to_sysfs_dirent(pos);
810 pos = rb_next(pos); 825 pos = rb_next(pos);
811 if (sysfs_type(sd) != SYSFS_DIR) 826 if (sysfs_type(sd) != SYSFS_DIR)
812 sysfs_remove_one(&acxt, sd); 827 sysfs_remove_one(&acxt, sd);
@@ -919,38 +934,36 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp)
919} 934}
920 935
921static struct sysfs_dirent *sysfs_dir_pos(const void *ns, 936static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
922 struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) 937 struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos)
923{ 938{
924 if (pos) { 939 if (pos) {
925 int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && 940 int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
926 pos->s_parent == parent_sd && 941 pos->s_parent == parent_sd &&
927 ino == pos->s_ino; 942 hash == pos->s_hash;
928 sysfs_put(pos); 943 sysfs_put(pos);
929 if (!valid) 944 if (!valid)
930 pos = NULL; 945 pos = NULL;
931 } 946 }
932 if (!pos && (ino > 1) && (ino < INT_MAX)) { 947 if (!pos && (hash > 1) && (hash < INT_MAX)) {
933 struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node; 948 struct rb_node *node = parent_sd->s_dir.children.rb_node;
934 while (p) { 949 while (node) {
935#define node rb_entry(p, struct sysfs_dirent, inode_node) 950 pos = to_sysfs_dirent(node);
936 if (ino < node->s_ino) { 951
937 pos = node; 952 if (hash < pos->s_hash)
938 p = node->inode_node.rb_left; 953 node = node->rb_left;
939 } else if (ino > node->s_ino) { 954 else if (hash > pos->s_hash)
940 p = node->inode_node.rb_right; 955 node = node->rb_right;
941 } else { 956 else
942 pos = node;
943 break; 957 break;
944 }
945#undef node
946 } 958 }
947 } 959 }
960 /* Skip over entries in the wrong namespace */
948 while (pos && pos->s_ns != ns) { 961 while (pos && pos->s_ns != ns) {
949 struct rb_node *p = rb_next(&pos->inode_node); 962 struct rb_node *node = rb_next(&pos->s_rb);
950 if (!p) 963 if (!node)
951 pos = NULL; 964 pos = NULL;
952 else 965 else
953 pos = rb_entry(p, struct sysfs_dirent, inode_node); 966 pos = to_sysfs_dirent(node);
954 } 967 }
955 return pos; 968 return pos;
956} 969}
@@ -960,11 +973,11 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
960{ 973{
961 pos = sysfs_dir_pos(ns, parent_sd, ino, pos); 974 pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
962 if (pos) do { 975 if (pos) do {
963 struct rb_node *p = rb_next(&pos->inode_node); 976 struct rb_node *node = rb_next(&pos->s_rb);
964 if (!p) 977 if (!node)
965 pos = NULL; 978 pos = NULL;
966 else 979 else
967 pos = rb_entry(p, struct sysfs_dirent, inode_node); 980 pos = to_sysfs_dirent(node);
968 } while (pos && pos->s_ns != ns); 981 } while (pos && pos->s_ns != ns);
969 return pos; 982 return pos;
970} 983}
@@ -1006,7 +1019,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
1006 len = strlen(name); 1019 len = strlen(name);
1007 ino = pos->s_ino; 1020 ino = pos->s_ino;
1008 type = dt_type(pos); 1021 type = dt_type(pos);
1009 filp->f_pos = ino; 1022 filp->f_pos = pos->s_hash;
1010 filp->private_data = sysfs_get(pos); 1023 filp->private_data = sysfs_get(pos);
1011 1024
1012 mutex_unlock(&sysfs_mutex); 1025 mutex_unlock(&sysfs_mutex);