Commit 55f854dd authored by Laurent Vivier's avatar Laurent Vivier Committed by Jakub Kicinski
Browse files

qmi_wwan: allow max_mtu above hard_mtu to control rx_urb_size

Commit c7159e96 ("usbnet: limit max_mtu based on device's hard_mtu")
capped net->max_mtu to the device's hard_mtu in usbnet_probe(). While
this correctly prevents oversized packets on standard USB network
devices, it breaks the qmi_wwan driver.

qmi_wwan relies on userspace (e.g. ModemManager) setting a large MTU on
the wwan0 interface to configure rx_urb_size via usbnet_change_mtu().
QMI modems negotiate USB transfer sizes of 16,383 or 32,767 bytes, and
the USB receive buffers must be sized accordingly. With max_mtu capped
to hard_mtu (~1500 bytes), userspace can no longer raise the MTU, the
receive buffers remain small, and download speeds drop from >300 Mbps
to ~0.8 Mbps.

Introduce a FLAG_NOMAXMTU driver flag that allows individual usbnet
drivers to opt out of the max_mtu cap. Set this flag in qmi_wwan's
driver_info structures to restore the previous behavior for QMI devices,
while keeping the safety fix in place for all other usbnet drivers.

Fixes: c7159e96 ("usbnet: limit max_mtu based on device's hard_mtu")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/lkml/CAPh3n803k8JcBPV5qEzUB-oKzWkAs-D5CU7z=Vd_nLRCr5ZqQg@mail.gmail.com/


Reported-by: default avatarKoen Vandeputte <koen.vandeputte@citymesh.com>
Tested-by: default avatarDaniele Palmas <dnlplm@gmail.com>
Signed-off-by: default avatarLaurent Vivier <lvivier@redhat.com>
Link: https://patch.msgid.link/20260304134338.1785002-1-lvivier@redhat.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 03910cdc
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -928,7 +928,7 @@ static int qmi_wwan_resume(struct usb_interface *intf)

static const struct driver_info	qmi_wwan_info = {
	.description	= "WWAN/QMI device",
	.flags		= FLAG_WWAN | FLAG_SEND_ZLP,
	.flags		= FLAG_WWAN | FLAG_NOMAXMTU | FLAG_SEND_ZLP,
	.bind		= qmi_wwan_bind,
	.unbind		= qmi_wwan_unbind,
	.manage_power	= qmi_wwan_manage_power,
@@ -937,7 +937,7 @@ static const struct driver_info qmi_wwan_info = {

static const struct driver_info	qmi_wwan_info_quirk_dtr = {
	.description	= "WWAN/QMI device",
	.flags		= FLAG_WWAN | FLAG_SEND_ZLP,
	.flags		= FLAG_WWAN | FLAG_NOMAXMTU | FLAG_SEND_ZLP,
	.bind		= qmi_wwan_bind,
	.unbind		= qmi_wwan_unbind,
	.manage_power	= qmi_wwan_manage_power,
+4 −3
Original line number Diff line number Diff line
@@ -1829,11 +1829,12 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
		if ((dev->driver_info->flags & FLAG_NOARP) != 0)
			net->flags |= IFF_NOARP;

		if (net->max_mtu > (dev->hard_mtu - net->hard_header_len))
		if ((dev->driver_info->flags & FLAG_NOMAXMTU) == 0 &&
		    net->max_mtu > (dev->hard_mtu - net->hard_header_len))
			net->max_mtu = dev->hard_mtu - net->hard_header_len;

		if (net->mtu > net->max_mtu)
			net->mtu = net->max_mtu;
		if (net->mtu > (dev->hard_mtu - net->hard_header_len))
			net->mtu = dev->hard_mtu - net->hard_header_len;

	} else if (!info->in || !info->out)
		status = usbnet_get_endpoints(dev, udev);
+1 −0
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@ struct driver_info {
#define FLAG_MULTI_PACKET	0x2000
#define FLAG_RX_ASSEMBLE	0x4000	/* rx packets may span >1 frames */
#define FLAG_NOARP		0x8000	/* device can't do ARP */
#define FLAG_NOMAXMTU		0x10000	/* allow max_mtu above hard_mtu */

	/* init device ... can sleep, or cause probe() failure */
	int	(*bind)(struct usbnet *, struct usb_interface *);