From 637f847b68741de42547d5f846c71ed5fe859b6e Mon Sep 17 00:00:00 2001 From: TarAldarion Date: Mon, 2 Mar 2020 12:13:05 +0000 Subject: [PATCH] Fix for packets being rejected in the ring buffer used by the xHCI controller. When a packet larger than MTU arrives in Linux from the modem, it is discarded with -EOVERFLOW error (Babble error). This is seen on USB3.0 and USB2.0 busses. This is essentially because the MRU (Max Receive Size) is not a separate entity to the MTU (Max Transmit Size) and the received packets can be larger than those transmitted. Following the babble error there were an endless supply of zero-length URBs which are rejected with -EPROTO (increasing the rx input error counter each time). This is only seen on USB3.0. These continue to come ad infinitum until the modem is shutdown. There appears to be a bug in the core USB handling code in Linux that doesn't deal well with network MTUs smaller than 1500 bytes. By default the dev->hard_mtu (the real MTU) is in lockstep with dev->rx_urb_size (essentially an MRU), and it's the latter that is causing trouble. This has nothing to do with the modems; the issue can be reproduced by getting a USB-Ethernet dongle, setting the MTU to 1430, and pinging with size greater than 1406. --- drivers/net/usb/qmi_wwan.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 5754bb6ca0ee..545c77257f4e 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -815,6 +815,13 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) } dev->net->netdev_ops = &qmi_wwan_netdev_ops; dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group; + /* LTE Networks don't always respect their own MTU on receive side; + * e.g. AT&T pushes 1430 MTU but still allows 1500 byte packets from + * far-end network. Make receive buffer large enough to accommodate + * them, and add four bytes so MTU does not equal MRU on network + * with 1500 MTU otherwise usbnet_change_mtu() will change both. + */ + dev->rx_urb_size = ETH_DATA_LEN + 4; err: return status; }