Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753085AbdFQR1B (ORCPT ); Sat, 17 Jun 2017 13:27:01 -0400 Received: from szxga01-in.huawei.com ([45.249.212.187]:8747 "EHLO szxga01-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752577AbdFQRZq (ORCPT ); Sat, 17 Jun 2017 13:25:46 -0400 From: Salil Mehta To: CC: , , , , , , , Subject: [PATCH V3 net-next 7/8] net: hns3: Add Ethtool support to HNS3 driver Date: Sat, 17 Jun 2017 18:24:30 +0100 Message-ID: <20170617172431.177044-8-salil.mehta@huawei.com> X-Mailer: git-send-email 2.8.3 In-Reply-To: <20170617172431.177044-1-salil.mehta@huawei.com> References: <20170617172431.177044-1-salil.mehta@huawei.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.203.181.160] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A0B0206.59456616.00C6,ss=1,re=0.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: 6f62d636c12bf8a0c1ec24f05b99b2d3 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 25942 Lines: 907 This patch adds the support of the Ethtool interface to the HNS3 Ethernet driver. Various commands to read the statistics, configure the offloading, loopback selftest etc. are supported. Signed-off-by: Daode Huang Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: Yisen Zhuang --- Patch V3: Address below comments: 1. Stephen Hemminger: https://lkml.org/lkml/2017/6/13/974 2. Andrew Lunn: https://lkml.org/lkml/2017/6/13/1037 Patch V2: No change Patch V1: Initial Submit --- .../ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c | 878 +++++++++++++++++++++ 1 file changed, 878 insertions(+) create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c new file mode 100644 index 0000000..9cc7712 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c @@ -0,0 +1,878 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include "hns3_enet.h" + +struct hns3_stats { + char stats_string[ETH_GSTRING_LEN]; + int stats_size; + int stats_offset; +}; + +/* netdev related stats */ +#define HNS3_NETDEV_STAT(_string, _member) \ + { _string, \ + FIELD_SIZEOF(struct rtnl_link_stats64, _member), \ + offsetof(struct rtnl_link_stats64, _member), \ + } + +static const struct hns3_stats hns3_netdev_stats[] = { + /* misc. Rx/Tx statistics */ + HNS3_NETDEV_STAT("rx_packets", rx_packets), + HNS3_NETDEV_STAT("tx_packets", tx_packets), + HNS3_NETDEV_STAT("rx_bytes", rx_bytes), + HNS3_NETDEV_STAT("tx_bytes", tx_bytes), + HNS3_NETDEV_STAT("rx_errors", rx_errors), + HNS3_NETDEV_STAT("tx_errors", tx_errors), + HNS3_NETDEV_STAT("rx_dropped", rx_dropped), + HNS3_NETDEV_STAT("tx_dropped", tx_dropped), + HNS3_NETDEV_STAT("multicast", multicast), + HNS3_NETDEV_STAT("collisions", collisions), + + /* detailed Rx errors */ + HNS3_NETDEV_STAT("rx_length_errors", rx_length_errors), + HNS3_NETDEV_STAT("rx_over_errors", rx_over_errors), + HNS3_NETDEV_STAT("rx_crc_errors", rx_crc_errors), + HNS3_NETDEV_STAT("rx_frame_errors", rx_frame_errors), + HNS3_NETDEV_STAT("rx_fifo_errors", rx_fifo_errors), + HNS3_NETDEV_STAT("rx_missed_errors", rx_missed_errors), + + /* detailed Tx errors */ + HNS3_NETDEV_STAT("tx_aborted_errors", tx_aborted_errors), + HNS3_NETDEV_STAT("tx_carrier_errors", tx_carrier_errors), + HNS3_NETDEV_STAT("tx_fifo_errors", tx_fifo_errors), + HNS3_NETDEV_STAT("tx_heartbeat_errors", tx_heartbeat_errors), + HNS3_NETDEV_STAT("tx_window_errors", tx_window_errors), + + /* for cslip etc */ + HNS3_NETDEV_STAT("rx_compressed", rx_compressed), + HNS3_NETDEV_STAT("tx_compressed", tx_compressed), +}; + +#define HNS3_NETDEV_STATS_COUNT ARRAY_SIZE(hns3_netdev_stats) + +/* tqp related stats */ +#define HNS3_TQP_STAT(_string, _member) \ + { _string, \ + FIELD_SIZEOF(struct ring_stats, _member), \ + offsetof(struct hns3_enet_ring, stats), \ + } + +static const struct hns3_stats hns3_txq_stats[] = { + /* Tx per-queue statistics */ + HNS3_TQP_STAT("tx_io_err_cnt", io_err_cnt), + HNS3_TQP_STAT("tx_sw_err_cnt", sw_err_cnt), + HNS3_TQP_STAT("tx_seg_pkt_cnt", seg_pkt_cnt), + HNS3_TQP_STAT("tx_pkts", tx_pkts), + HNS3_TQP_STAT("tx_bytes", tx_bytes), + HNS3_TQP_STAT("tx_err_cnt", tx_err_cnt), + HNS3_TQP_STAT("tx_restart_queue", restart_queue), + HNS3_TQP_STAT("tx_busy", tx_busy), +}; + +#define HNS3_TXQ_STATS_COUNT ARRAY_SIZE(hns3_txq_stats) + +static const struct hns3_stats hns3_rxq_stats[] = { + /* Rx per-queue statistics */ + HNS3_TQP_STAT("rx_io_err_cnt", io_err_cnt), + HNS3_TQP_STAT("rx_sw_err_cnt", sw_err_cnt), + HNS3_TQP_STAT("rx_seg_pkt_cnt", seg_pkt_cnt), + HNS3_TQP_STAT("rx_pkts", rx_pkts), + HNS3_TQP_STAT("rx_bytes", rx_bytes), + HNS3_TQP_STAT("rx_err_cnt", rx_err_cnt), + HNS3_TQP_STAT("rx_reuse_pg_cnt", reuse_pg_cnt), + HNS3_TQP_STAT("rx_err_pkt_len", err_pkt_len), + HNS3_TQP_STAT("rx_non_vld_descs", non_vld_descs), + HNS3_TQP_STAT("rx_err_bd_num", err_bd_num), + HNS3_TQP_STAT("rx_l2_err", l2_err), + HNS3_TQP_STAT("rx_l3l4_csum_err", l3l4_csum_err), +}; + +#define HNS3_RXQ_STATS_COUNT ARRAY_SIZE(hns3_rxq_stats) + +#define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT) + +struct hns3_link_mode_mapping { + u32 hns3_link_mode; + u32 ethtool_link_mode; +}; + +static const struct hns3_link_mode_mapping hns3_lm_map[] = { + {HNS3_LM_FIBRE_BIT, ETHTOOL_LINK_MODE_FIBRE_BIT}, + {HNS3_LM_AUTONEG_BIT, ETHTOOL_LINK_MODE_Autoneg_BIT}, + {HNS3_LM_TP_BIT, ETHTOOL_LINK_MODE_TP_BIT}, + {HNS3_LM_PAUSE_BIT, ETHTOOL_LINK_MODE_Pause_BIT}, + {HNS3_LM_BACKPLANE_BIT, ETHTOOL_LINK_MODE_Backplane_BIT}, + {HNS3_LM_10BASET_HALF_BIT, ETHTOOL_LINK_MODE_10baseT_Half_BIT}, + {HNS3_LM_10BASET_FULL_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT}, + {HNS3_LM_100BASET_HALF_BIT, ETHTOOL_LINK_MODE_100baseT_Half_BIT}, + {HNS3_LM_100BASET_FULL_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT}, + {HNS3_LM_1000BASET_FULL_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT}, +}; + +#define HNS3_DRV_TO_ETHTOOL_CAPS(caps, lk_ksettings, name) \ +{ \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(hns3_lm_map); i++) { \ + if ((caps) & hns3_lm_map[i].hns3_link_mode) \ + __set_bit(hns3_lm_map[i].ethtool_link_mode,\ + (lk_ksettings)->link_modes.name); \ + } \ +} + +static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop) +{ + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_ae_ops *ops = h->ae_algo->ops; + int ret = 0; + + switch (loop) { + case HNAE3_MAC_INTER_LOOP_MAC: + if (ops->set_loopback) + ret = ops->set_loopback(h, loop, true); + break; + case HNAE3_MAC_LOOP_NONE: + if (ops->set_loopback) + ret = ops->set_loopback(h, + HNAE3_MAC_INTER_LOOP_MAC, false); + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + if (loop == HNAE3_MAC_LOOP_NONE) + ops->set_promisc_mode(h, ndev->flags & IFF_PROMISC); + else + ops->set_promisc_mode(h, 1); + } + return ret; +} + +static int hns3_lp_up(struct net_device *ndev, enum hnae3_loop loop_mode) +{ + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_ae_ops *ops = h->ae_algo->ops; + int ret; + + if (ops->start) { + ret = ops->start(h); + if (ret) { + netdev_err(ndev, + "error: hns3_lb_up start ops return error:%d\n", + ret); + return ret; + } + } else { + netdev_err(ndev, "error: hns3_lb_up ops do NOT have start\n"); + } + + ret = hns3_lp_setup(ndev, loop_mode); + if (ret) + return ret; + + msleep(200); + + return 0; +} + +static int hns3_lp_down(struct net_device *ndev) +{ + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + int ret; + + ret = hns3_lp_setup(ndev, HNAE3_MAC_LOOP_NONE); + if (ret) + netdev_err(ndev, "lb_setup return error(%d)!\n", + ret); + + if (h->ae_algo->ops->stop) + h->ae_algo->ops->stop(h); + + usleep_range(10000, 20000); + + return 0; +} + +static void hns3_lp_setup_skb(struct sk_buff *skb) +{ + struct hns3_nic_priv *priv; + unsigned int frame_size; + struct net_device *ndev; + + ndev = skb->dev; + priv = netdev_priv(ndev); + frame_size = skb->len; + memset(skb->data, 0xFF, frame_size); + ether_addr_copy(skb->data, ndev->dev_addr); + skb->data[5] += 0x1f; + frame_size &= ~1ul; + memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); + memset(&skb->data[frame_size / 2 + 10], 0xBE, frame_size / 2 - 11); + memset(&skb->data[frame_size / 2 + 12], 0xAF, frame_size / 2 - 13); +} + +static bool hns3_lb_check_skb_data(struct sk_buff *skb) +{ + unsigned int frame_size; + char buff[33]; /* 32B data and the last character '\0' */ + int check_ok; + u32 i; + + frame_size = skb->len; + frame_size &= ~1ul; + check_ok = 0; + if (*(skb->data + 10) == 0xFF) { /* for rx check frame*/ + if ((*(skb->data + frame_size / 2 + 10) == 0xBE) && + (*(skb->data + frame_size / 2 + 12) == 0xAF)) + check_ok = 1; + } + + if (!check_ok) { + for (i = 0; i < skb->len; i++) { + snprintf(buff + i % 16 * 2, 3, /* tailing \0*/ + "%02x", *(skb->data + i)); + if ((i % 16 == 15) || (i == skb->len - 1)) + pr_info("%s\n", buff); + } + return false; + } + return true; +} + +static u32 hns3_lb_check_rx_ring(struct hns3_nic_priv *priv, + u32 start_ringid, + u32 end_ringid, + u32 budget) +{ + struct hns3_enet_ring *ring; + struct sk_buff *skb = NULL; + u32 rcv_good_pkt_cnt = 0; + int status; + u32 i; + + struct net_device *ndev = priv->netdev; + unsigned long rx_packets = ndev->stats.rx_packets; + unsigned long rx_bytes = ndev->stats.rx_bytes; + unsigned long rx_frame_errors = ndev->stats.rx_frame_errors; + + for (i = start_ringid; i <= end_ringid; i++) { + skb = NULL; + ring = priv->ring_data[i].ring; + status = hns3_clean_rx_ring_ex(ring, &skb, budget); + if (status > 0 && skb) { + if (hns3_lb_check_skb_data(skb)) + rcv_good_pkt_cnt += 1; + + dev_kfree_skb_any(skb); + } + } + ndev->stats.rx_packets = rx_packets; + ndev->stats.rx_bytes = rx_bytes; + ndev->stats.rx_frame_errors = rx_frame_errors; + return rcv_good_pkt_cnt; +} + +static void hns3_lb_clear_tx_ring(struct hns3_nic_priv *priv, + u32 start_ringid, + u32 end_ringid, + u32 budget) +{ + struct net_device *ndev = priv->netdev; + unsigned long rx_frame_errors = ndev->stats.rx_frame_errors; + unsigned long rx_packets = ndev->stats.rx_packets; + unsigned long rx_bytes = ndev->stats.rx_bytes; + struct netdev_queue *dev_queue; + struct hns3_enet_ring *ring; + int status; + u32 i; + + for (i = start_ringid; i <= end_ringid; i++) { + ring = priv->ring_data[i].ring; + status = hns3_clean_tx_ring(ring, budget); + if (status) + dev_err(priv->dev, + "hns3_clean_tx_ring failed ,status:%d\n", + status); + + dev_queue = netdev_get_tx_queue(ndev, + priv->ring_data[i].queue_index); + netdev_tx_reset_queue(dev_queue); + } + + ndev->stats.rx_packets = rx_packets; + ndev->stats.rx_bytes = rx_bytes; + ndev->stats.rx_frame_errors = rx_frame_errors; +} + +/* hns3_lp_run_test - run loopback test + * @ndev: net device + * @mode: loopback type + */ +static int hns3_lp_run_test(struct net_device *ndev, enum hnae3_loop mode) +{ +#define HNS3_NIC_LB_TEST_PKT_NUM 1 +#define HNS3_NIC_LB_TEST_RING_ID 0 +#define HNS3_NIC_LB_TEST_FRAME_SIZE 128 +/* Nic loopback test err */ +#define HNS3_NIC_LB_TEST_NO_MEM_ERR 1 +#define HNS3_NIC_LB_TEST_TX_CNT_ERR 2 +#define HNS3_NIC_LB_TEST_RX_CNT_ERR 3 +#define HNS3_NIC_LB_TEST_RX_PKG_ERR 4 + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_knic_private_info *kinfo = &h->kinfo; + int i, j, lc, good_cnt; + netdev_tx_t tx_ret_val; + struct sk_buff *skb; + unsigned int size; + int ret_val = 0; + + size = HNS3_NIC_LB_TEST_FRAME_SIZE; + /* Allocate test skb */ + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return HNS3_NIC_LB_TEST_NO_MEM_ERR; + + /* Place data into test skb */ + (void)skb_put(skb, size); + skb->dev = ndev; + hns3_lp_setup_skb(skb); + skb->queue_mapping = HNS3_NIC_LB_TEST_RING_ID; + lc = 1; + for (j = 0; j < lc; j++) { + /* Reset count of good packets */ + good_cnt = 0; + /* Place 64 packets on the transmit queue */ + for (i = 0; i < HNS3_NIC_LB_TEST_PKT_NUM; i++) { + (void)skb_get(skb); + tx_ret_val = (netdev_tx_t)hns3_nic_net_xmit_hw( + ndev, skb, + &tx_ring_data(priv, + skb->queue_mapping)); + if (tx_ret_val == NETDEV_TX_OK) { + good_cnt++; + } else { + dev_err(priv->dev, + "hns3_lb_run_test hns3_nic_net_xmit_hw FAILED %d\n", + tx_ret_val); + break; + } + } + if (good_cnt != HNS3_NIC_LB_TEST_PKT_NUM) { + ret_val = HNS3_NIC_LB_TEST_TX_CNT_ERR; + dev_err(priv->dev, "mode %d sent fail, cnt=0x%x, budget=0x%x\n", + mode, good_cnt, + HNS3_NIC_LB_TEST_PKT_NUM); + break; + } + + /* Allow 100 milliseconds for packets to go from Tx to Rx */ + msleep(100); + + good_cnt = hns3_lb_check_rx_ring(priv, + kinfo->num_tqps, + kinfo->num_tqps * 2 - 1, + HNS3_NIC_LB_TEST_PKT_NUM); + if (good_cnt != HNS3_NIC_LB_TEST_PKT_NUM) { + ret_val = HNS3_NIC_LB_TEST_RX_CNT_ERR; + dev_err(priv->dev, "mode %d recv fail, cnt=0x%x, budget=0x%x\n", + mode, good_cnt, + HNS3_NIC_LB_TEST_PKT_NUM); + break; + } + hns3_lb_clear_tx_ring(priv, + HNS3_NIC_LB_TEST_RING_ID, + HNS3_NIC_LB_TEST_RING_ID, + HNS3_NIC_LB_TEST_PKT_NUM); + } + + /* Free the original skb */ + kfree_skb(skb); + + return ret_val; +} + +/* hns_nic_self_test - self test + * @ndev: net device + * @eth_test: test cmd + * @data: test result + */ +static void hns3_self_test(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ +#define HNS3_SELF_TEST_TPYE_NUM 3 + + struct hns3_nic_priv *priv = netdev_priv(ndev); + struct hnae3_handle *h = priv->ae_handle; + int st_param[HNS3_SELF_TEST_TPYE_NUM][2]; + bool if_running = netif_running(ndev); + int test_index = 0; + int i; + + st_param[0][0] = HNAE3_MAC_INTER_LOOP_MAC; /* XGE not supported lb */ + st_param[0][1] = h->flags & HNAE3_SUPPORT_MAC_LOOPBACK; + st_param[1][0] = HNAE3_MAC_INTER_LOOP_SERDES; + st_param[1][1] = h->flags & HNAE3_SUPPORT_SERDES_LOOPBACK; + st_param[2][0] = HNAE3_MAC_INTER_LOOP_PHY; + st_param[2][1] = h->flags & HNAE3_SUPPORT_PHY_LOOPBACK; + + if (eth_test->flags == ETH_TEST_FL_OFFLINE) { + set_bit(HNS3_NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_close(ndev); + + for (i = 0; i < HNS3_SELF_TEST_TPYE_NUM; i++) { + if (!st_param[i][1]) + continue; /* NEXT testing */ + + data[test_index] = hns3_lp_up(ndev, + (enum hnae3_loop)st_param[i][0]); + if (!data[test_index]) { + data[test_index] = hns3_lp_run_test( + ndev, + (enum hnae3_loop)st_param[i][0]); + (void)hns3_lp_down(ndev); + } + + if (data[test_index]) + eth_test->flags |= ETH_TEST_FL_FAILED; + + test_index++; + } + + clear_bit(HNS3_NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_open(ndev); + } + (void)msleep_interruptible(4 * 1000); +} + +static int hns3_get_sset_count(struct net_device *netdev, int stringset) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_ae_ops *ops = h->ae_algo->ops; + + if (!ops->get_sset_count) { + netdev_err(netdev, "could not get string set count\n"); + return -EOPNOTSUPP; + } + + switch (stringset) { + case ETH_SS_STATS: + return (HNS3_NETDEV_STATS_COUNT + + (HNS3_TQP_STATS_COUNT * h->kinfo.num_tqps) + + ops->get_sset_count(h, stringset)); + + case ETH_SS_TEST: + return ops->get_sset_count(h, stringset); + } + + return 0; +} + +static u8 *hns3_get_strings_netdev(u8 *data) +{ + int i; + + for (i = 0; i < HNS3_NETDEV_STATS_COUNT; i++) { + memcpy(data, hns3_netdev_stats[i].stats_string, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + return data; +} + +static u8 *hns3_get_strings_tqps(struct hnae3_handle *handle, u8 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + int i, j; + + /* get strings for Tx */ + for (i = 0; i < kinfo->num_tqps; i++) { + for (j = 0; j < HNS3_TXQ_STATS_COUNT; j++) { + u8 gstr[ETH_GSTRING_LEN]; + + sprintf(gstr, "rcb_q%d_", i); + strcat(gstr, hns3_txq_stats[j].stats_string); + + memcpy(data, gstr, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + } + + /* get strings for Rx */ + for (i = 0; i < kinfo->num_tqps; i++) { + for (j = 0; j < HNS3_RXQ_STATS_COUNT; j++) { + u8 gstr[ETH_GSTRING_LEN]; + + sprintf(gstr, "rcb_q%d_", i); + strcat(gstr, hns3_rxq_stats[j].stats_string); + + memcpy(data, gstr, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + } + + return data; +} + +static void hns3_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_ae_ops *ops = h->ae_algo->ops; + char *buff = (char *)data; + + if (!ops->get_strings) { + netdev_err(netdev, "could not get strings!\n"); + return; + } + + switch (stringset) { + case ETH_SS_STATS: + buff = hns3_get_strings_netdev(buff); + buff = hns3_get_strings_tqps(h, buff); + h->ae_algo->ops->get_strings(h, stringset, (u8 *)buff); + break; + case ETH_SS_TEST: + ops->get_strings(h, stringset, data); + break; + } +} + +static u64 *hns3_get_stats_netdev(struct net_device *netdev, u64 *data) +{ + const struct rtnl_link_stats64 *net_stats; + struct rtnl_link_stats64 temp; + u8 *stat; + int i; + + net_stats = dev_get_stats(netdev, &temp); + + for (i = 0; i < HNS3_NETDEV_STATS_COUNT; i++) { + stat = (u8 *)net_stats + hns3_netdev_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + + return data; +} + +static u64 *hns3_get_stats_tqps(struct hnae3_handle *handle, u64 *data) +{ + struct hns3_nic_priv *nic_priv = (struct hns3_nic_priv *)handle->priv; + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hns3_enet_ring *ring; + u8 *stat; + int i; + + /* get stats for Tx */ + for (i = 0; i < kinfo->num_tqps; i++) { + ring = nic_priv->ring_data[i].ring; + for (i = 0; i < HNS3_TXQ_STATS_COUNT; i++) { + stat = (u8 *)ring + hns3_txq_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + } + + /* get stats for Rx */ + for (i = 0; i < kinfo->num_tqps; i++) { + ring = nic_priv->ring_data[i + kinfo->num_tqps].ring; + for (i = 0; i < HNS3_RXQ_STATS_COUNT; i++) { + stat = (u8 *)ring + hns3_rxq_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + } + + return data; +} + +/* hns3_get_stats - get detail statistics. + * @netdev: net device + * @stats: statistics info. + * @data: statistics data. + */ +void hns3_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u64 *p = data; + + if (!h->ae_algo->ops->get_stats || !h->ae_algo->ops->update_stats) { + netdev_err(netdev, "could not get any statistics\n"); + return; + } + + h->ae_algo->ops->update_stats(h, &netdev->stats); + + /* get netdev related stats */ + p = hns3_get_stats_netdev(netdev, p); + + /* get per-queue stats */ + p = hns3_get_stats_tqps(h, p); + + /* get MAC & other misc hardware stats */ + h->ae_algo->ops->get_stats(h, p); +} + +static void hns3_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *drvinfo) +{ + struct hns3_nic_priv *priv = netdev_priv(net_dev); + struct hnae3_handle *h = priv->ae_handle; + + strncpy(drvinfo->version, HNAE_DRIVER_VERSION, + sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; + + strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strncpy(drvinfo->bus_info, priv->dev->bus->name, + sizeof(drvinfo->bus_info)); + drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "0x%08x", + priv->ae_handle->ae_algo->ops->get_fw_version(h)); +} + +static u32 hns3_get_link(struct net_device *net_dev) +{ + struct hns3_nic_priv *priv = netdev_priv(net_dev); + struct hnae3_handle *h; + + h = priv->ae_handle; + + if (h->ae_algo && h->ae_algo->ops && h->ae_algo->ops->get_status) + return h->ae_algo->ops->get_status(h); + else + return 0; +} + +static void hns3_get_ringparam(struct net_device *net_dev, + struct ethtool_ringparam *param) +{ + struct hns3_nic_priv *priv = netdev_priv(net_dev); + int queue_num = priv->ae_handle->kinfo.num_tqps; + + param->tx_max_pending = HNS3_RING_MAX_PENDING; + param->rx_max_pending = HNS3_RING_MAX_PENDING; + + param->tx_pending = priv->ring_data[0].ring->desc_num; + param->rx_pending = priv->ring_data[queue_num].ring->desc_num; +} + +static void hns3_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns3_nic_priv *priv = netdev_priv(net_dev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo && h->ae_algo->ops && h->ae_algo->ops->get_pauseparam) + h->ae_algo->ops->get_pauseparam(h, ¶m->autoneg, + ¶m->rx_pause, ¶m->tx_pause); +} + +static int hns3_get_link_ksettings(struct net_device *net_dev, + struct ethtool_link_ksettings *cmd) +{ + struct hns3_nic_priv *priv = netdev_priv(net_dev); + struct hnae3_handle *h = priv->ae_handle; + u32 supported_caps; + u32 advertised_caps; + u8 media_type; + u8 link_stat; + u8 auto_neg; + u8 duplex; + u32 speed; + + if (!h->ae_algo || !h->ae_algo->ops) + return -ESRCH; + + /* 1.auto_neg&speed&duplex from cmd */ + if (h->ae_algo->ops->get_ksettings_an_result) { + h->ae_algo->ops->get_ksettings_an_result(h, &auto_neg, + &speed, &duplex); + cmd->base.autoneg = auto_neg; + cmd->base.speed = speed; + cmd->base.duplex = duplex; + + link_stat = hns3_get_link(net_dev); + if (!link_stat) { + cmd->base.speed = (u32)SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + } + + /* 2.media_type get from bios parameter block */ + if (h->ae_algo->ops->get_media_type) + h->ae_algo->ops->get_media_type(h, &media_type); + + switch (media_type) { + case HNAE3_MEDIA_TYPE_FIBER: + cmd->base.port = PORT_FIBRE; + supported_caps = HNS3_LM_FIBRE_BIT | HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | HNS3_LM_1000BASET_FULL_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_COPPER: + cmd->base.port = PORT_TP; + supported_caps = HNS3_LM_TP_BIT | HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | HNS3_LM_10BASET_HALF_BIT; + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_BACKPLANE: + cmd->base.port = PORT_NONE; + supported_caps = HNS3_LM_BACKPLANE_BIT | HNS3_LM_PAUSE_BIT | + HNS3_LM_AUTONEG_BIT | HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | HNS3_LM_10BASET_HALF_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_UNKNOWN: + default: + cmd->base.port = PORT_OTHER; + supported_caps = 0; + advertised_caps = 0; + break; + } + + ethtool_link_ksettings_zero_link_mode(cmd, supported); + HNS3_DRV_TO_ETHTOOL_CAPS(supported_caps, cmd, supported) + + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + HNS3_DRV_TO_ETHTOOL_CAPS(advertised_caps, cmd, advertising) + + /* 3.mdix_ctrl&mdix get from phy reg */ + if (h->ae_algo->ops->get_mdix_mode) + h->ae_algo->ops->get_mdix_mode(h, &cmd->base.eth_tp_mdix_ctrl, + &cmd->base.eth_tp_mdix); + /* 4.mdio_support */ + cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; + + return 0; +} + +static u32 hns3_get_rss_key_size(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || + !h->ae_algo->ops->get_rss_key_size) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss_key_size(h); +} + +static u32 hns3_get_rss_indir_size(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || + !h->ae_algo->ops->get_rss_indir_size) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss_indir_size(h); +} + +static int hns3_get_rss(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->get_rss) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss(h, indir, key, hfunc); +} + +static int hns3_set_rss(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->set_rss) + return -EOPNOTSUPP; + + /* currently we only support Toeplitz hash */ + if ((hfunc != ETH_RSS_HASH_NO_CHANGE) && (hfunc != ETH_RSS_HASH_TOP)) { + netdev_err(netdev, + "hash func not supported (only Toeplitz hash)\n"); + return -EOPNOTSUPP; + } + if (!indir) { + netdev_err(netdev, + "set rss failed for indir is empty\n"); + return -EOPNOTSUPP; + } + + return h->ae_algo->ops->set_rss(h, indir, key, hfunc); +} + +static int hns3_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->get_tc_size) + return -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = h->ae_algo->ops->get_tc_size(h); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct ethtool_ops hns3_ethtool_ops = { + .get_drvinfo = hns3_get_drvinfo, + .get_link = hns3_get_link, + .get_ringparam = hns3_get_ringparam, + .get_pauseparam = hns3_get_pauseparam, + .self_test = hns3_self_test, + .get_strings = hns3_get_strings, + .get_ethtool_stats = hns3_get_stats, + .get_sset_count = hns3_get_sset_count, + .get_rxnfc = hns3_get_rxnfc, + .get_rxfh_key_size = hns3_get_rss_key_size, + .get_rxfh_indir_size = hns3_get_rss_indir_size, + .get_rxfh = hns3_get_rss, + .set_rxfh = hns3_set_rss, + .get_link_ksettings = hns3_get_link_ksettings, +}; + +void hns3_ethtool_set_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &hns3_ethtool_ops; +} -- 2.7.4