]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
qmi_wwan: allow max_mtu above hard_mtu to control rx_urb_size
authorLaurent Vivier <lvivier@redhat.com>
Wed, 4 Mar 2026 13:43:38 +0000 (14:43 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sat, 7 Mar 2026 00:31:41 +0000 (16:31 -0800)
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")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/lkml/CAPh3n803k8JcBPV5qEzUB-oKzWkAs-D5CU7z=Vd_nLRCr5ZqQg@mail.gmail.com/
Reported-by: Koen Vandeputte <koen.vandeputte@citymesh.com>
Tested-by: Daniele Palmas <dnlplm@gmail.com>
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
Link: https://patch.msgid.link/20260304134338.1785002-1-lvivier@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/usb/qmi_wwan.c
drivers/net/usb/usbnet.c
include/linux/usb/usbnet.h

index 3a4985b582cb1255a95f4467b88b16ce3513e260..05acac10cd2bad2656e7c02c2c40fc9b14a5c907 100644 (file)
@@ -928,7 +928,7 @@ err:
 
 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,
index ed86ba87ca4e5c9c9cacf80042897abaed7437b8..b72ba0803392bb3cd0a8321c5d06dee29cc09690 100644 (file)
@@ -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);
index b0e84896e6aca3ff5a0284fbb2f5c3ebd41364e2..bbf799ccf3b30e77dd028e387ca605a03d2f0afc 100644 (file)
@@ -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 *);