Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Thomas Pedersen and committed by
Johannes Berg
f81a9ded a1193be8

+44 -12
+1
net/mac80211/ieee80211_i.h
··· 544 544 struct timer_list mesh_path_root_timer; 545 545 546 546 unsigned long wrkq_flags; 547 + unsigned long mbss_changed; 547 548 548 549 u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; 549 550 size_t mesh_id_len;
+41 -12
net/mac80211/mesh.c
··· 161 161 del_timer_sync(&sta->plink_timer); 162 162 } 163 163 164 - if (changed) { 165 - sdata_lock(sdata); 164 + if (changed) 166 165 ieee80211_mbss_info_change_notify(sdata, changed); 167 - sdata_unlock(sdata); 168 - } 169 166 } 170 167 171 168 int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) ··· 716 719 void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, 717 720 u32 changed) 718 721 { 719 - if (sdata->vif.bss_conf.enable_beacon && 720 - (changed & (BSS_CHANGED_BEACON | 721 - BSS_CHANGED_HT | 722 - BSS_CHANGED_BASIC_RATES | 723 - BSS_CHANGED_BEACON_INT))) 724 - if (ieee80211_mesh_rebuild_beacon(sdata)) 725 - return; 726 - ieee80211_bss_info_change_notify(sdata, changed); 722 + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 723 + unsigned long bits = changed; 724 + u32 bit; 725 + 726 + if (!bits) 727 + return; 728 + 729 + /* if we race with running work, worst case this work becomes a noop */ 730 + for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE) 731 + set_bit(bit, &ifmsh->mbss_changed); 732 + set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags); 733 + ieee80211_queue_work(&sdata->local->hw, &sdata->work); 727 734 } 728 735 729 736 int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ··· 799 798 del_timer_sync(&sdata->u.mesh.housekeeping_timer); 800 799 del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); 801 800 del_timer_sync(&sdata->u.mesh.mesh_path_timer); 801 + 802 + /* clear any mesh work (for next join) we may have accrued */ 803 + ifmsh->wrkq_flags = 0; 804 + ifmsh->mbss_changed = 0; 802 805 803 806 local->fif_other_bss--; 804 807 atomic_dec(&local->iff_allmultis); ··· 970 965 sdata_unlock(sdata); 971 966 } 972 967 968 + static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata) 969 + { 970 + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 971 + u32 bit, changed = 0; 972 + 973 + for_each_set_bit(bit, &ifmsh->mbss_changed, 974 + sizeof(changed) * BITS_PER_BYTE) { 975 + clear_bit(bit, &ifmsh->mbss_changed); 976 + changed |= BIT(bit); 977 + } 978 + 979 + if (sdata->vif.bss_conf.enable_beacon && 980 + (changed & (BSS_CHANGED_BEACON | 981 + BSS_CHANGED_HT | 982 + BSS_CHANGED_BASIC_RATES | 983 + BSS_CHANGED_BEACON_INT))) 984 + if (ieee80211_mesh_rebuild_beacon(sdata)) 985 + return; 986 + 987 + ieee80211_bss_info_change_notify(sdata, changed); 988 + } 989 + 973 990 void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) 974 991 { 975 992 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; ··· 1022 995 if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) 1023 996 mesh_sync_adjust_tbtt(sdata); 1024 997 998 + if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags)) 999 + mesh_bss_info_changed(sdata); 1025 1000 out: 1026 1001 sdata_unlock(sdata); 1027 1002 }
+2
net/mac80211/mesh.h
··· 58 58 * @MESH_WORK_ROOT: the mesh root station needs to send a frame 59 59 * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other 60 60 * mesh nodes 61 + * @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes 61 62 */ 62 63 enum mesh_deferred_task_flags { 63 64 MESH_WORK_HOUSEKEEPING, ··· 66 65 MESH_WORK_GROW_MPP_TABLE, 67 66 MESH_WORK_ROOT, 68 67 MESH_WORK_DRIFT_ADJUST, 68 + MESH_WORK_MBSS_CHANGED, 69 69 }; 70 70 71 71 /**