]>
Commit | Line | Data |
---|---|---|
23588859 AF |
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, ®, 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 |