diff options
author | Linus Lüssing <linus.luessing@web.de> | 2011-03-14 18:43:37 -0400 |
---|---|---|
committer | Sven Eckelmann <sven@narfation.org> | 2011-04-17 15:11:01 -0400 |
commit | e1a5382f978b67b5cc36eec65e6046730ce07714 (patch) | |
tree | f7ca07cde3a49858d0cfa33e0189a659a1fcc95d /net/batman-adv/gateway_client.c | |
parent | 57f0c07c4d0da8bcc23e21c330fe9c7c5cf776b5 (diff) |
batman-adv: Make orig_node->router an rcu protected pointer
The rcu protected macros rcu_dereference() and rcu_assign_pointer()
for the orig_node->router need to be used, as well as spin/rcu locking.
Otherwise we might end up using a router pointer pointing to already
freed memory.
Therefore this commit introduces the safe getter method
orig_node_get_router().
Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Diffstat (limited to 'net/batman-adv/gateway_client.c')
-rw-r--r-- | net/batman-adv/gateway_client.c | 80 |
1 files changed, 48 insertions, 32 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 879ac1594869..42a8a7ba06e6 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -98,6 +98,7 @@ void gw_election(struct bat_priv *bat_priv) | |||
98 | { | 98 | { |
99 | struct hlist_node *node; | 99 | struct hlist_node *node; |
100 | struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; | 100 | struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; |
101 | struct neigh_node *router; | ||
101 | uint8_t max_tq = 0; | 102 | uint8_t max_tq = 0; |
102 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; | 103 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
103 | int down, up; | 104 | int down, up; |
@@ -133,10 +134,11 @@ void gw_election(struct bat_priv *bat_priv) | |||
133 | } | 134 | } |
134 | 135 | ||
135 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { | 136 | hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { |
136 | if (!gw_node->orig_node->router) | 137 | if (gw_node->deleted) |
137 | continue; | 138 | continue; |
138 | 139 | ||
139 | if (gw_node->deleted) | 140 | router = orig_node_get_router(gw_node->orig_node); |
141 | if (!router) | ||
140 | continue; | 142 | continue; |
141 | 143 | ||
142 | switch (atomic_read(&bat_priv->gw_sel_class)) { | 144 | switch (atomic_read(&bat_priv->gw_sel_class)) { |
@@ -144,15 +146,14 @@ void gw_election(struct bat_priv *bat_priv) | |||
144 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, | 146 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, |
145 | &down, &up); | 147 | &down, &up); |
146 | 148 | ||
147 | tmp_gw_factor = (gw_node->orig_node->router->tq_avg * | 149 | tmp_gw_factor = (router->tq_avg * router->tq_avg * |
148 | gw_node->orig_node->router->tq_avg * | ||
149 | down * 100 * 100) / | 150 | down * 100 * 100) / |
150 | (TQ_LOCAL_WINDOW_SIZE * | 151 | (TQ_LOCAL_WINDOW_SIZE * |
151 | TQ_LOCAL_WINDOW_SIZE * 64); | 152 | TQ_LOCAL_WINDOW_SIZE * 64); |
152 | 153 | ||
153 | if ((tmp_gw_factor > max_gw_factor) || | 154 | if ((tmp_gw_factor > max_gw_factor) || |
154 | ((tmp_gw_factor == max_gw_factor) && | 155 | ((tmp_gw_factor == max_gw_factor) && |
155 | (gw_node->orig_node->router->tq_avg > max_tq))) | 156 | (router->tq_avg > max_tq))) |
156 | curr_gw_tmp = gw_node; | 157 | curr_gw_tmp = gw_node; |
157 | break; | 158 | break; |
158 | 159 | ||
@@ -164,19 +165,25 @@ void gw_election(struct bat_priv *bat_priv) | |||
164 | * soon as a better gateway appears which has | 165 | * soon as a better gateway appears which has |
165 | * $routing_class more tq points) | 166 | * $routing_class more tq points) |
166 | **/ | 167 | **/ |
167 | if (gw_node->orig_node->router->tq_avg > max_tq) | 168 | if (router->tq_avg > max_tq) |
168 | curr_gw_tmp = gw_node; | 169 | curr_gw_tmp = gw_node; |
169 | break; | 170 | break; |
170 | } | 171 | } |
171 | 172 | ||
172 | if (gw_node->orig_node->router->tq_avg > max_tq) | 173 | if (router->tq_avg > max_tq) |
173 | max_tq = gw_node->orig_node->router->tq_avg; | 174 | max_tq = router->tq_avg; |
174 | 175 | ||
175 | if (tmp_gw_factor > max_gw_factor) | 176 | if (tmp_gw_factor > max_gw_factor) |
176 | max_gw_factor = tmp_gw_factor; | 177 | max_gw_factor = tmp_gw_factor; |
178 | |||
179 | neigh_node_free_ref(router); | ||
177 | } | 180 | } |
178 | 181 | ||
179 | if (curr_gw != curr_gw_tmp) { | 182 | if (curr_gw != curr_gw_tmp) { |
183 | router = orig_node_get_router(curr_gw_tmp->orig_node); | ||
184 | if (!router) | ||
185 | goto out; | ||
186 | |||
180 | if ((curr_gw) && (!curr_gw_tmp)) | 187 | if ((curr_gw) && (!curr_gw_tmp)) |
181 | bat_dbg(DBG_BATMAN, bat_priv, | 188 | bat_dbg(DBG_BATMAN, bat_priv, |
182 | "Removing selected gateway - " | 189 | "Removing selected gateway - " |
@@ -187,45 +194,47 @@ void gw_election(struct bat_priv *bat_priv) | |||
187 | "(gw_flags: %i, tq: %i)\n", | 194 | "(gw_flags: %i, tq: %i)\n", |
188 | curr_gw_tmp->orig_node->orig, | 195 | curr_gw_tmp->orig_node->orig, |
189 | curr_gw_tmp->orig_node->gw_flags, | 196 | curr_gw_tmp->orig_node->gw_flags, |
190 | curr_gw_tmp->orig_node->router->tq_avg); | 197 | router->tq_avg); |
191 | else | 198 | else |
192 | bat_dbg(DBG_BATMAN, bat_priv, | 199 | bat_dbg(DBG_BATMAN, bat_priv, |
193 | "Changing route to gateway %pM " | 200 | "Changing route to gateway %pM " |
194 | "(gw_flags: %i, tq: %i)\n", | 201 | "(gw_flags: %i, tq: %i)\n", |
195 | curr_gw_tmp->orig_node->orig, | 202 | curr_gw_tmp->orig_node->orig, |
196 | curr_gw_tmp->orig_node->gw_flags, | 203 | curr_gw_tmp->orig_node->gw_flags, |
197 | curr_gw_tmp->orig_node->router->tq_avg); | 204 | router->tq_avg); |
198 | 205 | ||
206 | neigh_node_free_ref(router); | ||
199 | gw_select(bat_priv, curr_gw_tmp); | 207 | gw_select(bat_priv, curr_gw_tmp); |
200 | } | 208 | } |
201 | 209 | ||
210 | out: | ||
202 | rcu_read_unlock(); | 211 | rcu_read_unlock(); |
203 | } | 212 | } |
204 | 213 | ||
205 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | 214 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) |
206 | { | 215 | { |
207 | struct orig_node *curr_gw_orig; | 216 | struct orig_node *curr_gw_orig; |
217 | struct neigh_node *router_gw = NULL, *router_orig = NULL; | ||
208 | uint8_t gw_tq_avg, orig_tq_avg; | 218 | uint8_t gw_tq_avg, orig_tq_avg; |
209 | 219 | ||
210 | curr_gw_orig = gw_get_selected(bat_priv); | 220 | curr_gw_orig = gw_get_selected(bat_priv); |
211 | if (!curr_gw_orig) | 221 | if (!curr_gw_orig) |
212 | goto deselect; | 222 | goto deselect; |
213 | 223 | ||
214 | rcu_read_lock(); | 224 | router_gw = orig_node_get_router(curr_gw_orig); |
215 | if (!curr_gw_orig->router) | 225 | if (!router_gw) |
216 | goto deselect_rcu; | 226 | goto deselect; |
217 | 227 | ||
218 | /* this node already is the gateway */ | 228 | /* this node already is the gateway */ |
219 | if (curr_gw_orig == orig_node) | 229 | if (curr_gw_orig == orig_node) |
220 | goto out_rcu; | 230 | goto out; |
221 | |||
222 | if (!orig_node->router) | ||
223 | goto out_rcu; | ||
224 | 231 | ||
225 | gw_tq_avg = curr_gw_orig->router->tq_avg; | 232 | router_orig = orig_node_get_router(orig_node); |
226 | rcu_read_unlock(); | 233 | if (!router_orig) |
234 | goto out; | ||
227 | 235 | ||
228 | orig_tq_avg = orig_node->router->tq_avg; | 236 | gw_tq_avg = router_gw->tq_avg; |
237 | orig_tq_avg = router_orig->tq_avg; | ||
229 | 238 | ||
230 | /* the TQ value has to be better */ | 239 | /* the TQ value has to be better */ |
231 | if (orig_tq_avg < gw_tq_avg) | 240 | if (orig_tq_avg < gw_tq_avg) |
@@ -243,18 +252,16 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | |||
243 | "Restarting gateway selection: better gateway found (tq curr: " | 252 | "Restarting gateway selection: better gateway found (tq curr: " |
244 | "%i, tq new: %i)\n", | 253 | "%i, tq new: %i)\n", |
245 | gw_tq_avg, orig_tq_avg); | 254 | gw_tq_avg, orig_tq_avg); |
246 | goto deselect; | ||
247 | 255 | ||
248 | out_rcu: | ||
249 | rcu_read_unlock(); | ||
250 | goto out; | ||
251 | deselect_rcu: | ||
252 | rcu_read_unlock(); | ||
253 | deselect: | 256 | deselect: |
254 | gw_deselect(bat_priv); | 257 | gw_deselect(bat_priv); |
255 | out: | 258 | out: |
256 | if (curr_gw_orig) | 259 | if (curr_gw_orig) |
257 | orig_node_free_ref(curr_gw_orig); | 260 | orig_node_free_ref(curr_gw_orig); |
261 | if (router_gw) | ||
262 | neigh_node_free_ref(router_gw); | ||
263 | if (router_orig) | ||
264 | neigh_node_free_ref(router_orig); | ||
258 | 265 | ||
259 | return; | 266 | return; |
260 | } | 267 | } |
@@ -362,23 +369,30 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
362 | spin_unlock_bh(&bat_priv->gw_list_lock); | 369 | spin_unlock_bh(&bat_priv->gw_list_lock); |
363 | } | 370 | } |
364 | 371 | ||
372 | /** | ||
373 | * fails if orig_node has no router | ||
374 | */ | ||
365 | static int _write_buffer_text(struct bat_priv *bat_priv, | 375 | static int _write_buffer_text(struct bat_priv *bat_priv, |
366 | struct seq_file *seq, struct gw_node *gw_node) | 376 | struct seq_file *seq, struct gw_node *gw_node) |
367 | { | 377 | { |
368 | struct gw_node *curr_gw; | 378 | struct gw_node *curr_gw; |
369 | int down, up, ret; | 379 | struct neigh_node *router; |
380 | int down, up, ret = -1; | ||
370 | 381 | ||
371 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); | 382 | gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); |
372 | 383 | ||
384 | router = orig_node_get_router(gw_node->orig_node); | ||
385 | if (!router) | ||
386 | goto out; | ||
387 | |||
373 | rcu_read_lock(); | 388 | rcu_read_lock(); |
374 | curr_gw = rcu_dereference(bat_priv->curr_gw); | 389 | curr_gw = rcu_dereference(bat_priv->curr_gw); |
375 | 390 | ||
376 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", | 391 | ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", |
377 | (curr_gw == gw_node ? "=>" : " "), | 392 | (curr_gw == gw_node ? "=>" : " "), |
378 | gw_node->orig_node->orig, | 393 | gw_node->orig_node->orig, |
379 | gw_node->orig_node->router->tq_avg, | 394 | router->tq_avg, router->addr, |
380 | gw_node->orig_node->router->addr, | 395 | router->if_incoming->net_dev->name, |
381 | gw_node->orig_node->router->if_incoming->net_dev->name, | ||
382 | gw_node->orig_node->gw_flags, | 396 | gw_node->orig_node->gw_flags, |
383 | (down > 2048 ? down / 1024 : down), | 397 | (down > 2048 ? down / 1024 : down), |
384 | (down > 2048 ? "MBit" : "KBit"), | 398 | (down > 2048 ? "MBit" : "KBit"), |
@@ -386,6 +400,8 @@ static int _write_buffer_text(struct bat_priv *bat_priv, | |||
386 | (up > 2048 ? "MBit" : "KBit")); | 400 | (up > 2048 ? "MBit" : "KBit")); |
387 | 401 | ||
388 | rcu_read_unlock(); | 402 | rcu_read_unlock(); |
403 | neigh_node_free_ref(router); | ||
404 | out: | ||
389 | return ret; | 405 | return ret; |
390 | } | 406 | } |
391 | 407 | ||
@@ -423,10 +439,10 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset) | |||
423 | if (gw_node->deleted) | 439 | if (gw_node->deleted) |
424 | continue; | 440 | continue; |
425 | 441 | ||
426 | if (!gw_node->orig_node->router) | 442 | /* fails if orig_node has no router */ |
443 | if (_write_buffer_text(bat_priv, seq, gw_node) < 0) | ||
427 | continue; | 444 | continue; |
428 | 445 | ||
429 | _write_buffer_text(bat_priv, seq, gw_node); | ||
430 | gw_count++; | 446 | gw_count++; |
431 | } | 447 | } |
432 | rcu_read_unlock(); | 448 | rcu_read_unlock(); |