wil6210: relaxed check for BACK start sequence
[deliverable/linux.git] / drivers / net / wireless / ath / wil6210 / rx_reorder.c
CommitLineData
b4490f42
VK
1#include "wil6210.h"
2#include "txrx.h"
3
4#define SEQ_MODULO 0x1000
5#define SEQ_MASK 0xfff
6
7static inline int seq_less(u16 sq1, u16 sq2)
8{
9 return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
10}
11
12static inline u16 seq_inc(u16 sq)
13{
14 return (sq + 1) & SEQ_MASK;
15}
16
17static inline u16 seq_sub(u16 sq1, u16 sq2)
18{
19 return (sq1 - sq2) & SEQ_MASK;
20}
21
22static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq)
23{
24 return seq_sub(seq, r->ssn) % r->buf_size;
25}
26
27static void wil_release_reorder_frame(struct wil6210_priv *wil,
28 struct wil_tid_ampdu_rx *r,
29 int index)
30{
31 struct net_device *ndev = wil_to_ndev(wil);
32 struct sk_buff *skb = r->reorder_buf[index];
33
34 if (!skb)
35 goto no_frame;
36
37 /* release the frame from the reorder ring buffer */
38 r->stored_mpdu_num--;
39 r->reorder_buf[index] = NULL;
40 wil_netif_rx_any(skb, ndev);
41
42no_frame:
43 r->head_seq_num = seq_inc(r->head_seq_num);
44}
45
46static void wil_release_reorder_frames(struct wil6210_priv *wil,
47 struct wil_tid_ampdu_rx *r,
48 u16 hseq)
49{
50 int index;
51
52 while (seq_less(r->head_seq_num, hseq)) {
53 index = reorder_index(r, r->head_seq_num);
54 wil_release_reorder_frame(wil, r, index);
55 }
56}
57
58static void wil_reorder_release(struct wil6210_priv *wil,
59 struct wil_tid_ampdu_rx *r)
60{
61 int index = reorder_index(r, r->head_seq_num);
62
63 while (r->reorder_buf[index]) {
64 wil_release_reorder_frame(wil, r, index);
65 index = reorder_index(r, r->head_seq_num);
66 }
67}
68
69void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
70{
71 struct net_device *ndev = wil_to_ndev(wil);
72 struct vring_rx_desc *d = wil_skb_rxdesc(skb);
73 int tid = wil_rxdesc_tid(d);
74 int cid = wil_rxdesc_cid(d);
75 int mid = wil_rxdesc_mid(d);
76 u16 seq = wil_rxdesc_seq(d);
77 struct wil_sta_info *sta = &wil->sta[cid];
78 struct wil_tid_ampdu_rx *r = sta->tid_rx[tid];
79 u16 hseq;
80 int index;
81
82 wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n",
83 mid, cid, tid, seq);
84
85 if (!r) {
86 wil_netif_rx_any(skb, ndev);
87 return;
88 }
89
90 hseq = r->head_seq_num;
91
92 spin_lock(&r->reorder_lock);
93
c888cdd4
VK
94 /** Due to the race between WMI events, where BACK establishment
95 * reported, and data Rx, few packets may be pass up before reorder
96 * buffer get allocated. Catch up by pretending SSN is what we
97 * see in the 1-st Rx packet
98 */
99 if (r->first_time) {
100 r->first_time = false;
101 if (seq != r->head_seq_num) {
102 wil_err(wil, "Error: 1-st frame with wrong sequence"
103 " %d, should be %d. Fixing...\n", seq,
104 r->head_seq_num);
105 r->head_seq_num = seq;
106 r->ssn = seq;
107 }
108 }
109
b4490f42
VK
110 /* frame with out of date sequence number */
111 if (seq_less(seq, r->head_seq_num)) {
112 dev_kfree_skb(skb);
113 goto out;
114 }
115
116 /*
117 * If frame the sequence number exceeds our buffering window
118 * size release some previous frames to make room for this one.
119 */
120 if (!seq_less(seq, r->head_seq_num + r->buf_size)) {
121 hseq = seq_inc(seq_sub(seq, r->buf_size));
122 /* release stored frames up to new head to stack */
123 wil_release_reorder_frames(wil, r, hseq);
124 }
125
126 /* Now the new frame is always in the range of the reordering buffer */
127
128 index = reorder_index(r, seq);
129
130 /* check if we already stored this frame */
131 if (r->reorder_buf[index]) {
132 dev_kfree_skb(skb);
133 goto out;
134 }
135
136 /*
137 * If the current MPDU is in the right order and nothing else
138 * is stored we can process it directly, no need to buffer it.
139 * If it is first but there's something stored, we may be able
140 * to release frames after this one.
141 */
142 if (seq == r->head_seq_num && r->stored_mpdu_num == 0) {
143 r->head_seq_num = seq_inc(r->head_seq_num);
144 wil_netif_rx_any(skb, ndev);
145 goto out;
146 }
147
148 /* put the frame in the reordering buffer */
149 r->reorder_buf[index] = skb;
150 r->reorder_time[index] = jiffies;
151 r->stored_mpdu_num++;
152 wil_reorder_release(wil, r);
153
154out:
155 spin_unlock(&r->reorder_lock);
156}
157
158struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
159 int size, u16 ssn)
160{
161 struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL);
162 if (!r)
163 return NULL;
164
165 r->reorder_buf =
166 kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL);
167 r->reorder_time =
168 kcalloc(size, sizeof(unsigned long), GFP_KERNEL);
169 if (!r->reorder_buf || !r->reorder_time) {
170 kfree(r->reorder_buf);
171 kfree(r->reorder_time);
172 kfree(r);
173 return NULL;
174 }
175
176 spin_lock_init(&r->reorder_lock);
177 r->ssn = ssn;
178 r->head_seq_num = ssn;
179 r->buf_size = size;
180 r->stored_mpdu_num = 0;
c888cdd4 181 r->first_time = true;
b4490f42
VK
182 return r;
183}
184
185void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
186 struct wil_tid_ampdu_rx *r)
187{
188 if (!r)
189 return;
190 wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size);
191 kfree(r->reorder_buf);
192 kfree(r->reorder_time);
193 kfree(r);
194}
This page took 0.061274 seconds and 5 git commands to generate.