aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv
diff options
context:
space:
mode:
authorSimon Wunderlich <siwu@hrz.tu-chemnitz.de>2011-04-17 14:34:27 -0400
committerSven Eckelmann <sven@narfation.org>2011-04-17 15:11:02 -0400
commitba85fac28005a59e6e03fdb13918fc6f6e69a3ca (patch)
tree4b4def788586b0d99a1ed54c2224135df72adc8a /net/batman-adv
parentc4aac1ab9b973798163b34939b522f01e4d28ac9 (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.c114
-rw-r--r--net/batman-adv/types.h2
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
93static 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
107static 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
126static void softif_neigh_deselect(struct bat_priv *bat_priv)
127{
128 softif_neigh_select(bat_priv, NULL);
129}
130
93void softif_neigh_purge(struct bat_priv *bat_priv) 131void 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
126static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, 171static 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
263out: 312out:
264 softif_neigh_free_ref(softif_neigh); 313 softif_neigh_free_ref(softif_neigh);
265err: 314err:
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:
410dropped_freed: 464dropped_freed:
411 bat_priv->stats.tx_dropped++; 465 bat_priv->stats.tx_dropped++;
412end: 466end:
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
491dropped: 549dropped:
492 kfree_skb(skb); 550 kfree_skb(skb);
493out: 551out:
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;