From: Jakub Kicinski <kuba@kernel.org>
To: davem@davemloft.net
Cc: netdev@vger.kernel.org, jiri@resnulli.us, leon@kernel.org,
mkubecek@suse.cz, andrew@lunn.ch, f.fainelli@gmail.com,
Jakub Kicinski <kuba@kernel.org>
Subject: [PATCH net-next 4/4] ethtool: don't drop the rtnl_lock half way thru the ioctl
Date: Fri, 29 Oct 2021 21:06:11 -0700 [thread overview]
Message-ID: <20211030040611.1751638-5-kuba@kernel.org> (raw)
In-Reply-To: <20211030040611.1751638-1-kuba@kernel.org>
devlink compat code needs to drop rtnl_lock to take
devlink->lock to ensure correct lock ordering.
This is problematic because we're not strictly guaranteed
that the netdev will not disappear after we re-lock.
It may open a possibility of nested ->begin / ->complete
calls.
Instead of calling into devlink under rtnl_lock take
a ref on the devlink instance and make the call after
we've dropped rtnl_lock.
We (continue to) assume that netdevs have an implicit
reference on the devlink returned from ndo_get_devlink_port
Note that ndo_get_devlink_port will now get called
under rtnl_lock. That should be fine since none of
the drivers seem to be taking serious locks inside
ndo_get_devlink_port.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/net/devlink.h | 4 ++--
net/core/devlink.c | 45 +++++++------------------------------------
net/ethtool/ioctl.c | 36 ++++++++++++++++++++++++++++++----
3 files changed, 41 insertions(+), 44 deletions(-)
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 991ce48f77ca..3d6ae9b546b4 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -1729,9 +1729,9 @@ devlink_trap_policers_unregister(struct devlink *devlink,
struct devlink *__must_check devlink_try_get(struct devlink *devlink);
void devlink_put(struct devlink *devlink);
-void devlink_compat_running_version(struct net_device *dev,
+void devlink_compat_running_version(struct devlink *devlink,
char *buf, size_t len);
-int devlink_compat_flash_update(struct net_device *dev, const char *file_name);
+int devlink_compat_flash_update(struct devlink *devlink, const char *file_name);
int devlink_compat_phys_port_name_get(struct net_device *dev,
char *name, size_t len);
int devlink_compat_switch_id_get(struct net_device *dev,
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 100d87fd3f65..6b5ee862429e 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -11283,55 +11283,28 @@ static struct devlink_port *netdev_to_devlink_port(struct net_device *dev)
return dev->netdev_ops->ndo_get_devlink_port(dev);
}
-static struct devlink *netdev_to_devlink(struct net_device *dev)
-{
- struct devlink_port *devlink_port = netdev_to_devlink_port(dev);
-
- if (!devlink_port)
- return NULL;
-
- return devlink_port->devlink;
-}
-
-void devlink_compat_running_version(struct net_device *dev,
+void devlink_compat_running_version(struct devlink *devlink,
char *buf, size_t len)
{
- struct devlink *devlink;
-
- dev_hold(dev);
- rtnl_unlock();
-
- devlink = netdev_to_devlink(dev);
- if (!devlink || !devlink->ops->info_get)
- goto out;
+ if (!devlink->ops->info_get)
+ return;
mutex_lock(&devlink->lock);
__devlink_compat_running_version(devlink, buf, len);
mutex_unlock(&devlink->lock);
-
-out:
- rtnl_lock();
- dev_put(dev);
}
-int devlink_compat_flash_update(struct net_device *dev, const char *file_name)
+int devlink_compat_flash_update(struct devlink *devlink, const char *file_name)
{
struct devlink_flash_update_params params = {};
- struct devlink *devlink;
int ret;
- dev_hold(dev);
- rtnl_unlock();
-
- devlink = netdev_to_devlink(dev);
- if (!devlink || !devlink->ops->flash_update) {
- ret = -EOPNOTSUPP;
- goto out;
- }
+ if (!devlink->ops->flash_update)
+ return -EOPNOTSUPP;
ret = request_firmware(¶ms.fw, file_name, devlink->dev);
if (ret)
- goto out;
+ return ret;
mutex_lock(&devlink->lock);
devlink_flash_update_begin_notify(devlink);
@@ -11341,10 +11314,6 @@ int devlink_compat_flash_update(struct net_device *dev, const char *file_name)
release_firmware(params.fw);
-out:
- rtnl_lock();
- dev_put(dev);
-
return ret;
}
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 1980e37b6472..65e9bc1058b5 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -34,12 +34,27 @@
/* State held across locks and calls for commands which have devlink fallback */
struct ethtool_devlink_compat {
+ struct devlink *devlink;
union {
struct ethtool_flash efl;
struct ethtool_drvinfo info;
};
};
+static struct devlink *netdev_to_devlink_get(struct net_device *dev)
+{
+ struct devlink_port *devlink_port;
+
+ if (!dev->netdev_ops->ndo_get_devlink_port)
+ return NULL;
+
+ devlink_port = dev->netdev_ops->ndo_get_devlink_port(dev);
+ if (!devlink_port)
+ return NULL;
+
+ return devlink_try_get(devlink_port->devlink);
+}
+
/*
* Some useful ethtool_ops methods that're device independent.
* If we find that all drivers want to do the same thing here,
@@ -751,8 +766,8 @@ ethtool_get_drvinfo(struct net_device *dev, struct ethtool_devlink_compat *rsp)
rsp->info.eedump_len = ops->get_eeprom_len(dev);
if (!rsp->info.fw_version[0])
- devlink_compat_running_version(dev, rsp->info.fw_version,
- sizeof(rsp->info.fw_version));
+ rsp->devlink = netdev_to_devlink_get(dev);
+
return 0;
}
@@ -2184,8 +2199,10 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
static int
ethtool_flash_device(struct net_device *dev, struct ethtool_devlink_compat *req)
{
- if (!dev->ethtool_ops->flash_device)
- return devlink_compat_flash_update(dev, req->efl.data);
+ if (!dev->ethtool_ops->flash_device) {
+ req->devlink = netdev_to_devlink_get(dev);
+ return 0;
+ }
return dev->ethtool_ops->flash_device(dev, &req->efl);
}
@@ -3027,7 +3044,16 @@ int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
goto exit_free;
switch (ethcmd) {
+ case ETHTOOL_FLASHDEV:
+ if (state->devlink)
+ rc = devlink_compat_flash_update(state->devlink,
+ state->efl.data);
+ break;
case ETHTOOL_GDRVINFO:
+ if (state->devlink)
+ devlink_compat_running_version(state->devlink,
+ state->info.fw_version,
+ sizeof(state->info.fw_version));
if (copy_to_user(useraddr, &state->info, sizeof(state->info))) {
rc = -EFAULT;
goto exit_free;
@@ -3036,6 +3062,8 @@ int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
}
exit_free:
+ if (state->devlink)
+ devlink_put(state->devlink);
kfree(state);
return rc;
}
--
2.31.1
next prev parent reply other threads:[~2021-10-30 4:06 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-30 4:06 [PATCH net-next 0/4] improve ethtool/rtnl vs devlink locking Jakub Kicinski
2021-10-30 4:06 ` [PATCH net-next 1/4] ethtool: push the rtnl_lock into dev_ethtool() Jakub Kicinski
2021-10-30 4:06 ` [PATCH net-next 2/4] ethtool: handle info/flash data copying outside rtnl_lock Jakub Kicinski
2021-10-30 4:06 ` [PATCH net-next 3/4] devlink: expose get/put functions Jakub Kicinski
2021-10-30 4:06 ` Jakub Kicinski [this message]
2021-10-30 8:50 ` [PATCH net-next 4/4] ethtool: don't drop the rtnl_lock half way thru the ioctl kernel test robot
2021-10-30 8:50 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211030040611.1751638-5-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=f.fainelli@gmail.com \
--cc=jiri@resnulli.us \
--cc=leon@kernel.org \
--cc=mkubecek@suse.cz \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.