aboutsummaryrefslogtreecommitdiff
path: root/net/ethtool
diff options
context:
space:
mode:
authorHeiner Kallweit2021-08-01 12:41:31 +0200
committerDavid S. Miller2021-08-03 12:58:22 +0100
commitd43c65b05b848e0b2db1a6c78b02c189da3a95b5 (patch)
tree55e8a56bf50929280aa1eb05ef8229c05970fc11 /net/ethtool
parent41107ac22fcf39c45afaf1a59e259e5e0059e31a (diff)
ethtool: runtime-resume netdev parent in ethnl_ops_begin
If a network device is runtime-suspended then: - network device may be flagged as detached and all ethtool ops (even if not accessing the device) will fail because netif_device_present() returns false - ethtool ops may fail because device is not accessible (e.g. because being in D3 in case of a PCI device) It may not be desirable that userspace can't use even simple ethtool ops that not access the device if interface or link is down. To be more friendly to userspace let's ensure that device is runtime-resumed when executing the respective ethtool op in kernel. Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ethtool')
-rw-r--r--net/ethtool/netlink.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index e628d17f595c..417aaf9ca219 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -2,6 +2,7 @@
#include <net/sock.h>
#include <linux/ethtool_netlink.h>
+#include <linux/pm_runtime.h>
#include "netlink.h"
static struct genl_family ethtool_genl_family;
@@ -31,22 +32,40 @@ const struct nla_policy ethnl_header_policy_stats[] = {
int ethnl_ops_begin(struct net_device *dev)
{
+ int ret;
+
if (!dev)
return 0;
- if (!netif_device_present(dev))
- return -ENODEV;
+ if (dev->dev.parent)
+ pm_runtime_get_sync(dev->dev.parent);
- if (dev->ethtool_ops->begin)
- return dev->ethtool_ops->begin(dev);
- else
- return 0;
+ if (!netif_device_present(dev)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (dev->ethtool_ops->begin) {
+ ret = dev->ethtool_ops->begin(dev);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ if (dev->dev.parent)
+ pm_runtime_put(dev->dev.parent);
+
+ return ret;
}
void ethnl_ops_complete(struct net_device *dev)
{
if (dev && dev->ethtool_ops->complete)
dev->ethtool_ops->complete(dev);
+
+ if (dev->dev.parent)
+ pm_runtime_put(dev->dev.parent);
}
/**