diff options
author | Thomas Pedersen <thomas@cozybit.com> | 2013-06-10 16:17:21 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-06-11 07:14:42 -0400 |
commit | ecccd072b07e7fd09c54d0f86f9374e2645cde97 (patch) | |
tree | 8f2bc22f2aa1cb5a96d31ec3f7d87e6400d915e2 /net/mac80211 | |
parent | 780b40df12cf0161d8ccc5381940e04584793933 (diff) |
mac80211: fix mesh deadlock
The patch "cfg80211/mac80211: use cfg80211 wdev mutex in
mac80211" introduced several deadlocks by converting the
ifmsh->mtx to wdev->mtx. Solve these by:
1. drop the cancel_work_sync() in ieee80211_stop_mesh().
Instead make the mesh work conditional on whether the mesh
is running or not.
2. lock the mesh work with sdata_lock() to protect beacon
updates and prevent races with wdev->mesh_id_len or
cfg80211.
Signed-off-by: Thomas Pedersen <thomas@cozybit.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/mesh.c | 29 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 7 |
2 files changed, 18 insertions, 18 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 73a597bad6e0..d5faf91632c1 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
@@ -579,9 +579,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) | |||
579 | mesh_path_expire(sdata); | 579 | mesh_path_expire(sdata); |
580 | 580 | ||
581 | changed = mesh_accept_plinks_update(sdata); | 581 | changed = mesh_accept_plinks_update(sdata); |
582 | sdata_lock(sdata); | ||
583 | ieee80211_mbss_info_change_notify(sdata, changed); | 582 | ieee80211_mbss_info_change_notify(sdata, changed); |
584 | sdata_unlock(sdata); | ||
585 | 583 | ||
586 | mod_timer(&ifmsh->housekeeping_timer, | 584 | mod_timer(&ifmsh->housekeeping_timer, |
587 | round_jiffies(jiffies + | 585 | round_jiffies(jiffies + |
@@ -788,12 +786,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) | |||
788 | sdata->vif.bss_conf.enable_beacon = false; | 786 | sdata->vif.bss_conf.enable_beacon = false; |
789 | clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); | 787 | clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); |
790 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); | 788 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); |
791 | sdata_lock(sdata); | ||
792 | bcn = rcu_dereference_protected(ifmsh->beacon, | 789 | bcn = rcu_dereference_protected(ifmsh->beacon, |
793 | lockdep_is_held(&sdata->wdev.mtx)); | 790 | lockdep_is_held(&sdata->wdev.mtx)); |
794 | rcu_assign_pointer(ifmsh->beacon, NULL); | 791 | rcu_assign_pointer(ifmsh->beacon, NULL); |
795 | kfree_rcu(bcn, rcu_head); | 792 | kfree_rcu(bcn, rcu_head); |
796 | sdata_unlock(sdata); | ||
797 | 793 | ||
798 | /* flush STAs and mpaths on this iface */ | 794 | /* flush STAs and mpaths on this iface */ |
799 | sta_info_flush(sdata); | 795 | sta_info_flush(sdata); |
@@ -806,14 +802,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) | |||
806 | del_timer_sync(&sdata->u.mesh.housekeeping_timer); | 802 | del_timer_sync(&sdata->u.mesh.housekeeping_timer); |
807 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); | 803 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); |
808 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); | 804 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); |
809 | /* | ||
810 | * If the timer fired while we waited for it, it will have | ||
811 | * requeued the work. Now the work will be running again | ||
812 | * but will not rearm the timer again because it checks | ||
813 | * whether the interface is running, which, at this point, | ||
814 | * it no longer is. | ||
815 | */ | ||
816 | cancel_work_sync(&sdata->work); | ||
817 | 805 | ||
818 | local->fif_other_bss--; | 806 | local->fif_other_bss--; |
819 | atomic_dec(&local->iff_allmultis); | 807 | atomic_dec(&local->iff_allmultis); |
@@ -954,6 +942,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
954 | struct ieee80211_mgmt *mgmt; | 942 | struct ieee80211_mgmt *mgmt; |
955 | u16 stype; | 943 | u16 stype; |
956 | 944 | ||
945 | sdata_lock(sdata); | ||
946 | |||
947 | /* mesh already went down */ | ||
948 | if (!sdata->wdev.mesh_id_len) | ||
949 | goto out; | ||
950 | |||
957 | rx_status = IEEE80211_SKB_RXCB(skb); | 951 | rx_status = IEEE80211_SKB_RXCB(skb); |
958 | mgmt = (struct ieee80211_mgmt *) skb->data; | 952 | mgmt = (struct ieee80211_mgmt *) skb->data; |
959 | stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; | 953 | stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; |
@@ -971,12 +965,20 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
971 | ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); | 965 | ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); |
972 | break; | 966 | break; |
973 | } | 967 | } |
968 | out: | ||
969 | sdata_unlock(sdata); | ||
974 | } | 970 | } |
975 | 971 | ||
976 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) | 972 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) |
977 | { | 973 | { |
978 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 974 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
979 | 975 | ||
976 | sdata_lock(sdata); | ||
977 | |||
978 | /* mesh already went down */ | ||
979 | if (!sdata->wdev.mesh_id_len) | ||
980 | goto out; | ||
981 | |||
980 | if (ifmsh->preq_queue_len && | 982 | if (ifmsh->preq_queue_len && |
981 | time_after(jiffies, | 983 | time_after(jiffies, |
982 | ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) | 984 | ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) |
@@ -996,6 +998,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) | |||
996 | 998 | ||
997 | if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) | 999 | if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) |
998 | mesh_sync_adjust_tbtt(sdata); | 1000 | mesh_sync_adjust_tbtt(sdata); |
1001 | |||
1002 | out: | ||
1003 | sdata_unlock(sdata); | ||
999 | } | 1004 | } |
1000 | 1005 | ||
1001 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) | 1006 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) |
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6c4da99bc4fb..09bebed99416 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -517,9 +517,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, | |||
517 | ieee80211_mps_frame_release(sta, elems); | 517 | ieee80211_mps_frame_release(sta, elems); |
518 | out: | 518 | out: |
519 | rcu_read_unlock(); | 519 | rcu_read_unlock(); |
520 | sdata_lock(sdata); | ||
521 | ieee80211_mbss_info_change_notify(sdata, changed); | 520 | ieee80211_mbss_info_change_notify(sdata, changed); |
522 | sdata_unlock(sdata); | ||
523 | } | 521 | } |
524 | 522 | ||
525 | static void mesh_plink_timer(unsigned long data) | 523 | static void mesh_plink_timer(unsigned long data) |
@@ -1070,9 +1068,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, | |||
1070 | 1068 | ||
1071 | rcu_read_unlock(); | 1069 | rcu_read_unlock(); |
1072 | 1070 | ||
1073 | if (changed) { | 1071 | if (changed) |
1074 | sdata_lock(sdata); | ||
1075 | ieee80211_mbss_info_change_notify(sdata, changed); | 1072 | ieee80211_mbss_info_change_notify(sdata, changed); |
1076 | sdata_unlock(sdata); | ||
1077 | } | ||
1078 | } | 1073 | } |