]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ethtool: Add link extended state
authorAmit Cohen <amitc@mellanox.com>
Mon, 29 Jun 2020 20:46:16 +0000 (23:46 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 30 Jun 2020 00:45:02 +0000 (17:45 -0700)
Currently, drivers can only tell whether the link is up/down using
LINKSTATE_GET, but no additional information is given.

Add attributes to LINKSTATE_GET command in order to allow drivers
to expose the user more information in addition to link state to ease
the debug process, for example, reason for link down state.

Extended state consists of two attributes - link_ext_state and
link_ext_substate. The idea is to avoid 'vendor specific' states in order
to prevent drivers to use specific link_ext_state that can be in the future
common link_ext_state.

The substates allows drivers to add more information to the common
link_ext_state. For example, vendor can expose 'Autoneg' as link_ext_state
and add 'No partner detected during force mode' as link_ext_substate.

If a driver cannot pinpoint the extended state with the substate
accuracy, it is free to expose only the extended state and omit the
substate attribute.

Signed-off-by: Amit Cohen <amitc@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/ethtool.h
include/uapi/linux/ethtool.h
include/uapi/linux/ethtool_netlink.h
net/ethtool/linkstate.c

index a23b26eab479007408a90a2ebe4dfd823d8078a2..48ad3b6a01508c1218b04a77efcb1166320272b3 100644 (file)
@@ -86,6 +86,22 @@ struct net_device;
 u32 ethtool_op_get_link(struct net_device *dev);
 int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *eti);
 
+
+/**
+ * struct ethtool_link_ext_state_info - link extended state and substate.
+ */
+struct ethtool_link_ext_state_info {
+       enum ethtool_link_ext_state link_ext_state;
+       union {
+               enum ethtool_link_ext_substate_autoneg autoneg;
+               enum ethtool_link_ext_substate_link_training link_training;
+               enum ethtool_link_ext_substate_link_logical_mismatch link_logical_mismatch;
+               enum ethtool_link_ext_substate_bad_signal_integrity bad_signal_integrity;
+               enum ethtool_link_ext_substate_cable_issue cable_issue;
+               u8 __link_ext_substate;
+       };
+};
+
 /**
  * ethtool_rxfh_indir_default - get default value for RX flow hash indirection
  * @index: Index in RX flow hash indirection table
@@ -245,6 +261,11 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
  * @get_link: Report whether physical link is up.  Will only be called if
  *     the netdev is up.  Should usually be set to ethtool_op_get_link(),
  *     which uses netif_carrier_ok().
+ * @get_link_ext_state: Report link extended state. Should set link_ext_state and
+ *     link_ext_substate (link_ext_substate of 0 means link_ext_substate is unknown,
+ *     do not attach ext_substate attribute to netlink message). If link_ext_state
+ *     and link_ext_substate are unknown, return -ENODATA. If not implemented,
+ *     link_ext_state and link_ext_substate will not be sent to userspace.
  * @get_eeprom: Read data from the device EEPROM.
  *     Should fill in the magic field.  Don't need to check len for zero
  *     or wraparound.  Fill in the data argument with the eeprom values
@@ -384,6 +405,8 @@ struct ethtool_ops {
        void    (*set_msglevel)(struct net_device *, u32);
        int     (*nway_reset)(struct net_device *);
        u32     (*get_link)(struct net_device *);
+       int     (*get_link_ext_state)(struct net_device *,
+                                     struct ethtool_link_ext_state_info *);
        int     (*get_eeprom_len)(struct net_device *);
        int     (*get_eeprom)(struct net_device *,
                              struct ethtool_eeprom *, u8 *);
index f4662b3a9e1ef6dc9aafa1bc9034a89b4e1b1600..d1413538ef30570e7e0922a6a3bc1b611b4282c2 100644 (file)
@@ -579,6 +579,76 @@ struct ethtool_pauseparam {
        __u32   tx_pause;
 };
 
+/**
+ * enum ethtool_link_ext_state - link extended state
+ */
+enum ethtool_link_ext_state {
+       ETHTOOL_LINK_EXT_STATE_AUTONEG,
+       ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
+       ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
+       ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
+       ETHTOOL_LINK_EXT_STATE_NO_CABLE,
+       ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
+       ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE,
+       ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE,
+       ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED,
+       ETHTOOL_LINK_EXT_STATE_OVERHEAT,
+};
+
+/**
+ * enum ethtool_link_ext_substate_autoneg - more information in addition to
+ * ETHTOOL_LINK_EXT_STATE_AUTONEG.
+ */
+enum ethtool_link_ext_substate_autoneg {
+       ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1,
+       ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED,
+       ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED,
+       ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE,
+       ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE,
+       ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD,
+};
+
+/**
+ * enum ethtool_link_ext_substate_link_training - more information in addition to
+ * ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE.
+ */
+enum ethtool_link_ext_substate_link_training {
+       ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1,
+       ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT,
+       ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY,
+       ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT,
+};
+
+/**
+ * enum ethtool_link_ext_substate_logical_mismatch - more information in addition
+ * to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH.
+ */
+enum ethtool_link_ext_substate_link_logical_mismatch {
+       ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1,
+       ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK,
+       ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS,
+       ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED,
+       ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED,
+};
+
+/**
+ * enum ethtool_link_ext_substate_bad_signal_integrity - more information in
+ * addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY.
+ */
+enum ethtool_link_ext_substate_bad_signal_integrity {
+       ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1,
+       ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE,
+};
+
+/**
+ * enum ethtool_link_ext_substate_cable_issue - more information in
+ * addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE.
+ */
+enum ethtool_link_ext_substate_cable_issue {
+       ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1,
+       ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE,
+};
+
 #define ETH_GSTRING_LEN                32
 
 /**
index 4dda5e4244a7a6980a3d5baacb602ca2e3a92f82..c12ce4df4b6b7e12cef7bb8bed8c3f5dcdb97e43 100644 (file)
@@ -236,6 +236,8 @@ enum {
        ETHTOOL_A_LINKSTATE_LINK,               /* u8 */
        ETHTOOL_A_LINKSTATE_SQI,                /* u32 */
        ETHTOOL_A_LINKSTATE_SQI_MAX,            /* u32 */
