[PATCH] net: usb: cdc_ether: add quirk for AMI BMC stale link events
From: Jinhui Guo <hidden>
Date: 2026-07-30 05:15:55
Also in:
linux-usb, lkml
Subsystem:
networking drivers, the rest, usb cdc ethernet driver, usb networking drivers · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, Oliver Neukum
On AMD Genoa/Turin platforms the BMC-provided USB-Ethernet gadget (American Megatrends, VID 0x046b PID 0xffb0) intermittently fails to respond to ARP after AC cold boot. usbmon captures a stale NETWORK_CONNECTION(off) immediately followed by NETWORK_CONNECTION(on) on the interrupt endpoint (~130us apart) after enumeration. Because alloc_netdev() leaves __LINK_STATE_NOCARRIER cleared, netif_carrier_ok() returns true when the spurious OFF arrives, so usbnet_cdc_status() cannot recognise it as redundant and schedules EVENT_LINK_CHANGE. __handle_link_change() then calls unlink_urbs(), killing ~60 rx URBs whose payload has already been DMA'd into memory — xHCI trace confirms them completing as -ECONNRESET with non-zero residual length. rx_complete() drops these unconditionally. The following ON restores the carrier and re-submits URBs, but the ARP reply is already lost; the interface looks "up but silent" until ifdown/ifup. Fix this by adding a device-specific quirk with FLAG_LINK_INTR set, which makes usbnet_probe() call netif_carrier_off() after bind. With initial carrier == OFF, usbnet_cdc_status() recognises the spurious OFF as matching the current state and drops it; the subsequent ON is the first real event and brings the link up cleanly without ever tearing down the rx queue. The scheduled link-change kevent is harmless because EVENT_DEV_OPEN is not yet set at probe time. This is applied as a device-specific quirk rather than a change to the shared cdc_info driver_info because some CDC devices never send NETWORK_CONNECTION notifications; forcing carrier off for them would leave the link permanently DOWN. Restricting the change to this VID/PID keeps that class of device untouched. Tested on Genoa and Turin across 100+ AC cold boot cycles; ping first-packet success rate went from intermittent to 100%. Signed-off-by: Jinhui Guo <redacted> --- drivers/net/usb/cdc_ether.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index a0a5740590b9..b5e4b195b69a 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c@@ -538,6 +538,16 @@ static const struct driver_info cdc_info = { .manage_power = usbnet_manage_power, }; +static const struct driver_info ami_bmc_info = { + .description = "AMI BMC USB Ethernet", + .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_LINK_INTR, + .bind = usbnet_cdc_bind, + .unbind = usbnet_cdc_unbind, + .status = usbnet_cdc_status, + .set_rx_mode = usbnet_cdc_update_filter, + .manage_power = usbnet_manage_power, +}; + static const struct driver_info zte_cdc_info = { .description = "ZTE CDC Ethernet Device", .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
@@ -946,6 +956,12 @@ static const struct usb_device_id products[] = { USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&wwan_info, +}, { + /* AMI BMC gadget */ + USB_DEVICE_AND_INTERFACE_INFO(0x046b, 0xffb0, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, + USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&ami_bmc_info, }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
--
2.20.1