aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-04-01 19:35:15 -0400
committerBen Hutchings <bhutchings@solarflare.com>2011-04-05 10:12:01 -0400
commit68f512f21a64c9b264df6c61a9333e7890faf74b (patch)
tree4635193aaec903f09ae69df4dc880296d74c5135 /net/core
parent8717d07b1143e0f150921f5bd7cfe7af579a995a (diff)
ethtool: Change ETHTOOL_PHYS_ID implementation to allow dropping RTNL
The ethtool ETHTOOL_PHYS_ID command runs for an arbitrarily long period of time, holding the RTNL lock. This blocks routing updates, device enumeration, and various important operations that one might want to keep running while hunting for the flashing LED. We need to drop the RTNL lock during this operation, but currently the core implementation is a thin wrapper around a driver operation and drivers may well depend upon holding the lock. Define a new driver operation 'set_phys_id' with an argument that sets the ID indicator on/off/inactive/active (the last optional, for any driver or firmware that prefers to handle blinking asynchronously). When this is defined, the ethtool core drops the lock while waiting and only acquires it around calls to this operation. Deprecate the 'phys_id' operation in favour of this. It can be removed once all in-tree drivers are converted. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/ethtool.c55
1 files changed, 53 insertions, 2 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 74ead9eca126..1c95f0fb8b3a 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:
1618static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) 1620static 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
1631static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) 1682static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)