diff options
-rw-r--r-- | net/batman-adv/gateway_client.c | 170 | ||||
-rw-r--r-- | net/batman-adv/gateway_client.h | 2 | ||||
-rw-r--r-- | net/batman-adv/unicast.c | 2 |
3 files changed, 100 insertions, 74 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 42a8a7ba06e6..2acd7a666bda 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -43,61 +43,75 @@ static void gw_node_free_ref(struct gw_node *gw_node) | |||
43 | call_rcu(&gw_node->rcu, gw_node_free_rcu); | 43 | call_rcu(&gw_node->rcu, gw_node_free_rcu); |
44 | } | 44 | } |
45 | 45 | ||
46 | struct orig_node *gw_get_selected(struct bat_priv *bat_priv) | 46 | static struct gw_node *gw_get_selected_gw_node(struct bat_priv *bat_priv) |
47 | { | 47 | { |
48 | struct gw_node *curr_gateway_tmp; | 48 | struct gw_node *gw_node; |
49 | struct orig_node *orig_node = NULL; | ||
50 | 49 | ||
51 | rcu_read_lock(); | 50 | rcu_read_lock(); |
52 | curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); | 51 | gw_node = rcu_dereference(bat_priv->curr_gw); |
53 | if (!curr_gateway_tmp) | 52 | if (!gw_node) |
54 | goto out; | ||
55 | |||
56 | orig_node = curr_gateway_tmp->orig_node; | ||
57 | if (!orig_node) | ||
58 | goto out; | 53 | goto out; |
59 | 54 | ||
60 | if (!atomic_inc_not_zero(&orig_node->refcount)) | 55 | if (!atomic_inc_not_zero(&gw_node->refcount)) |
61 | orig_node = NULL; | 56 | gw_node = NULL; |
62 | 57 | ||
63 | out: | 58 | out: |
64 | rcu_read_unlock(); | 59 | rcu_read_unlock(); |
65 | return orig_node; | 60 | return gw_node; |
66 | } | 61 | } |
67 | 62 | ||
68 | void gw_deselect(struct bat_priv *bat_priv) | 63 | struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv) |
69 | { | 64 | { |
70 | struct gw_node *gw_node; | 65 | struct gw_node *gw_node; |
66 | struct orig_node *orig_node = NULL; | ||
71 | 67 | ||
72 | spin_lock_bh(&bat_priv->gw_list_lock); | 68 | gw_node = gw_get_selected_gw_node(bat_priv); |
73 | gw_node = rcu_dereference(bat_priv->curr_gw); | 69 | if (!gw_node) |
74 | rcu_assign_pointer(bat_priv->curr_gw, NULL); | 70 | goto out; |
75 | spin_unlock_bh(&bat_priv->gw_list_lock); | 71 | |
72 | rcu_read_lock(); | ||
73 | orig_node = gw_node->orig_node; | ||
74 | if (!orig_node) | ||
75 | goto unlock; | ||
76 | |||
77 | if (!atomic_inc_not_zero(&orig_node->refcount)) | ||
78 | orig_node = NULL; | ||
76 | 79 | ||
80 | unlock: | ||
81 | rcu_read_unlock(); | ||
82 | out: | ||
77 | if (gw_node) | 83 | if (gw_node) |
78 | gw_node_free_ref(gw_node); | 84 | gw_node_free_ref(gw_node); |
85 | return orig_node; | ||
79 | } | 86 | } |
80 | 87 | ||
81 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) | 88 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) |
82 | { | 89 | { |
83 | struct gw_node *curr_gw_node; | 90 | struct gw_node *curr_gw_node; |
84 | 91 | ||
92 | spin_lock_bh(&bat_priv->gw_list_lock); | ||
93 | |||
85 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) | 94 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) |
86 | new_gw_node = NULL; | 95 | new_gw_node = NULL; |
87 | 96 | ||
88 | spin_lock_bh(&bat_priv->gw_list_lock); | 97 | curr_gw_node = bat_priv->curr_gw; |
89 | curr_gw_node = rcu_dereference(bat_priv->curr_gw); | ||
90 | rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); | 98 | rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); |
91 | spin_unlock_bh(&bat_priv->gw_list_lock); | ||
92 | 99 | ||
93 | if (curr_gw_node) | 100 | if (curr_gw_node) |
94 | gw_node_free_ref(curr_gw_node); | 101 | gw_node_free_ref(curr_gw_node); |
102 | |||
103 | spin_unlock_bh(&bat_priv->gw_list_lock); | ||
104 | } | ||
105 | |||
106 | void gw_deselect(struct bat_priv *bat_priv) | ||
107 | { | ||
108 | gw_select(bat_priv, NULL); | ||
95 | } | 109 | } |
96 | 110 | ||
97 | void gw_election(struct bat_priv *bat_priv) | 111 | void gw_election(struct bat_priv *bat_priv) |
98 | { | 112 | { |
99 | struct hlist_node *node; | 113 | struct hlist_node *node; |
100 | struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; | 114 | struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; |
101 | struct neigh_node *router; | 115 | struct neigh_node *router; |
102 | uint8_t max_tq = 0; | 116 | uint8_t max_tq = 0; |
103 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; | 117 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
@@ -112,25 +126,17 @@ void gw_election(struct bat_priv *bat_priv) | |||
112 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) | 126 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) |
113 | return; | 127 | return; |
114 | 128 | ||
115 | rcu_read_lock(); | 129 | curr_gw = gw_get_selected_gw_node(bat_priv); |
116 | curr_gw = rcu_dereference(bat_priv->curr_gw); | 130 | if (!curr_gw) |
117 | if (curr_gw) { | 131 | goto out; |
118 | rcu_read_unlock(); | ||
119 | return; | ||
120 | } | ||
121 | 132 | ||
133 | rcu_read_lock(); | ||
122 | if (hlist_empty(&bat_priv->gw_list)) { | 134 | if (hlist_empty(&bat_priv->gw_list)) { |
123 | 135 | bat_dbg(DBG_BATMAN, bat_priv, | |
124 | if (curr_gw) { | 136 | "Removing selected gateway - " |
125 | rcu_read_unlock(); | 137 | "no gateway in range\n"); |
126 | bat_dbg(DBG_BATMAN, bat_priv, | 138 | gw_deselect(bat_priv); |
127 | "Removing selected gateway - " | 139 | goto unlock; |
128 | "no gateway in range\n"); | ||
129 | gw_deselect(bat_priv); | ||
130 | } else | ||
131 | rcu_read_unlock(); | ||
132 | |||
133 | return; | ||
134 | } | 140 | } |
135 | 141 | ||
136 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { | 142 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { |
@@ -182,7 +188,7 @@ void gw_election(struct bat_priv *bat_priv) | |||
182 | if (curr_gw != curr_gw_tmp) { | 188 | if (curr_gw != curr_gw_tmp) { |
183 | router = orig_node_get_router(curr_gw_tmp->orig_node); | 189 | router = orig_node_get_router(curr_gw_tmp->orig_node); |
184 | if (!router) | 190 | if (!router) |
185 | goto out; | 191 | goto unlock; |
186 | 192 | ||
187 | if ((curr_gw) && (!curr_gw_tmp)) | 193 | if ((curr_gw) && (!curr_gw_tmp)) |
188 | bat_dbg(DBG_BATMAN, bat_priv, | 194 | bat_dbg(DBG_BATMAN, bat_priv, |
@@ -207,8 +213,11 @@ void gw_election(struct bat_priv *bat_priv) | |||
207 | gw_select(bat_priv, curr_gw_tmp); | 213 | gw_select(bat_priv, curr_gw_tmp); |
208 | } | 214 | } |
209 | 215 | ||
210 | out: | 216 | unlock: |
211 | rcu_read_unlock(); | 217 | rcu_read_unlock(); |
218 | out: | ||
219 | if (curr_gw) | ||
220 | gw_node_free_ref(curr_gw); | ||
212 | } | 221 | } |
213 | 222 | ||
214 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | 223 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) |
@@ -217,7 +226,7 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | |||
217 | struct neigh_node *router_gw = NULL, *router_orig = NULL; | 226 | struct neigh_node *router_gw = NULL, *router_orig = NULL; |
218 | uint8_t gw_tq_avg, orig_tq_avg; | 227 | uint8_t gw_tq_avg, orig_tq_avg; |
219 | 228 | ||
220 | curr_gw_orig = gw_get_selected(bat_priv); | 229 | curr_gw_orig = gw_get_selected_orig(bat_priv); |
221 | if (!curr_gw_orig) | 230 | if (!curr_gw_orig) |
222 | goto deselect; | 231 | goto deselect; |
223 | 232 | ||
@@ -299,7 +308,11 @@ void gw_node_update(struct bat_priv *bat_priv, | |||
299 | struct orig_node *orig_node, uint8_t new_gwflags) | 308 | struct orig_node *orig_node, uint8_t new_gwflags) |
300 | { | 309 | { |
301 | struct hlist_node *node; | 310 | struct hlist_node *node; |
302 | struct gw_node *gw_node; | 311 | struct gw_node *gw_node, *curr_gw; |
312 | |||
313 | curr_gw = gw_get_selected_gw_node(bat_priv); | ||
314 | if (!curr_gw) | ||
315 | goto out; | ||
303 | 316 | ||
304 | rcu_read_lock(); | 317 | rcu_read_lock(); |
305 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { | 318 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { |
@@ -320,22 +333,26 @@ void gw_node_update(struct bat_priv *bat_priv, | |||
320 | "Gateway %pM removed from gateway list\n", | 333 | "Gateway %pM removed from gateway list\n", |
321 | orig_node->orig); | 334 | orig_node->orig); |
322 | 335 | ||
323 | if (gw_node == rcu_dereference(bat_priv->curr_gw)) { | 336 | if (gw_node == curr_gw) |
324 | rcu_read_unlock(); | 337 | goto deselect; |
325 | gw_deselect(bat_priv); | ||
326 | return; | ||
327 | } | ||
328 | } | 338 | } |
329 | 339 | ||
330 | rcu_read_unlock(); | 340 | goto unlock; |
331 | return; | ||
332 | } | 341 | } |
333 | rcu_read_unlock(); | ||
334 | 342 | ||
335 | if (new_gwflags == 0) | 343 | if (new_gwflags == 0) |
336 | return; | 344 | goto unlock; |
337 | 345 | ||
338 | gw_node_add(bat_priv, orig_node, new_gwflags); | 346 | gw_node_add(bat_priv, orig_node, new_gwflags); |
347 | goto unlock; | ||
348 | |||
349 | deselect: | ||
350 | gw_deselect(bat_priv); | ||
351 | unlock: | ||
352 | rcu_read_unlock(); | ||
353 | out: | ||
354 | if (curr_gw) | ||
355 | gw_node_free_ref(curr_gw); | ||
339 | } | 356 | } |
340 | 357 | ||
341 | void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) | 358 | void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) |
@@ -345,9 +362,12 @@ void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) | |||
345 | 362 | ||
346 | void gw_node_purge(struct bat_priv *bat_priv) | 363 | void gw_node_purge(struct bat_priv *bat_priv) |
347 | { | 364 | { |
348 | struct gw_node *gw_node; | 365 | struct gw_node *gw_node, *curr_gw; |
349 | struct hlist_node *node, *node_tmp; | 366 | struct hlist_node *node, *node_tmp; |
350 | unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; | 367 | unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; |
368 | char do_deselect = 0; | ||
369 | |||
370 | curr_gw = gw_get_selected_gw_node(bat_priv); | ||
351 | 371 | ||
352 | spin_lock_bh(&bat_priv->gw_list_lock); | 372 | spin_lock_bh(&bat_priv->gw_list_lock); |
353 | 373 | ||
@@ -358,15 +378,21 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
358 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) | 378 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) |
359 | continue; | 379 | continue; |
360 | 380 | ||
361 | if (rcu_dereference(bat_priv->curr_gw) == gw_node) | 381 | if (curr_gw == gw_node) |
362 | gw_deselect(bat_priv); | 382 | do_deselect = 1; |
363 | 383 | ||
364 | hlist_del_rcu(&gw_node->list); | 384 | hlist_del_rcu(&gw_node->list); |
365 | gw_node_free_ref(gw_node); | 385 | gw_node_free_ref(gw_node); |
366 | } | 386 | } |
367 | 387 | ||
368 | |||
369 | spin_unlock_bh(&bat_priv->gw_list_lock); | 388 | spin_unlock_bh(&bat_priv->gw_list_lock); |
389 | |||
390 | /* gw_deselect() needs to acquire the gw_list_lock */ | ||
391 | if (do_deselect) | ||
392 | gw_deselect(bat_priv); | ||
393 | |||
394 | if (curr_gw) | ||
395 | gw_node_free_ref(curr_gw); | ||
370 | } | 396 | } |
371 | 397 | ||
372 | /** | 398 | /** |
@@ -385,22 +411,22 @@ static int _write_buffer_text(struct bat_priv *bat_priv, | |||
385 | if (!router) | 411 | if (!router) |
386 | goto out; | 412 | goto out; |
387 | 413 | ||
388 | rcu_read_lock(); | 414 | curr_gw = gw_get_selected_gw_node(bat_priv); |
389 | curr_gw = rcu_dereference(bat_priv->curr_gw); | ||
390 | 415 | ||
391 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", | 416 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", |
392 | (curr_gw == gw_node ? "=>" : " "), | 417 | (curr_gw == gw_node ? "=>" : " "), |
393 | gw_node->orig_node->orig, | 418 | gw_node->orig_node->orig, |
394 | router->tq_avg, router->addr, | 419 | router->tq_avg, router->addr, |
395 | router->if_incoming->net_dev->name, | 420 | router->if_incoming->net_dev->name, |
396 | gw_node->orig_node->gw_flags, | 421 | gw_node->orig_node->gw_flags, |
397 | (down > 2048 ? down / 1024 : down), | 422 | (down > 2048 ? down / 1024 : down), |
398 | (down > 2048 ? "MBit" : "KBit"), | 423 | (down > 2048 ? "MBit" : "KBit"), |
399 | (up > 2048 ? up / 1024 : up), | 424 | (up > 2048 ? up / 1024 : up), |
400 | (up > 2048 ? "MBit" : "KBit")); | 425 | (up > 2048 ? "MBit" : "KBit")); |
401 | 426 | ||
402 | rcu_read_unlock(); | ||
403 | neigh_node_free_ref(router); | 427 | neigh_node_free_ref(router); |
428 | if (curr_gw) | ||
429 | gw_node_free_ref(curr_gw); | ||
404 | out: | 430 | out: |
405 | return ret; | 431 | return ret; |
406 | } | 432 | } |
@@ -459,6 +485,7 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | |||
459 | struct iphdr *iphdr; | 485 | struct iphdr *iphdr; |
460 | struct ipv6hdr *ipv6hdr; | 486 | struct ipv6hdr *ipv6hdr; |
461 | struct udphdr *udphdr; | 487 | struct udphdr *udphdr; |
488 | struct gw_node *curr_gw; | ||
462 | unsigned int header_len = 0; | 489 | unsigned int header_len = 0; |
463 | 490 | ||
464 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) | 491 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) |
@@ -523,12 +550,11 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | |||
523 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) | 550 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) |
524 | return -1; | 551 | return -1; |
525 | 552 | ||
526 | rcu_read_lock(); | 553 | curr_gw = gw_get_selected_gw_node(bat_priv); |
527 | if (!rcu_dereference(bat_priv->curr_gw)) { | 554 | if (!curr_gw) |
528 | rcu_read_unlock(); | ||
529 | return 0; | 555 | return 0; |
530 | } | ||
531 | rcu_read_unlock(); | ||
532 | 556 | ||
557 | if (curr_gw) | ||
558 | gw_node_free_ref(curr_gw); | ||
533 | return 1; | 559 | return 1; |
534 | } | 560 | } |
diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index 97c31d178163..1ce8c6066da1 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h | |||
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | void gw_deselect(struct bat_priv *bat_priv); | 25 | void gw_deselect(struct bat_priv *bat_priv); |
26 | void gw_election(struct bat_priv *bat_priv); | 26 | void gw_election(struct bat_priv *bat_priv); |
27 | struct orig_node *gw_get_selected(struct bat_priv *bat_priv); | 27 | struct orig_node *gw_get_selected_orig(struct bat_priv *bat_priv); |
28 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node); | 28 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node); |
29 | void gw_node_update(struct bat_priv *bat_priv, | 29 | void gw_node_update(struct bat_priv *bat_priv, |
30 | struct orig_node *orig_node, uint8_t new_gwflags); | 30 | struct orig_node *orig_node, uint8_t new_gwflags); |
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 19f84bd443af..d46acc815138 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c | |||
@@ -289,7 +289,7 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) | |||
289 | 289 | ||
290 | /* get routing information */ | 290 | /* get routing information */ |
291 | if (is_multicast_ether_addr(ethhdr->h_dest)) { | 291 | if (is_multicast_ether_addr(ethhdr->h_dest)) { |
292 | orig_node = (struct orig_node *)gw_get_selected(bat_priv); | 292 | orig_node = (struct orig_node *)gw_get_selected_orig(bat_priv); |
293 | if (orig_node) | 293 | if (orig_node) |
294 | goto find_router; | 294 | goto find_router; |
295 | } | 295 | } |