diff options
author | Simon Wunderlich <siwu@hrz.tu-chemnitz.de> | 2011-01-19 15:01:43 -0500 |
---|---|---|
committer | Marek Lindner <lindner_marek@yahoo.de> | 2011-03-05 06:50:01 -0500 |
commit | a4c135c561106c397bae33455acfca4aa8065a30 (patch) | |
tree | 09613dd5443fb6abfdab5b851eccdd610d1b0783 /net/batman-adv | |
parent | 2ae2daf6c3f23364862a7d4f2ca79eab041b701b (diff) |
batman-adv: protect bonding with rcu locks
bonding / alternating candidates need to be secured by rcu locks
as well. This patch therefore converts the bonding list
from a plain pointer list to a rcu securable lists and references
the bonding candidates.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Diffstat (limited to 'net/batman-adv')
-rw-r--r-- | net/batman-adv/hard-interface.c | 2 | ||||
-rw-r--r-- | net/batman-adv/originator.c | 25 | ||||
-rw-r--r-- | net/batman-adv/originator.h | 1 | ||||
-rw-r--r-- | net/batman-adv/routing.c | 313 | ||||
-rw-r--r-- | net/batman-adv/routing.h | 6 | ||||
-rw-r--r-- | net/batman-adv/types.h | 9 | ||||
-rw-r--r-- | net/batman-adv/unicast.c | 2 |
7 files changed, 195 insertions, 163 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index f2131f45aa9b..e2b001ad45c4 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c | |||
@@ -271,7 +271,7 @@ static void hardif_activate_interface(struct batman_if *batman_if) | |||
271 | static void hardif_deactivate_interface(struct batman_if *batman_if) | 271 | static void hardif_deactivate_interface(struct batman_if *batman_if) |
272 | { | 272 | { |
273 | if ((batman_if->if_status != IF_ACTIVE) && | 273 | if ((batman_if->if_status != IF_ACTIVE) && |
274 | (batman_if->if_status != IF_TO_BE_ACTIVATED)) | 274 | (batman_if->if_status != IF_TO_BE_ACTIVATED)) |
275 | return; | 275 | return; |
276 | 276 | ||
277 | batman_if->if_status = IF_INACTIVE; | 277 | batman_if->if_status = IF_INACTIVE; |
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 71dfc24e961b..a85eadca6b2d 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c | |||
@@ -75,6 +75,14 @@ static void neigh_node_free_rcu(struct rcu_head *rcu) | |||
75 | kref_put(&neigh_node->refcount, neigh_node_free_ref); | 75 | kref_put(&neigh_node->refcount, neigh_node_free_ref); |
76 | } | 76 | } |
77 | 77 | ||
78 | void neigh_node_free_rcu_bond(struct rcu_head *rcu) | ||
79 | { | ||
80 | struct neigh_node *neigh_node; | ||
81 | |||
82 | neigh_node = container_of(rcu, struct neigh_node, rcu_bond); | ||
83 | kref_put(&neigh_node->refcount, neigh_node_free_ref); | ||
84 | } | ||
85 | |||
78 | struct neigh_node *create_neighbor(struct orig_node *orig_node, | 86 | struct neigh_node *create_neighbor(struct orig_node *orig_node, |
79 | struct orig_node *orig_neigh_node, | 87 | struct orig_node *orig_neigh_node, |
80 | uint8_t *neigh, | 88 | uint8_t *neigh, |
@@ -91,6 +99,7 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node, | |||
91 | return NULL; | 99 | return NULL; |
92 | 100 | ||
93 | INIT_HLIST_NODE(&neigh_node->list); | 101 | INIT_HLIST_NODE(&neigh_node->list); |
102 | INIT_LIST_HEAD(&neigh_node->bonding_list); | ||
94 | 103 | ||
95 | memcpy(neigh_node->addr, neigh, ETH_ALEN); | 104 | memcpy(neigh_node->addr, neigh, ETH_ALEN); |
96 | neigh_node->orig_node = orig_neigh_node; | 105 | neigh_node->orig_node = orig_neigh_node; |
@@ -106,13 +115,20 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node, | |||
106 | void orig_node_free_ref(struct kref *refcount) | 115 | void orig_node_free_ref(struct kref *refcount) |
107 | { | 116 | { |
108 | struct hlist_node *node, *node_tmp; | 117 | struct hlist_node *node, *node_tmp; |
109 | struct neigh_node *neigh_node; | 118 | struct neigh_node *neigh_node, *tmp_neigh_node; |
110 | struct orig_node *orig_node; | 119 | struct orig_node *orig_node; |
111 | 120 | ||
112 | orig_node = container_of(refcount, struct orig_node, refcount); | 121 | orig_node = container_of(refcount, struct orig_node, refcount); |
113 | 122 | ||
114 | spin_lock_bh(&orig_node->neigh_list_lock); | 123 | spin_lock_bh(&orig_node->neigh_list_lock); |
115 | 124 | ||
125 | /* for all bonding members ... */ | ||
126 | list_for_each_entry_safe(neigh_node, tmp_neigh_node, | ||
127 | &orig_node->bond_list, bonding_list) { | ||
128 | list_del_rcu(&neigh_node->bonding_list); | ||
129 | call_rcu(&neigh_node->rcu_bond, neigh_node_free_rcu_bond); | ||
130 | } | ||
131 | |||
116 | /* for all neighbors towards this originator ... */ | 132 | /* for all neighbors towards this originator ... */ |
117 | hlist_for_each_entry_safe(neigh_node, node, node_tmp, | 133 | hlist_for_each_entry_safe(neigh_node, node, node_tmp, |
118 | &orig_node->neigh_list, list) { | 134 | &orig_node->neigh_list, list) { |
@@ -207,6 +223,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) | |||
207 | return NULL; | 223 | return NULL; |
208 | 224 | ||
209 | INIT_HLIST_HEAD(&orig_node->neigh_list); | 225 | INIT_HLIST_HEAD(&orig_node->neigh_list); |
226 | INIT_LIST_HEAD(&orig_node->bond_list); | ||
210 | spin_lock_init(&orig_node->ogm_cnt_lock); | 227 | spin_lock_init(&orig_node->ogm_cnt_lock); |
211 | spin_lock_init(&orig_node->neigh_list_lock); | 228 | spin_lock_init(&orig_node->neigh_list_lock); |
212 | kref_init(&orig_node->refcount); | 229 | kref_init(&orig_node->refcount); |
@@ -220,6 +237,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) | |||
220 | orig_node->batman_seqno_reset = jiffies - 1 | 237 | orig_node->batman_seqno_reset = jiffies - 1 |
221 | - msecs_to_jiffies(RESET_PROTECTION_MS); | 238 | - msecs_to_jiffies(RESET_PROTECTION_MS); |
222 | 239 | ||
240 | atomic_set(&orig_node->bond_candidates, 0); | ||
241 | |||
223 | size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS; | 242 | size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS; |
224 | 243 | ||
225 | orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); | 244 | orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); |
@@ -295,6 +314,7 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv, | |||
295 | neigh_purged = true; | 314 | neigh_purged = true; |
296 | 315 | ||
297 | hlist_del_rcu(&neigh_node->list); | 316 | hlist_del_rcu(&neigh_node->list); |
317 | bonding_candidate_del(orig_node, neigh_node); | ||
298 | call_rcu(&neigh_node->rcu, neigh_node_free_rcu); | 318 | call_rcu(&neigh_node->rcu, neigh_node_free_rcu); |
299 | } else { | 319 | } else { |
300 | if ((!*best_neigh_node) || | 320 | if ((!*best_neigh_node) || |
@@ -326,9 +346,6 @@ static bool purge_orig_node(struct bat_priv *bat_priv, | |||
326 | best_neigh_node, | 346 | best_neigh_node, |
327 | orig_node->hna_buff, | 347 | orig_node->hna_buff, |
328 | orig_node->hna_buff_len); | 348 | orig_node->hna_buff_len); |
329 | /* update bonding candidates, we could have lost | ||
330 | * some candidates. */ | ||
331 | update_bonding_candidates(orig_node); | ||
332 | } | 349 | } |
333 | } | 350 | } |
334 | 351 | ||
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index edc64dc33b12..360dfd19a32f 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h | |||
@@ -26,6 +26,7 @@ int originator_init(struct bat_priv *bat_priv); | |||
26 | void originator_free(struct bat_priv *bat_priv); | 26 | void originator_free(struct bat_priv *bat_priv); |
27 | void purge_orig_ref(struct bat_priv *bat_priv); | 27 | void purge_orig_ref(struct bat_priv *bat_priv); |
28 | void orig_node_free_ref(struct kref *refcount); | 28 | void orig_node_free_ref(struct kref *refcount); |
29 | void neigh_node_free_rcu_bond(struct rcu_head *rcu); | ||
29 | struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); | 30 | struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); |
30 | struct neigh_node *create_neighbor(struct orig_node *orig_node, | 31 | struct neigh_node *create_neighbor(struct orig_node *orig_node, |
31 | struct orig_node *orig_neigh_node, | 32 | struct orig_node *orig_neigh_node, |
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 7627ebe50c4b..1ad14da20839 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c | |||
@@ -271,6 +271,101 @@ out: | |||
271 | return ret; | 271 | return ret; |
272 | } | 272 | } |
273 | 273 | ||
274 | /* caller must hold the neigh_list_lock */ | ||
275 | void bonding_candidate_del(struct orig_node *orig_node, | ||
276 | struct neigh_node *neigh_node) | ||
277 | { | ||
278 | /* this neighbor is not part of our candidate list */ | ||
279 | if (list_empty(&neigh_node->bonding_list)) | ||
280 | goto out; | ||
281 | |||
282 | list_del_rcu(&neigh_node->bonding_list); | ||
283 | call_rcu(&neigh_node->rcu_bond, neigh_node_free_rcu_bond); | ||
284 | INIT_LIST_HEAD(&neigh_node->bonding_list); | ||
285 | atomic_dec(&orig_node->bond_candidates); | ||
286 | |||
287 | out: | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | static void bonding_candidate_add(struct orig_node *orig_node, | ||
292 | struct neigh_node *neigh_node) | ||
293 | { | ||
294 | struct hlist_node *node; | ||
295 | struct neigh_node *tmp_neigh_node; | ||
296 | uint8_t best_tq, interference_candidate = 0; | ||
297 | |||
298 | spin_lock_bh(&orig_node->neigh_list_lock); | ||
299 | |||
300 | /* only consider if it has the same primary address ... */ | ||
301 | if (!compare_orig(orig_node->orig, | ||
302 | neigh_node->orig_node->primary_addr)) | ||
303 | goto candidate_del; | ||
304 | |||
305 | if (!orig_node->router) | ||
306 | goto candidate_del; | ||
307 | |||
308 | best_tq = orig_node->router->tq_avg; | ||
309 | |||
310 | /* ... and is good enough to be considered */ | ||
311 | if (neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) | ||
312 | goto candidate_del; | ||
313 | |||
314 | /** | ||
315 | * check if we have another candidate with the same mac address or | ||
316 | * interface. If we do, we won't select this candidate because of | ||
317 | * possible interference. | ||
318 | */ | ||
319 | hlist_for_each_entry_rcu(tmp_neigh_node, node, | ||
320 | &orig_node->neigh_list, list) { | ||
321 | |||
322 | if (tmp_neigh_node == neigh_node) | ||
323 | continue; | ||
324 | |||
325 | /* we only care if the other candidate is even | ||
326 | * considered as candidate. */ | ||
327 | if (list_empty(&tmp_neigh_node->bonding_list)) | ||
328 | continue; | ||
329 | |||
330 | if ((neigh_node->if_incoming == tmp_neigh_node->if_incoming) || | ||
331 | (compare_orig(neigh_node->addr, tmp_neigh_node->addr))) { | ||
332 | interference_candidate = 1; | ||
333 | break; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | /* don't care further if it is an interference candidate */ | ||
338 | if (interference_candidate) | ||
339 | goto candidate_del; | ||
340 | |||
341 | /* this neighbor already is part of our candidate list */ | ||
342 | if (!list_empty(&neigh_node->bonding_list)) | ||
343 | goto out; | ||
344 | |||
345 | list_add_rcu(&neigh_node->bonding_list, &orig_node->bond_list); | ||
346 | kref_get(&neigh_node->refcount); | ||
347 | atomic_inc(&orig_node->bond_candidates); | ||
348 | goto out; | ||
349 | |||
350 | candidate_del: | ||
351 | bonding_candidate_del(orig_node, neigh_node); | ||
352 | |||
353 | out: | ||
354 | spin_unlock_bh(&orig_node->neigh_list_lock); | ||
355 | return; | ||
356 | } | ||
357 | |||
358 | /* copy primary address for bonding */ | ||
359 | static void bonding_save_primary(struct orig_node *orig_node, | ||
360 | struct orig_node *orig_neigh_node, | ||
361 | struct batman_packet *batman_packet) | ||
362 | { | ||
363 | if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) | ||
364 | return; | ||
365 | |||
366 | memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN); | ||
367 | } | ||
368 | |||
274 | static void update_orig(struct bat_priv *bat_priv, | 369 | static void update_orig(struct bat_priv *bat_priv, |
275 | struct orig_node *orig_node, | 370 | struct orig_node *orig_node, |
276 | struct ethhdr *ethhdr, | 371 | struct ethhdr *ethhdr, |
@@ -339,6 +434,8 @@ static void update_orig(struct bat_priv *bat_priv, | |||
339 | neigh_node->last_ttl = batman_packet->ttl; | 434 | neigh_node->last_ttl = batman_packet->ttl; |
340 | } | 435 | } |
341 | 436 | ||
437 | bonding_candidate_add(orig_node, neigh_node); | ||
438 | |||
342 | tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? | 439 | tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? |
343 | batman_packet->num_hna * ETH_ALEN : hna_buff_len); | 440 | batman_packet->num_hna * ETH_ALEN : hna_buff_len); |
344 | 441 | ||
@@ -497,123 +594,10 @@ err: | |||
497 | return -1; | 594 | return -1; |
498 | } | 595 | } |
499 | 596 | ||
500 | /* copy primary address for bonding */ | ||
501 | static void mark_bonding_address(struct orig_node *orig_node, | ||
502 | struct orig_node *orig_neigh_node, | ||
503 | struct batman_packet *batman_packet) | ||
504 | |||
505 | { | ||
506 | if (batman_packet->flags & PRIMARIES_FIRST_HOP) | ||
507 | memcpy(orig_neigh_node->primary_addr, | ||
508 | orig_node->orig, ETH_ALEN); | ||
509 | |||
510 | return; | ||
511 | } | ||
512 | |||
513 | /* mark possible bond.candidates in the neighbor list */ | ||
514 | void update_bonding_candidates(struct orig_node *orig_node) | ||
515 | { | ||
516 | int candidates; | ||
517 | int interference_candidate; | ||
518 | int best_tq; | ||
519 | struct hlist_node *node, *node2; | ||
520 | struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; | ||
521 | struct neigh_node *first_candidate, *last_candidate; | ||
522 | |||
523 | /* update the candidates for this originator */ | ||
524 | if (!orig_node->router) { | ||
525 | orig_node->bond.candidates = 0; | ||
526 | return; | ||
527 | } | ||
528 | |||
529 | best_tq = orig_node->router->tq_avg; | ||
530 | |||
531 | /* update bond.candidates */ | ||
532 | |||
533 | candidates = 0; | ||
534 | |||
535 | /* mark other nodes which also received "PRIMARIES FIRST HOP" packets | ||
536 | * as "bonding partner" */ | ||
537 | |||
538 | /* first, zero the list */ | ||
539 | rcu_read_lock(); | ||
540 | hlist_for_each_entry_rcu(tmp_neigh_node, node, | ||
541 | &orig_node->neigh_list, list) { | ||
542 | tmp_neigh_node->next_bond_candidate = NULL; | ||
543 | } | ||
544 | rcu_read_unlock(); | ||
545 | |||
546 | first_candidate = NULL; | ||
547 | last_candidate = NULL; | ||
548 | |||
549 | rcu_read_lock(); | ||
550 | hlist_for_each_entry_rcu(tmp_neigh_node, node, | ||
551 | &orig_node->neigh_list, list) { | ||
552 | |||
553 | /* only consider if it has the same primary address ... */ | ||
554 | if (memcmp(orig_node->orig, | ||
555 | tmp_neigh_node->orig_node->primary_addr, | ||
556 | ETH_ALEN) != 0) | ||
557 | continue; | ||
558 | |||
559 | /* ... and is good enough to be considered */ | ||
560 | if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) | ||
561 | continue; | ||
562 | |||
563 | /* check if we have another candidate with the same | ||
564 | * mac address or interface. If we do, we won't | ||
565 | * select this candidate because of possible interference. */ | ||
566 | |||
567 | interference_candidate = 0; | ||
568 | hlist_for_each_entry_rcu(tmp_neigh_node2, node2, | ||
569 | &orig_node->neigh_list, list) { | ||
570 | |||
571 | if (tmp_neigh_node2 == tmp_neigh_node) | ||
572 | continue; | ||
573 | |||
574 | /* we only care if the other candidate is even | ||
575 | * considered as candidate. */ | ||
576 | if (!tmp_neigh_node2->next_bond_candidate) | ||
577 | continue; | ||
578 | |||
579 | |||
580 | if ((tmp_neigh_node->if_incoming == | ||
581 | tmp_neigh_node2->if_incoming) | ||
582 | || (memcmp(tmp_neigh_node->addr, | ||
583 | tmp_neigh_node2->addr, ETH_ALEN) == 0)) { | ||
584 | |||
585 | interference_candidate = 1; | ||
586 | break; | ||
587 | } | ||
588 | } | ||
589 | /* don't care further if it is an interference candidate */ | ||
590 | if (interference_candidate) | ||
591 | continue; | ||
592 | |||
593 | if (!first_candidate) { | ||
594 | first_candidate = tmp_neigh_node; | ||
595 | tmp_neigh_node->next_bond_candidate = first_candidate; | ||
596 | } else | ||
597 | tmp_neigh_node->next_bond_candidate = last_candidate; | ||
598 | |||
599 | last_candidate = tmp_neigh_node; | ||
600 | |||
601 | candidates++; | ||
602 | } | ||
603 | rcu_read_unlock(); | ||
604 | |||
605 | if (candidates > 0) { | ||
606 | first_candidate->next_bond_candidate = last_candidate; | ||
607 | orig_node->bond.selected = first_candidate; | ||
608 | } | ||
609 | |||
610 | orig_node->bond.candidates = candidates; | ||
611 | } | ||
612 | |||
613 | void receive_bat_packet(struct ethhdr *ethhdr, | 597 | void receive_bat_packet(struct ethhdr *ethhdr, |
614 | struct batman_packet *batman_packet, | 598 | struct batman_packet *batman_packet, |
615 | unsigned char *hna_buff, int hna_buff_len, | 599 | unsigned char *hna_buff, int hna_buff_len, |
616 | struct batman_if *if_incoming) | 600 | struct batman_if *if_incoming) |
617 | { | 601 | { |
618 | struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); | 602 | struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); |
619 | struct batman_if *batman_if; | 603 | struct batman_if *batman_if; |
@@ -795,6 +779,8 @@ void receive_bat_packet(struct ethhdr *ethhdr, | |||
795 | is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, | 779 | is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, |
796 | batman_packet, if_incoming); | 780 | batman_packet, if_incoming); |
797 | 781 | ||
782 | bonding_save_primary(orig_node, orig_neigh_node, batman_packet); | ||
783 | |||
798 | /* update ranking if it is not a duplicate or has the same | 784 | /* update ranking if it is not a duplicate or has the same |
799 | * seqno and similar ttl as the non-duplicate */ | 785 | * seqno and similar ttl as the non-duplicate */ |
800 | if (is_bidirectional && | 786 | if (is_bidirectional && |
@@ -804,9 +790,6 @@ void receive_bat_packet(struct ethhdr *ethhdr, | |||
804 | update_orig(bat_priv, orig_node, ethhdr, batman_packet, | 790 | update_orig(bat_priv, orig_node, ethhdr, batman_packet, |
805 | if_incoming, hna_buff, hna_buff_len, is_duplicate); | 791 | if_incoming, hna_buff, hna_buff_len, is_duplicate); |
806 | 792 | ||
807 | mark_bonding_address(orig_node, orig_neigh_node, batman_packet); | ||
808 | update_bonding_candidates(orig_node); | ||
809 | |||
810 | /* is single hop (direct) neighbor */ | 793 | /* is single hop (direct) neighbor */ |
811 | if (is_single_hop_neigh) { | 794 | if (is_single_hop_neigh) { |
812 | 795 | ||
@@ -1095,14 +1078,15 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) | |||
1095 | } | 1078 | } |
1096 | 1079 | ||
1097 | /* find a suitable router for this originator, and use | 1080 | /* find a suitable router for this originator, and use |
1098 | * bonding if possible. */ | 1081 | * bonding if possible. increases the found neighbors |
1082 | * refcount.*/ | ||
1099 | struct neigh_node *find_router(struct bat_priv *bat_priv, | 1083 | struct neigh_node *find_router(struct bat_priv *bat_priv, |
1100 | struct orig_node *orig_node, | 1084 | struct orig_node *orig_node, |
1101 | struct batman_if *recv_if) | 1085 | struct batman_if *recv_if) |
1102 | { | 1086 | { |
1103 | struct orig_node *primary_orig_node; | 1087 | struct orig_node *primary_orig_node; |
1104 | struct orig_node *router_orig; | 1088 | struct orig_node *router_orig; |
1105 | struct neigh_node *router, *first_candidate, *best_router; | 1089 | struct neigh_node *router, *first_candidate, *tmp_neigh_node; |
1106 | static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; | 1090 | static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; |
1107 | int bonding_enabled; | 1091 | int bonding_enabled; |
1108 | 1092 | ||
@@ -1114,18 +1098,25 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, | |||
1114 | 1098 | ||
1115 | /* without bonding, the first node should | 1099 | /* without bonding, the first node should |
1116 | * always choose the default router. */ | 1100 | * always choose the default router. */ |
1117 | |||
1118 | bonding_enabled = atomic_read(&bat_priv->bonding); | 1101 | bonding_enabled = atomic_read(&bat_priv->bonding); |
1119 | 1102 | ||
1120 | if ((!recv_if) && (!bonding_enabled)) | 1103 | rcu_read_lock(); |
1121 | return orig_node->router; | 1104 | /* select default router to output */ |
1122 | 1105 | router = orig_node->router; | |
1123 | router_orig = orig_node->router->orig_node; | 1106 | router_orig = orig_node->router->orig_node; |
1107 | if (!router_orig) { | ||
1108 | rcu_read_unlock(); | ||
1109 | return NULL; | ||
1110 | } | ||
1111 | |||
1112 | |||
1113 | if ((!recv_if) && (!bonding_enabled)) | ||
1114 | goto return_router; | ||
1124 | 1115 | ||
1125 | /* if we have something in the primary_addr, we can search | 1116 | /* if we have something in the primary_addr, we can search |
1126 | * for a potential bonding candidate. */ | 1117 | * for a potential bonding candidate. */ |
1127 | if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) | 1118 | if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) |
1128 | return orig_node->router; | 1119 | goto return_router; |
1129 | 1120 | ||
1130 | /* find the orig_node which has the primary interface. might | 1121 | /* find the orig_node which has the primary interface. might |
1131 | * even be the same as our router_orig in many cases */ | 1122 | * even be the same as our router_orig in many cases */ |
@@ -1134,60 +1125,81 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, | |||
1134 | router_orig->orig, ETH_ALEN) == 0) { | 1125 | router_orig->orig, ETH_ALEN) == 0) { |
1135 | primary_orig_node = router_orig; | 1126 | primary_orig_node = router_orig; |
1136 | } else { | 1127 | } else { |
1137 | rcu_read_lock(); | ||
1138 | primary_orig_node = hash_find(bat_priv->orig_hash, compare_orig, | 1128 | primary_orig_node = hash_find(bat_priv->orig_hash, compare_orig, |
1139 | choose_orig, | 1129 | choose_orig, |
1140 | router_orig->primary_addr); | 1130 | router_orig->primary_addr); |
1141 | rcu_read_unlock(); | ||
1142 | |||
1143 | if (!primary_orig_node) | 1131 | if (!primary_orig_node) |
1144 | return orig_node->router; | 1132 | goto return_router; |
1145 | } | 1133 | } |
1146 | 1134 | ||
1147 | /* with less than 2 candidates, we can't do any | 1135 | /* with less than 2 candidates, we can't do any |
1148 | * bonding and prefer the original router. */ | 1136 | * bonding and prefer the original router. */ |
1149 | 1137 | if (atomic_read(&primary_orig_node->bond_candidates) < 2) | |
1150 | if (primary_orig_node->bond.candidates < 2) | 1138 | goto return_router; |
1151 | return orig_node->router; | ||
1152 | 1139 | ||
1153 | 1140 | ||
1154 | /* all nodes between should choose a candidate which | 1141 | /* all nodes between should choose a candidate which |
1155 | * is is not on the interface where the packet came | 1142 | * is is not on the interface where the packet came |
1156 | * in. */ | 1143 | * in. */ |
1157 | first_candidate = primary_orig_node->bond.selected; | 1144 | |
1158 | router = first_candidate; | 1145 | first_candidate = NULL; |
1146 | router = NULL; | ||
1159 | 1147 | ||
1160 | if (bonding_enabled) { | 1148 | if (bonding_enabled) { |
1161 | /* in the bonding case, send the packets in a round | 1149 | /* in the bonding case, send the packets in a round |
1162 | * robin fashion over the remaining interfaces. */ | 1150 | * robin fashion over the remaining interfaces. */ |
1163 | do { | 1151 | |
1152 | list_for_each_entry_rcu(tmp_neigh_node, | ||
1153 | &primary_orig_node->bond_list, bonding_list) { | ||
1154 | if (!first_candidate) | ||
1155 | first_candidate = tmp_neigh_node; | ||
1164 | /* recv_if == NULL on the first node. */ | 1156 | /* recv_if == NULL on the first node. */ |
1165 | if (router->if_incoming != recv_if) | 1157 | if (tmp_neigh_node->if_incoming != recv_if) { |
1158 | router = tmp_neigh_node; | ||
1166 | break; | 1159 | break; |
1160 | } | ||
1161 | } | ||
1167 | 1162 | ||
1168 | router = router->next_bond_candidate; | 1163 | /* use the first candidate if nothing was found. */ |
1169 | } while (router != first_candidate); | 1164 | if (!router) |
1165 | router = first_candidate; | ||
1170 | 1166 | ||
1171 | primary_orig_node->bond.selected = router->next_bond_candidate; | 1167 | /* selected should point to the next element |
1168 | * after the current router */ | ||
1169 | spin_lock_bh(&primary_orig_node->neigh_list_lock); | ||
1170 | /* this is a list_move(), which unfortunately | ||
1171 | * does not exist as rcu version */ | ||
1172 | list_del_rcu(&primary_orig_node->bond_list); | ||
1173 | list_add_rcu(&primary_orig_node->bond_list, | ||
1174 | &router->bonding_list); | ||
1175 | spin_unlock_bh(&primary_orig_node->neigh_list_lock); | ||
1172 | 1176 | ||
1173 | } else { | 1177 | } else { |
1174 | /* if bonding is disabled, use the best of the | 1178 | /* if bonding is disabled, use the best of the |
1175 | * remaining candidates which are not using | 1179 | * remaining candidates which are not using |
1176 | * this interface. */ | 1180 | * this interface. */ |
1177 | best_router = first_candidate; | 1181 | list_for_each_entry_rcu(tmp_neigh_node, |
1182 | &primary_orig_node->bond_list, bonding_list) { | ||
1183 | if (!first_candidate) | ||
1184 | first_candidate = tmp_neigh_node; | ||
1178 | 1185 | ||
1179 | do { | ||
1180 | /* recv_if == NULL on the first node. */ | 1186 | /* recv_if == NULL on the first node. */ |
1181 | if ((router->if_incoming != recv_if) && | 1187 | if (tmp_neigh_node->if_incoming != recv_if) |
1182 | (router->tq_avg > best_router->tq_avg)) | 1188 | /* if we don't have a router yet |
1183 | best_router = router; | 1189 | * or this one is better, choose it. */ |
1184 | 1190 | if ((!router) || | |
1185 | router = router->next_bond_candidate; | 1191 | (tmp_neigh_node->tq_avg > router->tq_avg)) { |
1186 | } while (router != first_candidate); | 1192 | router = tmp_neigh_node; |
1193 | } | ||
1194 | } | ||
1187 | 1195 | ||
1188 | router = best_router; | 1196 | /* use the first candidate if nothing was found. */ |
1197 | if (!router) | ||
1198 | router = first_candidate; | ||
1189 | } | 1199 | } |
1190 | 1200 | return_router: | |
1201 | kref_get(&router->refcount); | ||
1202 | rcu_read_unlock(); | ||
1191 | return router; | 1203 | return router; |
1192 | } | 1204 | } |
1193 | 1205 | ||
@@ -1247,6 +1259,7 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, | |||
1247 | unicast_packet->dest)); | 1259 | unicast_packet->dest)); |
1248 | rcu_read_unlock(); | 1260 | rcu_read_unlock(); |
1249 | 1261 | ||
1262 | /* find_router() increases neigh_nodes refcount if found. */ | ||
1250 | router = find_router(bat_priv, orig_node, recv_if); | 1263 | router = find_router(bat_priv, orig_node, recv_if); |
1251 | 1264 | ||
1252 | if (!router) { | 1265 | if (!router) { |
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index a09d16f0c3ab..e2a9872a1589 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h | |||
@@ -39,7 +39,9 @@ int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if); | |||
39 | int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if); | 39 | int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if); |
40 | int recv_bat_packet(struct sk_buff *skb, struct batman_if *recv_if); | 40 | int recv_bat_packet(struct sk_buff *skb, struct batman_if *recv_if); |
41 | struct neigh_node *find_router(struct bat_priv *bat_priv, | 41 | struct neigh_node *find_router(struct bat_priv *bat_priv, |
42 | struct orig_node *orig_node, struct batman_if *recv_if); | 42 | struct orig_node *orig_node, |
43 | void update_bonding_candidates(struct orig_node *orig_node); | 43 | struct batman_if *recv_if); |
44 | void bonding_candidate_del(struct orig_node *orig_node, | ||
45 | struct neigh_node *neigh_node); | ||
44 | 46 | ||
45 | #endif /* _NET_BATMAN_ADV_ROUTING_H_ */ | 47 | #endif /* _NET_BATMAN_ADV_ROUTING_H_ */ |
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index ff70afc376da..1f833f04222e 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h | |||
@@ -90,10 +90,8 @@ struct orig_node { | |||
90 | struct bat_priv *bat_priv; | 90 | struct bat_priv *bat_priv; |
91 | unsigned long last_frag_packet; | 91 | unsigned long last_frag_packet; |
92 | spinlock_t ogm_cnt_lock; /* protects ogm counter */ | 92 | spinlock_t ogm_cnt_lock; /* protects ogm counter */ |
93 | struct { | 93 | atomic_t bond_candidates; |
94 | uint8_t candidates; | 94 | struct list_head bond_list; |
95 | struct neigh_node *selected; | ||
96 | } bond; | ||
97 | }; | 95 | }; |
98 | 96 | ||
99 | struct gw_node { | 97 | struct gw_node { |
@@ -116,11 +114,12 @@ struct neigh_node { | |||
116 | uint8_t tq_index; | 114 | uint8_t tq_index; |
117 | uint8_t tq_avg; | 115 | uint8_t tq_avg; |
118 | uint8_t last_ttl; | 116 | uint8_t last_ttl; |
119 | struct neigh_node *next_bond_candidate; | 117 | struct list_head bonding_list; |
120 | unsigned long last_valid; | 118 | unsigned long last_valid; |
121 | unsigned long real_bits[NUM_WORDS]; | 119 | unsigned long real_bits[NUM_WORDS]; |
122 | struct kref refcount; | 120 | struct kref refcount; |
123 | struct rcu_head rcu; | 121 | struct rcu_head rcu; |
122 | struct rcu_head rcu_bond; | ||
124 | struct orig_node *orig_node; | 123 | struct orig_node *orig_node; |
125 | struct batman_if *if_incoming; | 124 | struct batman_if *if_incoming; |
126 | }; | 125 | }; |
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 4687027f1495..00bfeaf9ece3 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c | |||
@@ -299,6 +299,7 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) | |||
299 | if (!orig_node) | 299 | if (!orig_node) |
300 | orig_node = transtable_search(bat_priv, ethhdr->h_dest); | 300 | orig_node = transtable_search(bat_priv, ethhdr->h_dest); |
301 | 301 | ||
302 | /* find_router() increases neigh_nodes refcount if found. */ | ||
302 | router = find_router(bat_priv, orig_node, NULL); | 303 | router = find_router(bat_priv, orig_node, NULL); |
303 | 304 | ||
304 | if (!router) | 305 | if (!router) |
@@ -306,7 +307,6 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) | |||
306 | 307 | ||
307 | /* don't lock while sending the packets ... we therefore | 308 | /* don't lock while sending the packets ... we therefore |
308 | * copy the required data before sending */ | 309 | * copy the required data before sending */ |
309 | |||
310 | batman_if = router->if_incoming; | 310 | batman_if = router->if_incoming; |
311 | memcpy(dstaddr, router->addr, ETH_ALEN); | 311 | memcpy(dstaddr, router->addr, ETH_ALEN); |
312 | 312 | ||