diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2010-04-06 05:18:47 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-04-07 14:38:05 -0400 |
commit | 098a607091426e79178b9a6c318d993fea131791 (patch) | |
tree | d62c9993e49f90ca962cd763b096911d22d7e051 /net/mac80211/sta_info.c | |
parent | 618f356b95e37ca0c30b3b513898fda54abd52a6 (diff) |
mac80211: clean up/fix aggregation code
The aggregation code has a number of quirks, like
inventing an unneeded WLAN_BACK_TIMER value and
leaking memory under certain circumstances during
station destruction. Fix these issues by using
the regular aggregation session teardown code and
blocking new aggregation sessions, all before the
station is really destructed.
As a side effect, this gets rid of the long code
block to destroy aggregation safely.
Additionally, rename tid_state_rx which can only
have the values IDLE and OPERATIONAL to
tid_active_rx to make it easier to understand
that there is no bitwise stuff going on on the
RX side -- the TX side remains because it needs
to keep track of the driver and peer states.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r-- | net/mac80211/sta_info.c | 58 |
1 files changed, 10 insertions, 48 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index bd11753c1525..5bf044b92dca 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -238,9 +238,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
238 | * enable session_timer's data differentiation. refer to | 238 | * enable session_timer's data differentiation. refer to |
239 | * sta_rx_agg_session_timer_expired for useage */ | 239 | * sta_rx_agg_session_timer_expired for useage */ |
240 | sta->timer_to_tid[i] = i; | 240 | sta->timer_to_tid[i] = i; |
241 | /* rx */ | ||
242 | sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE; | ||
243 | sta->ampdu_mlme.tid_rx[i] = NULL; | ||
244 | /* tx */ | 241 | /* tx */ |
245 | sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE; | 242 | sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE; |
246 | sta->ampdu_mlme.tid_tx[i] = NULL; | 243 | sta->ampdu_mlme.tid_tx[i] = NULL; |
@@ -606,7 +603,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) | |||
606 | struct ieee80211_sub_if_data *sdata; | 603 | struct ieee80211_sub_if_data *sdata; |
607 | struct sk_buff *skb; | 604 | struct sk_buff *skb; |
608 | unsigned long flags; | 605 | unsigned long flags; |
609 | int ret, i; | 606 | int ret; |
610 | 607 | ||
611 | might_sleep(); | 608 | might_sleep(); |
612 | 609 | ||
@@ -616,6 +613,15 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) | |||
616 | local = sta->local; | 613 | local = sta->local; |
617 | sdata = sta->sdata; | 614 | sdata = sta->sdata; |
618 | 615 | ||
616 | /* | ||
617 | * Before removing the station from the driver and | ||
618 | * rate control, it might still start new aggregation | ||
619 | * sessions -- block that to make sure the tear-down | ||
620 | * will be sufficient. | ||
621 | */ | ||
622 | set_sta_flags(sta, WLAN_STA_BLOCK_BA); | ||
623 | ieee80211_sta_tear_down_BA_sessions(sta); | ||
624 | |||
619 | spin_lock_irqsave(&local->sta_lock, flags); | 625 | spin_lock_irqsave(&local->sta_lock, flags); |
620 | ret = sta_info_hash_del(local, sta); | 626 | ret = sta_info_hash_del(local, sta); |
621 | /* this might still be the pending list ... which is fine */ | 627 | /* this might still be the pending list ... which is fine */ |
@@ -700,50 +706,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) | |||
700 | while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) | 706 | while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) |
701 | dev_kfree_skb_any(skb); | 707 | dev_kfree_skb_any(skb); |
702 | 708 | ||
703 | for (i = 0; i < STA_TID_NUM; i++) { | ||
704 | struct tid_ampdu_rx *tid_rx; | ||
705 | struct tid_ampdu_tx *tid_tx; | ||
706 | |||
707 | spin_lock_bh(&sta->lock); | ||
708 | tid_rx = sta->ampdu_mlme.tid_rx[i]; | ||
709 | /* Make sure timer won't free the tid_rx struct, see below */ | ||
710 | if (tid_rx) | ||
711 | tid_rx->shutdown = true; | ||
712 | |||
713 | spin_unlock_bh(&sta->lock); | ||
714 | |||
715 | /* | ||
716 | * Outside spinlock - shutdown is true now so that the timer | ||
717 | * won't free tid_rx, we have to do that now. Can't let the | ||
718 | * timer do it because we have to sync the timer outside the | ||
719 | * lock that it takes itself. | ||
720 | */ | ||
721 | if (tid_rx) { | ||
722 | del_timer_sync(&tid_rx->session_timer); | ||
723 | kfree(tid_rx); | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * No need to do such complications for TX agg sessions, the | ||
728 | * path leading to freeing the tid_tx struct goes via a call | ||
729 | * from the driver, and thus needs to look up the sta struct | ||
730 | * again, which cannot be found when we get here. Hence, we | ||
731 | * just need to delete the timer and free the aggregation | ||
732 | * info; we won't be telling the peer about it then but that | ||
733 | * doesn't matter if we're not talking to it again anyway. | ||
734 | */ | ||
735 | tid_tx = sta->ampdu_mlme.tid_tx[i]; | ||
736 | if (tid_tx) { | ||
737 | del_timer_sync(&tid_tx->addba_resp_timer); | ||
738 | /* | ||
739 | * STA removed while aggregation session being | ||
740 | * started? Bit odd, but purge frames anyway. | ||
741 | */ | ||
742 | skb_queue_purge(&tid_tx->pending); | ||
743 | kfree(tid_tx); | ||
744 | } | ||
745 | } | ||
746 | |||
747 | __sta_info_free(local, sta); | 709 | __sta_info_free(local, sta); |
748 | 710 | ||
749 | return 0; | 711 | return 0; |