Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: don't drop the rtnl_lock half way thru the ioctl

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>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jakub Kicinski and committed by
David S. Miller
1af0a094 46db1b77

+43 -46
+4 -4
include/net/devlink.h
··· 1729 1729 struct devlink *__must_check devlink_try_get(struct devlink *devlink); 1730 1730 void devlink_put(struct devlink *devlink); 1731 1731 1732 - void devlink_compat_running_version(struct net_device *dev, 1732 + void devlink_compat_running_version(struct devlink *devlink, 1733 1733 char *buf, size_t len); 1734 - int devlink_compat_flash_update(struct net_device *dev, const char *file_name); 1734 + int devlink_compat_flash_update(struct devlink *devlink, const char *file_name); 1735 1735 int devlink_compat_phys_port_name_get(struct net_device *dev, 1736 1736 char *name, size_t len); 1737 1737 int devlink_compat_switch_id_get(struct net_device *dev, ··· 1749 1749 } 1750 1750 1751 1751 static inline void 1752 - devlink_compat_running_version(struct net_device *dev, char *buf, size_t len) 1752 + devlink_compat_running_version(struct devlink *devlink, char *buf, size_t len) 1753 1753 { 1754 1754 } 1755 1755 1756 1756 static inline int 1757 - devlink_compat_flash_update(struct net_device *dev, const char *file_name) 1757 + devlink_compat_flash_update(struct devlink *devlink, const char *file_name) 1758 1758 { 1759 1759 return -EOPNOTSUPP; 1760 1760 }
+7 -38
net/core/devlink.c
··· 11283 11283 return dev->netdev_ops->ndo_get_devlink_port(dev); 11284 11284 } 11285 11285 11286 - static struct devlink *netdev_to_devlink(struct net_device *dev) 11287 - { 11288 - struct devlink_port *devlink_port = netdev_to_devlink_port(dev); 11289 - 11290 - if (!devlink_port) 11291 - return NULL; 11292 - 11293 - return devlink_port->devlink; 11294 - } 11295 - 11296 - void devlink_compat_running_version(struct net_device *dev, 11286 + void devlink_compat_running_version(struct devlink *devlink, 11297 11287 char *buf, size_t len) 11298 11288 { 11299 - struct devlink *devlink; 11300 - 11301 - dev_hold(dev); 11302 - rtnl_unlock(); 11303 - 11304 - devlink = netdev_to_devlink(dev); 11305 - if (!devlink || !devlink->ops->info_get) 11306 - goto out; 11289 + if (!devlink->ops->info_get) 11290 + return; 11307 11291 11308 11292 mutex_lock(&devlink->lock); 11309 11293 __devlink_compat_running_version(devlink, buf, len); 11310 11294 mutex_unlock(&devlink->lock); 11311 - 11312 - out: 11313 - rtnl_lock(); 11314 - dev_put(dev); 11315 11295 } 11316 11296 11317 - int devlink_compat_flash_update(struct net_device *dev, const char *file_name) 11297 + int devlink_compat_flash_update(struct devlink *devlink, const char *file_name) 11318 11298 { 11319 11299 struct devlink_flash_update_params params = {}; 11320 - struct devlink *devlink; 11321 11300 int ret; 11322 11301 11323 - dev_hold(dev); 11324 - rtnl_unlock(); 11325 - 11326 - devlink = netdev_to_devlink(dev); 11327 - if (!devlink || !devlink->ops->flash_update) { 11328 - ret = -EOPNOTSUPP; 11329 - goto out; 11330 - } 11302 + if (!devlink->ops->flash_update) 11303 + return -EOPNOTSUPP; 11331 11304 11332 11305 ret = request_firmware(&params.fw, file_name, devlink->dev); 11333 11306 if (ret) 11334 - goto out; 11307 + return ret; 11335 11308 11336 11309 mutex_lock(&devlink->lock); 11337 11310 devlink_flash_update_begin_notify(devlink); ··· 11313 11340 mutex_unlock(&devlink->lock); 11314 11341 11315 11342 release_firmware(params.fw); 11316 - 11317 - out: 11318 - rtnl_lock(); 11319 - dev_put(dev); 11320 11343 11321 11344 return ret; 11322 11345 }
+32 -4
net/ethtool/ioctl.c
··· 34 34 35 35 /* State held across locks and calls for commands which have devlink fallback */ 36 36 struct ethtool_devlink_compat { 37 + struct devlink *devlink; 37 38 union { 38 39 struct ethtool_flash efl; 39 40 struct ethtool_drvinfo info; 40 41 }; 41 42 }; 43 + 44 + static struct devlink *netdev_to_devlink_get(struct net_device *dev) 45 + { 46 + struct devlink_port *devlink_port; 47 + 48 + if (!dev->netdev_ops->ndo_get_devlink_port) 49 + return NULL; 50 + 51 + devlink_port = dev->netdev_ops->ndo_get_devlink_port(dev); 52 + if (!devlink_port) 53 + return NULL; 54 + 55 + return devlink_try_get(devlink_port->devlink); 56 + } 42 57 43 58 /* 44 59 * Some useful ethtool_ops methods that're device independent. ··· 766 751 rsp->info.eedump_len = ops->get_eeprom_len(dev); 767 752 768 753 if (!rsp->info.fw_version[0]) 769 - devlink_compat_running_version(dev, rsp->info.fw_version, 770 - sizeof(rsp->info.fw_version)); 754 + rsp->devlink = netdev_to_devlink_get(dev); 755 + 771 756 return 0; 772 757 } 773 758 ··· 2199 2184 static int 2200 2185 ethtool_flash_device(struct net_device *dev, struct ethtool_devlink_compat *req) 2201 2186 { 2202 - if (!dev->ethtool_ops->flash_device) 2203 - return devlink_compat_flash_update(dev, req->efl.data); 2187 + if (!dev->ethtool_ops->flash_device) { 2188 + req->devlink = netdev_to_devlink_get(dev); 2189 + return 0; 2190 + } 2204 2191 2205 2192 return dev->ethtool_ops->flash_device(dev, &req->efl); 2206 2193 } ··· 3044 3027 goto exit_free; 3045 3028 3046 3029 switch (ethcmd) { 3030 + case ETHTOOL_FLASHDEV: 3031 + if (state->devlink) 3032 + rc = devlink_compat_flash_update(state->devlink, 3033 + state->efl.data); 3034 + break; 3047 3035 case ETHTOOL_GDRVINFO: 3036 + if (state->devlink) 3037 + devlink_compat_running_version(state->devlink, 3038 + state->info.fw_version, 3039 + sizeof(state->info.fw_version)); 3048 3040 if (copy_to_user(useraddr, &state->info, sizeof(state->info))) { 3049 3041 rc = -EFAULT; 3050 3042 goto exit_free; ··· 3062 3036 } 3063 3037 3064 3038 exit_free: 3039 + if (state->devlink) 3040 + devlink_put(state->devlink); 3065 3041 kfree(state); 3066 3042 return rc; 3067 3043 }