aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorChristian Lamparter <chunkeey@googlemail.com>2010-08-04 19:36:41 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-08-16 15:26:39 -0400
commit2bff8ebf32a7c5ec9e5f5eeffef94a8cb622f5f0 (patch)
treeba1a904e56eb202481c039d28a0d769d4bcf796f /net
parent071d9ac253ff51154beb7e33967168e30bc96053 (diff)
mac80211: AMPDU rx reorder timeout timer
This patch introduces a new timer, which will release queued-up MPDUs from the reorder buffer, whenever they've waited for more than HT_RX_REORDER_BUF_TIMEOUT (which is at around 100 ms). The advantage of having a dedicated timer, instead of relying on a constant stream of freshly arriving aMPDUs to release the old ones, is particularly observable when even a small fraction of MPDUs are forever lost at low network speeds. Previously under these circumstances frames would become stuck in the reorder buffer and the network stack of both HT peers throttled back, instead of revving up and gunning the pipes. Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/agg-rx.c22
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/rx.c70
-rw-r--r--net/mac80211/sta_info.h16
4 files changed, 97 insertions, 12 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 965b272499fd..58eab9e8e4ee 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -86,6 +86,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
86 tid, 0, reason); 86 tid, 0, reason);
87 87
88 del_timer_sync(&tid_rx->session_timer); 88 del_timer_sync(&tid_rx->session_timer);
89 del_timer_sync(&tid_rx->reorder_timer);
89 90
90 call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx); 91 call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
91} 92}
@@ -120,6 +121,20 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
120 ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); 121 ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
121} 122}
122 123
124static void sta_rx_agg_reorder_timer_expired(unsigned long data)
125{
126 u8 *ptid = (u8 *)data;
127 u8 *timer_to_id = ptid - *ptid;
128 struct sta_info *sta = container_of(timer_to_id, struct sta_info,
129 timer_to_tid[0]);
130
131 rcu_read_lock();
132 spin_lock(&sta->lock);
133 ieee80211_release_reorder_timeout(sta, *ptid);
134 spin_unlock(&sta->lock);
135 rcu_read_unlock();
136}
137
123static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid, 138static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
124 u8 dialog_token, u16 status, u16 policy, 139 u8 dialog_token, u16 status, u16 policy,
125 u16 buf_size, u16 timeout) 140 u16 buf_size, u16 timeout)
@@ -251,11 +266,18 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
251 goto end; 266 goto end;
252 } 267 }
253 268
269 spin_lock_init(&tid_agg_rx->reorder_lock);
270
254 /* rx timer */ 271 /* rx timer */
255 tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired; 272 tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired;
256 tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; 273 tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
257 init_timer(&tid_agg_rx->session_timer); 274 init_timer(&tid_agg_rx->session_timer);
258 275
276 /* rx reorder timer */
277 tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired;
278 tid_agg_rx->reorder_timer.data = (unsigned long)&sta->timer_to_tid[tid];
279 init_timer(&tid_agg_rx->reorder_timer);
280
259 /* prepare reordering buffer */ 281 /* prepare reordering buffer */
260 tid_agg_rx->reorder_buf = 282 tid_agg_rx->reorder_buf =
261 kcalloc(buf_size, sizeof(struct sk_buff *), GFP_ATOMIC); 283 kcalloc(buf_size, sizeof(struct sk_buff *), GFP_ATOMIC);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index fb4363e148f2..b44e03a02da9 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1136,6 +1136,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);
1136void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); 1136void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
1137void ieee80211_ba_session_work(struct work_struct *work); 1137void ieee80211_ba_session_work(struct work_struct *work);
1138void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); 1138void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
1139void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
1139 1140
1140/* Spectrum management */ 1141/* Spectrum management */
1141void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 1142void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d5b91b6eb120..f24a0a1cff1a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -572,6 +572,8 @@ static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
572 * frames that have not yet been received are assumed to be lost and the skb 572 * frames that have not yet been received are assumed to be lost and the skb
573 * can be released for processing. This may also release other skb's from the 573 * can be released for processing. This may also release other skb's from the
574 * reorder buffer if there are no additional gaps between the frames. 574 * reorder buffer if there are no additional gaps between the frames.
575 *
576 * Callers must hold tid_agg_rx->reorder_lock.
575 */ 577 */
576#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10) 578#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10)
577 579
@@ -579,7 +581,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
579 struct tid_ampdu_rx *tid_agg_rx, 581 struct tid_ampdu_rx *tid_agg_rx,
580 struct sk_buff_head *frames) 582 struct sk_buff_head *frames)
581{ 583{
582 int index; 584 int index, j;
583 585
584 /* release the buffer until next missing frame */ 586 /* release the buffer until next missing frame */
585 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % 587 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
@@ -590,7 +592,6 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
590 * No buffers ready to be released, but check whether any 592 * No buffers ready to be released, but check whether any
591 * frames in the reorder buffer have timed out. 593 * frames in the reorder buffer have timed out.
592 */ 594 */
593 int j;
594 int skipped = 1; 595 int skipped = 1;
595 for (j = (index + 1) % tid_agg_rx->buf_size; j != index; 596 for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
596 j = (j + 1) % tid_agg_rx->buf_size) { 597 j = (j + 1) % tid_agg_rx->buf_size) {
@@ -600,7 +601,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
600 } 601 }
601 if (!time_after(jiffies, tid_agg_rx->reorder_time[j] + 602 if (!time_after(jiffies, tid_agg_rx->reorder_time[j] +
602 HT_RX_REORDER_BUF_TIMEOUT)) 603 HT_RX_REORDER_BUF_TIMEOUT))
603 break; 604 goto set_release_timer;
604 605
605#ifdef CONFIG_MAC80211_HT_DEBUG 606#ifdef CONFIG_MAC80211_HT_DEBUG
606 if (net_ratelimit()) 607 if (net_ratelimit())
@@ -624,6 +625,25 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
624 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % 625 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
625 tid_agg_rx->buf_size; 626 tid_agg_rx->buf_size;
626 } 627 }
628
629 if (tid_agg_rx->stored_mpdu_num) {
630 j = index = seq_sub(tid_agg_rx->head_seq_num,
631 tid_agg_rx->ssn) % tid_agg_rx->buf_size;
632
633 for (; j != (index - 1) % tid_agg_rx->buf_size;
634 j = (j + 1) % tid_agg_rx->buf_size) {
635 if (tid_agg_rx->reorder_buf[j])
636 break;
637 }
638
639 set_release_timer:
640
641 mod_timer(&tid_agg_rx->reorder_timer,
642 tid_agg_rx->reorder_time[j] +
643 HT_RX_REORDER_BUF_TIMEOUT);
644 } else {
645 del_timer(&tid_agg_rx->reorder_timer);
646 }
627} 647}
628 648
629/* 649/*
@@ -641,14 +661,16 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
641 u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; 661 u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
642 u16 head_seq_num, buf_size; 662 u16 head_seq_num, buf_size;
643 int index; 663 int index;
664 bool ret = true;
644 665
645 buf_size = tid_agg_rx->buf_size; 666 buf_size = tid_agg_rx->buf_size;
646 head_seq_num = tid_agg_rx->head_seq_num; 667 head_seq_num = tid_agg_rx->head_seq_num;
647 668
669 spin_lock(&tid_agg_rx->reorder_lock);
648 /* frame with out of date sequence number */ 670 /* frame with out of date sequence number */
649 if (seq_less(mpdu_seq_num, head_seq_num)) { 671 if (seq_less(mpdu_seq_num, head_seq_num)) {
650 dev_kfree_skb(skb); 672 dev_kfree_skb(skb);
651 return true; 673 goto out;
652 } 674 }
653 675
654 /* 676 /*
@@ -669,7 +691,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
669 /* check if we already stored this frame */ 691 /* check if we already stored this frame */
670 if (tid_agg_rx->reorder_buf[index]) { 692 if (tid_agg_rx->reorder_buf[index]) {
671 dev_kfree_skb(skb); 693 dev_kfree_skb(skb);
672 return true; 694 goto out;
673 } 695 }
674 696
675 /* 697 /*
@@ -679,7 +701,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
679 if (mpdu_seq_num == tid_agg_rx->head_seq_num && 701 if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
680 tid_agg_rx->stored_mpdu_num == 0) { 702 tid_agg_rx->stored_mpdu_num == 0) {
681 tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); 703 tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
682 return false; 704 ret = false;
705 goto out;
683 } 706 }
684 707
685 /* put the frame in the reordering buffer */ 708 /* put the frame in the reordering buffer */
@@ -688,7 +711,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
688 tid_agg_rx->stored_mpdu_num++; 711 tid_agg_rx->stored_mpdu_num++;
689 ieee80211_sta_reorder_release(hw, tid_agg_rx, frames); 712 ieee80211_sta_reorder_release(hw, tid_agg_rx, frames);
690 713
691 return true; 714 out:
715 spin_unlock(&tid_agg_rx->reorder_lock);
716 return ret;
692} 717}
693 718
694/* 719/*
@@ -2387,6 +2412,37 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
2387#undef CALL_RXH 2412#undef CALL_RXH
2388} 2413}
2389 2414
2415/*
2416 * This function makes calls into the RX path. Therefore the
2417 * caller must hold the sta_info->lock and everything has to
2418 * be under rcu_read_lock protection as well.
2419 */
2420void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
2421{
2422 struct sk_buff_head frames;
2423 struct ieee80211_rx_data rx = { };
2424
2425 __skb_queue_head_init(&frames);
2426
2427 /* construct rx struct */
2428 rx.sta = sta;
2429 rx.sdata = sta->sdata;
2430 rx.local = sta->local;
2431 rx.queue = tid;
2432 rx.flags |= IEEE80211_RX_RA_MATCH;
2433
2434 if (unlikely(test_bit(SCAN_HW_SCANNING, &sta->local->scanning) ||
2435 test_bit(SCAN_OFF_CHANNEL, &sta->local->scanning)))
2436 rx.flags |= IEEE80211_RX_IN_SCAN;
2437
2438 spin_lock(&sta->ampdu_mlme.tid_rx[tid]->reorder_lock);
2439 ieee80211_sta_reorder_release(&sta->local->hw,
2440 sta->ampdu_mlme.tid_rx[tid], &frames);
2441 spin_unlock(&sta->ampdu_mlme.tid_rx[tid]->reorder_lock);
2442
2443 ieee80211_rx_handlers(&rx, &frames);
2444}
2445
2390/* main receive path */ 2446/* main receive path */
2391 2447
2392static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, 2448static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 54262e72376d..810c5ce98316 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -103,6 +103,7 @@ struct tid_ampdu_tx {
103 * @reorder_buf: buffer to reorder incoming aggregated MPDUs 103 * @reorder_buf: buffer to reorder incoming aggregated MPDUs
104 * @reorder_time: jiffies when skb was added 104 * @reorder_time: jiffies when skb was added
105 * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) 105 * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
106 * @reorder_timer: releases expired frames from the reorder buffer.
106 * @head_seq_num: head sequence number in reordering buffer. 107 * @head_seq_num: head sequence number in reordering buffer.
107 * @stored_mpdu_num: number of MPDUs in reordering buffer 108 * @stored_mpdu_num: number of MPDUs in reordering buffer
108 * @ssn: Starting Sequence Number expected to be aggregated. 109 * @ssn: Starting Sequence Number expected to be aggregated.
@@ -110,20 +111,25 @@ struct tid_ampdu_tx {
110 * @timeout: reset timer value (in TUs). 111 * @timeout: reset timer value (in TUs).
111 * @dialog_token: dialog token for aggregation session 112 * @dialog_token: dialog token for aggregation session
112 * @rcu_head: RCU head used for freeing this struct 113 * @rcu_head: RCU head used for freeing this struct
114 * @reorder_lock: serializes access to reorder buffer, see below.
113 * 115 *
114 * This structure is protected by RCU and the per-station 116 * This structure is protected by RCU and the per-station
115 * spinlock. Assignments to the array holding it must hold 117 * spinlock. Assignments to the array holding it must hold
116 * the spinlock, only the RX path can access it under RCU 118 * the spinlock.
117 * lock-free. The RX path, since it is single-threaded, 119 *
118 * can even modify the structure without locking since the 120 * The @reorder_lock is used to protect the variables and
119 * only other modifications to it are done when the struct 121 * arrays such as @reorder_buf, @reorder_time, @head_seq_num,
120 * can not yet or no longer be found by the RX path. 122 * @stored_mpdu_num and @reorder_time from being corrupted by
123 * concurrent access of the RX path and the expired frame
124 * release timer.
121 */ 125 */
122struct tid_ampdu_rx { 126struct tid_ampdu_rx {
123 struct rcu_head rcu_head; 127 struct rcu_head rcu_head;
128 spinlock_t reorder_lock;
124 struct sk_buff **reorder_buf; 129 struct sk_buff **reorder_buf;
125 unsigned long *reorder_time; 130 unsigned long *reorder_time;
126 struct timer_list session_timer; 131 struct timer_list session_timer;
132 struct timer_list reorder_timer;
127 u16 head_seq_num; 133 u16 head_seq_num;
128 u16 stored_mpdu_num; 134 u16 stored_mpdu_num;
129 u16 ssn; 135 u16 ssn;