Skip to content

Commit 1adc58e

Browse files
committed
Merge branch 'devlink-locking'
Jakub Kicinski says: ==================== improve ethtool/rtnl vs devlink locking During ethtool netlink development we decided to move some of the commmands to devlink. Since we don't want drivers to implement both devlink and ethtool version of the commands ethtool ioctl falls back to calling devlink. Unfortunately devlink locks must be taken before rtnl_lock. This results in a questionable dev_hold() / rtnl_unlock() / devlink / rtnl_lock() / dev_put() pattern. This method "works" but it working depends on drivers in question not doing much in ethtool_ops->begin / complete, and on the netdev not having needs_free_netdev set. Since commit 437ebfd ("devlink: Count struct devlink consumers") we can hold a reference on a devlink instance and prevent it from going away (sort of like netdev with dev_hold()). We can use this to create a more natural reference nesting where we get a ref on the devlink instance and make the devlink call entirely outside of the rtnl_lock section. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents c6e03db + 1af0a09 commit 1adc58e

File tree

4 files changed

+136
-87
lines changed

4 files changed

+136
-87
lines changed

include/net/devlink.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,23 +1726,35 @@ devlink_trap_policers_unregister(struct devlink *devlink,
17261726

17271727
#if IS_ENABLED(CONFIG_NET_DEVLINK)
17281728

1729-
void devlink_compat_running_version(struct net_device *dev,
1729+
struct devlink *__must_check devlink_try_get(struct devlink *devlink);
1730+
void devlink_put(struct devlink *devlink);
1731+
1732+
void devlink_compat_running_version(struct devlink *devlink,
17301733
char *buf, size_t len);
1731-
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);
17321735
int devlink_compat_phys_port_name_get(struct net_device *dev,
17331736
char *name, size_t len);
17341737
int devlink_compat_switch_id_get(struct net_device *dev,
17351738
struct netdev_phys_item_id *ppid);
17361739

17371740
#else
17381741

1742+
static inline struct devlink *devlink_try_get(struct devlink *devlink)
1743+
{
1744+
return NULL;
1745+
}
1746+
1747+
static inline void devlink_put(struct devlink *devlink)
1748+
{
1749+
}
1750+
17391751
static inline void
1740-
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)
17411753
{
17421754
}
17431755

17441756
static inline int
1745-
devlink_compat_flash_update(struct net_device *dev, const char *file_name)
1757+
devlink_compat_flash_update(struct devlink *devlink, const char *file_name)
17461758
{
17471759
return -EOPNOTSUPP;
17481760
}

net/core/dev_ioctl.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,9 +518,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr,
518518

519519
case SIOCETHTOOL:
520520
dev_load(net, ifr->ifr_name);
521-
rtnl_lock();
522521
ret = dev_ethtool(net, ifr, data);
523-
rtnl_unlock();
524522
if (colon)
525523
*colon = ':';
526524
return ret;

net/core/devlink.c

Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,17 @@ struct net *devlink_net(const struct devlink *devlink)
182182
}
183183
EXPORT_SYMBOL_GPL(devlink_net);
184184

185-
static void devlink_put(struct devlink *devlink)
185+
void devlink_put(struct devlink *devlink)
186186
{
187187
if (refcount_dec_and_test(&devlink->refcount))
188188
complete(&devlink->comp);
189189
}
190190

191-
static bool __must_check devlink_try_get(struct devlink *devlink)
191+
struct devlink *__must_check devlink_try_get(struct devlink *devlink)
192192
{
193-
return refcount_inc_not_zero(&devlink->refcount);
193+
if (refcount_inc_not_zero(&devlink->refcount))
194+
return devlink;
195+
return NULL;
194196
}
195197

196198
static struct devlink *devlink_get_from_attrs(struct net *net,
@@ -11281,55 +11283,28 @@ static struct devlink_port *netdev_to_devlink_port(struct net_device *dev)
1128111283
return dev->netdev_ops->ndo_get_devlink_port(dev);
1128211284
}
1128311285

11284-
static struct devlink *netdev_to_devlink(struct net_device *dev)
11285-
{
11286-
struct devlink_port *devlink_port = netdev_to_devlink_port(dev);
11287-
11288-
if (!devlink_port)
11289-
return NULL;
11290-
11291-
return devlink_port->devlink;
11292-
}
11293-
11294-
void devlink_compat_running_version(struct net_device *dev,
11286+
void devlink_compat_running_version(struct devlink *devlink,
1129511287
char *buf, size_t len)
1129611288
{
11297-
struct devlink *devlink;
11298-
11299-
dev_hold(dev);
11300-
rtnl_unlock();
11301-
11302-
devlink = netdev_to_devlink(dev);
11303-
if (!devlink || !devlink->ops->info_get)
11304-
goto out;
11289+
if (!devlink->ops->info_get)
11290+
return;
1130511291

1130611292
mutex_lock(&devlink->lock);
1130711293
__devlink_compat_running_version(devlink, buf, len);
1130811294
mutex_unlock(&devlink->lock);
11309-
11310-
out:
11311-
rtnl_lock();
11312-
dev_put(dev);
1131311295
}
1131411296

11315-
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)
1131611298
{
1131711299
struct devlink_flash_update_params params = {};
11318-
struct devlink *devlink;
1131911300
int ret;
1132011301

11321-
dev_hold(dev);
11322-
rtnl_unlock();
11323-
11324-
devlink = netdev_to_devlink(dev);
11325-
if (!devlink || !devlink->ops->flash_update) {
11326-
ret = -EOPNOTSUPP;
11327-
goto out;
11328-
}
11302+
if (!devlink->ops->flash_update)
11303+
return -EOPNOTSUPP;
1132911304

1133011305
ret = request_firmware(&params.fw, file_name, devlink->dev);
1133111306
if (ret)
11332-
goto out;
11307+
return ret;
1133311308

1133411309
mutex_lock(&devlink->lock);
1133511310
devlink_flash_update_begin_notify(devlink);
@@ -11339,10 +11314,6 @@ int devlink_compat_flash_update(struct net_device *dev, const char *file_name)
1133911314

1134011315
release_firmware(params.fw);
1134111316

11342-
out:
11343-
rtnl_lock();
11344-
dev_put(dev);
11345-
1134611317
return ret;
1134711318
}
1134811319

net/ethtool/ioctl.c

Lines changed: 108 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,29 @@
3232
#include <generated/utsrelease.h>
3333
#include "common.h"
3434

35+
/* State held across locks and calls for commands which have devlink fallback */
36+
struct ethtool_devlink_compat {
37+
struct devlink *devlink;
38+
union {
39+
struct ethtool_flash efl;
40+
struct ethtool_drvinfo info;
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+
}
57+
3558
/*
3659
* Some useful ethtool_ops methods that're device independent.
3760
* If we find that all drivers want to do the same thing here,
@@ -697,22 +720,20 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
697720
return ret;
698721
}
699722

700-
static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
701-
void __user *useraddr)
723+
static int
724+
ethtool_get_drvinfo(struct net_device *dev, struct ethtool_devlink_compat *rsp)
702725
{
703-
struct ethtool_drvinfo info;
704726
const struct ethtool_ops *ops = dev->ethtool_ops;
705727

706-
memset(&info, 0, sizeof(info));
707-
info.cmd = ETHTOOL_GDRVINFO;
708-
strlcpy(info.version, UTS_RELEASE, sizeof(info.version));
728+
rsp->info.cmd = ETHTOOL_GDRVINFO;
729+
strlcpy(rsp->info.version, UTS_RELEASE, sizeof(rsp->info.version));
709730
if (ops->get_drvinfo) {
710-
ops->get_drvinfo(dev, &info);
731+
ops->get_drvinfo(dev, &rsp->info);
711732
} else if (dev->dev.parent && dev->dev.parent->driver) {
712-
strlcpy(info.bus_info, dev_name(dev->dev.parent),
713-
sizeof(info.bus_info));
714-
strlcpy(info.driver, dev->dev.parent->driver->name,
715-
sizeof(info.driver));
733+
strlcpy(rsp->info.bus_info, dev_name(dev->dev.parent),
734+
sizeof(rsp->info.bus_info));
735+
strlcpy(rsp->info.driver, dev->dev.parent->driver->name,
736+
sizeof(rsp->info.driver));
716737
} else {
717738
return -EOPNOTSUPP;
718739
}
@@ -726,30 +747,27 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
726747

727748
rc = ops->get_sset_count(dev, ETH_SS_TEST);
728749
if (rc >= 0)
729-
info.testinfo_len = rc;
750+
rsp->info.testinfo_len = rc;
730751
rc = ops->get_sset_count(dev, ETH_SS_STATS);
731752
if (rc >= 0)
732-
info.n_stats = rc;
753+
rsp->info.n_stats = rc;
733754
rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
734755
if (rc >= 0)
735-
info.n_priv_flags = rc;
756+
rsp->info.n_priv_flags = rc;
736757
}
737758
if (ops->get_regs_len) {
738759
int ret = ops->get_regs_len(dev);
739760

740761
if (ret > 0)
741-
info.regdump_len = ret;
762+
rsp->info.regdump_len = ret;
742763
}
743764

744765
if (ops->get_eeprom_len)
745-
info.eedump_len = ops->get_eeprom_len(dev);
766+
rsp->info.eedump_len = ops->get_eeprom_len(dev);
746767

747-
if (!info.fw_version[0])
748-
devlink_compat_running_version(dev, info.fw_version,
749-
sizeof(info.fw_version));
768+
if (!rsp->info.fw_version[0])
769+
rsp->devlink = netdev_to_devlink_get(dev);
750770

751-
if (copy_to_user(useraddr, &info, sizeof(info)))
752-
return -EFAULT;
753771
return 0;
754772
}
755773

@@ -2178,19 +2196,15 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
21782196
return actor(dev, edata.data);
21792197
}
21802198

2181-
static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
2182-
char __user *useraddr)
2199+
static int
2200+
ethtool_flash_device(struct net_device *dev, struct ethtool_devlink_compat *req)
21832201
{
2184-
struct ethtool_flash efl;
2185-
2186-
if (copy_from_user(&efl, useraddr, sizeof(efl)))
2187-
return -EFAULT;
2188-
efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
2189-
2190-
if (!dev->ethtool_ops->flash_device)
2191-
return devlink_compat_flash_update(dev, efl.data);
2202+
if (!dev->ethtool_ops->flash_device) {
2203+
req->devlink = netdev_to_devlink_get(dev);
2204+
return 0;
2205+
}
21922206

2193-
return dev->ethtool_ops->flash_device(dev, &efl);
2207+
return dev->ethtool_ops->flash_device(dev, &req->efl);
21942208
}
21952209

21962210
static int ethtool_set_dump(struct net_device *dev,
@@ -2700,19 +2714,19 @@ static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr)
27002714

27012715
/* The main entry point in this file. Called from net/core/dev_ioctl.c */
27022716

2703-
int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
2717+
static int
2718+
__dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr,
2719+
u32 ethcmd, struct ethtool_devlink_compat *devlink_state)
27042720
{
2705-
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
2706-
u32 ethcmd, sub_cmd;
2721+
struct net_device *dev;
2722+
u32 sub_cmd;
27072723
int rc;
27082724
netdev_features_t old_features;
27092725

2726+
dev = __dev_get_by_name(net, ifr->ifr_name);
27102727
if (!dev)
27112728
return -ENODEV;
27122729

2713-
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
2714-
return -EFAULT;
2715-
27162730
if (ethcmd == ETHTOOL_PERQUEUE) {
27172731
if (copy_from_user(&sub_cmd, useraddr + sizeof(ethcmd), sizeof(sub_cmd)))
27182732
return -EFAULT;
@@ -2786,7 +2800,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
27862800
rc = ethtool_set_settings(dev, useraddr);
27872801
break;
27882802
case ETHTOOL_GDRVINFO:
2789-
rc = ethtool_get_drvinfo(dev, useraddr);
2803+
rc = ethtool_get_drvinfo(dev, devlink_state);
27902804
break;
27912805
case ETHTOOL_GREGS:
27922806
rc = ethtool_get_regs(dev, useraddr);
@@ -2888,7 +2902,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
28882902
rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
28892903
break;
28902904
case ETHTOOL_FLASHDEV:
2891-
rc = ethtool_flash_device(dev, useraddr);
2905+
rc = ethtool_flash_device(dev, devlink_state);
28922906
break;
28932907
case ETHTOOL_RESET:
28942908
rc = ethtool_reset(dev, useraddr);
@@ -3000,6 +3014,60 @@ int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
30003014
return rc;
30013015
}
30023016

3017+
int dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr)
3018+
{
3019+
struct ethtool_devlink_compat *state;
3020+
u32 ethcmd;
3021+
int rc;
3022+
3023+
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
3024+
return -EFAULT;
3025+
3026+
state = kzalloc(sizeof(*state), GFP_KERNEL);
3027+
if (!state)
3028+
return -ENOMEM;
3029+
3030+
switch (ethcmd) {
3031+
case ETHTOOL_FLASHDEV:
3032+
if (copy_from_user(&state->efl, useraddr, sizeof(state->efl))) {
3033+
rc = -EFAULT;
3034+
goto exit_free;
3035+
}
3036+
state->efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
3037+
break;
3038+
}
3039+
3040+
rtnl_lock();
3041+
rc = __dev_ethtool(net, ifr, useraddr, ethcmd, state);
3042+
rtnl_unlock();
3043+
if (rc)
3044+
goto exit_free;
3045+
3046+
switch (ethcmd) {
3047+
case ETHTOOL_FLASHDEV:
3048+
if (state->devlink)
3049+
rc = devlink_compat_flash_update(state->devlink,
3050+
state->efl.data);
3051+
break;
3052+
case ETHTOOL_GDRVINFO:
3053+
if (state->devlink)
3054+
devlink_compat_running_version(state->devlink,
3055+
state->info.fw_version,
3056+
sizeof(state->info.fw_version));
3057+
if (copy_to_user(useraddr, &state->info, sizeof(state->info))) {
3058+
rc = -EFAULT;
3059+
goto exit_free;
3060+
}
3061+
break;
3062+
}
3063+
3064+
exit_free:
3065+
if (state->devlink)
3066+
devlink_put(state->devlink);
3067+
kfree(state);
3068+
return rc;
3069+
}
3070+
30033071
struct ethtool_rx_flow_key {
30043072
struct flow_dissector_key_basic basic;
30053073
union {

0 commit comments

Comments
 (0)