Commit f81a9ded authored by Thomas Pedersen's avatar Thomas Pedersen Committed by Johannes Berg
Browse files

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: default avatarThomas Pedersen <thomas@cozybit.com>
[fix comment position]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a1193be8
...@@ -544,6 +544,7 @@ struct ieee80211_if_mesh { ...@@ -544,6 +544,7 @@ struct ieee80211_if_mesh {
struct timer_list mesh_path_root_timer; struct timer_list mesh_path_root_timer;
unsigned long wrkq_flags; unsigned long wrkq_flags;
unsigned long mbss_changed;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN]; u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
size_t mesh_id_len; size_t mesh_id_len;
......
...@@ -161,11 +161,8 @@ void mesh_sta_cleanup(struct sta_info *sta) ...@@ -161,11 +161,8 @@ void mesh_sta_cleanup(struct sta_info *sta)
del_timer_sync(&sta->plink_timer); del_timer_sync(&sta->plink_timer);
} }
if (changed) { if (changed)
sdata_lock(sdata);
ieee80211_mbss_info_change_notify(sdata, changed); ieee80211_mbss_info_change_notify(sdata, changed);
sdata_unlock(sdata);
}
} }
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) 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,14 +716,18 @@ ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)
void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed) u32 changed)
{ {
if (sdata->vif.bss_conf.enable_beacon && struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
(changed & (BSS_CHANGED_BEACON | unsigned long bits = changed;
BSS_CHANGED_HT | u32 bit;
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT))) if (!bits)
if (ieee80211_mesh_rebuild_beacon(sdata)) return;
return;
ieee80211_bss_info_change_notify(sdata, changed); /* if we race with running work, worst case this work becomes a noop */
for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE)
set_bit(bit, &ifmsh->mbss_changed);
set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
} }
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) 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,6 +800,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_timer); del_timer_sync(&sdata->u.mesh.mesh_path_timer);
/* clear any mesh work (for next join) we may have accrued */
ifmsh->wrkq_flags = 0;
ifmsh->mbss_changed = 0;
local->fif_other_bss--; local->fif_other_bss--;
atomic_dec(&local->iff_allmultis); atomic_dec(&local->iff_allmultis);
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
...@@ -965,6 +970,28 @@ out: ...@@ -965,6 +970,28 @@ out:
sdata_unlock(sdata); sdata_unlock(sdata);
} }
static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
u32 bit, changed = 0;
for_each_set_bit(bit, &ifmsh->mbss_changed,
sizeof(changed) * BITS_PER_BYTE) {
clear_bit(bit, &ifmsh->mbss_changed);
changed |= BIT(bit);
}
if (sdata->vif.bss_conf.enable_beacon &&
(changed & (BSS_CHANGED_BEACON |
BSS_CHANGED_HT |
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT)))
if (ieee80211_mesh_rebuild_beacon(sdata))
return;
ieee80211_bss_info_change_notify(sdata, changed);
}
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
...@@ -995,6 +1022,8 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) ...@@ -995,6 +1022,8 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))
mesh_sync_adjust_tbtt(sdata); mesh_sync_adjust_tbtt(sdata);
if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
mesh_bss_info_changed(sdata);
out: out:
sdata_unlock(sdata); sdata_unlock(sdata);
} }
......
...@@ -58,6 +58,7 @@ enum mesh_path_flags { ...@@ -58,6 +58,7 @@ enum mesh_path_flags {
* @MESH_WORK_ROOT: the mesh root station needs to send a frame * @MESH_WORK_ROOT: the mesh root station needs to send a frame
* @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
* mesh nodes * mesh nodes
* @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes
*/ */
enum mesh_deferred_task_flags { enum mesh_deferred_task_flags {
MESH_WORK_HOUSEKEEPING, MESH_WORK_HOUSEKEEPING,
...@@ -65,6 +66,7 @@ enum mesh_deferred_task_flags { ...@@ -65,6 +66,7 @@ enum mesh_deferred_task_flags {
MESH_WORK_GROW_MPP_TABLE, MESH_WORK_GROW_MPP_TABLE,
MESH_WORK_ROOT, MESH_WORK_ROOT,
MESH_WORK_DRIFT_ADJUST, MESH_WORK_DRIFT_ADJUST,
MESH_WORK_MBSS_CHANGED,
}; };
/** /**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment