Commit 5a9f7b04 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: use separate spinlock for sta flags

David Ellingsworth posted a bug that was only noticable on UP/NO-PREEMPT
and Michael correctly analysed it to be a spin_lock_bh() section within
a spin_lock_irqsave() section. This adds a separate spinlock for the
sta_info flags to fix that issue and avoid having to take much care
about where the sta flag manipulation functions are called.
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Reported-By: default avatarDavid Ellingsworth <david@identd.dyndns.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 135a2110
...@@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, ...@@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return NULL; return NULL;
spin_lock_init(&sta->lock); spin_lock_init(&sta->lock);
spin_lock_init(&sta->flaglock);
memcpy(sta->addr, addr, ETH_ALEN); memcpy(sta->addr, addr, ETH_ALEN);
sta->local = local; sta->local = local;
......
...@@ -164,6 +164,7 @@ struct sta_ampdu_mlme { ...@@ -164,6 +164,7 @@ struct sta_ampdu_mlme {
* @aid: STA's unique AID (1..2007, 0 = not assigned yet), * @aid: STA's unique AID (1..2007, 0 = not assigned yet),
* only used in AP (and IBSS?) mode * only used in AP (and IBSS?) mode
* @flags: STA flags, see &enum ieee80211_sta_info_flags * @flags: STA flags, see &enum ieee80211_sta_info_flags
* @flaglock: spinlock for flags accesses
* @ps_tx_buf: buffer of frames to transmit to this station * @ps_tx_buf: buffer of frames to transmit to this station
* when it leaves power saving state * when it leaves power saving state
* @tx_filtered: buffer of frames we already tried to transmit * @tx_filtered: buffer of frames we already tried to transmit
...@@ -186,6 +187,7 @@ struct sta_info { ...@@ -186,6 +187,7 @@ struct sta_info {
struct rate_control_ref *rate_ctrl; struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv; void *rate_ctrl_priv;
spinlock_t lock; spinlock_t lock;
spinlock_t flaglock;
struct ieee80211_ht_info ht_info; struct ieee80211_ht_info ht_info;
u64 supp_rates[IEEE80211_NUM_BANDS]; u64 supp_rates[IEEE80211_NUM_BANDS];
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
...@@ -198,7 +200,10 @@ struct sta_info { ...@@ -198,7 +200,10 @@ struct sta_info {
*/ */
u8 pin_status; u8 pin_status;
/* frequently updated information, locked with lock spinlock */ /*
* frequently updated, locked with own spinlock (flaglock),
* use the accessors defined below
*/
u32 flags; u32 flags;
/* /*
...@@ -293,34 +298,41 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta) ...@@ -293,34 +298,41 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta)
static inline void set_sta_flags(struct sta_info *sta, const u32 flags) static inline void set_sta_flags(struct sta_info *sta, const u32 flags)
{ {
spin_lock_bh(&sta->lock); unsigned long irqfl;
spin_lock_irqsave(&sta->flaglock, irqfl);
sta->flags |= flags; sta->flags |= flags;
spin_unlock_bh(&sta->lock); spin_unlock_irqrestore(&sta->flaglock, irqfl);
} }
static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) static inline void clear_sta_flags(struct sta_info *sta, const u32 flags)
{ {
spin_lock_bh(&sta->lock); unsigned long irqfl;
spin_lock_irqsave(&sta->flaglock, irqfl);
sta->flags &= ~flags; sta->flags &= ~flags;
spin_unlock_bh(&sta->lock); spin_unlock_irqrestore(&sta->flaglock, irqfl);
} }
static inline void set_and_clear_sta_flags(struct sta_info *sta, static inline void set_and_clear_sta_flags(struct sta_info *sta,
const u32 set, const u32 clear) const u32 set, const u32 clear)
{ {
spin_lock_bh(&sta->lock); unsigned long irqfl;
spin_lock_irqsave(&sta->flaglock, irqfl);
sta->flags |= set; sta->flags |= set;
sta->flags &= ~clear; sta->flags &= ~clear;
spin_unlock_bh(&sta->lock); spin_unlock_irqrestore(&sta->flaglock, irqfl);
} }
static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags)
{ {
u32 ret; u32 ret;
unsigned long irqfl;
spin_lock_bh(&sta->lock); spin_lock_irqsave(&sta->flaglock, irqfl);
ret = sta->flags & flags; ret = sta->flags & flags;
spin_unlock_bh(&sta->lock); spin_unlock_irqrestore(&sta->flaglock, irqfl);
return ret; return ret;
} }
...@@ -329,11 +341,12 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta, ...@@ -329,11 +341,12 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta,
const u32 flags) const u32 flags)
{ {
u32 ret; u32 ret;
unsigned long irqfl;
spin_lock_bh(&sta->lock); spin_lock_irqsave(&sta->flaglock, irqfl);
ret = sta->flags & flags; ret = sta->flags & flags;
sta->flags &= ~flags; sta->flags &= ~flags;
spin_unlock_bh(&sta->lock); spin_unlock_irqrestore(&sta->flaglock, irqfl);
return ret; return ret;
} }
...@@ -341,10 +354,11 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta, ...@@ -341,10 +354,11 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta,
static inline u32 get_sta_flags(struct sta_info *sta) static inline u32 get_sta_flags(struct sta_info *sta)
{ {
u32 ret; u32 ret;
unsigned long irqfl;
spin_lock_bh(&sta->lock); spin_lock_irqsave(&sta->flaglock, irqfl);
ret = sta->flags; ret = sta->flags;
spin_unlock_bh(&sta->lock); spin_unlock_irqrestore(&sta->flaglock, irqfl);
return ret; return ret;
} }
......
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