raspberrypi / linux

Kernel source tree for Raspberry Pi-provided kernel builds. Issues unrelated to the linux kernel should be posted on the community forum at https://forums.raspberrypi.com/
Other
11.13k stars 4.98k forks source link

smsc95xx driver slow when ethernet cable unplugged #5199

Open jrichardson-neurio opened 2 years ago

jrichardson-neurio commented 2 years ago

Describe the bug

After upgrading to kirkstone and the 5.15 kernel I've noticed the smsc95xx driver is slow when the ethernet cable is unplugged. Running 'ifconfig' or 'ip' can take up to 20 seconds to complete but usually more like 5. Performance is good when the cable is plugged.

Steps to reproduce the behaviour

Unplug ethernet cable ifconfig or ip a

Device (s)

Raspberry Pi 3 Mod. B

System

OS: kirkstone branch of yocto poky, meta-openembedded, and meta-raspberrypi.

Linux lcm_0AFD 5.15.34-v7 #1 SMP Tue Oct 4 00:13:54 UTC 2022 armv7l GNU/Linux

Logs

No response

Additional context

It looks like this occurred around the 2.0.0 driver version on this commit:

commit 05b35e7eb9a11bbe8102836965e634c04e712c88 Author: Andre Edich andre.edich@microchip.com Date: Wed Aug 26 13:17:17 2020 +0200

smsc95xx: add phylib support

The ethtool_ops get_link() function changed from usbnet_get_link() to smsc95xx_get_link().

On the current driver version 2.0.0 (commit fe83b18ef954a4b7eb920a40172a58f0c5f5aa8a) changing it back to usbnet_get_link() results in WARN's and is still slow. If I use ethtool_op_get_link() it is fast but it takes a long time to get an IP address when the cable is plugged in as it tries to resolve link status.

The ioctl sent from ifconfig is SIOCGIFCONF. The call stack is, roughly:

net/core/dev_ioctl.c: int dev_ifconf(struct net net, struct ifconf __user uifc) {

/* Loop over the interfaces, and write an info block for each. */ **rtnl_lock();** for_each_netdev(net, dev) { When rtnl_lock() is called I can see multiple calls into get_link in smsc95xx.c where it takes many seconds to determine link status (unplugged) before reporting. Expected behaviour is performance similar to the 5.4 kernel when the driver version was "1.0.6".
pelwell commented 2 years ago

ipconfig is instantaneous for me using a Pi 3B running RPiOS bullseye with a 5.15.61 kernel, whether or not the Ethernet cable is plugged in.

jrichardson-neurio commented 2 years ago

I found the problem. SIOCETHTOOL ioctl was being called every second by ifplugd also. This causes get_link() to be called. smsc95xx_get_link() then calls phy_read_status(). It looks like that call is redundant because the phy state machine is already calling it. There is a huge performance hit in that scenario. It actually affects ifup from working on unrelated interfaces such as wifi. It will time out and fail to bring up the interface.

The best solution I've found is to get rid of get_link in smsc95xx or use ethtool_op_get_link. ethtool won't call it if it's unimplemented. I'll test ifplugd further.

If I kill ifplugd ifconfig is fast when cable is unplugged. I don't think that phy_read_status call from get_link is the right thing to do. Not exactly sure yet why the performace is so bad.

Try running '/usr/sbin/ifplugd -i eth0 -fI -u0 -d10' (busybox ifplugd) and see if you can repro.

jrichardson-neurio commented 2 years ago

I removed get_link from smsc95xx.c and it's fine. SIOCETHTOOL will be unimplemented for this driver but ifplugd has other methods of determining link state. When ifplugd calls this ioctl it takes 4 seconds to complete due to lan87xx_read_status() in the smsc phy sleeping. It looks like multiple paths into that function is the issue. The comment says it all:

Due to this abnormality I think get_state() should be removed from the smsc95xx driver. Maybe you could use the ethtool version to keep that ioctl implemented for some apps that may use it but it still introduces unnecessary overhead. That would be up to the maintainers of this driver.