diff options
author | Simon Wunderlich <siwu@hrz.tu-chemnitz.de> | 2011-04-17 14:34:27 -0400 |
---|---|---|
committer | Sven Eckelmann <sven@narfation.org> | 2011-04-17 15:11:02 -0400 |
commit | ba85fac28005a59e6e03fdb13918fc6f6e69a3ca (patch) | |
tree | 4b4def788586b0d99a1ed54c2224135df72adc8a /net/batman-adv | |
parent | c4aac1ab9b973798163b34939b522f01e4d28ac9 (diff) |
batman-adv: protect softif_neigh by rcu
Add get/set wrapper functions for softif_neigh and
use rcu functions to manipulate the pointers.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Diffstat (limited to 'net/batman-adv')
-rw-r--r-- | net/batman-adv/soft-interface.c | 114 | ||||
-rw-r--r-- | net/batman-adv/types.h | 2 |
2 files changed, 88 insertions, 28 deletions
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 9ed26140a269..ad6da4c7ddb5 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c | |||
@@ -90,10 +90,51 @@ static void softif_neigh_free_ref(struct softif_neigh *softif_neigh) | |||
90 | call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); | 90 | call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); |
91 | } | 91 | } |
92 | 92 | ||
93 | static struct softif_neigh *softif_neigh_get_selected(struct bat_priv *bat_priv) | ||
94 | { | ||
95 | struct softif_neigh *neigh; | ||
96 | |||
97 | rcu_read_lock(); | ||
98 | neigh = rcu_dereference(bat_priv->softif_neigh); | ||
99 | |||
100 | if (neigh && !atomic_inc_not_zero(&neigh->refcount)) | ||
101 | neigh = NULL; | ||
102 | |||
103 | rcu_read_unlock(); | ||
104 | return neigh; | ||
105 | } | ||
106 | |||
107 | static void softif_neigh_select(struct bat_priv *bat_priv, | ||
108 | struct softif_neigh *new_neigh) | ||
109 | { | ||
110 | struct softif_neigh *curr_neigh; | ||
111 | |||
112 | spin_lock_bh(&bat_priv->softif_neigh_lock); | ||
113 | |||
114 | if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount)) | ||
115 | new_neigh = NULL; | ||
116 | |||
117 | curr_neigh = bat_priv->softif_neigh; | ||
118 | rcu_assign_pointer(bat_priv->softif_neigh, new_neigh); | ||
119 | |||
120 | if (curr_neigh) | ||
121 | softif_neigh_free_ref(curr_neigh); | ||
122 | |||
123 | spin_unlock_bh(&bat_priv->softif_neigh_lock); | ||
124 | } | ||
125 | |||
126 | static void softif_neigh_deselect(struct bat_priv *bat_priv) | ||
127 | { | ||
128 | softif_neigh_select(bat_priv, NULL); | ||
129 | } | ||
130 | |||
93 | void softif_neigh_purge(struct bat_priv *bat_priv) | 131 | void softif_neigh_purge(struct bat_priv *bat_priv) |
94 | { | 132 | { |
95 | struct softif_neigh *softif_neigh, *softif_neigh_tmp; | 133 | struct softif_neigh *softif_neigh, *curr_softif_neigh; |
96 | struct hlist_node *node, *node_tmp; | 134 | struct hlist_node *node, *node_tmp; |
135 | char do_deselect = 0; | ||
136 | |||
137 | curr_softif_neigh = softif_neigh_get_selected(bat_priv); | ||
97 | 138 | ||
98 | spin_lock_bh(&bat_priv->softif_neigh_lock); | 139 | spin_lock_bh(&bat_priv->softif_neigh_lock); |
99 | 140 | ||
@@ -105,22 +146,26 @@ void softif_neigh_purge(struct bat_priv *bat_priv) | |||
105 | (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) | 146 | (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) |
106 | continue; | 147 | continue; |
107 | 148 | ||
108 | hlist_del_rcu(&softif_neigh->list); | 149 | if (curr_softif_neigh == softif_neigh) { |
109 | |||
110 | if (bat_priv->softif_neigh == softif_neigh) { | ||
111 | bat_dbg(DBG_ROUTES, bat_priv, | 150 | bat_dbg(DBG_ROUTES, bat_priv, |
112 | "Current mesh exit point '%pM' vanished " | 151 | "Current mesh exit point '%pM' vanished " |
113 | "(vid: %d).\n", | 152 | "(vid: %d).\n", |
114 | softif_neigh->addr, softif_neigh->vid); | 153 | softif_neigh->addr, softif_neigh->vid); |
115 | softif_neigh_tmp = bat_priv->softif_neigh; | 154 | do_deselect = 1; |
116 | bat_priv->softif_neigh = NULL; | ||
117 | softif_neigh_free_ref(softif_neigh_tmp); | ||
118 | } | 155 | } |
119 | 156 | ||
157 | hlist_del_rcu(&softif_neigh->list); | ||
120 | softif_neigh_free_ref(softif_neigh); | 158 | softif_neigh_free_ref(softif_neigh); |
121 | } | 159 | } |
122 | 160 | ||
123 | spin_unlock_bh(&bat_priv->softif_neigh_lock); | 161 | spin_unlock_bh(&bat_priv->softif_neigh_lock); |
162 | |||
163 | /* soft_neigh_deselect() needs to acquire the softif_neigh_lock */ | ||
164 | if (do_deselect) | ||
165 | softif_neigh_deselect(bat_priv); | ||
166 | |||
167 | if (curr_softif_neigh) | ||
168 | softif_neigh_free_ref(curr_softif_neigh); | ||
124 | } | 169 | } |
125 | 170 | ||
126 | static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, | 171 | static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, |
@@ -171,6 +216,7 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) | |||
171 | struct bat_priv *bat_priv = netdev_priv(net_dev); | 216 | struct bat_priv *bat_priv = netdev_priv(net_dev); |
172 | struct softif_neigh *softif_neigh; | 217 | struct softif_neigh *softif_neigh; |
173 | struct hlist_node *node; | 218 | struct hlist_node *node; |
219 | struct softif_neigh *curr_softif_neigh; | ||
174 | 220 | ||
175 | if (!bat_priv->primary_if) { | 221 | if (!bat_priv->primary_if) { |
176 | return seq_printf(seq, "BATMAN mesh %s disabled - " | 222 | return seq_printf(seq, "BATMAN mesh %s disabled - " |
@@ -180,14 +226,17 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) | |||
180 | 226 | ||
181 | seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); | 227 | seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); |
182 | 228 | ||
229 | curr_softif_neigh = softif_neigh_get_selected(bat_priv); | ||
183 | rcu_read_lock(); | 230 | rcu_read_lock(); |
184 | hlist_for_each_entry_rcu(softif_neigh, node, | 231 | hlist_for_each_entry_rcu(softif_neigh, node, |
185 | &bat_priv->softif_neigh_list, list) | 232 | &bat_priv->softif_neigh_list, list) |
186 | seq_printf(seq, "%s %pM (vid: %d)\n", | 233 | seq_printf(seq, "%s %pM (vid: %d)\n", |
187 | bat_priv->softif_neigh == softif_neigh | 234 | curr_softif_neigh == softif_neigh |
188 | ? "=>" : " ", softif_neigh->addr, | 235 | ? "=>" : " ", softif_neigh->addr, |
189 | softif_neigh->vid); | 236 | softif_neigh->vid); |
190 | rcu_read_unlock(); | 237 | rcu_read_unlock(); |
238 | if (curr_softif_neigh) | ||
239 | softif_neigh_free_ref(curr_softif_neigh); | ||
191 | 240 | ||
192 | return 0; | 241 | return 0; |
193 | } | 242 | } |
@@ -198,7 +247,8 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, | |||
198 | struct bat_priv *bat_priv = netdev_priv(dev); | 247 | struct bat_priv *bat_priv = netdev_priv(dev); |
199 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; | 248 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; |
200 | struct batman_packet *batman_packet; | 249 | struct batman_packet *batman_packet; |
201 | struct softif_neigh *softif_neigh, *softif_neigh_tmp; | 250 | struct softif_neigh *softif_neigh; |
251 | struct softif_neigh *curr_softif_neigh = NULL; | ||
202 | 252 | ||
203 | if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) | 253 | if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) |
204 | batman_packet = (struct batman_packet *) | 254 | batman_packet = (struct batman_packet *) |
@@ -223,7 +273,8 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, | |||
223 | if (!softif_neigh) | 273 | if (!softif_neigh) |
224 | goto err; | 274 | goto err; |
225 | 275 | ||
226 | if (bat_priv->softif_neigh == softif_neigh) | 276 | curr_softif_neigh = softif_neigh_get_selected(bat_priv); |
277 | if (curr_softif_neigh == softif_neigh) | ||
227 | goto out; | 278 | goto out; |
228 | 279 | ||
229 | /* we got a neighbor but its mac is 'bigger' than ours */ | 280 | /* we got a neighbor but its mac is 'bigger' than ours */ |
@@ -232,38 +283,39 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, | |||
232 | goto out; | 283 | goto out; |
233 | 284 | ||
234 | /* switch to new 'smallest neighbor' */ | 285 | /* switch to new 'smallest neighbor' */ |
235 | if ((bat_priv->softif_neigh) && | 286 | if ((curr_softif_neigh) && |
236 | (memcmp(softif_neigh->addr, bat_priv->softif_neigh->addr, | 287 | (memcmp(softif_neigh->addr, curr_softif_neigh->addr, |
237 | ETH_ALEN) < 0)) { | 288 | ETH_ALEN) < 0)) { |
238 | bat_dbg(DBG_ROUTES, bat_priv, | 289 | bat_dbg(DBG_ROUTES, bat_priv, |
239 | "Changing mesh exit point from %pM (vid: %d) " | 290 | "Changing mesh exit point from %pM (vid: %d) " |
240 | "to %pM (vid: %d).\n", | 291 | "to %pM (vid: %d).\n", |
241 | bat_priv->softif_neigh->addr, | 292 | curr_softif_neigh->addr, |
242 | bat_priv->softif_neigh->vid, | 293 | curr_softif_neigh->vid, |
243 | softif_neigh->addr, softif_neigh->vid); | 294 | softif_neigh->addr, softif_neigh->vid); |
244 | softif_neigh_tmp = bat_priv->softif_neigh; | 295 | |
245 | bat_priv->softif_neigh = softif_neigh; | 296 | softif_neigh_select(bat_priv, softif_neigh); |
246 | softif_neigh_free_ref(softif_neigh_tmp); | 297 | goto out; |
247 | /* we need to hold the additional reference */ | ||
248 | goto err; | ||
249 | } | 298 | } |
250 | 299 | ||
251 | /* close own batX device and use softif_neigh as exit node */ | 300 | /* close own batX device and use softif_neigh as exit node */ |
252 | if ((!bat_priv->softif_neigh) && | 301 | if ((!curr_softif_neigh) && |
253 | (memcmp(softif_neigh->addr, | 302 | (memcmp(softif_neigh->addr, |
254 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { | 303 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { |
255 | bat_dbg(DBG_ROUTES, bat_priv, | 304 | bat_dbg(DBG_ROUTES, bat_priv, |
256 | "Setting mesh exit point to %pM (vid: %d).\n", | 305 | "Setting mesh exit point to %pM (vid: %d).\n", |
257 | softif_neigh->addr, softif_neigh->vid); | 306 | softif_neigh->addr, softif_neigh->vid); |
258 | bat_priv->softif_neigh = softif_neigh; | 307 | |
259 | /* we need to hold the additional reference */ | 308 | softif_neigh_select(bat_priv, softif_neigh); |
260 | goto err; | 309 | goto out; |
261 | } | 310 | } |
262 | 311 | ||
263 | out: | 312 | out: |
264 | softif_neigh_free_ref(softif_neigh); | 313 | softif_neigh_free_ref(softif_neigh); |
265 | err: | 314 | err: |
266 | kfree_skb(skb); | 315 | kfree_skb(skb); |
316 | if (curr_softif_neigh) | ||
317 | softif_neigh_free_ref(curr_softif_neigh); | ||
318 | |||
267 | return; | 319 | return; |
268 | } | 320 | } |
269 | 321 | ||
@@ -321,6 +373,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) | |||
321 | struct bat_priv *bat_priv = netdev_priv(soft_iface); | 373 | struct bat_priv *bat_priv = netdev_priv(soft_iface); |
322 | struct bcast_packet *bcast_packet; | 374 | struct bcast_packet *bcast_packet; |
323 | struct vlan_ethhdr *vhdr; | 375 | struct vlan_ethhdr *vhdr; |
376 | struct softif_neigh *curr_softif_neigh = NULL; | ||
324 | int data_len = skb->len, ret; | 377 | int data_len = skb->len, ret; |
325 | short vid = -1; | 378 | short vid = -1; |
326 | bool do_bcast = false; | 379 | bool do_bcast = false; |
@@ -348,7 +401,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) | |||
348 | * if we have a another chosen mesh exit node in range | 401 | * if we have a another chosen mesh exit node in range |
349 | * it will transport the packets to the mesh | 402 | * it will transport the packets to the mesh |
350 | */ | 403 | */ |
351 | if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) | 404 | curr_softif_neigh = softif_neigh_get_selected(bat_priv); |
405 | if ((curr_softif_neigh) && (curr_softif_neigh->vid == vid)) | ||
352 | goto dropped; | 406 | goto dropped; |
353 | 407 | ||
354 | /* TODO: check this for locks */ | 408 | /* TODO: check this for locks */ |
@@ -410,6 +464,8 @@ dropped: | |||
410 | dropped_freed: | 464 | dropped_freed: |
411 | bat_priv->stats.tx_dropped++; | 465 | bat_priv->stats.tx_dropped++; |
412 | end: | 466 | end: |
467 | if (curr_softif_neigh) | ||
468 | softif_neigh_free_ref(curr_softif_neigh); | ||
413 | return NETDEV_TX_OK; | 469 | return NETDEV_TX_OK; |
414 | } | 470 | } |
415 | 471 | ||
@@ -421,6 +477,7 @@ void interface_rx(struct net_device *soft_iface, | |||
421 | struct unicast_packet *unicast_packet; | 477 | struct unicast_packet *unicast_packet; |
422 | struct ethhdr *ethhdr; | 478 | struct ethhdr *ethhdr; |
423 | struct vlan_ethhdr *vhdr; | 479 | struct vlan_ethhdr *vhdr; |
480 | struct softif_neigh *curr_softif_neigh = NULL; | ||
424 | short vid = -1; | 481 | short vid = -1; |
425 | int ret; | 482 | int ret; |
426 | 483 | ||
@@ -450,7 +507,8 @@ void interface_rx(struct net_device *soft_iface, | |||
450 | * if we have a another chosen mesh exit node in range | 507 | * if we have a another chosen mesh exit node in range |
451 | * it will transport the packets to the non-mesh network | 508 | * it will transport the packets to the non-mesh network |
452 | */ | 509 | */ |
453 | if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) { | 510 | curr_softif_neigh = softif_neigh_get_selected(bat_priv); |
511 | if (curr_softif_neigh && (curr_softif_neigh->vid == vid)) { | ||
454 | skb_push(skb, hdr_size); | 512 | skb_push(skb, hdr_size); |
455 | unicast_packet = (struct unicast_packet *)skb->data; | 513 | unicast_packet = (struct unicast_packet *)skb->data; |
456 | 514 | ||
@@ -461,7 +519,7 @@ void interface_rx(struct net_device *soft_iface, | |||
461 | skb_reset_mac_header(skb); | 519 | skb_reset_mac_header(skb); |
462 | 520 | ||
463 | memcpy(unicast_packet->dest, | 521 | memcpy(unicast_packet->dest, |
464 | bat_priv->softif_neigh->addr, ETH_ALEN); | 522 | curr_softif_neigh->addr, ETH_ALEN); |
465 | ret = route_unicast_packet(skb, recv_if); | 523 | ret = route_unicast_packet(skb, recv_if); |
466 | if (ret == NET_RX_DROP) | 524 | if (ret == NET_RX_DROP) |
467 | goto dropped; | 525 | goto dropped; |
@@ -486,11 +544,13 @@ void interface_rx(struct net_device *soft_iface, | |||
486 | soft_iface->last_rx = jiffies; | 544 | soft_iface->last_rx = jiffies; |
487 | 545 | ||
488 | netif_rx(skb); | 546 | netif_rx(skb); |
489 | return; | 547 | goto out; |
490 | 548 | ||
491 | dropped: | 549 | dropped: |
492 | kfree_skb(skb); | 550 | kfree_skb(skb); |
493 | out: | 551 | out: |
552 | if (curr_softif_neigh) | ||
553 | softif_neigh_free_ref(curr_softif_neigh); | ||
494 | return; | 554 | return; |
495 | } | 555 | } |
496 | 556 | ||
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 091476df4f0e..75123b1ae0de 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h | |||
@@ -147,7 +147,7 @@ struct bat_priv { | |||
147 | atomic_t batman_queue_left; | 147 | atomic_t batman_queue_left; |
148 | char num_ifaces; | 148 | char num_ifaces; |
149 | struct hlist_head softif_neigh_list; | 149 | struct hlist_head softif_neigh_list; |
150 | struct softif_neigh *softif_neigh; | 150 | struct softif_neigh __rcu *softif_neigh; |
151 | struct debug_log *debug_log; | 151 | struct debug_log *debug_log; |
152 | struct hard_iface *primary_if; | 152 | struct hard_iface *primary_if; |
153 | struct kobject *mesh_obj; | 153 | struct kobject *mesh_obj; |