aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/ethtool.h30
-rw-r--r--net/core/ethtool.c55
2 files changed, 82 insertions, 3 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index ead7dcb1bf1e..c04d1316d221 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -663,6 +663,22 @@ struct ethtool_rx_ntuple_list {
663 unsigned int count; 663 unsigned int count;
664}; 664};
665 665
666/**
667 * enum ethtool_phys_id_state - indicator state for physical identification
668 * @ETHTOOL_ID_INACTIVE: Physical ID indicator should be deactivated
669 * @ETHTOOL_ID_ACTIVE: Physical ID indicator should be activated
670 * @ETHTOOL_ID_ON: LED should be turned on (used iff %ETHTOOL_ID_ACTIVE
671 * is not supported)
672 * @ETHTOOL_ID_OFF: LED should be turned off (used iff %ETHTOOL_ID_ACTIVE
673 * is not supported)
674 */
675enum ethtool_phys_id_state {
676 ETHTOOL_ID_INACTIVE,
677 ETHTOOL_ID_ACTIVE,
678 ETHTOOL_ID_ON,
679 ETHTOOL_ID_OFF
680};
681
666struct net_device; 682struct net_device;
667 683
668/* Some generic methods drivers may use in their ethtool_ops */ 684/* Some generic methods drivers may use in their ethtool_ops */
@@ -741,7 +757,18 @@ bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported);
741 * segmentation offload on or off. Returns a negative error code or zero. 757 * segmentation offload on or off. Returns a negative error code or zero.
742 * @self_test: Run specified self-tests 758 * @self_test: Run specified self-tests
743 * @get_strings: Return a set of strings that describe the requested objects 759 * @get_strings: Return a set of strings that describe the requested objects
744 * @phys_id: Identify the physical device, e.g. by flashing an LED 760 * @set_phys_id: Identify the physical devices, e.g. by flashing an LED
761 * attached to it. The implementation may update the indicator
762 * asynchronously or synchronously, but in either case it must return
763 * quickly. It is initially called with the argument %ETHTOOL_ID_ACTIVE,
764 * and must either activate asynchronous updates or return -%EINVAL.
765 * If it returns -%EINVAL then it will be called again at intervals with
766 * argument %ETHTOOL_ID_ON or %ETHTOOL_ID_OFF and should set the state of
767 * the indicator accordingly. Finally, it is called with the argument
768 * %ETHTOOL_ID_INACTIVE and must deactivate the indicator. Returns a
769 * negative error code or zero.
770 * @phys_id: Deprecated in favour of @set_phys_id.
771 * Identify the physical device, e.g. by flashing an LED
745 * attached to it until interrupted by a signal or the given time 772 * attached to it until interrupted by a signal or the given time
746 * (in seconds) elapses. If the given time is zero, use a default 773 * (in seconds) elapses. If the given time is zero, use a default
747 * time limit. Returns a negative error code or zero. Being 774 * time limit. Returns a negative error code or zero. Being
@@ -830,6 +857,7 @@ struct ethtool_ops {
830 int (*set_tso)(struct net_device *, u32); 857 int (*set_tso)(struct net_device *, u32);
831 void (*self_test)(struct net_device *, struct ethtool_test *, u64 *); 858 void (*self_test)(struct net_device *, struct ethtool_test *, u64 *);
832 void (*get_strings)(struct net_device *, u32 stringset, u8 *); 859 void (*get_strings)(struct net_device *, u32 stringset, u8 *);
860 int (*set_phys_id)(struct net_device *, enum ethtool_phys_id_state);
833 int (*phys_id)(struct net_device *, u32); 861 int (*phys_id)(struct net_device *, u32);
834 void (*get_ethtool_stats)(struct net_device *, 862 void (*get_ethtool_stats)(struct net_device *,
835 struct ethtool_stats *, u64 *); 863 struct ethtool_stats *, u64 *);
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)