diff options
author | Thomas Pedersen <thomas@cozybit.com> | 2013-06-13 18:54:41 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-06-18 09:57:27 -0400 |
commit | f81a9dedaff434604c7fc3d9c299d277b76db0a8 (patch) | |
tree | 0e11854521a0f93738bd451455046cbc0b986381 /net/mac80211/mesh.c | |
parent | a1193be83b4bb173228f04870afd6a4174b19130 (diff) |
mac80211: update mesh beacon on workqueue
Instead of updating the mesh beacon immediately when
requested (which would require the sdata_lock()), defer it
to the mac80211 workqueue.
Fixes yet another deadlock on calling sta_info_flush()
with the sdata_lock() held from ieee80211_stop_mesh(). We
could just drop the sdata_lock() around the
mesh_sta_cleanup() call, but this path is also taken from
several non-locked error paths.
Signed-off-by: Thomas Pedersen <thomas@cozybit.com>
[fix comment position]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mesh.c')
-rw-r--r-- | net/mac80211/mesh.c | 53 |
1 files changed, 41 insertions, 12 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6c33af482df4..d5dea94216e4 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
@@ -161,11 +161,8 @@ void mesh_sta_cleanup(struct sta_info *sta) | |||
161 | del_timer_sync(&sta->plink_timer); | 161 | del_timer_sync(&sta->plink_timer); |
162 | } | 162 | } |
163 | 163 | ||
164 | if (changed) { | 164 | if (changed) |
165 | sdata_lock(sdata); | ||
166 | ieee80211_mbss_info_change_notify(sdata, changed); | 165 | ieee80211_mbss_info_change_notify(sdata, changed); |
167 | sdata_unlock(sdata); | ||
168 | } | ||
169 | } | 166 | } |
170 | 167 | ||
171 | int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) | 168 | int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) |
@@ -719,14 +716,18 @@ ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata) | |||
719 | void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, | 716 | void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, |
720 | u32 changed) | 717 | u32 changed) |
721 | { | 718 | { |
722 | if (sdata->vif.bss_conf.enable_beacon && | 719 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
723 | (changed & (BSS_CHANGED_BEACON | | 720 | unsigned long bits = changed; |
724 | BSS_CHANGED_HT | | 721 | u32 bit; |
725 | BSS_CHANGED_BASIC_RATES | | 722 | |
726 | BSS_CHANGED_BEACON_INT))) | 723 | if (!bits) |
727 | if (ieee80211_mesh_rebuild_beacon(sdata)) | 724 | return; |
728 | return; | 725 | |
729 | ieee80211_bss_info_change_notify(sdata, changed); | 726 | /* if we race with running work, worst case this work becomes a noop */ |
727 | for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE) | ||
728 | set_bit(bit, &ifmsh->mbss_changed); | ||
729 | set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags); | ||
730 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | ||
730 | } | 731 | } |
731 | 732 | ||
732 | int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) | 733 | int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) |
@@ -799,6 +800,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) | |||
799 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); | 800 | del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); |
800 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); | 801 | del_timer_sync(&sdata->u.mesh.mesh_path_timer); |
801 | 802 | ||
803 | /* clear any mesh work (for next join) we may have accrued */ | ||
804 | ifmsh->wrkq_flags = 0; | ||
805 | ifmsh->mbss_changed = 0; | ||
806 | |||
802 | local->fif_other_bss--; | 807 | local->fif_other_bss--; |
803 | atomic_dec(&local->iff_allmultis); | 808 | atomic_dec(&local->iff_allmultis); |
804 | ieee80211_configure_filter(local); | 809 | ieee80211_configure_filter(local); |
@@ -965,6 +970,28 @@ out: | |||
965 | sdata_unlock(sdata); | 970 | sdata_unlock(sdata); |
966 | } | 971 | } |
967 | 972 | ||
973 | static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata) | ||
974 | { | ||
975 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
976 | u32 bit, changed = 0; | ||
977 | |||
978 | for_each_set_bit(bit, &ifmsh->mbss_changed, | ||
979 | sizeof(changed) * BITS_PER_BYTE) { | ||
980 | clear_bit(bit, &ifmsh->mbss_changed); | ||
981 | changed |= BIT(bit); | ||
982 | } | ||
983 | |||
984 | if (sdata->vif.bss_conf.enable_beacon && | ||
985 | (changed & (BSS_CHANGED_BEACON | | ||
986 | BSS_CHANGED_HT | | ||
987 | BSS_CHANGED_BASIC_RATES | | ||
988 | BSS_CHANGED_BEACON_INT))) | ||
989 | if (ieee80211_mesh_rebuild_beacon(sdata)) | ||
990 | return; | ||
991 | |||
992 | ieee80211_bss_info_change_notify(sdata, changed); | ||
993 | } | ||
994 | |||
968 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) | 995 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) |
969 | { | 996 | { |
970 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 997 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
@@ -995,6 +1022,8 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) | |||
995 | if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) | 1022 | if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) |
996 | mesh_sync_adjust_tbtt(sdata); | 1023 | mesh_sync_adjust_tbtt(sdata); |
997 | 1024 | ||
1025 | if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags)) | ||
1026 | mesh_bss_info_changed(sdata); | ||
998 | out: | 1027 | out: |
999 | sdata_unlock(sdata); | 1028 | sdata_unlock(sdata); |
1000 | } | 1029 | } |