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 | |
| 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>
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 1 | ||||
| -rw-r--r-- | net/mac80211/mesh.c | 53 | ||||
| -rw-r--r-- | net/mac80211/mesh.h | 2 |
3 files changed, 44 insertions, 12 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a4dfb0be53d7..194be3de16dd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
| @@ -544,6 +544,7 @@ struct ieee80211_if_mesh { | |||
| 544 | struct timer_list mesh_path_root_timer; | 544 | struct timer_list mesh_path_root_timer; |
| 545 | 545 | ||
| 546 | unsigned long wrkq_flags; | 546 | unsigned long wrkq_flags; |
| 547 | unsigned long mbss_changed; | ||
| 547 | 548 | ||
| 548 | u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; | 549 | u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; |
| 549 | size_t mesh_id_len; | 550 | size_t mesh_id_len; |
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 | } |
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 01a28bca6e9b..2bc7fd2f787d 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h | |||
| @@ -58,6 +58,7 @@ enum mesh_path_flags { | |||
| 58 | * @MESH_WORK_ROOT: the mesh root station needs to send a frame | 58 | * @MESH_WORK_ROOT: the mesh root station needs to send a frame |
| 59 | * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other | 59 | * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other |
| 60 | * mesh nodes | 60 | * mesh nodes |
| 61 | * @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes | ||
| 61 | */ | 62 | */ |
| 62 | enum mesh_deferred_task_flags { | 63 | enum mesh_deferred_task_flags { |
| 63 | MESH_WORK_HOUSEKEEPING, | 64 | MESH_WORK_HOUSEKEEPING, |
| @@ -65,6 +66,7 @@ enum mesh_deferred_task_flags { | |||
| 65 | MESH_WORK_GROW_MPP_TABLE, | 66 | MESH_WORK_GROW_MPP_TABLE, |
| 66 | MESH_WORK_ROOT, | 67 | MESH_WORK_ROOT, |
| 67 | MESH_WORK_DRIFT_ADJUST, | 68 | MESH_WORK_DRIFT_ADJUST, |
| 69 | MESH_WORK_MBSS_CHANGED, | ||
| 68 | }; | 70 | }; |
| 69 | 71 | ||
| 70 | /** | 72 | /** |
