diff options
-rw-r--r-- | include/linux/ethtool.h | 30 | ||||
-rw-r--r-- | net/core/ethtool.c | 55 |
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 | */ | ||
675 | enum ethtool_phys_id_state { | ||
676 | ETHTOOL_ID_INACTIVE, | ||
677 | ETHTOOL_ID_ACTIVE, | ||
678 | ETHTOOL_ID_ON, | ||
679 | ETHTOOL_ID_OFF | ||
680 | }; | ||
681 | |||
666 | struct net_device; | 682 | struct 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: | |||
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) |