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>
This commit is contained in:
parent
a5f390562a
commit
bc6b89237a
@ -42,8 +42,12 @@
|
|||||||
|
|
||||||
static void usbctrl_async_callback(struct urb *urb)
|
static void usbctrl_async_callback(struct urb *urb)
|
||||||
{
|
{
|
||||||
if (urb)
|
if (urb) {
|
||||||
kfree(urb->context);
|
/* free dr */
|
||||||
|
kfree(urb->setup_packet);
|
||||||
|
/* free databuf */
|
||||||
|
kfree(urb->transfer_buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
|
static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
|
||||||
@ -55,25 +59,31 @@ static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
|
|||||||
u8 reqtype;
|
u8 reqtype;
|
||||||
struct usb_ctrlrequest *dr;
|
struct usb_ctrlrequest *dr;
|
||||||
struct urb *urb;
|
struct urb *urb;
|
||||||
struct rtl819x_async_write_data {
|
const u16 databuf_maxlen = REALTEK_USB_VENQT_MAX_BUF_SIZE;
|
||||||
u8 data[REALTEK_USB_VENQT_MAX_BUF_SIZE];
|
u8 *databuf;
|
||||||
struct usb_ctrlrequest dr;
|
|
||||||
} *buf;
|
if (WARN_ON_ONCE(len > databuf_maxlen))
|
||||||
|
len = databuf_maxlen;
|
||||||
|
|
||||||
pipe = usb_sndctrlpipe(udev, 0); /* write_out */
|
pipe = usb_sndctrlpipe(udev, 0); /* write_out */
|
||||||
reqtype = REALTEK_USB_VENQT_WRITE;
|
reqtype = REALTEK_USB_VENQT_WRITE;
|
||||||
|
|
||||||
buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
|
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
|
||||||
if (!buf)
|
if (!dr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
databuf = kmalloc(databuf_maxlen, GFP_ATOMIC);
|
||||||
if (!urb) {
|
if (!databuf) {
|
||||||
kfree(buf);
|
kfree(dr);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
dr = &buf->dr;
|
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||||
|
if (!urb) {
|
||||||
|
kfree(databuf);
|
||||||
|
kfree(dr);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
dr->bRequestType = reqtype;
|
dr->bRequestType = reqtype;
|
||||||
dr->bRequest = request;
|
dr->bRequest = request;
|
||||||
@ -81,13 +91,15 @@ static int _usbctrl_vendorreq_async_write(struct usb_device *udev, u8 request,
|
|||||||
dr->wIndex = cpu_to_le16(index);
|
dr->wIndex = cpu_to_le16(index);
|
||||||
dr->wLength = cpu_to_le16(len);
|
dr->wLength = cpu_to_le16(len);
|
||||||
/* data are already in little-endian order */
|
/* data are already in little-endian order */
|
||||||
memcpy(buf, pdata, len);
|
memcpy(databuf, pdata, len);
|
||||||
usb_fill_control_urb(urb, udev, pipe,
|
usb_fill_control_urb(urb, udev, pipe,
|
||||||
(unsigned char *)dr, buf, len,
|
(unsigned char *)dr, databuf, len,
|
||||||
usbctrl_async_callback, buf);
|
usbctrl_async_callback, NULL);
|
||||||
rc = usb_submit_urb(urb, GFP_ATOMIC);
|
rc = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (rc < 0)
|
if (rc < 0) {
|
||||||
kfree(buf);
|
kfree(databuf);
|
||||||
|
kfree(dr);
|
||||||
|
}
|
||||||
usb_free_urb(urb);
|
usb_free_urb(urb);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user