diff options
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 33 |
1 files changed, 23 insertions, 10 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 7a85367b3c2f..d2c4da5a6a4f 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -205,18 +205,24 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, | |||
205 | struct ethtool_drvinfo info; | 205 | struct ethtool_drvinfo info; |
206 | const struct ethtool_ops *ops = dev->ethtool_ops; | 206 | const struct ethtool_ops *ops = dev->ethtool_ops; |
207 | 207 | ||
208 | if (!ops->get_drvinfo) | ||
209 | return -EOPNOTSUPP; | ||
210 | |||
211 | memset(&info, 0, sizeof(info)); | 208 | memset(&info, 0, sizeof(info)); |
212 | info.cmd = ETHTOOL_GDRVINFO; | 209 | info.cmd = ETHTOOL_GDRVINFO; |
213 | ops->get_drvinfo(dev, &info); | 210 | if (ops && ops->get_drvinfo) { |
211 | ops->get_drvinfo(dev, &info); | ||
212 | } else if (dev->dev.parent && dev->dev.parent->driver) { | ||
213 | strlcpy(info.bus_info, dev_name(dev->dev.parent), | ||
214 | sizeof(info.bus_info)); | ||
215 | strlcpy(info.driver, dev->dev.parent->driver->name, | ||
216 | sizeof(info.driver)); | ||
217 | } else { | ||
218 | return -EOPNOTSUPP; | ||
219 | } | ||
214 | 220 | ||
215 | /* | 221 | /* |
216 | * this method of obtaining string set info is deprecated; | 222 | * this method of obtaining string set info is deprecated; |
217 | * Use ETHTOOL_GSSET_INFO instead. | 223 | * Use ETHTOOL_GSSET_INFO instead. |
218 | */ | 224 | */ |
219 | if (ops->get_sset_count) { | 225 | if (ops && ops->get_sset_count) { |
220 | int rc; | 226 | int rc; |
221 | 227 | ||
222 | rc = ops->get_sset_count(dev, ETH_SS_TEST); | 228 | rc = ops->get_sset_count(dev, ETH_SS_TEST); |
@@ -229,9 +235,9 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, | |||
229 | if (rc >= 0) | 235 | if (rc >= 0) |
230 | info.n_priv_flags = rc; | 236 | info.n_priv_flags = rc; |
231 | } | 237 | } |
232 | if (ops->get_regs_len) | 238 | if (ops && ops->get_regs_len) |
233 | info.regdump_len = ops->get_regs_len(dev); | 239 | info.regdump_len = ops->get_regs_len(dev); |
234 | if (ops->get_eeprom_len) | 240 | if (ops && ops->get_eeprom_len) |
235 | info.eedump_len = ops->get_eeprom_len(dev); | 241 | info.eedump_len = ops->get_eeprom_len(dev); |
236 | 242 | ||
237 | if (copy_to_user(useraddr, &info, sizeof(info))) | 243 | if (copy_to_user(useraddr, &info, sizeof(info))) |
@@ -1402,12 +1408,19 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1402 | if (!dev || !netif_device_present(dev)) | 1408 | if (!dev || !netif_device_present(dev)) |
1403 | return -ENODEV; | 1409 | return -ENODEV; |
1404 | 1410 | ||
1405 | if (!dev->ethtool_ops) | ||
1406 | return -EOPNOTSUPP; | ||
1407 | |||
1408 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) | 1411 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) |
1409 | return -EFAULT; | 1412 | return -EFAULT; |
1410 | 1413 | ||
1414 | if (!dev->ethtool_ops) { | ||
1415 | /* ETHTOOL_GDRVINFO does not require any driver support. | ||
1416 | * It is also unprivileged and does not change anything, | ||
1417 | * so we can take a shortcut to it. */ | ||
1418 | if (ethcmd == ETHTOOL_GDRVINFO) | ||
1419 | return ethtool_get_drvinfo(dev, useraddr); | ||
1420 | else | ||
1421 | return -EOPNOTSUPP; | ||
1422 | } | ||
1423 | |||
1411 | /* Allow some commands to be done by anyone */ | 1424 | /* Allow some commands to be done by anyone */ |
1412 | switch (ethcmd) { | 1425 | switch (ethcmd) { |
1413 | case ETHTOOL_GDRVINFO: | 1426 | case ETHTOOL_GDRVINFO: |