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 | } |