aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorThomas Pedersen <thomas@cozybit.com>2013-06-10 16:17:21 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-06-11 07:14:42 -0400
commitecccd072b07e7fd09c54d0f86f9374e2645cde97 (patch)
tree8f2bc22f2aa1cb5a96d31ec3f7d87e6400d915e2 /net/mac80211
parent780b40df12cf0161d8ccc5381940e04584793933 (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.c29
-rw-r--r--net/mac80211/mesh_plink.c7
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 }
968out:
969 sdata_unlock(sdata);
974} 970}
975 971
976void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) 972void 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
1002out:
1003 sdata_unlock(sdata);
999} 1004}
1000 1005
1001void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) 1006void 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);
518out: 518out:
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
525static void mesh_plink_timer(unsigned long data) 523static 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}