]> git.ipfire.org Git - ipfire-2.x.git/blame - src/patches/backports-4.2.6-1-mt7601_upstream_fixes.patch
wget: Update to 1.20.2
[ipfire-2.x.git] / src / patches / backports-4.2.6-1-mt7601_upstream_fixes.patch
CommitLineData
23588859
AF
1From bed429e1ae8b7ee207e01f3aa60dcc0d06a8ed4d Mon Sep 17 00:00:00 2001
2From: Jakub Kicinski <kubakici@wp.pl>
3Date: Fri, 31 Jul 2015 15:04:46 +0200
4Subject: mt7601u: fix dma from stack address
5
6DMA to variables located on the stack is a bad idea.
7For simplicity and to avoid frequent allocations create
8a buffer inside the device structure. Protect this
9buffer with vendor_req_mutex. Don't protect vendor
10requests which don't use this buffer.
11
12Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
13Signed-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
20diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
21index 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
42diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c
43index 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;
159diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h
160index 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--
173cgit v0.12
174
175From d9517c0a5d7468a7ea63086057604fcb0fff480e Mon Sep 17 00:00:00 2001
176From: Jakub Kicinski <kubakici@wp.pl>
177Date: Fri, 31 Jul 2015 15:04:47 +0200
178Subject: mt7601u: use correct ieee80211_rx variant
179
180Rx is run inside a tasklet so ieee80211_rx() should be used
181instead of ieee80211_rx_ni().
182
183Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
184Signed-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
189diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
190index 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--
203cgit v0.12
204
205From 4513493d188d5e3052aee68eda85eaaa1a4e41c2 Mon Sep 17 00:00:00 2001
206From: Jakub Kicinski <kubakici@wp.pl>
207Date: Fri, 31 Jul 2015 15:04:48 +0200
208Subject: mt7601u: fix tx status reporting contexts
209
210mac80211 requires that rx path does not run concurrently with
211tx status reporting. Since rx path is run in driver tasklet,
212tx status cannot be reported directly from interrupt context
213(there would be no way to lock it out).
214
215Add tasklet for tx and move all possible code from irq handler
216there.
217
218Note: tx tasklet is needed because workqueue is queued very
219 rarely and that kills TCP performance.
220
221Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
222Signed-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
230diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
231index 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 }
296diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c
297index 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) {
308diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c
309index 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
324diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
325index 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--
339cgit v0.12
340
341From 78623bfb6f4cbdba3183621e8e0e781611217022 Mon Sep 17 00:00:00 2001
342From: Jakub Kicinski <kubakici@wp.pl>
343Date: Fri, 31 Jul 2015 15:04:49 +0200
344Subject: mt7601u: lock out rx path and tx status reporting
345
346mac80211 requires that rx path does not run concurrently with
347tx status reporting. Add a spinlock which will ensure that.
348
349Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
350Signed-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
359diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c
360index 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)
373diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c
374index 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);
385diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c
386index 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 }
401diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h
402index 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
424diff --git a/drivers/net/wireless/mediatek/mt7601u/tx.c b/drivers/net/wireless/mediatek/mt7601u/tx.c
425index 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--
440cgit v0.12
441