+       ETHTOOL_A_LINKSTATE_EXT_STATE,          /* u8 */
+       ETHTOOL_A_LINKSTATE_EXT_SUBSTATE,       /* u8 */
 
        /* add new constants above here */
        __ETHTOOL_A_LINKSTATE_CNT,
index afe5ac8a0f00329252e306c78403384c06deb821..4834091ec24c426c3ef6a81334856f4c275f1a3c 100644 (file)
@@ -9,10 +9,12 @@ struct linkstate_req_info {
 };
 
 struct linkstate_reply_data {
-       struct ethnl_reply_data         base;
-       int                             link;
-       int                             sqi;
-       int                             sqi_max;
+       struct ethnl_reply_data                 base;
+       int                                     link;
+       int                                     sqi;
+       int                                     sqi_max;
+       bool                                    link_ext_state_provided;
+       struct ethtool_link_ext_state_info      ethtool_link_ext_state_info;
 };
 
 #define LINKSTATE_REPDATA(__reply_base) \
@@ -25,6 +27,8 @@ linkstate_get_policy[ETHTOOL_A_LINKSTATE_MAX + 1] = {
        [ETHTOOL_A_LINKSTATE_LINK]              = { .type = NLA_REJECT },
        [ETHTOOL_A_LINKSTATE_SQI]               = { .type = NLA_REJECT },
        [ETHTOOL_A_LINKSTATE_SQI_MAX]           = { .type = NLA_REJECT },
+       [ETHTOOL_A_LINKSTATE_EXT_STATE]         = { .type = NLA_REJECT },
+       [ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]      = { .type = NLA_REJECT },
 };
 
 static int linkstate_get_sqi(struct net_device *dev)
@@ -61,6 +65,23 @@ static int linkstate_get_sqi_max(struct net_device *dev)
        mutex_unlock(&phydev->lock);
 
        return ret;
+};
+
+static int linkstate_get_link_ext_state(struct net_device *dev,
+                                       struct linkstate_reply_data *data)
+{
+       int err;
+
+       if (!dev->ethtool_ops->get_link_ext_state)
+               return -EOPNOTSUPP;
+
+       err = dev->ethtool_ops->get_link_ext_state(dev, &data->ethtool_link_ext_state_info);
+       if (err)
+               return err;
+
+       data->link_ext_state_provided = true;
+
+       return 0;
 }
 
 static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
@@ -86,6 +107,12 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
                goto out;
        data->sqi_max = ret;
 
+       if (dev->flags & IFF_UP) {
+               ret = linkstate_get_link_ext_state(dev, data);
+               if (ret < 0 && ret != -EOPNOTSUPP && ret != -ENODATA)
+                       goto out;
+       }
+
        ret = 0;
 out:
        ethnl_ops_complete(dev);
@@ -107,6 +134,12 @@ static int linkstate_reply_size(const struct ethnl_req_info *req_base,
        if (data->sqi_max != -EOPNOTSUPP)
                len += nla_total_size(sizeof(u32));
 
+       if (data->link_ext_state_provided)
+               len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_STATE */
+
+       if (data->ethtool_link_ext_state_info.__link_ext_substate)
+               len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_SUBSTATE */
+
        return len;
 }
 
@@ -128,6 +161,17 @@ static int linkstate_fill_reply(struct sk_buff *skb,
            nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI_MAX, data->sqi_max))
                return -EMSGSIZE;
 
+       if (data->link_ext_state_provided) {
+               if (nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_STATE,
+                              data->ethtool_link_ext_state_info.link_ext_state))
+                       return -EMSGSIZE;
+
+               if (data->ethtool_link_ext_state_info.__link_ext_substate &&
+                   nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_SUBSTATE,
+                              data->ethtool_link_ext_state_info.__link_ext_substate))
+                       return -EMSGSIZE;
+       }
+
        return 0;
 }