diff options
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 106 |
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 | ||
1449 | static 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 | |||
1464 | static 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 | |||
1444 | static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) | 1478 | static 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: | |||
1618 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) | 1652 | static 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 | ||
1631 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) | 1715 | static 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 | } |