diff options
Diffstat (limited to 'net/batman-adv/hard-interface.c')
-rw-r--r-- | net/batman-adv/hard-interface.c | 107 |
1 files changed, 61 insertions, 46 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index b3058e46ee6b..dfbfccc9fe40 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c | |||
@@ -31,9 +31,6 @@ | |||
31 | 31 | ||
32 | #include <linux/if_arp.h> | 32 | #include <linux/if_arp.h> |
33 | 33 | ||
34 | /* protect update critical side of hardif_list - but not the content */ | ||
35 | static DEFINE_SPINLOCK(hardif_list_lock); | ||
36 | |||
37 | 34 | ||
38 | static int batman_skb_recv(struct sk_buff *skb, | 35 | static int batman_skb_recv(struct sk_buff *skb, |
39 | struct net_device *dev, | 36 | struct net_device *dev, |
@@ -110,47 +107,57 @@ out: | |||
110 | return hard_iface; | 107 | return hard_iface; |
111 | } | 108 | } |
112 | 109 | ||
113 | static void update_primary_addr(struct bat_priv *bat_priv) | 110 | static void primary_if_update_addr(struct bat_priv *bat_priv) |
114 | { | 111 | { |
115 | struct vis_packet *vis_packet; | 112 | struct vis_packet *vis_packet; |
113 | struct hard_iface *primary_if; | ||
114 | |||
115 | primary_if = primary_if_get_selected(bat_priv); | ||
116 | if (!primary_if) | ||
117 | goto out; | ||
116 | 118 | ||
117 | vis_packet = (struct vis_packet *) | 119 | vis_packet = (struct vis_packet *) |
118 | bat_priv->my_vis_info->skb_packet->data; | 120 | bat_priv->my_vis_info->skb_packet->data; |
119 | memcpy(vis_packet->vis_orig, | 121 | memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN); |
120 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); | ||
121 | memcpy(vis_packet->sender_orig, | 122 | memcpy(vis_packet->sender_orig, |
122 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); | 123 | primary_if->net_dev->dev_addr, ETH_ALEN); |
124 | |||
125 | out: | ||
126 | if (primary_if) | ||
127 | hardif_free_ref(primary_if); | ||
123 | } | 128 | } |
124 | 129 | ||
125 | static void set_primary_if(struct bat_priv *bat_priv, | 130 | static void primary_if_select(struct bat_priv *bat_priv, |
126 | struct hard_iface *hard_iface) | 131 | struct hard_iface *new_hard_iface) |
127 | { | 132 | { |
133 | struct hard_iface *curr_hard_iface; | ||
128 | struct batman_packet *batman_packet; | 134 | struct batman_packet *batman_packet; |
129 | struct hard_iface *old_if; | ||
130 | 135 | ||
131 | if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount)) | 136 | ASSERT_RTNL(); |
132 | hard_iface = NULL; | 137 | |
138 | if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount)) | ||
139 | new_hard_iface = NULL; | ||
133 | 140 | ||
134 | old_if = bat_priv->primary_if; | 141 | curr_hard_iface = bat_priv->primary_if; |
135 | bat_priv->primary_if = hard_iface; | 142 | rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); |
136 | 143 | ||
137 | if (old_if) | 144 | if (curr_hard_iface) |
138 | hardif_free_ref(old_if); | 145 | hardif_free_ref(curr_hard_iface); |
139 | 146 | ||
140 | if (!bat_priv->primary_if) | 147 | if (!new_hard_iface) |
141 | return; | 148 | return; |
142 | 149 | ||
143 | batman_packet = (struct batman_packet *)(hard_iface->packet_buff); | 150 | batman_packet = (struct batman_packet *)(new_hard_iface->packet_buff); |
144 | batman_packet->flags = PRIMARIES_FIRST_HOP; | 151 | batman_packet->flags = PRIMARIES_FIRST_HOP; |
145 | batman_packet->ttl = TTL; | 152 | batman_packet->ttl = TTL; |
146 | 153 | ||
147 | update_primary_addr(bat_priv); | 154 | primary_if_update_addr(bat_priv); |
148 | 155 | ||
149 | /*** | 156 | /*** |
150 | * hacky trick to make sure that we send the HNA information via | 157 | * hacky trick to make sure that we send the TT information via |
151 | * our new primary interface | 158 | * our new primary interface |
152 | */ | 159 | */ |
153 | atomic_set(&bat_priv->hna_local_changed, 1); | 160 | atomic_set(&bat_priv->tt_local_changed, 1); |
154 | } | 161 | } |
155 | 162 | ||
156 | static bool hardif_is_iface_up(struct hard_iface *hard_iface) | 163 | static bool hardif_is_iface_up(struct hard_iface *hard_iface) |
@@ -236,9 +243,10 @@ void update_min_mtu(struct net_device *soft_iface) | |||
236 | static void hardif_activate_interface(struct hard_iface *hard_iface) | 243 | static void hardif_activate_interface(struct hard_iface *hard_iface) |
237 | { | 244 | { |
238 | struct bat_priv *bat_priv; | 245 | struct bat_priv *bat_priv; |
246 | struct hard_iface *primary_if = NULL; | ||
239 | 247 | ||
240 | if (hard_iface->if_status != IF_INACTIVE) | 248 | if (hard_iface->if_status != IF_INACTIVE) |
241 | return; | 249 | goto out; |
242 | 250 | ||
243 | bat_priv = netdev_priv(hard_iface->soft_iface); | 251 | bat_priv = netdev_priv(hard_iface->soft_iface); |
244 | 252 | ||
@@ -249,14 +257,18 @@ static void hardif_activate_interface(struct hard_iface *hard_iface) | |||
249 | * the first active interface becomes our primary interface or | 257 | * the first active interface becomes our primary interface or |
250 | * the next active interface after the old primay interface was removed | 258 | * the next active interface after the old primay interface was removed |
251 | */ | 259 | */ |
252 | if (!bat_priv->primary_if) | 260 | primary_if = primary_if_get_selected(bat_priv); |
253 | set_primary_if(bat_priv, hard_iface); | 261 | if (!primary_if) |
262 | primary_if_select(bat_priv, hard_iface); | ||
254 | 263 | ||
255 | bat_info(hard_iface->soft_iface, "Interface activated: %s\n", | 264 | bat_info(hard_iface->soft_iface, "Interface activated: %s\n", |
256 | hard_iface->net_dev->name); | 265 | hard_iface->net_dev->name); |
257 | 266 | ||
258 | update_min_mtu(hard_iface->soft_iface); | 267 | update_min_mtu(hard_iface->soft_iface); |
259 | return; | 268 | |
269 | out: | ||
270 | if (primary_if) | ||
271 | hardif_free_ref(primary_if); | ||
260 | } | 272 | } |
261 | 273 | ||
262 | static void hardif_deactivate_interface(struct hard_iface *hard_iface) | 274 | static void hardif_deactivate_interface(struct hard_iface *hard_iface) |
@@ -327,7 +339,7 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) | |||
327 | batman_packet->flags = 0; | 339 | batman_packet->flags = 0; |
328 | batman_packet->ttl = 2; | 340 | batman_packet->ttl = 2; |
329 | batman_packet->tq = TQ_MAX_VALUE; | 341 | batman_packet->tq = TQ_MAX_VALUE; |
330 | batman_packet->num_hna = 0; | 342 | batman_packet->num_tt = 0; |
331 | 343 | ||
332 | hard_iface->if_num = bat_priv->num_ifaces; | 344 | hard_iface->if_num = bat_priv->num_ifaces; |
333 | bat_priv->num_ifaces++; | 345 | bat_priv->num_ifaces++; |
@@ -386,12 +398,13 @@ err: | |||
386 | void hardif_disable_interface(struct hard_iface *hard_iface) | 398 | void hardif_disable_interface(struct hard_iface *hard_iface) |
387 | { | 399 | { |
388 | struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); | 400 | struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); |
401 | struct hard_iface *primary_if = NULL; | ||
389 | 402 | ||
390 | if (hard_iface->if_status == IF_ACTIVE) | 403 | if (hard_iface->if_status == IF_ACTIVE) |
391 | hardif_deactivate_interface(hard_iface); | 404 | hardif_deactivate_interface(hard_iface); |
392 | 405 | ||
393 | if (hard_iface->if_status != IF_INACTIVE) | 406 | if (hard_iface->if_status != IF_INACTIVE) |
394 | return; | 407 | goto out; |
395 | 408 | ||
396 | bat_info(hard_iface->soft_iface, "Removing interface: %s\n", | 409 | bat_info(hard_iface->soft_iface, "Removing interface: %s\n", |
397 | hard_iface->net_dev->name); | 410 | hard_iface->net_dev->name); |
@@ -400,11 +413,12 @@ void hardif_disable_interface(struct hard_iface *hard_iface) | |||
400 | bat_priv->num_ifaces--; | 413 | bat_priv->num_ifaces--; |
401 | orig_hash_del_if(hard_iface, bat_priv->num_ifaces); | 414 | orig_hash_del_if(hard_iface, bat_priv->num_ifaces); |
402 | 415 | ||
403 | if (hard_iface == bat_priv->primary_if) { | 416 | primary_if = primary_if_get_selected(bat_priv); |
417 | if (hard_iface == primary_if) { | ||
404 | struct hard_iface *new_if; | 418 | struct hard_iface *new_if; |
405 | 419 | ||
406 | new_if = hardif_get_active(hard_iface->soft_iface); | 420 | new_if = hardif_get_active(hard_iface->soft_iface); |
407 | set_primary_if(bat_priv, new_if); | 421 | primary_if_select(bat_priv, new_if); |
408 | 422 | ||
409 | if (new_if) | 423 | if (new_if) |
410 | hardif_free_ref(new_if); | 424 | hardif_free_ref(new_if); |
@@ -425,6 +439,10 @@ void hardif_disable_interface(struct hard_iface *hard_iface) | |||
425 | 439 | ||
426 | hard_iface->soft_iface = NULL; | 440 | hard_iface->soft_iface = NULL; |
427 | hardif_free_ref(hard_iface); | 441 | hardif_free_ref(hard_iface); |
442 | |||
443 | out: | ||
444 | if (primary_if) | ||
445 | hardif_free_ref(primary_if); | ||
428 | } | 446 | } |
429 | 447 | ||
430 | static struct hard_iface *hardif_add_interface(struct net_device *net_dev) | 448 | static struct hard_iface *hardif_add_interface(struct net_device *net_dev) |
@@ -432,6 +450,8 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev) | |||
432 | struct hard_iface *hard_iface; | 450 | struct hard_iface *hard_iface; |
433 | int ret; | 451 | int ret; |
434 | 452 | ||
453 | ASSERT_RTNL(); | ||
454 | |||
435 | ret = is_valid_iface(net_dev); | 455 | ret = is_valid_iface(net_dev); |
436 | if (ret != 1) | 456 | if (ret != 1) |
437 | goto out; | 457 | goto out; |
@@ -458,10 +478,7 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev) | |||
458 | atomic_set(&hard_iface->refcount, 2); | 478 | atomic_set(&hard_iface->refcount, 2); |
459 | 479 | ||
460 | check_known_mac_addr(hard_iface->net_dev); | 480 | check_known_mac_addr(hard_iface->net_dev); |
461 | |||
462 | spin_lock(&hardif_list_lock); | ||
463 | list_add_tail_rcu(&hard_iface->list, &hardif_list); | 481 | list_add_tail_rcu(&hard_iface->list, &hardif_list); |
464 | spin_unlock(&hardif_list_lock); | ||
465 | 482 | ||
466 | return hard_iface; | 483 | return hard_iface; |
467 | 484 | ||
@@ -475,6 +492,8 @@ out: | |||
475 | 492 | ||
476 | static void hardif_remove_interface(struct hard_iface *hard_iface) | 493 | static void hardif_remove_interface(struct hard_iface *hard_iface) |
477 | { | 494 | { |
495 | ASSERT_RTNL(); | ||
496 | |||
478 | /* first deactivate interface */ | 497 | /* first deactivate interface */ |
479 | if (hard_iface->if_status != IF_NOT_IN_USE) | 498 | if (hard_iface->if_status != IF_NOT_IN_USE) |
480 | hardif_disable_interface(hard_iface); | 499 | hardif_disable_interface(hard_iface); |
@@ -490,20 +509,11 @@ static void hardif_remove_interface(struct hard_iface *hard_iface) | |||
490 | void hardif_remove_interfaces(void) | 509 | void hardif_remove_interfaces(void) |
491 | { | 510 | { |
492 | struct hard_iface *hard_iface, *hard_iface_tmp; | 511 | struct hard_iface *hard_iface, *hard_iface_tmp; |
493 | struct list_head if_queue; | ||
494 | |||
495 | INIT_LIST_HEAD(&if_queue); | ||
496 | 512 | ||
497 | spin_lock(&hardif_list_lock); | 513 | rtnl_lock(); |
498 | list_for_each_entry_safe(hard_iface, hard_iface_tmp, | 514 | list_for_each_entry_safe(hard_iface, hard_iface_tmp, |
499 | &hardif_list, list) { | 515 | &hardif_list, list) { |
500 | list_del_rcu(&hard_iface->list); | 516 | list_del_rcu(&hard_iface->list); |
501 | list_add_tail(&hard_iface->list, &if_queue); | ||
502 | } | ||
503 | spin_unlock(&hardif_list_lock); | ||
504 | |||
505 | rtnl_lock(); | ||
506 | list_for_each_entry_safe(hard_iface, hard_iface_tmp, &if_queue, list) { | ||
507 | hardif_remove_interface(hard_iface); | 517 | hardif_remove_interface(hard_iface); |
508 | } | 518 | } |
509 | rtnl_unlock(); | 519 | rtnl_unlock(); |
@@ -514,6 +524,7 @@ static int hard_if_event(struct notifier_block *this, | |||
514 | { | 524 | { |
515 | struct net_device *net_dev = (struct net_device *)ptr; | 525 | struct net_device *net_dev = (struct net_device *)ptr; |
516 | struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); | 526 | struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); |
527 | struct hard_iface *primary_if = NULL; | ||
517 | struct bat_priv *bat_priv; | 528 | struct bat_priv *bat_priv; |
518 | 529 | ||
519 | if (!hard_iface && event == NETDEV_REGISTER) | 530 | if (!hard_iface && event == NETDEV_REGISTER) |
@@ -531,9 +542,7 @@ static int hard_if_event(struct notifier_block *this, | |||
531 | hardif_deactivate_interface(hard_iface); | 542 | hardif_deactivate_interface(hard_iface); |
532 | break; | 543 | break; |
533 | case NETDEV_UNREGISTER: | 544 | case NETDEV_UNREGISTER: |
534 | spin_lock(&hardif_list_lock); | ||
535 | list_del_rcu(&hard_iface->list); | 545 | list_del_rcu(&hard_iface->list); |
536 | spin_unlock(&hardif_list_lock); | ||
537 | 546 | ||
538 | hardif_remove_interface(hard_iface); | 547 | hardif_remove_interface(hard_iface); |
539 | break; | 548 | break; |
@@ -549,8 +558,12 @@ static int hard_if_event(struct notifier_block *this, | |||
549 | update_mac_addresses(hard_iface); | 558 | update_mac_addresses(hard_iface); |
550 | 559 | ||
551 | bat_priv = netdev_priv(hard_iface->soft_iface); | 560 | bat_priv = netdev_priv(hard_iface->soft_iface); |
552 | if (hard_iface == bat_priv->primary_if) | 561 | primary_if = primary_if_get_selected(bat_priv); |
553 | update_primary_addr(bat_priv); | 562 | if (!primary_if) |
563 | goto hardif_put; | ||
564 | |||
565 | if (hard_iface == primary_if) | ||
566 | primary_if_update_addr(bat_priv); | ||
554 | break; | 567 | break; |
555 | default: | 568 | default: |
556 | break; | 569 | break; |
@@ -559,6 +572,8 @@ static int hard_if_event(struct notifier_block *this, | |||
559 | hardif_put: | 572 | hardif_put: |
560 | hardif_free_ref(hard_iface); | 573 | hardif_free_ref(hard_iface); |
561 | out: | 574 | out: |
575 | if (primary_if) | ||
576 | hardif_free_ref(primary_if); | ||
562 | return NOTIFY_DONE; | 577 | return NOTIFY_DONE; |
563 | } | 578 | } |
564 | 579 | ||