aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r--net/core/ethtool.c106
1 files changed, 98 insertions, 8 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 74ead9eca126..d8b1a8d85a96 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -21,6 +21,8 @@
21#include <linux/uaccess.h> 21#include <linux/uaccess.h>
22#include <linux/vmalloc.h> 22#include <linux/vmalloc.h>
23#include <linux/slab.h> 23#include <linux/slab.h>
24#include <linux/rtnetlink.h>
25#include <linux/sched.h>
24 26
25/* 27/*
26 * Some useful ethtool_ops methods that're device independent. 28 * Some useful ethtool_ops methods that're device independent.
@@ -317,7 +319,7 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
317 319
318 dev->wanted_features &= ~features[0].valid; 320 dev->wanted_features &= ~features[0].valid;
319 dev->wanted_features |= features[0].valid & features[0].requested; 321 dev->wanted_features |= features[0].valid & features[0].requested;
320 netdev_update_features(dev); 322 __netdev_update_features(dev);
321 323
322 if ((dev->wanted_features ^ dev->features) & features[0].valid) 324 if ((dev->wanted_features ^ dev->features) & features[0].valid)
323 ret |= ETHTOOL_F_WISH; 325 ret |= ETHTOOL_F_WISH;
@@ -359,7 +361,7 @@ static const char netdev_features_strings[ETHTOOL_DEV_FEATURE_WORDS * 32][ETH_GS
359 /* NETIF_F_NTUPLE */ "rx-ntuple-filter", 361 /* NETIF_F_NTUPLE */ "rx-ntuple-filter",
360 /* NETIF_F_RXHASH */ "rx-hashing", 362 /* NETIF_F_RXHASH */ "rx-hashing",
361 /* NETIF_F_RXCSUM */ "rx-checksum", 363 /* NETIF_F_RXCSUM */ "rx-checksum",
362 "", 364 /* NETIF_F_NOCACHE_COPY */ "tx-nocache-copy"
363 "", 365 "",
364}; 366};
365 367
@@ -499,7 +501,7 @@ static int ethtool_set_one_feature(struct net_device *dev,
499 else 501 else
500 dev->wanted_features &= ~mask; 502 dev->wanted_features &= ~mask;
501 503
502 netdev_update_features(dev); 504 __netdev_update_features(dev);
503 return 0; 505 return 0;
504 } 506 }
505 507
@@ -544,14 +546,14 @@ int __ethtool_set_flags(struct net_device *dev, u32 data)
544 } 546 }
545 547
546 /* allow changing only bits set in hw_features */ 548 /* allow changing only bits set in hw_features */
547 changed = (data ^ dev->wanted_features) & flags_dup_features; 549 changed = (data ^ dev->features) & flags_dup_features;
548 if (changed & ~dev->hw_features) 550 if (changed & ~dev->hw_features)
549 return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; 551 return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP;
550 552
551 dev->wanted_features = 553 dev->wanted_features =
552 (dev->wanted_features & ~changed) | data; 554 (dev->wanted_features & ~changed) | (data & dev->hw_features);
553 555
554 netdev_update_features(dev); 556 __netdev_update_features(dev);
555 557
556 return 0; 558 return 0;
557} 559}
@@ -908,6 +910,9 @@ static noinline_for_stack int ethtool_set_rx_ntuple(struct net_device *dev,
908 struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL; 910 struct ethtool_rx_ntuple_flow_spec_container *fsc = NULL;
909 int ret; 911 int ret;
910 912
913 if (!ops->set_rx_ntuple)
914 return -EOPNOTSUPP;
915
911 if (!(dev->features & NETIF_F_NTUPLE)) 916 if (!(dev->features & NETIF_F_NTUPLE))
912 return -EINVAL; 917 return -EINVAL;
913 918
@@ -1441,6 +1446,35 @@ static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
1441 return dev->ethtool_ops->set_ringparam(dev, &ringparam); 1446 return dev->ethtool_ops->set_ringparam(dev, &ringparam);
1442} 1447}
1443 1448
1449static noinline_for_stack int ethtool_get_channels(struct net_device *dev,
1450 void __user *useraddr)
1451{
1452 struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };
1453
1454 if (!dev->ethtool_ops->get_channels)
1455 return -EOPNOTSUPP;
1456
1457 dev->ethtool_ops->get_channels(dev, &channels);
1458
1459 if (copy_to_user(useraddr, &channels, sizeof(channels)))
1460 return -EFAULT;
1461 return 0;
1462}
1463
1464static noinline_for_stack int ethtool_set_channels(struct net_device *dev,
1465 void __user *useraddr)
1466{
1467 struct ethtool_channels channels;
1468
1469 if (!dev->ethtool_ops->set_channels)
1470 return -EOPNOTSUPP;
1471
1472 if (copy_from_user(&channels, useraddr, sizeof(channels)))
1473 return -EFAULT;
1474
1475 return dev->ethtool_ops->set_channels(dev, &channels);
1476}
1477
1444static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) 1478static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
1445{ 1479{
1446 struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; 1480 struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
@@ -1618,14 +1652,64 @@ out:
1618static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) 1652static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
1619{ 1653{
1620 struct ethtool_value id; 1654 struct ethtool_value id;
1655 static bool busy;
1656 int rc;
1621 1657
1622 if (!dev->ethtool_ops->phys_id) 1658 if (!dev->ethtool_ops->set_phys_id && !dev->ethtool_ops->phys_id)
1623 return -EOPNOTSUPP; 1659 return -EOPNOTSUPP;
1624 1660
1661 if (busy)
1662 return -EBUSY;
1663
1625 if (copy_from_user(&id, useraddr, sizeof(id))) 1664 if (copy_from_user(&id, useraddr, sizeof(id)))
1626 return -EFAULT; 1665 return -EFAULT;
1627 1666
1628 return dev->ethtool_ops->phys_id(dev, id.data); 1667 if (!dev->ethtool_ops->set_phys_id)
1668 /* Do it the old way */
1669 return dev->ethtool_ops->phys_id(dev, id.data);
1670
1671 rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE);
1672 if (rc < 0)
1673 return rc;
1674
1675 /* Drop the RTNL lock while waiting, but prevent reentry or
1676 * removal of the device.
1677 */
1678 busy = true;
1679 dev_hold(dev);
1680 rtnl_unlock();
1681
1682 if (rc == 0) {
1683 /* Driver will handle this itself */
1684 schedule_timeout_interruptible(
1685 id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
1686 } else {
1687 /* Driver expects to be called at twice the frequency in rc */
1688 int n = rc * 2, i, interval = HZ / n;
1689
1690 /* Count down seconds */
1691 do {
1692 /* Count down iterations per second */
1693 i = n;
1694 do {
1695 rtnl_lock();
1696 rc = dev->ethtool_ops->set_phys_id(dev,
1697 (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON);
1698 rtnl_unlock();
1699 if (rc)
1700 break;
1701 schedule_timeout_interruptible(interval);
1702 } while (!signal_pending(current) && --i != 0);
1703 } while (!signal_pending(current) &&
1704 (id.data == 0 || --id.data != 0));
1705 }
1706
1707 rtnl_lock();
1708 dev_put(dev);
1709 busy = false;
1710
1711 (void)dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE);
1712 return rc;
1629} 1713}
1630 1714
1631static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) 1715static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
@@ -1953,6 +2037,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
1953 case ETHTOOL_SGRO: 2037 case ETHTOOL_SGRO:
1954 rc = ethtool_set_one_feature(dev, useraddr, ethcmd); 2038 rc = ethtool_set_one_feature(dev, useraddr, ethcmd);
1955 break; 2039 break;
2040 case ETHTOOL_GCHANNELS:
2041 rc = ethtool_get_channels(dev, useraddr);
2042 break;
2043 case ETHTOOL_SCHANNELS:
2044 rc = ethtool_set_channels(dev, useraddr);
2045 break;
1956 default: 2046 default:
1957 rc = -EOPNOTSUPP; 2047 rc = -EOPNOTSUPP;
1958 } 2048 }