mt7601u: fix dma from stack address
authorJakub Kicinski <kubakici@wp.pl>
Fri, 31 Jul 2015 13:04:46 +0000 (15:04 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 10 Aug 2015 19:19:31 +0000 (22:19 +0300)
DMA to variables located on the stack is a bad idea.
For simplicity and to avoid frequent allocations create
a buffer inside the device structure.  Protect this
buffer with vendor_req_mutex.  Don't protect vendor
requests which don't use this buffer.

Signed-off-by: Jakub Kicinski <kubakici@wp.pl>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mediatek/mt7601u/mt7601u.h
drivers/net/wireless/mediatek/mt7601u/usb.c
drivers/net/wireless/mediatek/mt7601u/usb.h

index 9102be6b95cb70bb51f4ce34864549046e922605..6bdfc1103fccafabacf5572d04821366131e91c5 100644 (file)
@@ -146,7 +146,7 @@ enum {
  * @rx_lock:           protects @rx_q.
  * @con_mon_lock:      protects @ap_bssid, @bcn_*, @avg_rssi.
  * @mutex:             ensures exclusive access from mac80211 callbacks.
- * @vendor_req_mutex:  ensures atomicity of vendor requests.
+ * @vendor_req_mutex:  protects @vend_buf, ensures atomicity of split writes.
  * @reg_atomic_mutex:  ensures atomicity of indirect register accesses
  *                     (accesses to RF and BBP).
  * @hw_atomic_mutex:   ensures exclusive access to HW during critical
@@ -184,6 +184,8 @@ struct mt7601u_dev {
        struct mt7601u_eeprom_params *ee;
 
        struct mutex vendor_req_mutex;
+       void *vend_buf;
+
        struct mutex reg_atomic_mutex;
        struct mutex hw_atomic_mutex;
 
index 54dba400186511bf2842972f631bd2dde51c5a46..416c6045ff3128005664fd8600d97c3cba9245a9 100644 (file)
@@ -92,10 +92,9 @@ void mt7601u_complete_urb(struct urb *urb)
        complete(cmpl);
 }
 
-static int
-__mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
-                        const u8 direction, const u16 val, const u16 offset,
-                        void *buf, const size_t buflen)
+int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
+                          const u8 direction, const u16 val, const u16 offset,
+                          void *buf, const size_t buflen)
 {
        int i, ret;
        struct usb_device *usb_dev = mt7601u_to_usb_dev(dev);
@@ -110,6 +109,8 @@ __mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
                trace_mt_vend_req(dev, pipe, req, req_type, val, offset,
                                  buf, buflen, ret);
 
+               if (ret == -ENODEV)
+                       set_bit(MT7601U_STATE_REMOVED, &dev->state);
                if (ret >= 0 || ret == -ENODEV)
                        return ret;
 
@@ -122,25 +123,6 @@ __mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
        return ret;
 }
 
-int
-mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req,
-                      const u8 direction, const u16 val, const u16 offset,
-                      void *buf, const size_t buflen)
-{
-       int ret;
-
-       mutex_lock(&dev->vendor_req_mutex);
-
-       ret = __mt7601u_vendor_request(dev, req, direction, val, offset,
-                                      buf, buflen);
-       if (ret == -ENODEV)
-               set_bit(MT7601U_STATE_REMOVED, &dev->state);
-
-       mutex_unlock(&dev->vendor_req_mutex);
-
-       return ret;
-}
-
 void mt7601u_vendor_reset(struct mt7601u_dev *dev)
 {
        mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT,
@@ -150,19 +132,21 @@ void mt7601u_vendor_reset(struct mt7601u_dev *dev)
 u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
 {
        int ret;
-       __le32 reg;
-       u32 val;
+       u32 val = ~0;
 
        WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset);
 
+       mutex_lock(&dev->vendor_req_mutex);
+
        ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN,
-                                    0, offset, &reg, sizeof(reg));
-       val = le32_to_cpu(reg);
-       if (ret > 0 && ret != sizeof(reg)) {
+                                    0, offset, dev->vend_buf, MT_VEND_BUF);
+       if (ret == MT_VEND_BUF)
+               val = get_unaligned_le32(dev->vend_buf);
+       else if (ret > 0)
                dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n",
                        ret, offset);
-               val = ~0;
-       }
+
+       mutex_unlock(&dev->vendor_req_mutex);
 
        trace_reg_read(dev, offset, val);
        return val;
@@ -173,12 +157,17 @@ int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
 {
        int ret;
 
+       mutex_lock(&dev->vendor_req_mutex);
+
        ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
                                     val & 0xffff, offset, NULL, 0);
-       if (ret)
-               return ret;
-       return mt7601u_vendor_request(dev, req, USB_DIR_OUT,
-                                     val >> 16, offset + 2, NULL, 0);
+       if (!ret)
+               ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
+                                            val >> 16, offset + 2, NULL, 0);
+
+       mutex_unlock(&dev->vendor_req_mutex);
+
+       return ret;
 }
 
 void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val)
@@ -275,6 +264,12 @@ static int mt7601u_probe(struct usb_interface *usb_intf,
 
        usb_set_intfdata(usb_intf, dev);
 
+       dev->vend_buf = devm_kmalloc(dev->dev, MT_VEND_BUF, GFP_KERNEL);
+       if (!dev->vend_buf) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
        ret = mt7601u_assign_pipes(usb_intf, dev);
        if (ret)
                goto err;
index 49e188fa37983788b99bcd082e82ef8f5a43e4f7..bc182022b9d6398ed748ce7b3b7153c9831d8d4a 100644 (file)
@@ -23,6 +23,8 @@
 
 #define MT_VEND_DEV_MODE_RESET 1
 
+#define MT_VEND_BUF            sizeof(__le32)
+
 enum mt_vendor_req {
        MT_VEND_DEV_MODE = 1,
        MT_VEND_WRITE = 2,