linux/drivers/net/usb
You-Sheng Yang d64c7a0803 r8152: check disconnect status after long sleep
Dell USB Type C docking WD19/WD19DC attaches additional peripherals as:

  /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 5000M
      |__ Port 1: Dev 11, If 0, Class=Hub, Driver=hub/4p, 5000M
          |__ Port 3: Dev 12, If 0, Class=Hub, Driver=hub/4p, 5000M
          |__ Port 4: Dev 13, If 0, Class=Vendor Specific Class,
              Driver=r8152, 5000M

where usb 2-1-3 is a hub connecting all USB Type-A/C ports on the dock.

When hotplugging such dock with additional usb devices already attached on
it, the probing process may reset usb 2.1 port, therefore r8152 ethernet
device is also reset. However, during r8152 device init there are several
for-loops that, when it's unable to retrieve hardware registers due to
being disconnected from USB, may take up to 14 seconds each in practice,
and that has to be completed before USB may re-enumerate devices on the
bus. As a result, devices attached to the dock will only be available
after nearly 1 minute after the dock was plugged in:

  [ 216.388290] [250] r8152 2-1.4:1.0: usb_probe_interface
  [ 216.388292] [250] r8152 2-1.4:1.0: usb_probe_interface - got id
  [ 258.830410] r8152 2-1.4:1.0 (unnamed net_device) (uninitialized): PHY not ready
  [ 258.830460] r8152 2-1.4:1.0 (unnamed net_device) (uninitialized): Invalid header when reading pass-thru MAC addr
  [ 258.830464] r8152 2-1.4:1.0 (unnamed net_device) (uninitialized): Get ether addr fail

This happens in, for example, r8153_init:

  static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size,
			    void *data, u16 type)
  {
    if (test_bit(RTL8152_UNPLUG, &tp->flags))
      return -ENODEV;
    ...
  }

  static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index)
  {
    u32 data;
    ...
    generic_ocp_read(tp, index, sizeof(tmp), &tmp, type | byen);

    data = __le32_to_cpu(tmp);
    ...
    return (u16)data;
  }

  static void r8153_init(struct r8152 *tp)
  {
    ...
    if (test_bit(RTL8152_UNPLUG, &tp->flags))
      return;

    for (i = 0; i < 500; i++) {
      if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
          AUTOLOAD_DONE)
        break;
      msleep(20);
    }
    ...
  }

Since ocp_read_word() doesn't check the return status of
generic_ocp_read(), and the only exit condition for the loop is to have
a match in the returned value, such loops will only ends after exceeding
its maximum runs when the device has been marked as disconnected, which
takes 500 * 20ms = 10 seconds in theory, 14 in practice.

To solve this long latency another test to RTL8152_UNPLUG flag should be
added after those 20ms sleep to skip unnecessary loops, so that the device
probe can complete early and proceed to parent port reset/reprobe process.

This can be reproduced on all kernel versions up to latest v5.6-rc2, but
after v5.5-rc7 the reproduce rate is dramatically lowered to 1/30 or less
while it was around 1/2.

Signed-off-by: You-Sheng Yang <vicamo.yang@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 21:19:41 -08:00
..
aqc111.c
aqc111.h net: usb: aqc111: Use the correct style for SPDX License Identifier 2019-11-27 11:27:01 -08:00
asix.h
asix_common.c net: usb: Merge cpu_to_le32s + memcpy to put_unaligned_le32 2019-07-22 20:44:14 -07:00
asix_devices.c
ax88172a.c net: convert suitable drivers to use phy_do_ioctl_running 2020-01-23 10:49:30 +01:00
ax88179_178a.c ax88179_178a: add ethtool_op_get_ts_info() 2019-11-24 18:40:54 -08:00
catc.c netdev: pass the stuck queue to the timeout handler 2019-12-12 21:38:57 -08:00
cdc-phonet.c
cdc_eem.c
cdc_ether.c r8152: Add macpassthru support for ThinkPad Thunderbolt 3 Dock Gen 2 2019-11-05 18:30:30 -08:00
cdc_mbim.c
cdc_ncm.c net: cdc_ncm: Signedness bug in cdc_ncm_set_dgram_size() 2019-11-13 14:30:16 -08:00
cdc_subset.c
ch9200.c net: ch9200: remove unnecessary return 2020-01-07 13:30:36 -08:00
cx82310_eth.c cx82310_eth: fix a memory leak bug 2019-08-18 13:01:54 -07:00
dm9601.c
gl620a.c
hso.c netdev: pass the stuck queue to the timeout handler 2019-12-12 21:38:57 -08:00
huawei_cdc_ncm.c
int51x1.c
ipheth.c netdev: pass the stuck queue to the timeout handler 2019-12-12 21:38:57 -08:00
kalmia.c net: kalmia: fix memory leaks 2019-08-18 13:03:21 -07:00
kaweth.c netdev: pass the stuck queue to the timeout handler 2019-12-12 21:38:57 -08:00
Kconfig
lan78xx.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net 2020-01-26 10:40:21 +01:00
lan78xx.h
lg-vl600.c net: usb: Delete unnecessary checks before the macro call “dev_kfree_skb” 2019-08-22 16:22:03 -07:00
Makefile
mcs7830.c
net1080.c
pegasus.c netdev: pass the stuck queue to the timeout handler 2019-12-12 21:38:57 -08:00
pegasus.h
plusb.c
qmi_wwan.c net: usb: qmi_wwan: restore mtu min/max values after raw_ip switch 2020-02-23 16:13:50 -08:00
r8152.c r8152: check disconnect status after long sleep 2020-02-29 21:19:41 -08:00
rndis_host.c
rtl8150.c netdev: pass the stuck queue to the timeout handler 2019-12-12 21:38:57 -08:00
sierra_net.c treewide: Use sizeof_field() macro 2019-12-09 10:36:44 -08:00
smsc75xx.c net: usb: Merge cpu_to_le32s + memcpy to put_unaligned_le32 2019-07-22 20:44:14 -07:00
smsc75xx.h
smsc95xx.c
smsc95xx.h
sr9700.c
sr9700.h
sr9800.c net: usb: sr9800: fix uninitialized local variable 2019-10-15 21:02:12 -07:00
sr9800.h
usbnet.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net 2019-12-22 15:15:05 -08:00
zaurus.c