diff options
author | David S. Miller <davem@davemloft.net> | 2011-04-06 15:27:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-06 15:27:34 -0400 |
commit | 4c844d97d269a7ec4a6ba7d530aa876ac64dfb76 (patch) | |
tree | 67198ffbe0d8652f9a26548a7435f7514912a8cc /net/core/ethtool.c | |
parent | 66ee33bfda6237b009b6fb0e48690e31800ff334 (diff) | |
parent | c5e129ac2fc72c119b85db79a629de66332f136d (diff) |
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/sfc-next-2.6
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 55 |
1 files changed, 53 insertions, 2 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 719670ae199c..1b7fa984de7d 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. |
@@ -1618,14 +1620,63 @@ out: | |||
1618 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) | 1620 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) |
1619 | { | 1621 | { |
1620 | struct ethtool_value id; | 1622 | struct ethtool_value id; |
1623 | static bool busy; | ||
1624 | int rc; | ||
1621 | 1625 | ||
1622 | if (!dev->ethtool_ops->phys_id) | 1626 | if (!dev->ethtool_ops->set_phys_id && !dev->ethtool_ops->phys_id) |
1623 | return -EOPNOTSUPP; | 1627 | return -EOPNOTSUPP; |
1624 | 1628 | ||
1629 | if (busy) | ||
1630 | return -EBUSY; | ||
1631 | |||
1625 | if (copy_from_user(&id, useraddr, sizeof(id))) | 1632 | if (copy_from_user(&id, useraddr, sizeof(id))) |
1626 | return -EFAULT; | 1633 | return -EFAULT; |
1627 | 1634 | ||
1628 | return dev->ethtool_ops->phys_id(dev, id.data); | 1635 | if (!dev->ethtool_ops->set_phys_id) |
1636 | /* Do it the old way */ | ||
1637 | return dev->ethtool_ops->phys_id(dev, id.data); | ||
1638 | |||
1639 | rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); | ||
1640 | if (rc && rc != -EINVAL) | ||
1641 | return rc; | ||
1642 | |||
1643 | /* Drop the RTNL lock while waiting, but prevent reentry or | ||
1644 | * removal of the device. | ||
1645 | */ | ||
1646 | busy = true; | ||
1647 | dev_hold(dev); | ||
1648 | rtnl_unlock(); | ||
1649 | |||
1650 | if (rc == 0) { | ||
1651 | /* Driver will handle this itself */ | ||
1652 | schedule_timeout_interruptible( | ||
1653 | id.data ? id.data : MAX_SCHEDULE_TIMEOUT); | ||
1654 | } else { | ||
1655 | /* Driver expects to be called periodically */ | ||
1656 | do { | ||
1657 | rtnl_lock(); | ||
1658 | rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ON); | ||
1659 | rtnl_unlock(); | ||
1660 | if (rc) | ||
1661 | break; | ||
1662 | schedule_timeout_interruptible(HZ / 2); | ||
1663 | |||
1664 | rtnl_lock(); | ||
1665 | rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_OFF); | ||
1666 | rtnl_unlock(); | ||
1667 | if (rc) | ||
1668 | break; | ||
1669 | schedule_timeout_interruptible(HZ / 2); | ||
1670 | } while (!signal_pending(current) && | ||
1671 | (id.data == 0 || --id.data != 0)); | ||
1672 | } | ||
1673 | |||
1674 | rtnl_lock(); | ||
1675 | dev_put(dev); | ||
1676 | busy = false; | ||
1677 | |||
1678 | (void)dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); | ||
1679 | return rc; | ||
1629 | } | 1680 | } |
1630 | 1681 | ||
1631 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) | 1682 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) |