diff options
author | Marek Lindner <lindner_marek@yahoo.de> | 2011-01-19 15:01:40 -0500 |
---|---|---|
committer | Marek Lindner <lindner_marek@yahoo.de> | 2011-03-05 06:49:58 -0500 |
commit | fb778ea173fcd58b8fc3d75c674f07fab187b55f (patch) | |
tree | b14cfc99b7ca61ddcb49cc56c9a8e2822675debc /net/batman-adv/hash.h | |
parent | a775eb847ae66211577d4fd2c46749b77c9993c9 (diff) |
batman-adv: protect each hash row with rcu locks
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Diffstat (limited to 'net/batman-adv/hash.h')
-rw-r--r-- | net/batman-adv/hash.h | 73 |
1 files changed, 49 insertions, 24 deletions
diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index eae24402fd0a..3c48c6bb1acd 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h | |||
@@ -39,10 +39,12 @@ typedef void (*hashdata_free_cb)(void *, void *); | |||
39 | struct element_t { | 39 | struct element_t { |
40 | void *data; /* pointer to the data */ | 40 | void *data; /* pointer to the data */ |
41 | struct hlist_node hlist; /* bucket list pointer */ | 41 | struct hlist_node hlist; /* bucket list pointer */ |
42 | struct rcu_head rcu; | ||
42 | }; | 43 | }; |
43 | 44 | ||
44 | struct hashtable_t { | 45 | struct hashtable_t { |
45 | struct hlist_head *table; /* the hashtable itself, with the buckets */ | 46 | struct hlist_head *table; /* the hashtable itself with the buckets */ |
47 | spinlock_t *list_locks; /* spinlock for each hash list entry */ | ||
46 | int size; /* size of hashtable */ | 48 | int size; /* size of hashtable */ |
47 | }; | 49 | }; |
48 | 50 | ||
@@ -52,6 +54,8 @@ struct hashtable_t *hash_new(int size); | |||
52 | /* free only the hashtable and the hash itself. */ | 54 | /* free only the hashtable and the hash itself. */ |
53 | void hash_destroy(struct hashtable_t *hash); | 55 | void hash_destroy(struct hashtable_t *hash); |
54 | 56 | ||
57 | void bucket_free_rcu(struct rcu_head *rcu); | ||
58 | |||
55 | /* remove the hash structure. if hashdata_free_cb != NULL, this function will be | 59 | /* remove the hash structure. if hashdata_free_cb != NULL, this function will be |
56 | * called to remove the elements inside of the hash. if you don't remove the | 60 | * called to remove the elements inside of the hash. if you don't remove the |
57 | * elements, memory might be leaked. */ | 61 | * elements, memory might be leaked. */ |
@@ -61,19 +65,22 @@ static inline void hash_delete(struct hashtable_t *hash, | |||
61 | struct hlist_head *head; | 65 | struct hlist_head *head; |
62 | struct hlist_node *walk, *safe; | 66 | struct hlist_node *walk, *safe; |
63 | struct element_t *bucket; | 67 | struct element_t *bucket; |
68 | spinlock_t *list_lock; /* spinlock to protect write access */ | ||
64 | int i; | 69 | int i; |
65 | 70 | ||
66 | for (i = 0; i < hash->size; i++) { | 71 | for (i = 0; i < hash->size; i++) { |
67 | head = &hash->table[i]; | 72 | head = &hash->table[i]; |
73 | list_lock = &hash->list_locks[i]; | ||
68 | 74 | ||
69 | hlist_for_each_safe(walk, safe, head) { | 75 | spin_lock_bh(list_lock); |
70 | bucket = hlist_entry(walk, struct element_t, hlist); | 76 | hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { |
71 | if (free_cb) | 77 | if (free_cb) |
72 | free_cb(bucket->data, arg); | 78 | free_cb(bucket->data, arg); |
73 | 79 | ||
74 | hlist_del(walk); | 80 | hlist_del_rcu(walk); |
75 | kfree(bucket); | 81 | call_rcu(&bucket->rcu, bucket_free_rcu); |
76 | } | 82 | } |
83 | spin_unlock_bh(list_lock); | ||
77 | } | 84 | } |
78 | 85 | ||
79 | hash_destroy(hash); | 86 | hash_destroy(hash); |
@@ -88,29 +95,39 @@ static inline int hash_add(struct hashtable_t *hash, | |||
88 | struct hlist_head *head; | 95 | struct hlist_head *head; |
89 | struct hlist_node *walk, *safe; | 96 | struct hlist_node *walk, *safe; |
90 | struct element_t *bucket; | 97 | struct element_t *bucket; |
98 | spinlock_t *list_lock; /* spinlock to protect write access */ | ||
91 | 99 | ||
92 | if (!hash) | 100 | if (!hash) |
93 | return -1; | 101 | goto err; |
94 | 102 | ||
95 | index = choose(data, hash->size); | 103 | index = choose(data, hash->size); |
96 | head = &hash->table[index]; | 104 | head = &hash->table[index]; |
105 | list_lock = &hash->list_locks[index]; | ||
97 | 106 | ||
98 | hlist_for_each_safe(walk, safe, head) { | 107 | rcu_read_lock(); |
99 | bucket = hlist_entry(walk, struct element_t, hlist); | 108 | hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { |
100 | if (compare(bucket->data, data)) | 109 | if (compare(bucket->data, data)) |
101 | return -1; | 110 | goto err_unlock; |
102 | } | 111 | } |
112 | rcu_read_unlock(); | ||
103 | 113 | ||
104 | /* no duplicate found in list, add new element */ | 114 | /* no duplicate found in list, add new element */ |
105 | bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); | 115 | bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); |
106 | |||
107 | if (!bucket) | 116 | if (!bucket) |
108 | return -1; | 117 | goto err; |
109 | 118 | ||
110 | bucket->data = data; | 119 | bucket->data = data; |
111 | hlist_add_head(&bucket->hlist, head); | 120 | |
121 | spin_lock_bh(list_lock); | ||
122 | hlist_add_head_rcu(&bucket->hlist, head); | ||
123 | spin_unlock_bh(list_lock); | ||
112 | 124 | ||
113 | return 0; | 125 | return 0; |
126 | |||
127 | err_unlock: | ||
128 | rcu_read_unlock(); | ||
129 | err: | ||
130 | return -1; | ||
114 | } | 131 | } |
115 | 132 | ||
116 | /* removes data from hash, if found. returns pointer do data on success, so you | 133 | /* removes data from hash, if found. returns pointer do data on success, so you |
@@ -125,25 +142,31 @@ static inline void *hash_remove(struct hashtable_t *hash, | |||
125 | struct hlist_node *walk; | 142 | struct hlist_node *walk; |
126 | struct element_t *bucket; | 143 | struct element_t *bucket; |
127 | struct hlist_head *head; | 144 | struct hlist_head *head; |
128 | void *data_save; | 145 | void *data_save = NULL; |
129 | 146 | ||
130 | index = choose(data, hash->size); | 147 | index = choose(data, hash->size); |
131 | head = &hash->table[index]; | 148 | head = &hash->table[index]; |
132 | 149 | ||
150 | spin_lock_bh(&hash->list_locks[index]); | ||
133 | hlist_for_each_entry(bucket, walk, head, hlist) { | 151 | hlist_for_each_entry(bucket, walk, head, hlist) { |
134 | if (compare(bucket->data, data)) { | 152 | if (compare(bucket->data, data)) { |
135 | data_save = bucket->data; | 153 | data_save = bucket->data; |
136 | hlist_del(walk); | 154 | hlist_del_rcu(walk); |
137 | kfree(bucket); | 155 | call_rcu(&bucket->rcu, bucket_free_rcu); |
138 | return data_save; | 156 | break; |
139 | } | 157 | } |
140 | } | 158 | } |
159 | spin_unlock_bh(&hash->list_locks[index]); | ||
141 | 160 | ||
142 | return NULL; | 161 | return data_save; |
143 | } | 162 | } |
144 | 163 | ||
145 | /* finds data, based on the key in keydata. returns the found data on success, | 164 | /** |
146 | * or NULL on error */ | 165 | * finds data, based on the key in keydata. returns the found data on success, |
166 | * or NULL on error | ||
167 | * | ||
168 | * caller must lock with rcu_read_lock() / rcu_read_unlock() | ||
169 | **/ | ||
147 | static inline void *hash_find(struct hashtable_t *hash, | 170 | static inline void *hash_find(struct hashtable_t *hash, |
148 | hashdata_compare_cb compare, | 171 | hashdata_compare_cb compare, |
149 | hashdata_choose_cb choose, void *keydata) | 172 | hashdata_choose_cb choose, void *keydata) |
@@ -152,6 +175,7 @@ static inline void *hash_find(struct hashtable_t *hash, | |||
152 | struct hlist_head *head; | 175 | struct hlist_head *head; |
153 | struct hlist_node *walk; | 176 | struct hlist_node *walk; |
154 | struct element_t *bucket; | 177 | struct element_t *bucket; |
178 | void *bucket_data = NULL; | ||
155 | 179 | ||
156 | if (!hash) | 180 | if (!hash) |
157 | return NULL; | 181 | return NULL; |
@@ -159,13 +183,14 @@ static inline void *hash_find(struct hashtable_t *hash, | |||
159 | index = choose(keydata , hash->size); | 183 | index = choose(keydata , hash->size); |
160 | head = &hash->table[index]; | 184 | head = &hash->table[index]; |
161 | 185 | ||
162 | hlist_for_each(walk, head) { | 186 | hlist_for_each_entry(bucket, walk, head, hlist) { |
163 | bucket = hlist_entry(walk, struct element_t, hlist); | 187 | if (compare(bucket->data, keydata)) { |
164 | if (compare(bucket->data, keydata)) | 188 | bucket_data = bucket->data; |
165 | return bucket->data; | 189 | break; |
190 | } | ||
166 | } | 191 | } |
167 | 192 | ||
168 | return NULL; | 193 | return bucket_data; |
169 | } | 194 | } |
170 | 195 | ||
171 | #endif /* _NET_BATMAN_ADV_HASH_H_ */ | 196 | #endif /* _NET_BATMAN_ADV_HASH_H_ */ |