aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorThomas Pedersen <thomas@cozybit.com>2013-06-13 15:54:41 -0700
committerJohannes Berg <johannes.berg@intel.com>2013-06-18 15:57:27 +0200
commitf81a9dedaff434604c7fc3d9c299d277b76db0a8 (patch)
tree0e11854521a0f93738bd451455046cbc0b986381 /net/mac80211
parentnl80211: use attributes to parse beacons (diff)
downloadlinux-dev-f81a9dedaff434604c7fc3d9c299d277b76db0a8.tar.xz
linux-dev-f81a9dedaff434604c7fc3d9c299d277b76db0a8.zip
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')
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/mesh.c53
-rw-r--r--net/mac80211/mesh.h2
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 {
struct timer_list mesh_path_root_timer;
unsigned long wrkq_flags;
+ unsigned long mbss_changed;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
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)
del_timer_sync(&sta->plink_timer);
}
- if (changed) {
- sdata_lock(sdata);
+ if (changed)
ieee80211_mbss_info_change_notify(sdata, changed);
- sdata_unlock(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)
void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed)
{
- 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);
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ unsigned long bits = changed;
+ u32 bit;
+
+ if (!bits)
+ return;
+
+ /* 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)
@@ -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_timer);
+ /* clear any mesh work (for next join) we may have accrued */
+ ifmsh->wrkq_flags = 0;
+ ifmsh->mbss_changed = 0;
+
local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
ieee80211_configure_filter(local);
@@ -965,6 +970,28 @@ out:
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)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
@@ -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))
mesh_sync_adjust_tbtt(sdata);
+ if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags))
+ mesh_bss_info_changed(sdata);
out:
sdata_unlock(sdata);
}
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 {
* @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 nodes
+ * @MESH_WORK_MBSS_CHANGED: rebuild beacon and notify driver of BSS changes
*/
enum mesh_deferred_task_flags {
MESH_WORK_HOUSEKEEPING,
@@ -65,6 +66,7 @@ enum mesh_deferred_task_flags {
MESH_WORK_GROW_MPP_TABLE,
MESH_WORK_ROOT,
MESH_WORK_DRIFT_ADJUST,
+ MESH_WORK_MBSS_CHANGED,
};
/**