diff options
| author | Jussi Kivilinna <jussi.kivilinna@mbnet.fi> | 2013-02-18 03:29:30 -0500 |
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2013-02-18 15:30:40 -0500 |
| commit | bc6b89237acb3dee6af6e64e51a18255fef89cc2 (patch) | |
| tree | 7a9d124856f3e1792c6e2272ad6c1464e3ffc705 | |
| parent | a5f390562a375a315292648e2da865a12b42f280 (diff) | |
rtlwifi: usb: allocate URB control message setup_packet and data buffer separately
rtlwifi allocates both setup_packet and data buffer of control message urb,
using shared kmalloc in _usbctrl_vendorreq_async_write. Structure used for
allocating is:
struct {
u8 data[254];
struct usb_ctrlrequest dr;
};
Because 'struct usb_ctrlrequest' is __packed, setup packet is unaligned and
DMA mapping of both 'data' and 'dr' confuses ARM/sunxi, leading to memory
corruptions and freezes.
Patch changes setup packet to be allocated separately.
[v2]:
- Use WARN_ON_ONCE instead of WARN_ON
Cc: <stable@vger.kernel.org>
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
| -rw-r--r-- | drivers/net/wireless/rtlwifi/usb.c | 44 |
1 files changed, 28 insertions, 16 deletions
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 476eaef5e4a9..156b52732f3d 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c | |||
| @@ -42,8 +42,12 @@ | |||
| 42 | 42 | ||
| 43 | static void usbctrl_async_callback(struct urb *urb) | 43 | static void usbctrl_async_callback(struct urb *urb) |
| 44 | { | 44 | { |
| 45 | if (urb) | 45 | if (urb) { |
| 46 | kfree(urb->context); | 46 | /* free dr */ |
| 47 | kfree(urb->setup_packet); | ||
| 48 | /* free databuf */ | ||
| 49 | kfree(urb->transfer_buffer); | ||
| 50 | } | ||
| 47 | } | 51 | } |
| 48 | 52 | ||
| 49 | static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request, | 53 | static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request, |
| @@ -55,39 +59,47 @@ static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request, | |||
| 55 | u8 reqtype; | 59 | u8 reqtype; |
| 56 | struct usb_ctrlrequest *dr; | 60 | struct usb_ctrlrequest *dr; |
| 57 | struct urb *urb; | 61 | struct urb *urb; |
| 58 | struct rtl819x_async_write_data { | 62 | const u16 databuf_maxlen = REALTEK_USB_VENQT_MAX_BUF_SIZE; |
| 59 | u8 data[REALTEK_USB_VENQT_MAX_BUF_SIZE]; | 63 | u8 *databuf; |
| 60 | struct usb_ctrlrequest dr; | 64 | |
| 61 | } *buf; | 65 | if (WARN_ON_ONCE(len > databuf_maxlen)) |
| 66 | len = databuf_maxlen; | ||
| 62 | 67 | ||
| 63 | pipe = usb_sndctrlpipe(udev, 0); /* write_out */ | 68 | pipe = usb_sndctrlpipe(udev, 0); /* write_out */ |
| 64 | reqtype = REALTEK_USB_VENQT_WRITE; | 69 | reqtype = REALTEK_USB_VENQT_WRITE; |
| 65 | 70 | ||
| 66 | buf = kmalloc(sizeof(*buf), GFP_ATOMIC); | 71 | dr = kmalloc(sizeof(*dr), GFP_ATOMIC); |
| 67 | if (!buf) | 72 | if (!dr) |
| 68 | return -ENOMEM; | 73 | return -ENOMEM; |
| 69 | 74 | ||
| 75 | databuf = kmalloc(databuf_maxlen, GFP_ATOMIC); | ||
| 76 | if (!databuf) { | ||
| 77 | kfree(dr); | ||
| 78 | return -ENOMEM; | ||
| 79 | } | ||
| 80 | |||
| 70 | urb = usb_alloc_urb(0, GFP_ATOMIC); | 81 | urb = usb_alloc_urb(0, GFP_ATOMIC); |
| 71 | if (!urb) { | 82 | if (!urb) { |
| 72 | kfree(buf); | 83 | kfree(databuf); |
| 84 | kfree(dr); | ||
| 73 | return -ENOMEM; | 85 | return -ENOMEM; |
| 74 | } | 86 | } |
| 75 | 87 | ||
| 76 | dr = &buf->dr; | ||
| 77 | |||
| 78 | dr->bRequestType = reqtype; | 88 | dr->bRequestType = reqtype; |
| 79 | dr->bRequest = request; | 89 | dr->bRequest = request; |
| 80 | dr->wValue = cpu_to_le16(value); | 90 | dr->wValue = cpu_to_le16(value); |
| 81 | dr->wIndex = cpu_to_le16(index); | 91 | dr->wIndex = cpu_to_le16(index); |
| 82 | dr->wLength = cpu_to_le16(len); | 92 | dr->wLength = cpu_to_le16(len); |
| 83 | /* data are already in little-endian order */ | 93 | /* data are already in little-endian order */ |
| 84 | memcpy(buf, pdata, len); | 94 | memcpy(databuf, pdata, len); |
| 85 | usb_fill_control_urb(urb, udev, pipe, | 95 | usb_fill_control_urb(urb, udev, pipe, |
| 86 | (unsigned char *)dr, buf, len, | 96 | (unsigned char *)dr, databuf, len, |
| 87 | usbctrl_async_callback, buf); | 97 | usbctrl_async_callback, NULL); |
| 88 | rc = usb_submit_urb(urb, GFP_ATOMIC); | 98 | rc = usb_submit_urb(urb, GFP_ATOMIC); |
| 89 | if (rc < 0) | 99 | if (rc < 0) { |
| 90 | kfree(buf); | 100 | kfree(databuf); |
| 101 | kfree(dr); | ||
| 102 | } | ||
| 91 | usb_free_urb(urb); | 103 | usb_free_urb(urb); |
| 92 | return rc; | 104 | return rc; |
| 93 | } | 105 | } |
