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/routing.c | |
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/routing.c')
-rw-r--r-- | net/batman-adv/routing.c | 313 |
1 files changed, 163 insertions, 150 deletions
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) { |