Re: [PATCH] qmi_wwan: allow max_mtu above hard_mtu to control rx_urb_size

From: Thorsten Leemhuis

Date: Wed Mar 04 2026 - 03:43:43 EST


On 3/2/26 11:28, Laurent Vivier wrote:
> Commit c7159e960f14 ("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: c7159e960f14 ("usbnet: limit max_mtu based on device's hard_mtu")

[disclaimer: I'm not a reviewer, just someone that keeps and eye on
regressions]

Given that the regression afaics was found in a stable series with that
commit backported, would you maybe we willing to add a stable tag to
ensure fast backporting to all affected series? See
Documentation/process/stable-kernel-rules.rst for all details, but
normally this will do:

Cc: stable@xxxxxxxxxxxxxxx

> Reported-by: Koen Vandeputte <koen.vandeputte@xxxxxxxxxxxx>

While at it: please considered linking to the report using a Link or
Closes tag like this

Link:
https://lore.kernel.org/lkml/CAPh3n803k8JcBPV5qEzUB-oKzWkAs-D5CU7z=Vd_nLRCr5ZqQg@xxxxxxxxxxxxxx/

That is something Linus really wants[1] and actually something that
Documentation/process/submitting-patches.rst and
Documentation/process/5.Posting.rst asks for.

[1] see here:
https://lore.kernel.org/all/CAHk-=wj2kJRPWx8B09AAtzj+_g+T6UBX11TP0ebs1WJdTtv=WQ@xxxxxxxxxxxxxx/
https://lore.kernel.org/all/CAHk-=wjMmSZzMJ3Xnskdg4+GGz=5p5p+GSYyFBTh0f-DgvdBWg@xxxxxxxxxxxxxx/
https://lore.kernel.org/all/CAHk-=wjxzafG-=J8oT30s7upn4RhBs6TX-uVFZ5rME+L5_DoJA@xxxxxxxxxxxxxx/

Ciao, Thorsten

> Tested-by: Daniele Palmas <dnlplm@xxxxxxxxx>
> Signed-off-by: Laurent Vivier <lvivier@xxxxxxxxxx>
> ---
> drivers/net/usb/qmi_wwan.c | 4 ++--
> drivers/net/usb/usbnet.c | 7 ++++---
> include/linux/usb/usbnet.h | 1 +
> 3 files changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
> index 3a4985b582cb..05acac10cd2b 100644
> --- a/drivers/net/usb/qmi_wwan.c
> +++ b/drivers/net/usb/qmi_wwan.c
> @@ -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,
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index ed86ba87ca4e..b72ba0803392 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -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);
> diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
> index b0e84896e6ac..bbf799ccf3b3 100644
> --- a/include/linux/usb/usbnet.h
> +++ b/include/linux/usb/usbnet.h
> @@ -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 *);