diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-10-25 23:24:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-10-26 14:42:39 -0400 |
commit | ded85aa86bff953190cb893fceeecaadcab53a80 (patch) | |
tree | abbb2c614d4b1778c2afc34038ac06e0aac14381 | |
parent | ebb9fed2defa55f2ca91c8be582c59612e9940d1 (diff) |
fib_hash: fix rcu sparse and logical errors
While fixing CONFIG_SPARSE_RCU_POINTER errors, I had to fix accesses to
fz->fz_hash for real.
- &fz->fz_hash[fn_hash(f->fn_key, fz)]
+ rcu_dereference(fz->fz_hash) + fn_hash(f->fn_key, fz)
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/fib_hash.c | 36 |
1 files changed, 20 insertions, 16 deletions
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index 43e1c594ce8f..b232375a0b75 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c | |||
@@ -120,11 +120,12 @@ static inline void fn_rebuild_zone(struct fn_zone *fz, | |||
120 | struct fib_node *f; | 120 | struct fib_node *f; |
121 | 121 | ||
122 | hlist_for_each_entry_safe(f, node, n, &old_ht[i], fn_hash) { | 122 | hlist_for_each_entry_safe(f, node, n, &old_ht[i], fn_hash) { |
123 | struct hlist_head __rcu *new_head; | 123 | struct hlist_head *new_head; |
124 | 124 | ||
125 | hlist_del_rcu(&f->fn_hash); | 125 | hlist_del_rcu(&f->fn_hash); |
126 | 126 | ||
127 | new_head = &fz->fz_hash[fn_hash(f->fn_key, fz)]; | 127 | new_head = rcu_dereference_protected(fz->fz_hash, 1) + |
128 | fn_hash(f->fn_key, fz); | ||
128 | hlist_add_head_rcu(&f->fn_hash, new_head); | 129 | hlist_add_head_rcu(&f->fn_hash, new_head); |
129 | } | 130 | } |
130 | } | 131 | } |
@@ -179,8 +180,8 @@ static void fn_rehash_zone(struct fn_zone *fz) | |||
179 | memcpy(&nfz, fz, sizeof(nfz)); | 180 | memcpy(&nfz, fz, sizeof(nfz)); |
180 | 181 | ||
181 | write_seqlock_bh(&fz->fz_lock); | 182 | write_seqlock_bh(&fz->fz_lock); |
182 | old_ht = fz->fz_hash; | 183 | old_ht = rcu_dereference_protected(fz->fz_hash, 1); |
183 | nfz.fz_hash = ht; | 184 | RCU_INIT_POINTER(nfz.fz_hash, ht); |
184 | nfz.fz_hashmask = new_hashmask; | 185 | nfz.fz_hashmask = new_hashmask; |
185 | nfz.fz_divisor = new_divisor; | 186 | nfz.fz_divisor = new_divisor; |
186 | fn_rebuild_zone(&nfz, old_ht, old_divisor); | 187 | fn_rebuild_zone(&nfz, old_ht, old_divisor); |
@@ -236,7 +237,7 @@ fn_new_zone(struct fn_hash *table, int z) | |||
236 | seqlock_init(&fz->fz_lock); | 237 | seqlock_init(&fz->fz_lock); |
237 | fz->fz_divisor = z ? EMBEDDED_HASH_SIZE : 1; | 238 | fz->fz_divisor = z ? EMBEDDED_HASH_SIZE : 1; |
238 | fz->fz_hashmask = fz->fz_divisor - 1; | 239 | fz->fz_hashmask = fz->fz_divisor - 1; |
239 | fz->fz_hash = fz->fz_embedded_hash; | 240 | RCU_INIT_POINTER(fz->fz_hash, fz->fz_embedded_hash); |
240 | fz->fz_order = z; | 241 | fz->fz_order = z; |
241 | fz->fz_revorder = 32 - z; | 242 | fz->fz_revorder = 32 - z; |
242 | fz->fz_mask = inet_make_mask(z); | 243 | fz->fz_mask = inet_make_mask(z); |
@@ -272,7 +273,7 @@ int fib_table_lookup(struct fib_table *tb, | |||
272 | for (fz = rcu_dereference(t->fn_zone_list); | 273 | for (fz = rcu_dereference(t->fn_zone_list); |
273 | fz != NULL; | 274 | fz != NULL; |
274 | fz = rcu_dereference(fz->fz_next)) { | 275 | fz = rcu_dereference(fz->fz_next)) { |
275 | struct hlist_head __rcu *head; | 276 | struct hlist_head *head; |
276 | struct hlist_node *node; | 277 | struct hlist_node *node; |
277 | struct fib_node *f; | 278 | struct fib_node *f; |
278 | __be32 k; | 279 | __be32 k; |
@@ -282,7 +283,7 @@ int fib_table_lookup(struct fib_table *tb, | |||
282 | seq = read_seqbegin(&fz->fz_lock); | 283 | seq = read_seqbegin(&fz->fz_lock); |
283 | k = fz_key(flp->fl4_dst, fz); | 284 | k = fz_key(flp->fl4_dst, fz); |
284 | 285 | ||
285 | head = &fz->fz_hash[fn_hash(k, fz)]; | 286 | head = rcu_dereference(fz->fz_hash) + fn_hash(k, fz); |
286 | hlist_for_each_entry_rcu(f, node, head, fn_hash) { | 287 | hlist_for_each_entry_rcu(f, node, head, fn_hash) { |
287 | if (f->fn_key != k) | 288 | if (f->fn_key != k) |
288 | continue; | 289 | continue; |
@@ -311,6 +312,7 @@ void fib_table_select_default(struct fib_table *tb, | |||
311 | struct fib_info *last_resort; | 312 | struct fib_info *last_resort; |
312 | struct fn_hash *t = (struct fn_hash *)tb->tb_data; | 313 | struct fn_hash *t = (struct fn_hash *)tb->tb_data; |
313 | struct fn_zone *fz = t->fn_zones[0]; | 314 | struct fn_zone *fz = t->fn_zones[0]; |
315 | struct hlist_head *head; | ||
314 | 316 | ||
315 | if (fz == NULL) | 317 | if (fz == NULL) |
316 | return; | 318 | return; |
@@ -320,7 +322,8 @@ void fib_table_select_default(struct fib_table *tb, | |||
320 | order = -1; | 322 | order = -1; |
321 | 323 | ||
322 | rcu_read_lock(); | 324 | rcu_read_lock(); |
323 | hlist_for_each_entry_rcu(f, node, &fz->fz_hash[0], fn_hash) { | 325 | head = rcu_dereference(fz->fz_hash); |
326 | hlist_for_each_entry_rcu(f, node, head, fn_hash) { | ||
324 | struct fib_alias *fa; | 327 | struct fib_alias *fa; |
325 | 328 | ||
326 | list_for_each_entry_rcu(fa, &f->fn_alias, fa_list) { | 329 | list_for_each_entry_rcu(fa, &f->fn_alias, fa_list) { |
@@ -374,7 +377,7 @@ out: | |||
374 | /* Insert node F to FZ. */ | 377 | /* Insert node F to FZ. */ |
375 | static inline void fib_insert_node(struct fn_zone *fz, struct fib_node *f) | 378 | static inline void fib_insert_node(struct fn_zone *fz, struct fib_node *f) |
376 | { | 379 | { |
377 | struct hlist_head *head = &fz->fz_hash[fn_hash(f->fn_key, fz)]; | 380 | struct hlist_head *head = rtnl_dereference(fz->fz_hash) + fn_hash(f->fn_key, fz); |
378 | 381 | ||
379 | hlist_add_head_rcu(&f->fn_hash, head); | 382 | hlist_add_head_rcu(&f->fn_hash, head); |
380 | } | 383 | } |
@@ -382,7 +385,7 @@ static inline void fib_insert_node(struct fn_zone *fz, struct fib_node *f) | |||
382 | /* Return the node in FZ matching KEY. */ | 385 | /* Return the node in FZ matching KEY. */ |
383 | static struct fib_node *fib_find_node(struct fn_zone *fz, __be32 key) | 386 | static struct fib_node *fib_find_node(struct fn_zone *fz, __be32 key) |
384 | { | 387 | { |
385 | struct hlist_head *head = &fz->fz_hash[fn_hash(key, fz)]; | 388 | struct hlist_head *head = rtnl_dereference(fz->fz_hash) + fn_hash(key, fz); |
386 | struct hlist_node *node; | 389 | struct hlist_node *node; |
387 | struct fib_node *f; | 390 | struct fib_node *f; |
388 | 391 | ||
@@ -662,7 +665,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg) | |||
662 | 665 | ||
663 | static int fn_flush_list(struct fn_zone *fz, int idx) | 666 | static int fn_flush_list(struct fn_zone *fz, int idx) |
664 | { | 667 | { |
665 | struct hlist_head *head = &fz->fz_hash[idx]; | 668 | struct hlist_head *head = rtnl_dereference(fz->fz_hash) + idx; |
666 | struct hlist_node *node, *n; | 669 | struct hlist_node *node, *n; |
667 | struct fib_node *f; | 670 | struct fib_node *f; |
668 | int found = 0; | 671 | int found = 0; |
@@ -761,14 +764,15 @@ fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb, | |||
761 | struct fn_zone *fz) | 764 | struct fn_zone *fz) |
762 | { | 765 | { |
763 | int h, s_h; | 766 | int h, s_h; |
767 | struct hlist_head *head = rcu_dereference(fz->fz_hash); | ||
764 | 768 | ||
765 | if (fz->fz_hash == NULL) | 769 | if (head == NULL) |
766 | return skb->len; | 770 | return skb->len; |
767 | s_h = cb->args[3]; | 771 | s_h = cb->args[3]; |
768 | for (h = s_h; h < fz->fz_divisor; h++) { | 772 | for (h = s_h; h < fz->fz_divisor; h++) { |
769 | if (hlist_empty(&fz->fz_hash[h])) | 773 | if (hlist_empty(head + h)) |
770 | continue; | 774 | continue; |
771 | if (fn_hash_dump_bucket(skb, cb, tb, fz, &fz->fz_hash[h]) < 0) { | 775 | if (fn_hash_dump_bucket(skb, cb, tb, fz, head + h) < 0) { |
772 | cb->args[3] = h; | 776 | cb->args[3] = h; |
773 | return -1; | 777 | return -1; |
774 | } | 778 | } |
@@ -872,7 +876,7 @@ static struct fib_alias *fib_get_first(struct seq_file *seq) | |||
872 | if (!iter->zone->fz_nent) | 876 | if (!iter->zone->fz_nent) |
873 | continue; | 877 | continue; |
874 | 878 | ||
875 | iter->hash_head = iter->zone->fz_hash; | 879 | iter->hash_head = rcu_dereference(iter->zone->fz_hash); |
876 | maxslot = iter->zone->fz_divisor; | 880 | maxslot = iter->zone->fz_divisor; |
877 | 881 | ||
878 | for (iter->bucket = 0; iter->bucket < maxslot; | 882 | for (iter->bucket = 0; iter->bucket < maxslot; |
@@ -957,7 +961,7 @@ static struct fib_alias *fib_get_next(struct seq_file *seq) | |||
957 | goto out; | 961 | goto out; |
958 | 962 | ||
959 | iter->bucket = 0; | 963 | iter->bucket = 0; |
960 | iter->hash_head = iter->zone->fz_hash; | 964 | iter->hash_head = rcu_dereference(iter->zone->fz_hash); |
961 | 965 | ||
962 | hlist_for_each_entry(fn, node, iter->hash_head, fn_hash) { | 966 | hlist_for_each_entry(fn, node, iter->hash_head, fn_hash) { |
963 | list_for_each_entry(fa, &fn->fn_alias, fa_list) { | 967 | list_for_each_entry(fa, &fn->fn_alias, fa_list) { |