diff options
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/agg-rx.c | 22 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 1 | ||||
-rw-r--r-- | net/mac80211/rx.c | 70 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 16 |
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 | ||
124 | static 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 | |||
123 | static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid, | 138 | static 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); | |||
1136 | void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); | 1136 | void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); |
1137 | void ieee80211_ba_session_work(struct work_struct *work); | 1137 | void ieee80211_ba_session_work(struct work_struct *work); |
1138 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); | 1138 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); |
1139 | void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); | ||
1139 | 1140 | ||
1140 | /* Spectrum management */ | 1141 | /* Spectrum management */ |
1141 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1142 | void 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 | */ | ||
2420 | void 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 | ||
2392 | static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata, | 2448 | static 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 | */ |
122 | struct tid_ampdu_rx { | 126 | struct 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; |