diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-05-02 19:02:02 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-05-14 16:29:34 -0400 |
commit | 07346f81e87d6e4cca7ae9adfa711d0c61c87b56 (patch) | |
tree | 237450c49843e0e19afc79356240a891da64d9fa /net/mac80211/main.c | |
parent | 3434fbd39862d471c92b66c28cd449deea8e9f90 (diff) |
mac80211: proper STA info locking
As discussed earlier, we can unify locking in struct sta_info
and use just a single spinlock protecting all members of the
structure that need protection. Many don't, but one of the
especially bad ones is the 'flags' member that can currently
be clobbered when RX and TX is being processed on different
CPUs at the same time.
Because having four spinlocks for different, mostly exclusive
parts of a single structure is overkill, this patch also kills
the ampdu and mesh plink spinlocks and uses just a single one
for everything. Because none of the spinlocks are nested, this
is safe.
It remains to be seen whether or not we should make the sta
flags use atomic bit operations instead, for now though this
is a safe thing and using atomic operations instead will be
very simple using the new static inline functions this patch
introduces for accessing sta->flags.
Since spin_lock_bh() is used with this lock, there shouldn't
be any contention even if aggregation is enabled at around the
same time as both requires frame transmission/reception which
is in a bh context.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Tomas Winkler <tomasw@gmail.com>
Cc: Ron Rindjunsky <ron.rindjunsky@intel.com>
Cc: Luis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/main.c')
-rw-r--r-- | net/mac80211/main.c | 31 |
1 files changed, 16 insertions, 15 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 55e76117da9e..f277407f0f5a 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -346,6 +346,7 @@ static int ieee80211_open(struct net_device *dev) | |||
346 | goto err_del_interface; | 346 | goto err_del_interface; |
347 | } | 347 | } |
348 | 348 | ||
349 | /* no locking required since STA is not live yet */ | ||
349 | sta->flags |= WLAN_STA_AUTHORIZED; | 350 | sta->flags |= WLAN_STA_AUTHORIZED; |
350 | 351 | ||
351 | res = sta_info_insert(sta); | 352 | res = sta_info_insert(sta); |
@@ -588,7 +589,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
588 | return -ENOENT; | 589 | return -ENOENT; |
589 | } | 590 | } |
590 | 591 | ||
591 | spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); | 592 | spin_lock_bh(&sta->lock); |
592 | 593 | ||
593 | /* we have tried too many times, receiver does not want A-MPDU */ | 594 | /* we have tried too many times, receiver does not want A-MPDU */ |
594 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { | 595 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { |
@@ -691,7 +692,7 @@ start_ba_err: | |||
691 | spin_unlock_bh(&local->mdev->queue_lock); | 692 | spin_unlock_bh(&local->mdev->queue_lock); |
692 | ret = -EBUSY; | 693 | ret = -EBUSY; |
693 | start_ba_exit: | 694 | start_ba_exit: |
694 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | 695 | spin_unlock_bh(&sta->lock); |
695 | rcu_read_unlock(); | 696 | rcu_read_unlock(); |
696 | return ret; | 697 | return ret; |
697 | } | 698 | } |
@@ -719,7 +720,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, | |||
719 | 720 | ||
720 | /* check if the TID is in aggregation */ | 721 | /* check if the TID is in aggregation */ |
721 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 722 | state = &sta->ampdu_mlme.tid_state_tx[tid]; |
722 | spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); | 723 | spin_lock_bh(&sta->lock); |
723 | 724 | ||
724 | if (*state != HT_AGG_STATE_OPERATIONAL) { | 725 | if (*state != HT_AGG_STATE_OPERATIONAL) { |
725 | ret = -ENOENT; | 726 | ret = -ENOENT; |
@@ -749,7 +750,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, | |||
749 | } | 750 | } |
750 | 751 | ||
751 | stop_BA_exit: | 752 | stop_BA_exit: |
752 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | 753 | spin_unlock_bh(&sta->lock); |
753 | rcu_read_unlock(); | 754 | rcu_read_unlock(); |
754 | return ret; | 755 | return ret; |
755 | } | 756 | } |
@@ -778,12 +779,12 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
778 | } | 779 | } |
779 | 780 | ||
780 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 781 | state = &sta->ampdu_mlme.tid_state_tx[tid]; |
781 | spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); | 782 | spin_lock_bh(&sta->lock); |
782 | 783 | ||
783 | if (!(*state & HT_ADDBA_REQUESTED_MSK)) { | 784 | if (!(*state & HT_ADDBA_REQUESTED_MSK)) { |
784 | printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", | 785 | printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", |
785 | *state); | 786 | *state); |
786 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | 787 | spin_unlock_bh(&sta->lock); |
787 | rcu_read_unlock(); | 788 | rcu_read_unlock(); |
788 | return; | 789 | return; |
789 | } | 790 | } |
@@ -796,7 +797,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
796 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); | 797 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); |
797 | ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); | 798 | ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); |
798 | } | 799 | } |
799 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | 800 | spin_unlock_bh(&sta->lock); |
800 | rcu_read_unlock(); | 801 | rcu_read_unlock(); |
801 | } | 802 | } |
802 | EXPORT_SYMBOL(ieee80211_start_tx_ba_cb); | 803 | EXPORT_SYMBOL(ieee80211_start_tx_ba_cb); |
@@ -830,10 +831,10 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
830 | } | 831 | } |
831 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 832 | state = &sta->ampdu_mlme.tid_state_tx[tid]; |
832 | 833 | ||
833 | spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); | 834 | spin_lock_bh(&sta->lock); |
834 | if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { | 835 | if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { |
835 | printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); | 836 | printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); |
836 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | 837 | spin_unlock_bh(&sta->lock); |
837 | rcu_read_unlock(); | 838 | rcu_read_unlock(); |
838 | return; | 839 | return; |
839 | } | 840 | } |
@@ -860,7 +861,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
860 | sta->ampdu_mlme.addba_req_num[tid] = 0; | 861 | sta->ampdu_mlme.addba_req_num[tid] = 0; |
861 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 862 | kfree(sta->ampdu_mlme.tid_tx[tid]); |
862 | sta->ampdu_mlme.tid_tx[tid] = NULL; | 863 | sta->ampdu_mlme.tid_tx[tid] = NULL; |
863 | spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); | 864 | spin_unlock_bh(&sta->lock); |
864 | 865 | ||
865 | rcu_read_unlock(); | 866 | rcu_read_unlock(); |
866 | } | 867 | } |
@@ -1315,7 +1316,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | |||
1315 | * packet. If the STA went to power save mode, this will happen | 1316 | * packet. If the STA went to power save mode, this will happen |
1316 | * happen when it wakes up for the next time. | 1317 | * happen when it wakes up for the next time. |
1317 | */ | 1318 | */ |
1318 | sta->flags |= WLAN_STA_CLEAR_PS_FILT; | 1319 | set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT); |
1319 | 1320 | ||
1320 | /* | 1321 | /* |
1321 | * This code races in the following way: | 1322 | * This code races in the following way: |
@@ -1347,7 +1348,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | |||
1347 | * can be unknown, for example with different interrupt status | 1348 | * can be unknown, for example with different interrupt status |
1348 | * bits. | 1349 | * bits. |
1349 | */ | 1350 | */ |
1350 | if (sta->flags & WLAN_STA_PS && | 1351 | if (test_sta_flags(sta, WLAN_STA_PS) && |
1351 | skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { | 1352 | skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { |
1352 | ieee80211_remove_tx_extra(local, sta->key, skb, | 1353 | ieee80211_remove_tx_extra(local, sta->key, skb, |
1353 | &status->control); | 1354 | &status->control); |
@@ -1355,7 +1356,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | |||
1355 | return; | 1356 | return; |
1356 | } | 1357 | } |
1357 | 1358 | ||
1358 | if (!(sta->flags & WLAN_STA_PS) && | 1359 | if (!test_sta_flags(sta, WLAN_STA_PS) && |
1359 | !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { | 1360 | !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { |
1360 | /* Software retry the packet once */ | 1361 | /* Software retry the packet once */ |
1361 | status->control.flags |= IEEE80211_TXCTL_REQUEUE; | 1362 | status->control.flags |= IEEE80211_TXCTL_REQUEUE; |
@@ -1370,7 +1371,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | |||
1370 | "queue_len=%d PS=%d @%lu\n", | 1371 | "queue_len=%d PS=%d @%lu\n", |
1371 | wiphy_name(local->hw.wiphy), | 1372 | wiphy_name(local->hw.wiphy), |
1372 | skb_queue_len(&sta->tx_filtered), | 1373 | skb_queue_len(&sta->tx_filtered), |
1373 | !!(sta->flags & WLAN_STA_PS), jiffies); | 1374 | !!test_sta_flags(sta, WLAN_STA_PS), jiffies); |
1374 | dev_kfree_skb(skb); | 1375 | dev_kfree_skb(skb); |
1375 | } | 1376 | } |
1376 | 1377 | ||
@@ -1399,7 +1400,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1399 | struct sta_info *sta; | 1400 | struct sta_info *sta; |
1400 | sta = sta_info_get(local, hdr->addr1); | 1401 | sta = sta_info_get(local, hdr->addr1); |
1401 | if (sta) { | 1402 | if (sta) { |
1402 | if (sta->flags & WLAN_STA_PS) { | 1403 | if (test_sta_flags(sta, WLAN_STA_PS)) { |
1403 | /* | 1404 | /* |
1404 | * The STA is in power save mode, so assume | 1405 | * The STA is in power save mode, so assume |
1405 | * that this TX packet failed because of that. | 1406 | * that this TX packet failed because of that. |