CC: brcm2708: Fix Kernel Panic: DM9601 Fast Ethernet Adapter
authorMarian Hello <marian.hello@gmail.com>
Wed, 7 Dec 2016 16:06:47 +0000 (17:06 +0100)
committerZoltan HERPAI <wigyori@uid0.hu>
Wed, 7 Dec 2016 16:06:47 +0000 (17:06 +0100)
The dm9601 driver expects to receive a single encapsulated ethernet
frame from the device in one URB transfer, and it provides an URB
buffer of length 1,522 to receive it. This is not a round multiple
of USB transfer packets.

The device in question [1] provides a stream of such frames and it
does not conveniently slice them up as the dm9601 driver expects. We
can end up with 1,536 (0x600) bytes returned by the device in response
to the URB request. This may include several encapsulated ethernet
frames, and/or fragments thereof.

It seems to me that the kernel 'Oops' arises because the dwc_otg driver
does not notice that the destination buffer is too small to receive the
full 1,536 bytes. Comparing dwc_otg's update_urb_state_xfer_comp with
dwc2's dwc2_update_urb_state is suggestive.

More details: https://github.com/raspberrypi/linux/issues/1045
All Credits to: https://github.com/mw9

Signed-off-by: Marian Hello <marian.hello@gmail.com>
Reviewed-by: Zoltan HERPAI <wigyori@uid0.hu>
target/linux/brcm2708/patches-3.18/0700-dm9601-kernel-panic.patch [new file with mode: 0644]

diff --git a/target/linux/brcm2708/patches-3.18/0700-dm9601-kernel-panic.patch b/target/linux/brcm2708/patches-3.18/0700-dm9601-kernel-panic.patch
new file mode 100644 (file)
index 0000000..30a979d
--- /dev/null
@@ -0,0 +1,14 @@
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+@@ -738,6 +738,11 @@ static int update_urb_state_xfer_comp(dw
+                                            DWC_OTG_HC_XFER_COMPLETE,
+                                            &short_read);
++      if (urb->actual_length + xfer_length > urb->length) {
++      /*  dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__);*/
++              xfer_length = urb->length - urb->actual_length;
++      }
++
+       /* non DWORD-aligned buffer case handling. */
+       if (hc->align_buff && xfer_length && hc->ep_is_in) {
+               dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf,