aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorMichal Kazior <michal.kazior@tieto.com>2014-07-16 06:09:31 -0400
committerJohannes Berg <johannes.berg@intel.com>2014-07-21 10:17:26 -0400
commit83eb935ec74a91468776cd86415abcb6ee23cca8 (patch)
treee0977824eadbba57f30adaa47f7d31c2a60ed6f1 /net/mac80211
parent60e83deb4c1e7e8b6ab78e7331288bf4211bdeb6 (diff)
mac80211: fix Rx reordering with RX_FLAG_AMSDU_MORE
Some drivers (e.g. ath10k) report A-MSDU subframes individually with identical seqno. The A-MPDU Rx reorder code did not account for that which made it practically unusable with drivers using RX_FLAG_AMSDU_MORE because it would end up dropping a lot of frames resulting in confusion in upper network transport layers. Signed-off-by: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/agg-rx.c9
-rw-r--r--net/mac80211/ieee80211_i.h15
-rw-r--r--net/mac80211/rx.c57
-rw-r--r--net/mac80211/sta_info.h5
4 files changed, 62 insertions, 24 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 31bf2586fb84..d38c49b644cd 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
52 del_timer_sync(&tid_rx->reorder_timer); 52 del_timer_sync(&tid_rx->reorder_timer);
53 53
54 for (i = 0; i < tid_rx->buf_size; i++) 54 for (i = 0; i < tid_rx->buf_size; i++)
55 dev_kfree_skb(tid_rx->reorder_buf[i]); 55 __skb_queue_purge(&tid_rx->reorder_buf[i]);
56 kfree(tid_rx->reorder_buf); 56 kfree(tid_rx->reorder_buf);
57 kfree(tid_rx->reorder_time); 57 kfree(tid_rx->reorder_time);
58 kfree(tid_rx); 58 kfree(tid_rx);
@@ -232,7 +232,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
232 struct tid_ampdu_rx *tid_agg_rx; 232 struct tid_ampdu_rx *tid_agg_rx;
233 u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; 233 u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
234 u8 dialog_token; 234 u8 dialog_token;
235 int ret = -EOPNOTSUPP; 235 int i, ret = -EOPNOTSUPP;
236 236
237 /* extract session parameters from addba request frame */ 237 /* extract session parameters from addba request frame */
238 dialog_token = mgmt->u.action.u.addba_req.dialog_token; 238 dialog_token = mgmt->u.action.u.addba_req.dialog_token;
@@ -308,7 +308,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
308 308
309 /* prepare reordering buffer */ 309 /* prepare reordering buffer */
310 tid_agg_rx->reorder_buf = 310 tid_agg_rx->reorder_buf =
311 kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL); 311 kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
312 tid_agg_rx->reorder_time = 312 tid_agg_rx->reorder_time =
313 kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL); 313 kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
314 if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { 314 if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
@@ -318,6 +318,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
318 goto end; 318 goto end;
319 } 319 }
320 320
321 for (i = 0; i < buf_size; i++)
322 __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
323
321 ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, 324 ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
322 &sta->sta, tid, &start_seq_num, 0); 325 &sta->sta, tid, &start_seq_num, 0);
323 ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", 326 ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 49731dd044bb..c504e99a5404 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1729,6 +1729,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
1729 ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); 1729 ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
1730} 1730}
1731 1731
1732static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
1733{
1734 struct sk_buff *tail = skb_peek_tail(frames);
1735 struct ieee80211_rx_status *status;
1736
1737 if (!tail)
1738 return false;
1739
1740 status = IEEE80211_SKB_RXCB(tail);
1741 if (status->flag & RX_FLAG_AMSDU_MORE)
1742 return false;
1743
1744 return true;
1745}
1746
1732void ieee80211_dynamic_ps_enable_work(struct work_struct *work); 1747void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
1733void ieee80211_dynamic_ps_disable_work(struct work_struct *work); 1748void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
1734void ieee80211_dynamic_ps_timer(unsigned long data); 1749void ieee80211_dynamic_ps_timer(unsigned long data);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5a786d489f7e..bd2c9b22c945 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
688 int index, 688 int index,
689 struct sk_buff_head *frames) 689 struct sk_buff_head *frames)
690{ 690{
691 struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; 691 struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
692 struct sk_buff *skb;
692 struct ieee80211_rx_status *status; 693 struct ieee80211_rx_status *status;
693 694
694 lockdep_assert_held(&tid_agg_rx->reorder_lock); 695 lockdep_assert_held(&tid_agg_rx->reorder_lock);
695 696
696 if (!skb) 697 if (skb_queue_empty(skb_list))
697 goto no_frame; 698 goto no_frame;
698 699
699 /* release the frame from the reorder ring buffer */ 700 if (!ieee80211_rx_reorder_ready(skb_list)) {
701 __skb_queue_purge(skb_list);
702 goto no_frame;
703 }
704
705 /* release frames from the reorder ring buffer */
700 tid_agg_rx->stored_mpdu_num--; 706 tid_agg_rx->stored_mpdu_num--;
701 tid_agg_rx->reorder_buf[index] = NULL; 707 while ((skb = __skb_dequeue(skb_list))) {
702 status = IEEE80211_SKB_RXCB(skb); 708 status = IEEE80211_SKB_RXCB(skb);
703 status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE; 709 status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
704 __skb_queue_tail(frames, skb); 710 __skb_queue_tail(frames, skb);
711 }
705 712
706no_frame: 713no_frame:
707 tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num); 714 tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
@@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
738 struct tid_ampdu_rx *tid_agg_rx, 745 struct tid_ampdu_rx *tid_agg_rx,
739 struct sk_buff_head *frames) 746 struct sk_buff_head *frames)
740{ 747{
741 int index, j; 748 int index, i, j;
742 749
743 lockdep_assert_held(&tid_agg_rx->reorder_lock); 750 lockdep_assert_held(&tid_agg_rx->reorder_lock);
744 751
745 /* release the buffer until next missing frame */ 752 /* release the buffer until next missing frame */
746 index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; 753 index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
747 if (!tid_agg_rx->reorder_buf[index] && 754 if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
748 tid_agg_rx->stored_mpdu_num) { 755 tid_agg_rx->stored_mpdu_num) {
749 /* 756 /*
750 * No buffers ready to be released, but check whether any 757 * No buffers ready to be released, but check whether any
@@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
753 int skipped = 1; 760 int skipped = 1;
754 for (j = (index + 1) % tid_agg_rx->buf_size; j != index; 761 for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
755 j = (j + 1) % tid_agg_rx->buf_size) { 762 j = (j + 1) % tid_agg_rx->buf_size) {
756 if (!tid_agg_rx->reorder_buf[j]) { 763 if (!ieee80211_rx_reorder_ready(
764 &tid_agg_rx->reorder_buf[j])) {
757 skipped++; 765 skipped++;
758 continue; 766 continue;
759 } 767 }
@@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
762 HT_RX_REORDER_BUF_TIMEOUT)) 770 HT_RX_REORDER_BUF_TIMEOUT))
763 goto set_release_timer; 771 goto set_release_timer;
764 772
773 /* don't leave incomplete A-MSDUs around */
774 for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
775 i = (i + 1) % tid_agg_rx->buf_size)
776 __skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
777
765 ht_dbg_ratelimited(sdata, 778 ht_dbg_ratelimited(sdata,
766 "release an RX reorder frame due to timeout on earlier frames\n"); 779 "release an RX reorder frame due to timeout on earlier frames\n");
767 ieee80211_release_reorder_frame(sdata, tid_agg_rx, j, 780 ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
@@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
775 skipped) & IEEE80211_SN_MASK; 788 skipped) & IEEE80211_SN_MASK;
776 skipped = 0; 789 skipped = 0;
777 } 790 }
778 } else while (tid_agg_rx->reorder_buf[index]) { 791 } else while (ieee80211_rx_reorder_ready(
792 &tid_agg_rx->reorder_buf[index])) {
779 ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, 793 ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
780 frames); 794 frames);
781 index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; 795 index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
@@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
786 800
787 for (; j != (index - 1) % tid_agg_rx->buf_size; 801 for (; j != (index - 1) % tid_agg_rx->buf_size;
788 j = (j + 1) % tid_agg_rx->buf_size) { 802 j = (j + 1) % tid_agg_rx->buf_size) {
789 if (tid_agg_rx->reorder_buf[j]) 803 if (ieee80211_rx_reorder_ready(
804 &tid_agg_rx->reorder_buf[j]))
790 break; 805 break;
791 } 806 }
792 807
@@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
811 struct sk_buff_head *frames) 826 struct sk_buff_head *frames)
812{ 827{
813 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 828 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
829 struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
814 u16 sc = le16_to_cpu(hdr->seq_ctrl); 830 u16 sc = le16_to_cpu(hdr->seq_ctrl);
815 u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; 831 u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
816 u16 head_seq_num, buf_size; 832 u16 head_seq_num, buf_size;
@@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
845 index = mpdu_seq_num % tid_agg_rx->buf_size; 861 index = mpdu_seq_num % tid_agg_rx->buf_size;
846 862
847 /* check if we already stored this frame */ 863 /* check if we already stored this frame */
848 if (tid_agg_rx->reorder_buf[index]) { 864 if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
849 dev_kfree_skb(skb); 865 dev_kfree_skb(skb);
850 goto out; 866 goto out;
851 } 867 }
@@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
858 */ 874 */
859 if (mpdu_seq_num == tid_agg_rx->head_seq_num && 875 if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
860 tid_agg_rx->stored_mpdu_num == 0) { 876 tid_agg_rx->stored_mpdu_num == 0) {
861 tid_agg_rx->head_seq_num = 877 if (!(status->flag & RX_FLAG_AMSDU_MORE))
862 ieee80211_sn_inc(tid_agg_rx->head_seq_num); 878 tid_agg_rx->head_seq_num =
879 ieee80211_sn_inc(tid_agg_rx->head_seq_num);
863 ret = false; 880 ret = false;
864 goto out; 881 goto out;
865 } 882 }
866 883
867 /* put the frame in the reordering buffer */ 884 /* put the frame in the reordering buffer */
868 tid_agg_rx->reorder_buf[index] = skb; 885 __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
869 tid_agg_rx->reorder_time[index] = jiffies; 886 if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
870 tid_agg_rx->stored_mpdu_num++; 887 tid_agg_rx->reorder_time[index] = jiffies;
871 ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); 888 tid_agg_rx->stored_mpdu_num++;
889 ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
890 }
872 891
873 out: 892 out:
874 spin_unlock(&tid_agg_rx->reorder_lock); 893 spin_unlock(&tid_agg_rx->reorder_lock);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index e37f00969526..d411bcc8ef08 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -155,7 +155,8 @@ struct tid_ampdu_tx {
155/** 155/**
156 * struct tid_ampdu_rx - TID aggregation information (Rx). 156 * struct tid_ampdu_rx - TID aggregation information (Rx).
157 * 157 *
158 * @reorder_buf: buffer to reorder incoming aggregated MPDUs 158 * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
159 * A-MSDU with individually reported subframes.
159 * @reorder_time: jiffies when skb was added 160 * @reorder_time: jiffies when skb was added
160 * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) 161 * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
161 * @reorder_timer: releases expired frames from the reorder buffer. 162 * @reorder_timer: releases expired frames from the reorder buffer.
@@ -180,7 +181,7 @@ struct tid_ampdu_tx {
180struct tid_ampdu_rx { 181struct tid_ampdu_rx {
181 struct rcu_head rcu_head; 182 struct rcu_head rcu_head;
182 spinlock_t reorder_lock; 183 spinlock_t reorder_lock;
183 struct sk_buff **reorder_buf; 184 struct sk_buff_head *reorder_buf;
184 unsigned long *reorder_time; 185 unsigned long *reorder_time;
185 struct timer_list session_timer; 186 struct timer_list session_timer;
186 struct timer_list reorder_timer; 187 struct timer_list reorder_timer;