diff options
author | David S. Miller <davem@davemloft.net> | 2014-05-13 12:53:36 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-13 12:53:36 -0400 |
commit | 6262971a8a5082e3662238f50e3f3c351a2e552f (patch) | |
tree | b53a4b4317564939ab2608a4f07217e68de25885 /net | |
parent | 2176d5d41891753774f648b67470398a5acab584 (diff) | |
parent | 709de13f0c532fe9c468c094aff069a725ed57fe (diff) |
Merge tag 'batman-adv-fix-for-davem' of git://git.open-mesh.org/linux-merge
Included changes:
- properly release neigh_ifinfo in batadv_iv_ogm_process_per_outif()
- properly release orig_ifinfo->router when freeing orig_ifinfo
- properly release neigh_node objects during periodic check
- properly release neigh_info objects when the related hard_iface
is free'd
These changes are all very important because they fix some
reference counting imbalances that lead to the
impossibility of releasing the netdev object used by
batman-adv on shutdown.
The consequence is that such object cannot be destroyed by
the networking stack (the refcounter does not reach zero)
thus bringing the system in hanging state during a normal
reboot operation or a network reconfiguration.
Diffstat (limited to 'net')
-rw-r--r-- | net/batman-adv/bat_iv_ogm.c | 2 | ||||
-rw-r--r-- | net/batman-adv/originator.c | 59 |
2 files changed, 57 insertions, 4 deletions
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index b3bd4ec3fd94..f04224c32005 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c | |||
@@ -1545,6 +1545,8 @@ out_neigh: | |||
1545 | if ((orig_neigh_node) && (!is_single_hop_neigh)) | 1545 | if ((orig_neigh_node) && (!is_single_hop_neigh)) |
1546 | batadv_orig_node_free_ref(orig_neigh_node); | 1546 | batadv_orig_node_free_ref(orig_neigh_node); |
1547 | out: | 1547 | out: |
1548 | if (router_ifinfo) | ||
1549 | batadv_neigh_ifinfo_free_ref(router_ifinfo); | ||
1548 | if (router) | 1550 | if (router) |
1549 | batadv_neigh_node_free_ref(router); | 1551 | batadv_neigh_node_free_ref(router); |
1550 | if (router_router) | 1552 | if (router_router) |
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index ffd9dfbd9b0e..1785da37b82c 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c | |||
@@ -501,12 +501,17 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node, | |||
501 | static void batadv_orig_ifinfo_free_rcu(struct rcu_head *rcu) | 501 | static void batadv_orig_ifinfo_free_rcu(struct rcu_head *rcu) |
502 | { | 502 | { |
503 | struct batadv_orig_ifinfo *orig_ifinfo; | 503 | struct batadv_orig_ifinfo *orig_ifinfo; |
504 | struct batadv_neigh_node *router; | ||
504 | 505 | ||
505 | orig_ifinfo = container_of(rcu, struct batadv_orig_ifinfo, rcu); | 506 | orig_ifinfo = container_of(rcu, struct batadv_orig_ifinfo, rcu); |
506 | 507 | ||
507 | if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT) | 508 | if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT) |
508 | batadv_hardif_free_ref_now(orig_ifinfo->if_outgoing); | 509 | batadv_hardif_free_ref_now(orig_ifinfo->if_outgoing); |
509 | 510 | ||
511 | /* this is the last reference to this object */ | ||
512 | router = rcu_dereference_protected(orig_ifinfo->router, true); | ||
513 | if (router) | ||
514 | batadv_neigh_node_free_ref_now(router); | ||
510 | kfree(orig_ifinfo); | 515 | kfree(orig_ifinfo); |
511 | } | 516 | } |
512 | 517 | ||
@@ -702,6 +707,47 @@ free_orig_node: | |||
702 | } | 707 | } |
703 | 708 | ||
704 | /** | 709 | /** |
710 | * batadv_purge_neigh_ifinfo - purge obsolete ifinfo entries from neighbor | ||
711 | * @bat_priv: the bat priv with all the soft interface information | ||
712 | * @neigh: orig node which is to be checked | ||
713 | */ | ||
714 | static void | ||
715 | batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv, | ||
716 | struct batadv_neigh_node *neigh) | ||
717 | { | ||
718 | struct batadv_neigh_ifinfo *neigh_ifinfo; | ||
719 | struct batadv_hard_iface *if_outgoing; | ||
720 | struct hlist_node *node_tmp; | ||
721 | |||
722 | spin_lock_bh(&neigh->ifinfo_lock); | ||
723 | |||
724 | /* for all ifinfo objects for this neighinator */ | ||
725 | hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, | ||
726 | &neigh->ifinfo_list, list) { | ||
727 | if_outgoing = neigh_ifinfo->if_outgoing; | ||
728 | |||
729 | /* always keep the default interface */ | ||
730 | if (if_outgoing == BATADV_IF_DEFAULT) | ||
731 | continue; | ||
732 | |||
733 | /* don't purge if the interface is not (going) down */ | ||
734 | if ((if_outgoing->if_status != BATADV_IF_INACTIVE) && | ||
735 | (if_outgoing->if_status != BATADV_IF_NOT_IN_USE) && | ||
736 | (if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)) | ||
737 | continue; | ||
738 | |||
739 | batadv_dbg(BATADV_DBG_BATMAN, bat_priv, | ||
740 | "neighbor/ifinfo purge: neighbor %pM, iface: %s\n", | ||
741 | neigh->addr, if_outgoing->net_dev->name); | ||
742 | |||
743 | hlist_del_rcu(&neigh_ifinfo->list); | ||
744 | batadv_neigh_ifinfo_free_ref(neigh_ifinfo); | ||
745 | } | ||
746 | |||
747 | spin_unlock_bh(&neigh->ifinfo_lock); | ||
748 | } | ||
749 | |||
750 | /** | ||
705 | * batadv_purge_orig_ifinfo - purge obsolete ifinfo entries from originator | 751 | * batadv_purge_orig_ifinfo - purge obsolete ifinfo entries from originator |
706 | * @bat_priv: the bat priv with all the soft interface information | 752 | * @bat_priv: the bat priv with all the soft interface information |
707 | * @orig_node: orig node which is to be checked | 753 | * @orig_node: orig node which is to be checked |
@@ -800,6 +846,11 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, | |||
800 | 846 | ||
801 | hlist_del_rcu(&neigh_node->list); | 847 | hlist_del_rcu(&neigh_node->list); |
802 | batadv_neigh_node_free_ref(neigh_node); | 848 | batadv_neigh_node_free_ref(neigh_node); |
849 | } else { | ||
850 | /* only necessary if not the whole neighbor is to be | ||
851 | * deleted, but some interface has been removed. | ||
852 | */ | ||
853 | batadv_purge_neigh_ifinfo(bat_priv, neigh_node); | ||
803 | } | 854 | } |
804 | } | 855 | } |
805 | 856 | ||
@@ -857,7 +908,7 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, | |||
857 | { | 908 | { |
858 | struct batadv_neigh_node *best_neigh_node; | 909 | struct batadv_neigh_node *best_neigh_node; |
859 | struct batadv_hard_iface *hard_iface; | 910 | struct batadv_hard_iface *hard_iface; |
860 | bool changed; | 911 | bool changed_ifinfo, changed_neigh; |
861 | 912 | ||
862 | if (batadv_has_timed_out(orig_node->last_seen, | 913 | if (batadv_has_timed_out(orig_node->last_seen, |
863 | 2 * BATADV_PURGE_TIMEOUT)) { | 914 | 2 * BATADV_PURGE_TIMEOUT)) { |
@@ -867,10 +918,10 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, | |||
867 | jiffies_to_msecs(orig_node->last_seen)); | 918 | jiffies_to_msecs(orig_node->last_seen)); |
868 | return true; | 919 | return true; |
869 | } | 920 | } |
870 | changed = batadv_purge_orig_ifinfo(bat_priv, orig_node); | 921 | changed_ifinfo = batadv_purge_orig_ifinfo(bat_priv, orig_node); |
871 | changed = changed || batadv_purge_orig_neighbors(bat_priv, orig_node); | 922 | changed_neigh = batadv_purge_orig_neighbors(bat_priv, orig_node); |
872 | 923 | ||
873 | if (!changed) | 924 | if (!changed_ifinfo && !changed_neigh) |
874 | return false; | 925 | return false; |
875 | 926 | ||
876 | /* first for NULL ... */ | 927 | /* first for NULL ... */ |