]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/backports-4.2.6-1-mt7601_upstream_fixes.patch
Merge branch 'ipsec' into next
[ipfire-2.x.git] / src / patches / backports-4.2.6-1-mt7601_upstream_fixes.patch
1 From bed429e1ae8b7ee207e01f3aa60dcc0d06a8ed4d Mon Sep 17 00:00:00 2001
2 From: Jakub Kicinski <kubakici@wp.pl>
3 Date: Fri, 31 Jul 2015 15:04:46 +0200
4 Subject: mt7601u: fix dma from stack address
5
6 DMA to variables located on the stack is a bad idea.
7 For simplicity and to avoid frequent allocations create
8 a buffer inside the device structure. Protect this
9 buffer with vendor_req_mutex. Don't protect vendor
10 requests which don't use this buffer.
11
12 Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
13 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
14 ---
15 drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 4 +-
16 drivers/net/wireless/mediatek/mt7601u/usb.c | 63 ++++++++++++-------------
17 drivers/net/wireless/mediatek/mt7601u/usb.h | 2 +
18 3 files changed, 34 insertions(+), 35 deletions(-)
19
20 diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
21 index 9102be6b..6bdfc11 100644
22 --- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
23 +++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
24 @@ -146,7 +146,7 @@ enum {
25 * @rx_lock: protects @rx_q.
26 * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi.
27 * @mutex: ensures exclusive access from mac80211 callbacks.
28 - * @vendor_req_mutex: ensures atomicity of vendor requests.
29 + * @vendor_req_mutex: protects @vend_buf, ensures atomicity of split writes.
30 * @reg_atomic_mutex: ensures atomicity of indirect register accesses
31 * (accesses to RF and BBP).
32 * @hw_atomic_mutex: ensures exclusive access to HW during critical
33 @@ -184,6 +184,8 @@ struct mt7601u_dev {
34 struct mt7601u_eeprom_params *ee;
35
36 struct mutex vendor_req_mutex;
37 + void *vend_buf;
38 +
39 struct mutex reg_atomic_mutex;
40 struct mutex hw_atomic_mutex;
41
42 diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c
43 index 54dba40..416c604 100644
44 --- a/drivers/net/wireless/mediatek/mt7601u/usb.c
45 +++ b/drivers/net/wireless/mediatek/mt7601u/usb.c
46 @@ -92,10 +92,9 @@ void mt7601u_complete_urb(struct urb *urb)
47 complete(cmpl);
48 }
49
50 -static int
51 -__mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
52 - const u8 direction, const u16 val, const u16 offset,
53 - void *buf, const size_t buflen)
54 +int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
55 + const u8 direction, const u16 val, const u16 offset,
56 + void *buf, const size_t buflen)
57 {
58 int i, ret;
59 struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
60 @@ -110,6 +109,8 @@ __mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
61 trace_mt_vend_req(dev, pipe, req, req_type, val, offset,
62 buf, buflen, ret);
63
64 + if (ret == -ENODEV)
65 + set_bit(MT7601U_STATE_REMOVED, &dev->state);
66 if (ret >= 0 || ret == -ENODEV)
67 return ret;
68
69 @@ -122,25 +123,6 @@ __mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
70 return ret;
71 }
72
73 -int
74 -mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
75 - const u8 direction, const u16 val, const u16 offset,
76 - void *buf, const size_t buflen)
77 -{
78 - int ret;
79 -
80 - mutex_lock(&dev->vendor_req_mutex);
81 -
82 - ret = __mt7601u_vendor_request(dev, req, direction, val, offset,
83 - buf, buflen);
84 - if (ret == -ENODEV)
85 - set_bit(MT7601U_STATE_REMOVED, &dev->state);
86 -
87 - mutex_unlock(&dev->vendor_req_mutex);
88 -
89 - return ret;
90 -}
91 -
92 void mt7601u_vendor_reset(struct mt7601u_dev *dev)
93 {
94 mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT,
95 @@ -150,19 +132,21 @@ void mt7601u_vendor_reset(struct mt7601u_dev *dev)
96 u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
97 {
98 int ret;
99 - __le32 reg;
100 - u32 val;
101 + u32 val = ~0;
102
103 WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset);
104
105 + mutex_lock(&dev->vendor_req_mutex);
106 +
107 ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN,
108 - 0, offset, &reg, sizeof(reg));
109 - val = le32_to_cpu(reg);
110 - if (ret > 0 && ret != sizeof(reg)) {
111 + 0, offset, dev->vend_buf, MT_VEND_BUF);
112 + if (ret == MT_VEND_BUF)
113 + val = get_unaligned_le32(dev->vend_buf);
114 + else if (ret > 0)
115 dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n",
116 ret, offset);
117 - val = ~0;
118 - }
119 +
120 + mutex_unlock(&dev->vendor_req_mutex);
121
122 trace_reg_read(dev, offset, val);
123 return val;
124 @@ -173,12 +157,17 @@ int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
125 {
126 int ret;
127
128 + mutex_lock(&dev->vendor_req_mutex);
129 +
130 ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
131 val & 0xffff, offset, NULL, 0);
132 - if (ret)
133 - return ret;
134 - return mt7601u_vendor_request(dev, req, USB_DIR_OUT,
135 - val >> 16, offset + 2, NULL, 0);
136 + if (!ret)
137 + ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
138 + val >> 16, offset + 2, NULL, 0);
139 +
140 + mutex_unlock(&dev->vendor_req_mutex);
141 +
142 + return ret;
143 }
144
145 void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val)
146 @@ -275,6 +264,12 @@ static int mt7601u_probe(struct usb_interface *usb_intf,
147
148 usb_set_intfdata(usb_intf, dev);
149
150 + dev->vend_buf = devm_kmalloc(dev->dev, MT_VEND_BUF, GFP_KERNEL);
151 + if (!dev->vend_buf) {
152 + ret = -ENOMEM;
153 + goto err;
154 + }
155 +
156 ret = mt7601u_assign_pipes(usb_intf, dev);
157 if (ret)
158 goto err;
159 diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h
160 index 49e188f..bc18202 100644
161 --- a/drivers/net/wireless/mediatek/mt7601u/usb.h
162 +++ b/drivers/net/wireless/mediatek/mt7601u/usb.h
163 @@ -23,6 +23,8 @@
164
165 #define MT_VEND_DEV_MODE_RESET 1
166
167 +#define MT_VEND_BUF sizeof(__le32)
168 +
169 enum mt_vendor_req {
170 MT_VEND_DEV_MODE = 1,
171 MT_VEND_WRITE = 2,
172 --
173 cgit v0.12
174
175 From d9517c0a5d7468a7ea63086057604fcb0fff480e Mon Sep 17 00:00:00 2001
176 From: Jakub Kicinski <kubakici@wp.pl>
177 Date: Fri, 31 Jul 2015 15:04:47 +0200
178 Subject: mt7601u: use correct ieee80211_rx variant
179
180 Rx is run inside a tasklet so ieee80211_rx() should be used
181 instead of ieee80211_rx_ni().
182
183 Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
184 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
185 ---
186 drivers/net/wireless/mediatek/mt7601u/dma.c | 2 +-
187 1 file changed, 1 insertion(+), 1 deletion(-)
188
189 diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
190 index 7217da4..fb183e3 100644
191 --- a/drivers/net/wireless/mediatek/mt7601u/dma.c
192 +++ b/drivers/net/wireless/mediatek/mt7601u/dma.c
193 @@ -112,7 +112,7 @@ static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data,
194 if (!skb)
195 return;
196
197 - ieee80211_rx_ni(dev->hw, skb);
198 + ieee80211_rx(dev->hw, skb);
199 }
200
201 static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len)
202 --
203 cgit v0.12
204
205 From 4513493d188d5e3052aee68eda85eaaa1a4e41c2 Mon Sep 17 00:00:00 2001
206 From: Jakub Kicinski <kubakici@wp.pl>
207 Date: Fri, 31 Jul 2015 15:04:48 +0200
208 Subject: mt7601u: fix tx status reporting contexts
209
210 mac80211 requires that rx path does not run concurrently with
211 tx status reporting. Since rx path is run in driver tasklet,
212 tx status cannot be reported directly from interrupt context
213 (there would be no way to lock it out).
214
215 Add tasklet for tx and move all possible code from irq handler
216 there.
217
218 Note: tx tasklet is needed because workqueue is queued very
219 rarely and that kills TCP performance.
220
221 Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
222 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
223 ---
224 drivers/net/wireless/mediatek/mt7601u/dma.c | 30 +++++++++++++++++++++----
225 drivers/net/wireless/mediatek/mt7601u/init.c | 1 +
226 drivers/net/wireless/mediatek/mt7601u/mac.c | 4 ++++
227 drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 2 ++
228 4 files changed, 33 insertions(+), 4 deletions(-)
229
230 diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
231 index fb183e3..63c4854 100644
232 --- a/drivers/net/wireless/mediatek/mt7601u/dma.c
233 +++ b/drivers/net/wireless/mediatek/mt7601u/dma.c
234 @@ -236,23 +236,42 @@ static void mt7601u_complete_tx(struct urb *urb)
235 skb = q->e[q->start].skb;
236 trace_mt_tx_dma_done(dev, skb);
237
238 - mt7601u_tx_status(dev, skb);
239 + __skb_queue_tail(&dev->tx_skb_done, skb);
240 + tasklet_schedule(&dev->tx_tasklet);
241
242 if (q->used == q->entries - q->entries / 8)
243 ieee80211_wake_queue(dev->hw, skb_get_queue_mapping(skb));
244
245 q->start = (q->start + 1) % q->entries;
246 q->used--;
247 +out:
248 + spin_unlock_irqrestore(&dev->tx_lock, flags);
249 +}
250
251 - if (urb->status)
252 - goto out;
253 +static void mt7601u_tx_tasklet(unsigned long data)
254 +{
255 + struct mt7601u_dev *dev = (struct mt7601u_dev *) data;
256 + struct sk_buff_head skbs;
257 + unsigned long flags;
258 +
259 + __skb_queue_head_init(&skbs);
260 +
261 + spin_lock_irqsave(&dev->tx_lock, flags);
262
263 set_bit(MT7601U_STATE_MORE_STATS, &dev->state);
264 if (!test_and_set_bit(MT7601U_STATE_READING_STATS, &dev->state))
265 queue_delayed_work(dev->stat_wq, &dev->stat_work,
266 msecs_to_jiffies(10));
267 -out:
268 +
269 + skb_queue_splice_init(&dev->tx_skb_done, &skbs);
270 +
271 spin_unlock_irqrestore(&dev->tx_lock, flags);
272 +
273 + while (!skb_queue_empty(&skbs)) {
274 + struct sk_buff *skb = __skb_dequeue(&skbs);
275 +
276 + mt7601u_tx_status(dev, skb);
277 + }
278 }
279
280 static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev,
281 @@ -475,6 +494,7 @@ int mt7601u_dma_init(struct mt7601u_dev *dev)
282 {
283 int ret = -ENOMEM;
284
285 + tasklet_init(&dev->tx_tasklet, mt7601u_tx_tasklet, (unsigned long) dev);
286 tasklet_init(&dev->rx_tasklet, mt7601u_rx_tasklet, (unsigned long) dev);
287
288 ret = mt7601u_alloc_tx(dev);
289 @@ -502,4 +522,6 @@ void mt7601u_dma_cleanup(struct mt7601u_dev *dev)
290
291 mt7601u_free_rx(dev);
292 mt7601u_free_tx(dev);
293 +
294 + tasklet_kill(&dev->tx_tasklet);
295 }
296 diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c
297 index df3dd56..38eb20b 100644
298 --- a/drivers/net/wireless/mediatek/mt7601u/init.c
299 +++ b/drivers/net/wireless/mediatek/mt7601u/init.c
300 @@ -456,6 +456,7 @@ struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev)
301 spin_lock_init(&dev->lock);
302 spin_lock_init(&dev->con_mon_lock);
303 atomic_set(&dev->avg_ampdu_len, 1);
304 + skb_queue_head_init(&dev->tx_skb_done);
305
306 dev->stat_wq = alloc_workqueue("mt7601u", WQ_UNBOUND, 0);
307 if (!dev->stat_wq) {
308 diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c
309 index 7514bce..e3928cf 100644
310 --- a/drivers/net/wireless/mediatek/mt7601u/mac.c
311 +++ b/drivers/net/wireless/mediatek/mt7601u/mac.c
312 @@ -181,7 +181,11 @@ void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat)
313 }
314
315 mt76_mac_fill_tx_status(dev, &info, stat);
316 +
317 + local_bh_disable();
318 ieee80211_tx_status_noskb(dev->hw, sta, &info);
319 + local_bh_enable();
320 +
321 rcu_read_unlock();
322 }
323
324 diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
325 index 6bdfc11..bc5e294 100644
326 --- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
327 +++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
328 @@ -199,7 +199,9 @@ struct mt7601u_dev {
329
330 /* TX */
331 spinlock_t tx_lock;
332 + struct tasklet_struct tx_tasklet;
333 struct mt7601u_tx_queue *tx_q;
334 + struct sk_buff_head tx_skb_done;
335
336 atomic_t avg_ampdu_len;
337
338 --
339 cgit v0.12
340
341 From 78623bfb6f4cbdba3183621e8e0e781611217022 Mon Sep 17 00:00:00 2001
342 From: Jakub Kicinski <kubakici@wp.pl>
343 Date: Fri, 31 Jul 2015 15:04:49 +0200
344 Subject: mt7601u: lock out rx path and tx status reporting
345
346 mac80211 requires that rx path does not run concurrently with
347 tx status reporting. Add a spinlock which will ensure that.
348
349 Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
350 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
351 ---
352 drivers/net/wireless/mediatek/mt7601u/dma.c | 2 ++
353 drivers/net/wireless/mediatek/mt7601u/init.c | 1 +
354 drivers/net/wireless/mediatek/mt7601u/mac.c | 4 ++--
355 drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 4 +++-
356 drivers/net/wireless/mediatek/mt7601u/tx.c | 3 +++
357 5 files changed, 11 insertions(+), 3 deletions(-)
358
359 diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
360 index 63c4854..57a80cf 100644
361 --- a/drivers/net/wireless/mediatek/mt7601u/dma.c
362 +++ b/drivers/net/wireless/mediatek/mt7601u/dma.c
363 @@ -112,7 +112,9 @@ static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data,
364 if (!skb)
365 return;
366
367 + spin_lock(&dev->mac_lock);
368 ieee80211_rx(dev->hw, skb);
369 + spin_unlock(&dev->mac_lock);
370 }
371
372 static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len)
373 diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c
374 index 38eb20b..26190fd 100644
375 --- a/drivers/net/wireless/mediatek/mt7601u/init.c
376 +++ b/drivers/net/wireless/mediatek/mt7601u/init.c
377 @@ -454,6 +454,7 @@ struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev)
378 spin_lock_init(&dev->tx_lock);
379 spin_lock_init(&dev->rx_lock);
380 spin_lock_init(&dev->lock);
381 + spin_lock_init(&dev->mac_lock);
382 spin_lock_init(&dev->con_mon_lock);
383 atomic_set(&dev->avg_ampdu_len, 1);
384 skb_queue_head_init(&dev->tx_skb_done);
385 diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c
386 index e3928cf..e21c53e 100644
387 --- a/drivers/net/wireless/mediatek/mt7601u/mac.c
388 +++ b/drivers/net/wireless/mediatek/mt7601u/mac.c
389 @@ -182,9 +182,9 @@ void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat)
390
391 mt76_mac_fill_tx_status(dev, &info, stat);
392
393 - local_bh_disable();
394 + spin_lock_bh(&dev->mac_lock);
395 ieee80211_tx_status_noskb(dev->hw, sta, &info);
396 - local_bh_enable();
397 + spin_unlock_bh(&dev->mac_lock);
398
399 rcu_read_unlock();
400 }
401 diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
402 index bc5e294..428bd2f 100644
403 --- a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
404 +++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
405 @@ -141,8 +141,9 @@ enum {
406 /**
407 * struct mt7601u_dev - adapter structure
408 * @lock: protects @wcid->tx_rate.
409 + * @mac_lock: locks out mac80211's tx status and rx paths.
410 * @tx_lock: protects @tx_q and changes of MT7601U_STATE_*_STATS
411 - flags in @state.
412 + * flags in @state.
413 * @rx_lock: protects @rx_q.
414 * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi.
415 * @mutex: ensures exclusive access from mac80211 callbacks.
416 @@ -177,6 +178,7 @@ struct mt7601u_dev {
417 struct mt76_wcid __rcu *wcid[N_WCIDS];
418
419 spinlock_t lock;
420 + spinlock_t mac_lock;
421
422 const u16 *beacon_offsets;
423
424 diff --git a/drivers/net/wireless/mediatek/mt7601u/tx.c b/drivers/net/wireless/mediatek/mt7601u/tx.c
425 index 0be2080..a0a33dc 100644
426 --- a/drivers/net/wireless/mediatek/mt7601u/tx.c
427 +++ b/drivers/net/wireless/mediatek/mt7601u/tx.c
428 @@ -116,7 +116,10 @@ void mt7601u_tx_status(struct mt7601u_dev *dev, struct sk_buff *skb)
429 ieee80211_tx_info_clear_status(info);
430 info->status.rates[0].idx = -1;
431 info->flags |= IEEE80211_TX_STAT_ACK;
432 +
433 + spin_lock(&dev->mac_lock);
434 ieee80211_tx_status(dev->hw, skb);
435 + spin_unlock(&dev->mac_lock);
436 }
437
438 static int mt7601u_skb_rooms(struct mt7601u_dev *dev, struct sk_buff *skb)
439 --
440 cgit v0.12
441