diff options
Diffstat (limited to 'net/batman-adv/gateway_client.c')
-rw-r--r-- | net/batman-adv/gateway_client.c | 142 |
1 files changed, 91 insertions, 51 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 0065ffb8d96d..3cc43558cf9c 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: | 2 | * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors: |
3 | * | 3 | * |
4 | * Marek Lindner | 4 | * Marek Lindner |
5 | * | 5 | * |
@@ -28,58 +28,75 @@ | |||
28 | #include <linux/udp.h> | 28 | #include <linux/udp.h> |
29 | #include <linux/if_vlan.h> | 29 | #include <linux/if_vlan.h> |
30 | 30 | ||
31 | static void gw_node_free_ref(struct kref *refcount) | 31 | static void gw_node_free_rcu(struct rcu_head *rcu) |
32 | { | 32 | { |
33 | struct gw_node *gw_node; | 33 | struct gw_node *gw_node; |
34 | 34 | ||
35 | gw_node = container_of(refcount, struct gw_node, refcount); | 35 | gw_node = container_of(rcu, struct gw_node, rcu); |
36 | kfree(gw_node); | 36 | kfree(gw_node); |
37 | } | 37 | } |
38 | 38 | ||
39 | static void gw_node_free_rcu(struct rcu_head *rcu) | 39 | static void gw_node_free_ref(struct gw_node *gw_node) |
40 | { | 40 | { |
41 | struct gw_node *gw_node; | 41 | if (atomic_dec_and_test(&gw_node->refcount)) |
42 | 42 | call_rcu(&gw_node->rcu, gw_node_free_rcu); | |
43 | gw_node = container_of(rcu, struct gw_node, rcu); | ||
44 | kref_put(&gw_node->refcount, gw_node_free_ref); | ||
45 | } | 43 | } |
46 | 44 | ||
47 | void *gw_get_selected(struct bat_priv *bat_priv) | 45 | void *gw_get_selected(struct bat_priv *bat_priv) |
48 | { | 46 | { |
49 | struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; | 47 | struct gw_node *curr_gateway_tmp; |
48 | struct orig_node *orig_node = NULL; | ||
50 | 49 | ||
50 | rcu_read_lock(); | ||
51 | curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); | ||
51 | if (!curr_gateway_tmp) | 52 | if (!curr_gateway_tmp) |
52 | return NULL; | 53 | goto out; |
54 | |||
55 | orig_node = curr_gateway_tmp->orig_node; | ||
56 | if (!orig_node) | ||
57 | goto out; | ||
53 | 58 | ||
54 | return curr_gateway_tmp->orig_node; | 59 | if (!atomic_inc_not_zero(&orig_node->refcount)) |
60 | orig_node = NULL; | ||
61 | |||
62 | out: | ||
63 | rcu_read_unlock(); | ||
64 | return orig_node; | ||
55 | } | 65 | } |
56 | 66 | ||
57 | void gw_deselect(struct bat_priv *bat_priv) | 67 | void gw_deselect(struct bat_priv *bat_priv) |
58 | { | 68 | { |
59 | struct gw_node *gw_node = bat_priv->curr_gw; | 69 | struct gw_node *gw_node; |
60 | 70 | ||
61 | bat_priv->curr_gw = NULL; | 71 | spin_lock_bh(&bat_priv->gw_list_lock); |
72 | gw_node = rcu_dereference(bat_priv->curr_gw); | ||
73 | rcu_assign_pointer(bat_priv->curr_gw, NULL); | ||
74 | spin_unlock_bh(&bat_priv->gw_list_lock); | ||
62 | 75 | ||
63 | if (gw_node) | 76 | if (gw_node) |
64 | kref_put(&gw_node->refcount, gw_node_free_ref); | 77 | gw_node_free_ref(gw_node); |
65 | } | 78 | } |
66 | 79 | ||
67 | static struct gw_node *gw_select(struct bat_priv *bat_priv, | 80 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) |
68 | struct gw_node *new_gw_node) | ||
69 | { | 81 | { |
70 | struct gw_node *curr_gw_node = bat_priv->curr_gw; | 82 | struct gw_node *curr_gw_node; |
71 | 83 | ||
72 | if (new_gw_node) | 84 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) |
73 | kref_get(&new_gw_node->refcount); | 85 | new_gw_node = NULL; |
86 | |||
87 | spin_lock_bh(&bat_priv->gw_list_lock); | ||
88 | curr_gw_node = rcu_dereference(bat_priv->curr_gw); | ||
89 | rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); | ||
90 | spin_unlock_bh(&bat_priv->gw_list_lock); | ||
74 | 91 | ||
75 | bat_priv->curr_gw = new_gw_node; | 92 | if (curr_gw_node) |
76 | return curr_gw_node; | 93 | gw_node_free_ref(curr_gw_node); |
77 | } | 94 | } |
78 | 95 | ||
79 | void gw_election(struct bat_priv *bat_priv) | 96 | void gw_election(struct bat_priv *bat_priv) |
80 | { | 97 | { |
81 | struct hlist_node *node; | 98 | struct hlist_node *node; |
82 | struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL; | 99 | struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; |
83 | uint8_t max_tq = 0; | 100 | uint8_t max_tq = 0; |
84 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; | 101 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
85 | int down, up; | 102 | int down, up; |
@@ -93,19 +110,23 @@ void gw_election(struct bat_priv *bat_priv) | |||
93 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) | 110 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) |
94 | return; | 111 | return; |
95 | 112 | ||
96 | if (bat_priv->curr_gw) | 113 | rcu_read_lock(); |
114 | curr_gw = rcu_dereference(bat_priv->curr_gw); | ||
115 | if (curr_gw) { | ||
116 | rcu_read_unlock(); | ||
97 | return; | 117 | return; |
118 | } | ||
98 | 119 | ||
99 | rcu_read_lock(); | ||
100 | if (hlist_empty(&bat_priv->gw_list)) { | 120 | if (hlist_empty(&bat_priv->gw_list)) { |
101 | rcu_read_unlock(); | ||
102 | 121 | ||
103 | if (bat_priv->curr_gw) { | 122 | if (curr_gw) { |
123 | rcu_read_unlock(); | ||
104 | bat_dbg(DBG_BATMAN, bat_priv, | 124 | bat_dbg(DBG_BATMAN, bat_priv, |
105 | "Removing selected gateway - " | 125 | "Removing selected gateway - " |
106 | "no gateway in range\n"); | 126 | "no gateway in range\n"); |
107 | gw_deselect(bat_priv); | 127 | gw_deselect(bat_priv); |
108 | } | 128 | } else |
129 | rcu_read_unlock(); | ||
109 | 130 | ||
110 | return; | 131 | return; |
111 | } | 132 | } |
@@ -154,12 +175,12 @@ void gw_election(struct bat_priv *bat_priv) | |||
154 | max_gw_factor = tmp_gw_factor; | 175 | max_gw_factor = tmp_gw_factor; |
155 | } | 176 | } |
156 | 177 | ||
157 | if (bat_priv->curr_gw != curr_gw_tmp) { | 178 | if (curr_gw != curr_gw_tmp) { |
158 | if ((bat_priv->curr_gw) && (!curr_gw_tmp)) | 179 | if ((curr_gw) && (!curr_gw_tmp)) |
159 | bat_dbg(DBG_BATMAN, bat_priv, | 180 | bat_dbg(DBG_BATMAN, bat_priv, |
160 | "Removing selected gateway - " | 181 | "Removing selected gateway - " |
161 | "no gateway in range\n"); | 182 | "no gateway in range\n"); |
162 | else if ((!bat_priv->curr_gw) && (curr_gw_tmp)) | 183 | else if ((!curr_gw) && (curr_gw_tmp)) |
163 | bat_dbg(DBG_BATMAN, bat_priv, | 184 | bat_dbg(DBG_BATMAN, bat_priv, |
164 | "Adding route to gateway %pM " | 185 | "Adding route to gateway %pM " |
165 | "(gw_flags: %i, tq: %i)\n", | 186 | "(gw_flags: %i, tq: %i)\n", |
@@ -174,43 +195,43 @@ void gw_election(struct bat_priv *bat_priv) | |||
174 | curr_gw_tmp->orig_node->gw_flags, | 195 | curr_gw_tmp->orig_node->gw_flags, |
175 | curr_gw_tmp->orig_node->router->tq_avg); | 196 | curr_gw_tmp->orig_node->router->tq_avg); |
176 | 197 | ||
177 | old_gw_node = gw_select(bat_priv, curr_gw_tmp); | 198 | gw_select(bat_priv, curr_gw_tmp); |
178 | } | 199 | } |
179 | 200 | ||
180 | rcu_read_unlock(); | 201 | rcu_read_unlock(); |
181 | |||
182 | /* the kfree() has to be outside of the rcu lock */ | ||
183 | if (old_gw_node) | ||
184 | kref_put(&old_gw_node->refcount, gw_node_free_ref); | ||
185 | } | 202 | } |
186 | 203 | ||
187 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | 204 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) |
188 | { | 205 | { |
189 | struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; | 206 | struct gw_node *curr_gateway_tmp; |
190 | uint8_t gw_tq_avg, orig_tq_avg; | 207 | uint8_t gw_tq_avg, orig_tq_avg; |
191 | 208 | ||
209 | rcu_read_lock(); | ||
210 | curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); | ||
192 | if (!curr_gateway_tmp) | 211 | if (!curr_gateway_tmp) |
193 | return; | 212 | goto out_rcu; |
194 | 213 | ||
195 | if (!curr_gateway_tmp->orig_node) | 214 | if (!curr_gateway_tmp->orig_node) |
196 | goto deselect; | 215 | goto deselect_rcu; |
197 | 216 | ||
198 | if (!curr_gateway_tmp->orig_node->router) | 217 | if (!curr_gateway_tmp->orig_node->router) |
199 | goto deselect; | 218 | goto deselect_rcu; |
200 | 219 | ||
201 | /* this node already is the gateway */ | 220 | /* this node already is the gateway */ |
202 | if (curr_gateway_tmp->orig_node == orig_node) | 221 | if (curr_gateway_tmp->orig_node == orig_node) |
203 | return; | 222 | goto out_rcu; |
204 | 223 | ||
205 | if (!orig_node->router) | 224 | if (!orig_node->router) |
206 | return; | 225 | goto out_rcu; |
207 | 226 | ||
208 | gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; | 227 | gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; |
228 | rcu_read_unlock(); | ||
229 | |||
209 | orig_tq_avg = orig_node->router->tq_avg; | 230 | orig_tq_avg = orig_node->router->tq_avg; |
210 | 231 | ||
211 | /* the TQ value has to be better */ | 232 | /* the TQ value has to be better */ |
212 | if (orig_tq_avg < gw_tq_avg) | 233 | if (orig_tq_avg < gw_tq_avg) |
213 | return; | 234 | goto out; |
214 | 235 | ||
215 | /** | 236 | /** |
216 | * if the routing class is greater than 3 the value tells us how much | 237 | * if the routing class is greater than 3 the value tells us how much |
@@ -218,15 +239,23 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | |||
218 | **/ | 239 | **/ |
219 | if ((atomic_read(&bat_priv->gw_sel_class) > 3) && | 240 | if ((atomic_read(&bat_priv->gw_sel_class) > 3) && |
220 | (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) | 241 | (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) |
221 | return; | 242 | goto out; |
222 | 243 | ||
223 | bat_dbg(DBG_BATMAN, bat_priv, | 244 | bat_dbg(DBG_BATMAN, bat_priv, |
224 | "Restarting gateway selection: better gateway found (tq curr: " | 245 | "Restarting gateway selection: better gateway found (tq curr: " |
225 | "%i, tq new: %i)\n", | 246 | "%i, tq new: %i)\n", |
226 | gw_tq_avg, orig_tq_avg); | 247 | gw_tq_avg, orig_tq_avg); |
248 | goto deselect; | ||
227 | 249 | ||
250 | out_rcu: | ||
251 | rcu_read_unlock(); | ||
252 | goto out; | ||
253 | deselect_rcu: | ||
254 | rcu_read_unlock(); | ||
228 | deselect: | 255 | deselect: |
229 | gw_deselect(bat_priv); | 256 | gw_deselect(bat_priv); |
257 | out: | ||
258 | return; | ||
230 | } | 259 | } |
231 | 260 | ||
232 | static void gw_node_add(struct bat_priv *bat_priv, | 261 | static void gw_node_add(struct bat_priv *bat_priv, |
@@ -242,7 +271,7 @@ static void gw_node_add(struct bat_priv *bat_priv, | |||
242 | memset(gw_node, 0, sizeof(struct gw_node)); | 271 | memset(gw_node, 0, sizeof(struct gw_node)); |
243 | INIT_HLIST_NODE(&gw_node->list); | 272 | INIT_HLIST_NODE(&gw_node->list); |
244 | gw_node->orig_node = orig_node; | 273 | gw_node->orig_node = orig_node; |
245 | kref_init(&gw_node->refcount); | 274 | atomic_set(&gw_node->refcount, 1); |
246 | 275 | ||
247 | spin_lock_bh(&bat_priv->gw_list_lock); | 276 | spin_lock_bh(&bat_priv->gw_list_lock); |
248 | hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); | 277 | hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); |
@@ -283,7 +312,7 @@ void gw_node_update(struct bat_priv *bat_priv, | |||
283 | "Gateway %pM removed from gateway list\n", | 312 | "Gateway %pM removed from gateway list\n", |
284 | orig_node->orig); | 313 | orig_node->orig); |
285 | 314 | ||
286 | if (gw_node == bat_priv->curr_gw) { | 315 | if (gw_node == rcu_dereference(bat_priv->curr_gw)) { |
287 | rcu_read_unlock(); | 316 | rcu_read_unlock(); |
288 | gw_deselect(bat_priv); | 317 | gw_deselect(bat_priv); |
289 | return; | 318 | return; |
@@ -321,11 +350,11 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
321 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) | 350 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) |
322 | continue; | 351 | continue; |
323 | 352 | ||
324 | if (bat_priv->curr_gw == gw_node) | 353 | if (rcu_dereference(bat_priv->curr_gw) == gw_node) |
325 | gw_deselect(bat_priv); | 354 | gw_deselect(bat_priv); |
326 | 355 | ||
327 | hlist_del_rcu(&gw_node->list); | 356 | hlist_del_rcu(&gw_node->list); |
328 | call_rcu(&gw_node->rcu, gw_node_free_rcu); | 357 | gw_node_free_ref(gw_node); |
329 | } | 358 | } |
330 | 359 | ||
331 | 360 | ||
@@ -335,12 +364,16 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
335 | static int _write_buffer_text(struct bat_priv *bat_priv, | 364 | static int _write_buffer_text(struct bat_priv *bat_priv, |
336 | struct seq_file *seq, struct gw_node *gw_node) | 365 | struct seq_file *seq, struct gw_node *gw_node) |
337 | { | 366 | { |
338 | int down, up; | 367 | struct gw_node *curr_gw; |
368 | int down, up, ret; | ||
339 | 369 | ||
340 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); | 370 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); |
341 | 371 | ||
342 | return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", | 372 | rcu_read_lock(); |
343 | (bat_priv->curr_gw == gw_node ? "=>" : " "), | 373 | curr_gw = rcu_dereference(bat_priv->curr_gw); |
374 | |||
375 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", | ||
376 | (curr_gw == gw_node ? "=>" : " "), | ||
344 | gw_node->orig_node->orig, | 377 | gw_node->orig_node->orig, |
345 | gw_node->orig_node->router->tq_avg, | 378 | gw_node->orig_node->router->tq_avg, |
346 | gw_node->orig_node->router->addr, | 379 | gw_node->orig_node->router->addr, |
@@ -350,6 +383,9 @@ static int _write_buffer_text(struct bat_priv *bat_priv, | |||
350 | (down > 2048 ? "MBit" : "KBit"), | 383 | (down > 2048 ? "MBit" : "KBit"), |
351 | (up > 2048 ? up / 1024 : up), | 384 | (up > 2048 ? up / 1024 : up), |
352 | (up > 2048 ? "MBit" : "KBit")); | 385 | (up > 2048 ? "MBit" : "KBit")); |
386 | |||
387 | rcu_read_unlock(); | ||
388 | return ret; | ||
353 | } | 389 | } |
354 | 390 | ||
355 | int gw_client_seq_print_text(struct seq_file *seq, void *offset) | 391 | int gw_client_seq_print_text(struct seq_file *seq, void *offset) |
@@ -470,8 +506,12 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | |||
470 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) | 506 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) |
471 | return -1; | 507 | return -1; |
472 | 508 | ||
473 | if (!bat_priv->curr_gw) | 509 | rcu_read_lock(); |
510 | if (!rcu_dereference(bat_priv->curr_gw)) { | ||
511 | rcu_read_unlock(); | ||
474 | return 0; | 512 | return 0; |
513 | } | ||
514 | rcu_read_unlock(); | ||
475 | 515 | ||
476 | return 1; | 516 | return 1; |
477 | } | 517 | } |