diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-06-18 12:58:09 (GMT) |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-06-26 20:49:17 (GMT) |
commit | 5a9f7b047e81a73a1ce3e42ef87c28a61fd4df24 (patch) | |
tree | a9d727db68885155b36afb57e5f28df8cd82e029 /net | |
parent | 135a2110c55c71d7ccaf5ac66968b993347fe8e2 (diff) | |
download | linux-5a9f7b047e81a73a1ce3e42ef87c28a61fd4df24.tar.xz |
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: Johannes Berg <johannes@sipsolutions.net>
Reported-By: David Ellingsworth <david@identd.dyndns.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/sta_info.c | 1 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 40 |
2 files changed, 28 insertions, 13 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c24770c..b3c7331 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return NULL; spin_lock_init(&sta->lock); + spin_lock_init(&sta->flaglock); memcpy(sta->addr, addr, ETH_ALEN); sta->local = local; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 95753f8..fd228c1 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -164,6 +164,7 @@ struct sta_ampdu_mlme { * @aid: STA's unique AID (1..2007, 0 = not assigned yet), * only used in AP (and IBSS?) mode * @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 * when it leaves power saving state * @tx_filtered: buffer of frames we already tried to transmit @@ -186,6 +187,7 @@ struct sta_info { struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; + spinlock_t flaglock; struct ieee80211_ht_info ht_info; u64 supp_rates[IEEE80211_NUM_BANDS]; u8 addr[ETH_ALEN]; @@ -198,7 +200,10 @@ struct sta_info { */ u8 pin_status; - /* frequently updated information, locked with lock spinlock */ + /* + * frequently updated, locked with own spinlock (flaglock), + * use the accessors defined below + */ u32 flags; /* @@ -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) { - spin_lock_bh(&sta->lock); + unsigned long irqfl; + + spin_lock_irqsave(&sta->flaglock, irqfl); 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) { - spin_lock_bh(&sta->lock); + unsigned long irqfl; + + spin_lock_irqsave(&sta->flaglock, irqfl); 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, 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 &= ~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) { u32 ret; + unsigned long irqfl; - spin_lock_bh(&sta->lock); + spin_lock_irqsave(&sta->flaglock, irqfl); ret = sta->flags & flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); return ret; } @@ -329,11 +341,12 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta, const u32 flags) { u32 ret; + unsigned long irqfl; - spin_lock_bh(&sta->lock); + spin_lock_irqsave(&sta->flaglock, irqfl); ret = sta->flags & flags; sta->flags &= ~flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); return ret; } @@ -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) { u32 ret; + unsigned long irqfl; - spin_lock_bh(&sta->lock); + spin_lock_irqsave(&sta->flaglock, irqfl); ret = sta->flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); return ret; } |