summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJinhui Guo <guojinhui.liam@bytedance.com>2026-07-30 13:13:41 +0800
committerJakub Kicinski <kuba@kernel.org>2026-08-05 18:08:50 -0700
commit089ca284afb0f3b2870a5ce41524cbc91ed350d4 (patch)
tree008be8e05a9a92cfdc91d721ff88bcb40dc9a49e
parent90359b528888e1275b4fe014b31ee23bc7eaf7be (diff)
net: usb: cdc_ether: add quirk for AMI BMC stale link events
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 <guojinhui.liam@bytedance.com> Link: https://patch.msgid.link/20260730051341.24930-1-guojinhui.liam@bytedance.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/usb/cdc_ether.c16
1 files changed, 16 insertions, 0 deletions
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,
@@ -947,6 +957,12 @@ static const struct usb_device_id products[] = {
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),
.driver_info = (unsigned long) &cdc_info,