mac80211: introduce plink lock for plink fields

The mesh plink code uses sta->lock to serialize access to the
plink state fields between the peer link state machine and the
peer link timer.  Some paths (e.g. those involving
mps_qos_null_tx()) unfortunately hold this spinlock across
frame tx, which is soon to be disallowed.  Add a new spinlock
just for plink access.

Signed-off-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Bob Copeland 2015-04-13 17:26:28 -04:00 committed by Johannes Berg
parent 388f997620
commit 48bf6beddf
3 changed files with 25 additions and 18 deletions

View File

@ -72,10 +72,11 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
* *
* @sta: mesh peer link to restart * @sta: mesh peer link to restart
* *
* Locking: this function must be called holding sta->lock * Locking: this function must be called holding sta->plink_lock
*/ */
static inline void mesh_plink_fsm_restart(struct sta_info *sta) static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{ {
lockdep_assert_held(&sta->plink_lock);
sta->plink_state = NL80211_PLINK_LISTEN; sta->plink_state = NL80211_PLINK_LISTEN;
sta->llid = sta->plid = sta->reason = 0; sta->llid = sta->plid = sta->reason = 0;
sta->plink_retries = 0; sta->plink_retries = 0;
@ -213,13 +214,15 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
* All mesh paths with this peer as next hop will be flushed * All mesh paths with this peer as next hop will be flushed
* Returns beacon changed flag if the beacon content changed. * Returns beacon changed flag if the beacon content changed.
* *
* Locking: the caller must hold sta->lock * Locking: the caller must hold sta->plink_lock
*/ */
static u32 __mesh_plink_deactivate(struct sta_info *sta) static u32 __mesh_plink_deactivate(struct sta_info *sta)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed = 0; u32 changed = 0;
lockdep_assert_held(&sta->plink_lock);
if (sta->plink_state == NL80211_PLINK_ESTAB) if (sta->plink_state == NL80211_PLINK_ESTAB)
changed = mesh_plink_dec_estab_count(sdata); changed = mesh_plink_dec_estab_count(sdata);
sta->plink_state = NL80211_PLINK_BLOCKED; sta->plink_state = NL80211_PLINK_BLOCKED;
@ -244,13 +247,13 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed; u32 changed;
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->plink_lock);
changed = __mesh_plink_deactivate(sta); changed = __mesh_plink_deactivate(sta);
sta->reason = WLAN_REASON_MESH_PEER_CANCELED; sta->reason = WLAN_REASON_MESH_PEER_CANCELED;
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, sta->llid, sta->plid, sta->sta.addr, sta->llid, sta->plid,
sta->reason); sta->reason);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
return changed; return changed;
} }
@ -387,7 +390,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->plink_lock);
sta->last_rx = jiffies; sta->last_rx = jiffies;
/* rates and capabilities don't change during peering */ /* rates and capabilities don't change during peering */
@ -419,7 +422,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
else else
rate_control_rate_update(local, sband, sta, changed); rate_control_rate_update(local, sband, sta, changed);
out: out:
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
} }
static struct sta_info * static struct sta_info *
@ -552,7 +555,7 @@ static void mesh_plink_timer(unsigned long data)
if (sta->sdata->local->quiescing) if (sta->sdata->local->quiescing)
return; return;
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->plink_lock);
/* If a timer fires just before a state transition on another CPU, /* If a timer fires just before a state transition on another CPU,
* we may have already extended the timeout and changed state by the * we may have already extended the timeout and changed state by the
@ -563,7 +566,7 @@ static void mesh_plink_timer(unsigned long data)
mpl_dbg(sta->sdata, mpl_dbg(sta->sdata,
"Ignoring timer for %pM in state %s (timer adjusted)", "Ignoring timer for %pM in state %s (timer adjusted)",
sta->sta.addr, mplstates[sta->plink_state]); sta->sta.addr, mplstates[sta->plink_state]);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
return; return;
} }
@ -573,7 +576,7 @@ static void mesh_plink_timer(unsigned long data)
mpl_dbg(sta->sdata, mpl_dbg(sta->sdata,
"Ignoring timer for %pM in state %s (timer deleted)", "Ignoring timer for %pM in state %s (timer deleted)",
sta->sta.addr, mplstates[sta->plink_state]); sta->sta.addr, mplstates[sta->plink_state]);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
return; return;
} }
@ -619,7 +622,7 @@ static void mesh_plink_timer(unsigned long data)
default: default:
break; break;
} }
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
if (action) if (action)
mesh_plink_frame_tx(sdata, action, sta->sta.addr, mesh_plink_frame_tx(sdata, action, sta->sta.addr,
sta->llid, sta->plid, reason); sta->llid, sta->plid, reason);
@ -674,16 +677,16 @@ u32 mesh_plink_open(struct sta_info *sta)
if (!test_sta_flag(sta, WLAN_STA_AUTH)) if (!test_sta_flag(sta, WLAN_STA_AUTH))
return 0; return 0;
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->plink_lock);
sta->llid = mesh_get_new_llid(sdata); sta->llid = mesh_get_new_llid(sdata);
if (sta->plink_state != NL80211_PLINK_LISTEN && if (sta->plink_state != NL80211_PLINK_LISTEN &&
sta->plink_state != NL80211_PLINK_BLOCKED) { sta->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
return 0; return 0;
} }
sta->plink_state = NL80211_PLINK_OPN_SNT; sta->plink_state = NL80211_PLINK_OPN_SNT;
mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
mpl_dbg(sdata, mpl_dbg(sdata,
"Mesh plink: starting establishment with %pM\n", "Mesh plink: starting establishment with %pM\n",
sta->sta.addr); sta->sta.addr);
@ -700,10 +703,10 @@ u32 mesh_plink_block(struct sta_info *sta)
{ {
u32 changed; u32 changed;
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->plink_lock);
changed = __mesh_plink_deactivate(sta); changed = __mesh_plink_deactivate(sta);
sta->plink_state = NL80211_PLINK_BLOCKED; sta->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
return changed; return changed;
} }
@ -758,7 +761,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
mplstates[sta->plink_state], mplevents[event]); mplstates[sta->plink_state], mplevents[event]);
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->plink_lock);
switch (sta->plink_state) { switch (sta->plink_state) {
case NL80211_PLINK_LISTEN: case NL80211_PLINK_LISTEN:
switch (event) { switch (event) {
@ -872,7 +875,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
*/ */
break; break;
} }
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->plink_lock);
if (action) { if (action) {
mesh_plink_frame_tx(sdata, action, sta->sta.addr, mesh_plink_frame_tx(sdata, action, sta->sta.addr,
sta->llid, sta->plid, sta->reason); sta->llid, sta->plid, sta->reason);

View File

@ -295,6 +295,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx); mutex_init(&sta->ampdu_mlme.mtx);
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
spin_lock_init(&sta->plink_lock);
if (ieee80211_vif_is_mesh(&sdata->vif) && if (ieee80211_vif_is_mesh(&sdata->vif) &&
!sdata->u.mesh.user_mpm) !sdata->u.mesh.user_mpm)
init_timer(&sta->plink_timer); init_timer(&sta->plink_timer);

View File

@ -299,6 +299,7 @@ struct sta_ampdu_mlme {
* @tid_seq: per-TID sequence numbers for sending to this STA * @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers * @timer_to_tid: identity mapping to ID timers
* @plink_lock: serialize access to plink fields
* @llid: Local link ID * @llid: Local link ID
* @plid: Peer link ID * @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state * @reason: Cancel reason on PLINK_HOLDING state
@ -422,9 +423,10 @@ struct sta_info {
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
/* /*
* Mesh peer link attributes * Mesh peer link attributes, protected by plink_lock.
* TODO: move to a sub-structure that is referenced with pointer? * TODO: move to a sub-structure that is referenced with pointer?
*/ */
spinlock_t plink_lock;
u16 llid; u16 llid;
u16 plid; u16 plid;
u16 reason; u16 reason;
@ -432,6 +434,7 @@ struct sta_info {
enum nl80211_plink_state plink_state; enum nl80211_plink_state plink_state;
u32 plink_timeout; u32 plink_timeout;
struct timer_list plink_timer; struct timer_list plink_timer;
s64 t_offset; s64 t_offset;
s64 t_offset_setpoint; s64 t_offset_setpoint;
/* mesh power save */ /* mesh power save */