diff options
author | Linus Lüssing <linus.luessing@ascom.ch> | 2011-02-13 16:13:02 -0500 |
---|---|---|
committer | Marek Lindner <lindner_marek@yahoo.de> | 2011-03-05 06:50:10 -0500 |
commit | 5d02b3cdfafeb23ab7cf43ef1d2118007370e8d0 (patch) | |
tree | 4bd28fe2a1bca0a14710f01f98852f43ef2e330c /net/batman-adv/gateway_client.c | |
parent | f3e0008f01b275bd08bd416cfcaa7021dd6bc277 (diff) |
batman-adv: Make bat_priv->curr_gw an rcu protected pointer
The rcu protected macros rcu_dereference() and rcu_assign_pointer()
for the bat_priv->curr_gw need to be used, as well as spin/rcu locking.
Otherwise we might end up using a curr_gw pointer pointing to already
freed memory.
Reported-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Linus Lüssing <linus.luessing@ascom.ch>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Diffstat (limited to 'net/batman-adv/gateway_client.c')
-rw-r--r-- | net/batman-adv/gateway_client.c | 100 |
1 files changed, 70 insertions, 30 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 517e001605d9..a3e842fd0f9c 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -44,19 +44,29 @@ static void gw_node_free_ref(struct gw_node *gw_node) | |||
44 | 44 | ||
45 | void *gw_get_selected(struct bat_priv *bat_priv) | 45 | void *gw_get_selected(struct bat_priv *bat_priv) |
46 | { | 46 | { |
47 | struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; | 47 | struct gw_node *curr_gateway_tmp; |
48 | struct orig_node *orig_node = NULL; | ||
48 | 49 | ||
50 | rcu_read_lock(); | ||
51 | curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); | ||
49 | if (!curr_gateway_tmp) | 52 | if (!curr_gateway_tmp) |
50 | return NULL; | 53 | goto out; |
54 | |||
55 | orig_node = curr_gateway_tmp->orig_node; | ||
51 | 56 | ||
52 | return curr_gateway_tmp->orig_node; | 57 | out: |
58 | rcu_read_unlock(); | ||
59 | return orig_node; | ||
53 | } | 60 | } |
54 | 61 | ||
55 | void gw_deselect(struct bat_priv *bat_priv) | 62 | void gw_deselect(struct bat_priv *bat_priv) |
56 | { | 63 | { |
57 | struct gw_node *gw_node = bat_priv->curr_gw; | 64 | struct gw_node *gw_node; |
58 | 65 | ||
59 | bat_priv->curr_gw = NULL; | 66 | spin_lock_bh(&bat_priv->gw_list_lock); |
67 | gw_node = rcu_dereference(bat_priv->curr_gw); | ||
68 | rcu_assign_pointer(bat_priv->curr_gw, NULL); | ||
69 | spin_unlock_bh(&bat_priv->gw_list_lock); | ||
60 | 70 | ||
61 | if (gw_node) | 71 | if (gw_node) |
62 | gw_node_free_ref(gw_node); | 72 | gw_node_free_ref(gw_node); |
@@ -64,12 +74,15 @@ void gw_deselect(struct bat_priv *bat_priv) | |||
64 | 74 | ||
65 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) | 75 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) |
66 | { | 76 | { |
67 | struct gw_node *curr_gw_node = bat_priv->curr_gw; | 77 | struct gw_node *curr_gw_node; |
68 | 78 | ||
69 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) | 79 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) |
70 | new_gw_node = NULL; | 80 | new_gw_node = NULL; |
71 | 81 | ||
72 | bat_priv->curr_gw = new_gw_node; | 82 | spin_lock_bh(&bat_priv->gw_list_lock); |
83 | curr_gw_node = rcu_dereference(bat_priv->curr_gw); | ||
84 | rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); | ||
85 | spin_unlock_bh(&bat_priv->gw_list_lock); | ||
73 | 86 | ||
74 | if (curr_gw_node) | 87 | if (curr_gw_node) |
75 | gw_node_free_ref(curr_gw_node); | 88 | gw_node_free_ref(curr_gw_node); |
@@ -78,7 +91,7 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) | |||
78 | void gw_election(struct bat_priv *bat_priv) | 91 | void gw_election(struct bat_priv *bat_priv) |
79 | { | 92 | { |
80 | struct hlist_node *node; | 93 | struct hlist_node *node; |
81 | struct gw_node *gw_node, *curr_gw_tmp = NULL; | 94 | struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; |
82 | uint8_t max_tq = 0; | 95 | uint8_t max_tq = 0; |
83 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; | 96 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
84 | int down, up; | 97 | int down, up; |
@@ -92,19 +105,23 @@ void gw_election(struct bat_priv *bat_priv) | |||
92 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) | 105 | if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) |
93 | return; | 106 | return; |
94 | 107 | ||
95 | if (bat_priv->curr_gw) | 108 | rcu_read_lock(); |
109 | curr_gw = rcu_dereference(bat_priv->curr_gw); | ||
110 | if (curr_gw) { | ||
111 | rcu_read_unlock(); | ||
96 | return; | 112 | return; |
113 | } | ||
97 | 114 | ||
98 | rcu_read_lock(); | ||
99 | if (hlist_empty(&bat_priv->gw_list)) { | 115 | if (hlist_empty(&bat_priv->gw_list)) { |
100 | rcu_read_unlock(); | ||
101 | 116 | ||
102 | if (bat_priv->curr_gw) { | 117 | if (curr_gw) { |
118 | rcu_read_unlock(); | ||
103 | bat_dbg(DBG_BATMAN, bat_priv, | 119 | bat_dbg(DBG_BATMAN, bat_priv, |
104 | "Removing selected gateway - " | 120 | "Removing selected gateway - " |
105 | "no gateway in range\n"); | 121 | "no gateway in range\n"); |
106 | gw_deselect(bat_priv); | 122 | gw_deselect(bat_priv); |
107 | } | 123 | } else |
124 | rcu_read_unlock(); | ||
108 | 125 | ||
109 | return; | 126 | return; |
110 | } | 127 | } |
@@ -153,12 +170,12 @@ void gw_election(struct bat_priv *bat_priv) | |||
153 | max_gw_factor = tmp_gw_factor; | 170 | max_gw_factor = tmp_gw_factor; |
154 | } | 171 | } |
155 | 172 | ||
156 | if (bat_priv->curr_gw != curr_gw_tmp) { | 173 | if (curr_gw != curr_gw_tmp) { |
157 | if ((bat_priv->curr_gw) && (!curr_gw_tmp)) | 174 | if ((curr_gw) && (!curr_gw_tmp)) |
158 | bat_dbg(DBG_BATMAN, bat_priv, | 175 | bat_dbg(DBG_BATMAN, bat_priv, |
159 | "Removing selected gateway - " | 176 | "Removing selected gateway - " |
160 | "no gateway in range\n"); | 177 | "no gateway in range\n"); |
161 | else if ((!bat_priv->curr_gw) && (curr_gw_tmp)) | 178 | else if ((!curr_gw) && (curr_gw_tmp)) |
162 | bat_dbg(DBG_BATMAN, bat_priv, | 179 | bat_dbg(DBG_BATMAN, bat_priv, |
163 | "Adding route to gateway %pM " | 180 | "Adding route to gateway %pM " |
164 | "(gw_flags: %i, tq: %i)\n", | 181 | "(gw_flags: %i, tq: %i)\n", |
@@ -181,31 +198,35 @@ void gw_election(struct bat_priv *bat_priv) | |||
181 | 198 | ||
182 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | 199 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) |
183 | { | 200 | { |
184 | struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; | 201 | struct gw_node *curr_gateway_tmp; |
185 | uint8_t gw_tq_avg, orig_tq_avg; | 202 | uint8_t gw_tq_avg, orig_tq_avg; |
186 | 203 | ||
204 | rcu_read_lock(); | ||
205 | curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); | ||
187 | if (!curr_gateway_tmp) | 206 | if (!curr_gateway_tmp) |
188 | return; | 207 | goto out_rcu; |
189 | 208 | ||
190 | if (!curr_gateway_tmp->orig_node) | 209 | if (!curr_gateway_tmp->orig_node) |
191 | goto deselect; | 210 | goto deselect_rcu; |
192 | 211 | ||
193 | if (!curr_gateway_tmp->orig_node->router) | 212 | if (!curr_gateway_tmp->orig_node->router) |
194 | goto deselect; | 213 | goto deselect_rcu; |
195 | 214 | ||
196 | /* this node already is the gateway */ | 215 | /* this node already is the gateway */ |
197 | if (curr_gateway_tmp->orig_node == orig_node) | 216 | if (curr_gateway_tmp->orig_node == orig_node) |
198 | return; | 217 | goto out_rcu; |
199 | 218 | ||
200 | if (!orig_node->router) | 219 | if (!orig_node->router) |
201 | return; | 220 | goto out_rcu; |
202 | 221 | ||
203 | gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; | 222 | gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; |
223 | rcu_read_unlock(); | ||
224 | |||
204 | orig_tq_avg = orig_node->router->tq_avg; | 225 | orig_tq_avg = orig_node->router->tq_avg; |
205 | 226 | ||
206 | /* the TQ value has to be better */ | 227 | /* the TQ value has to be better */ |
207 | if (orig_tq_avg < gw_tq_avg) | 228 | if (orig_tq_avg < gw_tq_avg) |
208 | return; | 229 | goto out; |
209 | 230 | ||
210 | /** | 231 | /** |
211 | * if the routing class is greater than 3 the value tells us how much | 232 | * if the routing class is greater than 3 the value tells us how much |
@@ -213,15 +234,23 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | |||
213 | **/ | 234 | **/ |
214 | if ((atomic_read(&bat_priv->gw_sel_class) > 3) && | 235 | if ((atomic_read(&bat_priv->gw_sel_class) > 3) && |
215 | (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) | 236 | (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) |
216 | return; | 237 | goto out; |
217 | 238 | ||
218 | bat_dbg(DBG_BATMAN, bat_priv, | 239 | bat_dbg(DBG_BATMAN, bat_priv, |
219 | "Restarting gateway selection: better gateway found (tq curr: " | 240 | "Restarting gateway selection: better gateway found (tq curr: " |
220 | "%i, tq new: %i)\n", | 241 | "%i, tq new: %i)\n", |
221 | gw_tq_avg, orig_tq_avg); | 242 | gw_tq_avg, orig_tq_avg); |
243 | goto deselect; | ||
222 | 244 | ||
245 | out_rcu: | ||
246 | rcu_read_unlock(); | ||
247 | goto out; | ||
248 | deselect_rcu: | ||
249 | rcu_read_unlock(); | ||
223 | deselect: | 250 | deselect: |
224 | gw_deselect(bat_priv); | 251 | gw_deselect(bat_priv); |
252 | out: | ||
253 | return; | ||
225 | } | 254 | } |
226 | 255 | ||
227 | static void gw_node_add(struct bat_priv *bat_priv, | 256 | static void gw_node_add(struct bat_priv *bat_priv, |
@@ -278,7 +307,7 @@ void gw_node_update(struct bat_priv *bat_priv, | |||
278 | "Gateway %pM removed from gateway list\n", | 307 | "Gateway %pM removed from gateway list\n", |
279 | orig_node->orig); | 308 | orig_node->orig); |
280 | 309 | ||
281 | if (gw_node == bat_priv->curr_gw) { | 310 | if (gw_node == rcu_dereference(bat_priv->curr_gw)) { |
282 | rcu_read_unlock(); | 311 | rcu_read_unlock(); |
283 | gw_deselect(bat_priv); | 312 | gw_deselect(bat_priv); |
284 | return; | 313 | return; |
@@ -316,7 +345,7 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
316 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) | 345 | atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) |
317 | continue; | 346 | continue; |
318 | 347 | ||
319 | if (bat_priv->curr_gw == gw_node) | 348 | if (rcu_dereference(bat_priv->curr_gw) == gw_node) |
320 | gw_deselect(bat_priv); | 349 | gw_deselect(bat_priv); |
321 | 350 | ||
322 | hlist_del_rcu(&gw_node->list); | 351 | hlist_del_rcu(&gw_node->list); |
@@ -330,12 +359,16 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
330 | static int _write_buffer_text(struct bat_priv *bat_priv, | 359 | static int _write_buffer_text(struct bat_priv *bat_priv, |
331 | struct seq_file *seq, struct gw_node *gw_node) | 360 | struct seq_file *seq, struct gw_node *gw_node) |
332 | { | 361 | { |
333 | int down, up; | 362 | struct gw_node *curr_gw; |
363 | int down, up, ret; | ||
334 | 364 | ||
335 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); | 365 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); |
336 | 366 | ||
337 | return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", | 367 | rcu_read_lock(); |
338 | (bat_priv->curr_gw == gw_node ? "=>" : " "), | 368 | curr_gw = rcu_dereference(bat_priv->curr_gw); |
369 | |||
370 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", | ||
371 | (curr_gw == gw_node ? "=>" : " "), | ||
339 | gw_node->orig_node->orig, | 372 | gw_node->orig_node->orig, |
340 | gw_node->orig_node->router->tq_avg, | 373 | gw_node->orig_node->router->tq_avg, |
341 | gw_node->orig_node->router->addr, | 374 | gw_node->orig_node->router->addr, |
@@ -345,6 +378,9 @@ static int _write_buffer_text(struct bat_priv *bat_priv, | |||
345 | (down > 2048 ? "MBit" : "KBit"), | 378 | (down > 2048 ? "MBit" : "KBit"), |
346 | (up > 2048 ? up / 1024 : up), | 379 | (up > 2048 ? up / 1024 : up), |
347 | (up > 2048 ? "MBit" : "KBit")); | 380 | (up > 2048 ? "MBit" : "KBit")); |
381 | |||
382 | rcu_read_unlock(); | ||
383 | return ret; | ||
348 | } | 384 | } |
349 | 385 | ||
350 | int gw_client_seq_print_text(struct seq_file *seq, void *offset) | 386 | int gw_client_seq_print_text(struct seq_file *seq, void *offset) |
@@ -465,8 +501,12 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) | |||
465 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) | 501 | if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) |
466 | return -1; | 502 | return -1; |
467 | 503 | ||
468 | if (!bat_priv->curr_gw) | 504 | rcu_read_lock(); |
505 | if (!rcu_dereference(bat_priv->curr_gw)) { | ||
506 | rcu_read_unlock(); | ||
469 | return 0; | 507 | return 0; |
508 | } | ||
509 | rcu_read_unlock(); | ||
470 | 510 | ||
471 | return 1; | 511 | return 1; |
472 | } | 512 | } |