net: hns3: add querying fec statistics
authorHao Lan <lanhao@huawei.com>
Tue, 6 Sep 2022 09:12:22 +0000 (17:12 +0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 7 Sep 2022 15:20:15 +0000 (16:20 +0100)
FEC statistics can be used to check the transmission quality of links.
This patch implements the get_fec_stats callback of ethtool_ops to support
querying FEC statistics by command "ethtool -I --show-fec eth0".

Signed-off-by: Hao Lan <lanhao@huawei.com>
Signed-off-by: Guangbin Huang <huangguangbin2@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.c
drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h

index 9ae0941..74f7395 100644 (file)
@@ -97,6 +97,7 @@ enum HNAE3_DEV_CAP_BITS {
        HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B,
        HNAE3_DEV_SUPPORT_MC_MAC_MNG_B,
        HNAE3_DEV_SUPPORT_CQ_B,
+       HNAE3_DEV_SUPPORT_FEC_STATS_B,
 };
 
 #define hnae3_ae_dev_fd_supported(ae_dev) \
@@ -159,6 +160,9 @@ enum HNAE3_DEV_CAP_BITS {
 #define hnae3_ae_dev_cq_supported(ae_dev) \
        test_bit(HNAE3_DEV_SUPPORT_CQ_B, (ae_dev)->caps)
 
+#define hnae3_ae_dev_fec_stats_supported(ae_dev) \
+       test_bit(HNAE3_DEV_SUPPORT_FEC_STATS_B, (ae_dev)->caps)
+
 enum HNAE3_PF_CAP_BITS {
        HNAE3_PF_SUPPORT_VLAN_FLTR_MDF_B = 0,
 };
@@ -576,6 +580,8 @@ struct hnae3_ae_ops {
        void (*get_media_type)(struct hnae3_handle *handle, u8 *media_type,
                               u8 *module_type);
        int (*check_port_speed)(struct hnae3_handle *handle, u32 speed);
+       void (*get_fec_stats)(struct hnae3_handle *handle,
+                             struct ethtool_fec_stats *fec_stats);
        void (*get_fec)(struct hnae3_handle *handle, u8 *fec_ability,
                        u8 *fec_mode);
        int (*set_fec)(struct hnae3_handle *handle, u32 fec_mode);
index f9bd3fc..ca4efdd 100644 (file)
@@ -153,6 +153,7 @@ static const struct hclge_comm_caps_bit_map hclge_pf_cmd_caps[] = {
        {HCLGE_COMM_CAP_CQ_B, HNAE3_DEV_SUPPORT_CQ_B},
        {HCLGE_COMM_CAP_GRO_B, HNAE3_DEV_SUPPORT_GRO_B},
        {HCLGE_COMM_CAP_FD_B, HNAE3_DEV_SUPPORT_FD_B},
+       {HCLGE_COMM_CAP_FEC_STATS_B, HNAE3_DEV_SUPPORT_FEC_STATS_B},
 };
 
 static const struct hclge_comm_caps_bit_map hclge_vf_cmd_caps[] = {
index 8aaa5fd..5b66c7d 100644 (file)
@@ -103,6 +103,7 @@ enum hclge_opcode_type {
        HCLGE_OPC_MAC_TNL_INT_EN        = 0x0311,
        HCLGE_OPC_CLEAR_MAC_TNL_INT     = 0x0312,
        HCLGE_OPC_COMMON_LOOPBACK       = 0x0315,
+       HCLGE_OPC_QUERY_FEC_STATS       = 0x0316,
        HCLGE_OPC_CONFIG_FEC_MODE       = 0x031A,
        HCLGE_OPC_QUERY_ROH_TYPE_INFO   = 0x0389,
 
@@ -342,6 +343,7 @@ enum HCLGE_COMM_CAP_BITS {
        HCLGE_COMM_CAP_CQ_B = 18,
        HCLGE_COMM_CAP_GRO_B = 20,
        HCLGE_COMM_CAP_FD_B = 21,
+       HCLGE_COMM_CAP_FEC_STATS_B = 25,
 };
 
 enum HCLGE_COMM_API_CAP_BITS {
index 0f8f5c4..a3d4772 100644 (file)
@@ -402,6 +402,9 @@ static struct hns3_dbg_cap_info hns3_dbg_cap[] = {
        }, {
                .name = "support modify vlan filter state",
                .cap_bit = HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B,
+       }, {
+               .name = "support FEC statistics",
+               .cap_bit = HNAE3_DEV_SUPPORT_FEC_STATS_B,
        }
 };
 
index 3ca9c2b..31d1811 100644 (file)
@@ -1612,6 +1612,19 @@ static void hns3_set_msglevel(struct net_device *netdev, u32 msg_level)
        h->msg_enable = msg_level;
 }
 
+static void hns3_get_fec_stats(struct net_device *netdev,
+                              struct ethtool_fec_stats *fec_stats)
+{
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
+       const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+       if (!hnae3_ae_dev_fec_stats_supported(ae_dev) || !ops->get_fec_stats)
+               return;
+
+       ops->get_fec_stats(handle, fec_stats);
+}
+
 /* Translate local fec value into ethtool value. */
 static unsigned int loc_to_eth_fec(u8 loc_fec)
 {
@@ -2084,6 +2097,7 @@ static const struct ethtool_ops hns3_ethtool_ops = {
        .set_msglevel = hns3_set_msglevel,
        .get_fecparam = hns3_get_fecparam,
        .set_fecparam = hns3_set_fecparam,
+       .get_fec_stats = hns3_get_fec_stats,
        .get_module_info = hns3_get_module_info,
        .get_module_eeprom = hns3_get_module_eeprom,
        .get_priv_flags = hns3_get_priv_flags,
index 489a87e..7461b7e 100644 (file)
@@ -367,6 +367,20 @@ struct hclge_config_fec_cmd {
        u8 rsv[22];
 };
 
+#define HCLGE_FEC_STATS_CMD_NUM 4
+
+struct hclge_query_fec_stats_cmd {
+       /* fec rs mode total stats */
+       __le32 rs_fec_corr_blocks;
+       __le32 rs_fec_uncorr_blocks;
+       __le32 rs_fec_error_blocks;
+       /* fec base-r mode per lanes stats */
+       u8 base_r_lane_num;
+       u8 rsv[3];
+       __le32 base_r_fec_corr_blocks;
+       __le32 base_r_fec_uncorr_blocks;
+};
+
 #define HCLGE_MAC_UPLINK_PORT          0x100
 
 struct hclge_config_max_frm_size_cmd {
index 17269cc..a0136e2 100644 (file)
@@ -71,6 +71,7 @@ static void hclge_sync_mac_table(struct hclge_dev *hdev);
 static void hclge_restore_hw_table(struct hclge_dev *hdev);
 static void hclge_sync_promisc_mode(struct hclge_dev *hdev);
 static void hclge_sync_fd_table(struct hclge_dev *hdev);
+static void hclge_update_fec_stats(struct hclge_dev *hdev);
 
 static struct hnae3_ae_algo ae_algo;
 
@@ -679,6 +680,8 @@ static void hclge_update_stats_for_all(struct hclge_dev *hdev)
                }
        }
 
+       hclge_update_fec_stats(hdev);
+
        status = hclge_mac_update_stats(hdev);
        if (status)
                dev_err(&hdev->pdev->dev,
@@ -2753,6 +2756,157 @@ static int hclge_halt_autoneg(struct hnae3_handle *handle, bool halt)
        return 0;
 }
 
+static void hclge_parse_fec_stats_lanes(struct hclge_dev *hdev,
+                                       struct hclge_desc *desc, u32 desc_len)
+{
+       u32 lane_size = HCLGE_FEC_STATS_MAX_LANES * 2;
+       u32 desc_index = 0;
+       u32 data_index = 0;
+       u32 i;
+
+       for (i = 0; i < lane_size; i++) {
+               if (data_index >= HCLGE_DESC_DATA_LEN) {
+                       desc_index++;
+                       data_index = 0;
+               }
+
+               if (desc_index >= desc_len)
+                       return;
+
+               hdev->fec_stats.per_lanes[i] +=
+                       le32_to_cpu(desc[desc_index].data[data_index]);
+               data_index++;
+       }
+}
+
+static void hclge_parse_fec_stats(struct hclge_dev *hdev,
+                                 struct hclge_desc *desc, u32 desc_len)
+{
+       struct hclge_query_fec_stats_cmd *req;
+
+       req = (struct hclge_query_fec_stats_cmd *)desc[0].data;
+
+       hdev->fec_stats.base_r_lane_num = req->base_r_lane_num;
+       hdev->fec_stats.rs_corr_blocks +=
+               le32_to_cpu(req->rs_fec_corr_blocks);
+       hdev->fec_stats.rs_uncorr_blocks +=
+               le32_to_cpu(req->rs_fec_uncorr_blocks);
+       hdev->fec_stats.rs_error_blocks +=
+               le32_to_cpu(req->rs_fec_error_blocks);
+       hdev->fec_stats.base_r_corr_blocks +=
+               le32_to_cpu(req->base_r_fec_corr_blocks);
+       hdev->fec_stats.base_r_uncorr_blocks +=
+               le32_to_cpu(req->base_r_fec_uncorr_blocks);
+
+       hclge_parse_fec_stats_lanes(hdev, &desc[1], desc_len - 1);
+}
+
+static int hclge_update_fec_stats_hw(struct hclge_dev *hdev)
+{
+       struct hclge_desc desc[HCLGE_FEC_STATS_CMD_NUM];
+       int ret;
+       u32 i;
+
+       for (i = 0; i < HCLGE_FEC_STATS_CMD_NUM; i++) {
+               hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_QUERY_FEC_STATS,
+                                          true);
+               if (i != (HCLGE_FEC_STATS_CMD_NUM - 1))
+                       desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
+       }
+
+       ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_FEC_STATS_CMD_NUM);
+       if (ret)
+               return ret;
+
+       hclge_parse_fec_stats(hdev, desc, HCLGE_FEC_STATS_CMD_NUM);
+
+       return 0;
+}
+
+static void hclge_update_fec_stats(struct hclge_dev *hdev)
+{
+       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
+       int ret;
+
+       if (!hnae3_ae_dev_fec_stats_supported(ae_dev) ||
+           test_and_set_bit(HCLGE_STATE_FEC_STATS_UPDATING, &hdev->state))
+               return;
+
+       ret = hclge_update_fec_stats_hw(hdev);
+       if (ret)
+               dev_err(&hdev->pdev->dev,
+                       "failed to update fec stats, ret = %d\n", ret);
+
+       clear_bit(HCLGE_STATE_FEC_STATS_UPDATING, &hdev->state);
+}
+
+static void hclge_get_fec_stats_total(struct hclge_dev *hdev,
+                                     struct ethtool_fec_stats *fec_stats)
+{
+       fec_stats->corrected_blocks.total = hdev->fec_stats.rs_corr_blocks;
+       fec_stats->uncorrectable_blocks.total =
+               hdev->fec_stats.rs_uncorr_blocks;
+}
+
+static void hclge_get_fec_stats_lanes(struct hclge_dev *hdev,
+                                     struct ethtool_fec_stats *fec_stats)
+{
+       u32 i;
+
+       if (hdev->fec_stats.base_r_lane_num == 0 ||
+           hdev->fec_stats.base_r_lane_num > HCLGE_FEC_STATS_MAX_LANES) {
+               dev_err(&hdev->pdev->dev,
+                       "fec stats lane number(%llu) is invalid\n",
+                       hdev->fec_stats.base_r_lane_num);
+               return;
+       }
+
+       for (i = 0; i < hdev->fec_stats.base_r_lane_num; i++) {
+               fec_stats->corrected_blocks.lanes[i] =
+                       hdev->fec_stats.base_r_corr_per_lanes[i];
+               fec_stats->uncorrectable_blocks.lanes[i] =
+                       hdev->fec_stats.base_r_uncorr_per_lanes[i];
+       }
+}
+
+static void hclge_comm_get_fec_stats(struct hclge_dev *hdev,
+                                    struct ethtool_fec_stats *fec_stats)
+{
+       u32 fec_mode = hdev->hw.mac.fec_mode;
+
+       switch (fec_mode) {
+       case BIT(HNAE3_FEC_RS):
+       case BIT(HNAE3_FEC_LLRS):
+               hclge_get_fec_stats_total(hdev, fec_stats);
+               break;
+       case BIT(HNAE3_FEC_BASER):
+               hclge_get_fec_stats_lanes(hdev, fec_stats);
+               break;
+       default:
+               dev_err(&hdev->pdev->dev,
+                       "fec stats is not supported by current fec mode(0x%x)\n",
+                       fec_mode);
+               break;
+       }
+}
+
+static void hclge_get_fec_stats(struct hnae3_handle *handle,
+                               struct ethtool_fec_stats *fec_stats)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       u32 fec_mode = hdev->hw.mac.fec_mode;
+
+       if (fec_mode == BIT(HNAE3_FEC_NONE) ||
+           fec_mode == BIT(HNAE3_FEC_AUTO) ||
+           fec_mode == BIT(HNAE3_FEC_USER_DEF))
+               return;
+
+       hclge_update_fec_stats(hdev);
+
+       hclge_comm_get_fec_stats(hdev, fec_stats);
+}
+
 static int hclge_set_fec_hw(struct hclge_dev *hdev, u32 fec_mode)
 {
        struct hclge_config_fec_cmd *req;
@@ -11552,6 +11706,7 @@ out:
 static void hclge_stats_clear(struct hclge_dev *hdev)
 {
        memset(&hdev->mac_stats, 0, sizeof(hdev->mac_stats));
+       memset(&hdev->fec_stats, 0, sizeof(hdev->fec_stats));
 }
 
 static int hclge_set_mac_spoofchk(struct hclge_dev *hdev, int vf, bool enable)
@@ -12846,6 +13001,7 @@ static const struct hnae3_ae_ops hclge_ops = {
        .cfg_mac_speed_dup_h = hclge_cfg_mac_speed_dup_h,
        .get_media_type = hclge_get_media_type,
        .check_port_speed = hclge_check_port_speed,
+       .get_fec_stats = hclge_get_fec_stats,
        .get_fec = hclge_get_fec,
        .set_fec = hclge_set_fec,
        .get_rss_key_size = hclge_comm_get_rss_key_size,
index 8498cd8..ef0f67e 100644 (file)
@@ -216,6 +216,7 @@ enum HCLGE_DEV_STATE {
        HCLGE_STATE_FD_USER_DEF_CHANGED,
        HCLGE_STATE_PTP_EN,
        HCLGE_STATE_PTP_TX_HANDLING,
+       HCLGE_STATE_FEC_STATS_UPDATING,
        HCLGE_STATE_MAX
 };
 
@@ -492,6 +493,26 @@ struct hclge_mac_stats {
 
 #define HCLGE_STATS_TIMER_INTERVAL     300UL
 
+/* fec stats ,opcode id: 0x0316 */
+#define HCLGE_FEC_STATS_MAX_LANES      8
+struct hclge_fec_stats {
+       /* fec rs mode total stats */
+       u64 rs_corr_blocks;
+       u64 rs_uncorr_blocks;
+       u64 rs_error_blocks;
+       /* fec base-r mode per lanes stats */
+       u64 base_r_lane_num;
+       u64 base_r_corr_blocks;
+       u64 base_r_uncorr_blocks;
+       union {
+               struct {
+                       u64 base_r_corr_per_lanes[HCLGE_FEC_STATS_MAX_LANES];
+                       u64 base_r_uncorr_per_lanes[HCLGE_FEC_STATS_MAX_LANES];
+               };
+               u64 per_lanes[HCLGE_FEC_STATS_MAX_LANES * 2];
+       };
+};
+
 struct hclge_vlan_type_cfg {
        u16 rx_ot_fst_vlan_type;
        u16 rx_ot_sec_vlan_type;
@@ -830,6 +851,7 @@ struct hclge_dev {
        struct hclge_hw hw;
        struct hclge_misc_vector misc_vector;
        struct hclge_mac_stats mac_stats;
+       struct hclge_fec_stats fec_stats;
        unsigned long state;
        unsigned long flr_state;
        unsigned long last_reset_time;