ice: Add stats and ethtool support
authorAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tue, 20 Mar 2018 14:58:16 +0000 (07:58 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Mon, 26 Mar 2018 19:11:19 +0000 (12:11 -0700)
This patch implements a watchdog task to get packet statistics from
the device.

This patch also adds support for the following ethtool operations:

ethtool devname
ethtool -s devname [msglvl N] [msglevel type on|off]
ethtool -g|--show-ring devname
ethtool -G|--set-ring devname [rx N] [tx N]
ethtool -i|--driver devname
ethtool -d|--register-dump devname [raw on|off] [hex on|off] [file name]
ethtool -k|--show-features|--show-offload devname
ethtool -K|--features|--offload devname feature on|off
ethtool -P|--show-permaddr devname
ethtool -S|--statistics devname
ethtool -a|--show-pause devname
ethtool -A|--pause devname [autoneg on|off] [rx on|off] [tx on|off]
ethtool -r|--negotiate devname

CC: Andrew Lunn <andrew@lunn.ch>
CC: Jakub Kicinski <kubakici@wp.pl>
CC: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ice/Makefile
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_common.c
drivers/net/ethernet/intel/ice/ice_common.h
drivers/net/ethernet/intel/ice/ice_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_hw_autogen.h
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_type.h

index de82fc8..4058673 100644 (file)
@@ -13,4 +13,5 @@ ice-y := ice_main.o   \
         ice_nvm.o      \
         ice_switch.o   \
         ice_sched.o    \
-        ice_txrx.o
+        ice_txrx.o     \
+        ice_ethtool.o
index 51cc955..66d0976 100644 (file)
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/cpumask.h>
+#include <linux/rtnetlink.h>
 #include <linux/if_vlan.h>
 #include <linux/dma-mapping.h>
 #include <linux/pci.h>
 #include <linux/workqueue.h>
 #include <linux/aer.h>
 #include <linux/interrupt.h>
+#include <linux/ethtool.h>
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include <linux/bitmap.h>
 #include "ice_common.h"
 #include "ice_sched.h"
 
+extern const char ice_drv_ver[];
 #define ICE_BAR0               0
 #define ICE_DFLT_NUM_DESC      128
+#define ICE_MIN_NUM_DESC       8
+#define ICE_MAX_NUM_DESC       8160
 #define ICE_REQ_DESC_MULTIPLE  32
 #define ICE_INT_NAME_STR_LEN   (IFNAMSIZ + 16)
+#define ICE_ETHTOOL_FWVER_LEN  32
 #define ICE_AQ_LEN             64
 #define ICE_MIN_MSIX           2
 #define ICE_NO_VSI             0xffff
@@ -56,6 +62,8 @@
 #define ICE_RES_MISC_VEC_ID    (ICE_RES_VALID_BIT - 1)
 #define ICE_INVAL_Q_INDEX      0xffff
 
+#define ICE_VSIQF_HKEY_ARRAY_SIZE      ((VSIQF_HKEY_MAX_INDEX + 1) *   4)
+
 #define ICE_DFLT_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
 
 #define ICE_MAX_MTU    (ICE_AQ_SET_MAC_FRAME_SIZE_MAX - \
@@ -102,6 +110,7 @@ enum ice_state {
        __ICE_DOWN,
        __ICE_PFR_REQ,                  /* set by driver and peers */
        __ICE_ADMINQ_EVENT_PENDING,
+       __ICE_CFG_BUSY,
        __ICE_SERVICE_SCHED,
        __ICE_STATE_NBITS               /* must be last */
 };
@@ -118,8 +127,13 @@ struct ice_vsi {
 
        irqreturn_t (*irq_handler)(int irq, void *data);
 
+       u64 tx_linearize;
        DECLARE_BITMAP(state, __ICE_STATE_NBITS);
        unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+       u32 tx_restart;
+       u32 tx_busy;
+       u32 rx_buf_failed;
+       u32 rx_page_failed;
        int num_q_vectors;
        int base_vector;
        enum ice_vsi_type type;
@@ -141,8 +155,14 @@ struct ice_vsi {
 
        struct ice_aqc_vsi_props info;   /* VSI properties */
 
+       /* VSI stats */
+       struct rtnl_link_stats64 net_stats;
+       struct ice_eth_stats eth_stats;
+       struct ice_eth_stats eth_stats_prev;
+
        bool irqs_ready;
        bool current_isup;               /* Sync 'link up' logging */
+       bool stat_offsets_loaded;
 
        /* queue information */
        u8 tx_mapping_mode;              /* ICE_MAP_MODE_[CONTIG|SCATTER] */
@@ -205,8 +225,10 @@ struct ice_pf {
        u16 q_left_rx;          /* remaining num rx queues left unclaimed */
        u16 next_vsi;           /* Next free slot in pf->vsi[] - 0-based! */
        u16 num_alloc_vsi;
-
+       struct ice_hw_port_stats stats;
+       struct ice_hw_port_stats stats_prev;
        struct ice_hw hw;
+       bool stat_prev_loaded;  /* has previous stats been loaded */
        char int_name[ICE_INT_NAME_STR_LEN];
 };
 
@@ -239,8 +261,12 @@ static inline void ice_irq_dynamic_ena(struct ice_hw *hw, struct ice_vsi *vsi,
        wr32(hw, GLINT_DYN_CTL(vector), val);
 }
 
+void ice_set_ethtool_ops(struct net_device *netdev);
+int ice_up(struct ice_vsi *vsi);
+int ice_down(struct ice_vsi *vsi);
 int ice_set_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
 int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
 void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size);
+void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
 
 #endif /* _ICE_H_ */
index 051427d..ae7376f 100644 (file)
@@ -859,6 +859,45 @@ struct ice_aqc_get_phy_caps_data {
        } qual_modules[ICE_AQC_QUAL_MOD_COUNT_MAX];
 };
 
+/* Set PHY capabilities (direct 0x0601)
+ * NOTE: This command must be followed by setup link and restart auto-neg
+ */
+struct ice_aqc_set_phy_cfg {
+       u8 lport_num;
+       u8 reserved[7];
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+/* Set PHY config command data structure */
+struct ice_aqc_set_phy_cfg_data {
+       __le64 phy_type_low; /* Use values from ICE_PHY_TYPE_LOW_* */
+       __le64 rsvd0;
+       u8 caps;
+#define ICE_AQ_PHY_ENA_TX_PAUSE_ABILITY                BIT(0)
+#define ICE_AQ_PHY_ENA_RX_PAUSE_ABILITY                BIT(1)
+#define ICE_AQ_PHY_ENA_LOW_POWER               BIT(2)
+#define ICE_AQ_PHY_ENA_LINK                    BIT(3)
+#define ICE_AQ_PHY_ENA_ATOMIC_LINK             BIT(5)
+       u8 low_power_ctrl;
+       __le16 eee_cap; /* Value from ice_aqc_get_phy_caps */
+       __le16 eeer_value;
+       u8 link_fec_opt; /* Use defines from ice_aqc_get_phy_caps */
+       u8 rsvd1;
+};
+
+/* Restart AN command data structure (direct 0x0605)
+ * Also used for response, with only the lport_num field present.
+ */
+struct ice_aqc_restart_an {
+       u8 lport_num;
+       u8 reserved;
+       u8 cmd_flags;
+#define ICE_AQC_RESTART_AN_LINK_RESTART        BIT(1)
+#define ICE_AQC_RESTART_AN_LINK_ENABLE BIT(2)
+       u8 reserved2[13];
+};
+
 /* Get link status (indirect 0x0607), also used for Link Status Event */
 struct ice_aqc_get_link_status {
        u8 lport_num;
@@ -1137,6 +1176,8 @@ struct ice_aq_desc {
                struct ice_aqc_clear_pxe clear_pxe;
                struct ice_aqc_list_caps get_cap;
                struct ice_aqc_get_phy_caps get_phy;
+               struct ice_aqc_set_phy_cfg set_phy;
+               struct ice_aqc_restart_an restart_an;
                struct ice_aqc_get_sw_cfg get_sw_conf;
                struct ice_aqc_sw_rules sw_rules;
                struct ice_aqc_get_topo get_topo;
@@ -1222,6 +1263,8 @@ enum ice_adminq_opc {
 
        /* PHY commands */
        ice_aqc_opc_get_phy_caps                        = 0x0600,
+       ice_aqc_opc_set_phy_cfg                         = 0x0601,
+       ice_aqc_opc_restart_an                          = 0x0605,
        ice_aqc_opc_get_link_status                     = 0x0607,
 
        /* NVM commands */
index b4e8343..02d5892 100644 (file)
@@ -1261,6 +1261,201 @@ void ice_clear_pxe_mode(struct ice_hw *hw)
                ice_aq_clear_pxe_mode(hw);
 }
 
+/**
+ * ice_aq_set_phy_cfg
+ * @hw: pointer to the hw struct
+ * @lport: logical port number
+ * @cfg: structure with PHY configuration data to be set
+ * @cd: pointer to command details structure or NULL
+ *
+ * Set the various PHY configuration parameters supported on the Port.
+ * One or more of the Set PHY config parameters may be ignored in an MFP
+ * mode as the PF may not have the privilege to set some of the PHY Config
+ * parameters. This status will be indicated by the command response (0x0601).
+ */
+static enum ice_status
+ice_aq_set_phy_cfg(struct ice_hw *hw, u8 lport,
+                  struct ice_aqc_set_phy_cfg_data *cfg, struct ice_sq_cd *cd)
+{
+       struct ice_aqc_set_phy_cfg *cmd;
+       struct ice_aq_desc desc;
+
+       if (!cfg)
+               return ICE_ERR_PARAM;
+
+       cmd = &desc.params.set_phy;
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_phy_cfg);
+       cmd->lport_num = lport;
+
+       return ice_aq_send_cmd(hw, &desc, cfg, sizeof(*cfg), cd);
+}
+
+/**
+ * ice_update_link_info - update status of the HW network link
+ * @pi: port info structure of the interested logical port
+ */
+static enum ice_status
+ice_update_link_info(struct ice_port_info *pi)
+{
+       struct ice_aqc_get_phy_caps_data *pcaps;
+       struct ice_phy_info *phy_info;
+       enum ice_status status;
+       struct ice_hw *hw;
+
+       if (!pi)
+               return ICE_ERR_PARAM;
+
+       hw = pi->hw;
+
+       pcaps = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*pcaps), GFP_KERNEL);
+       if (!pcaps)
+               return ICE_ERR_NO_MEMORY;
+
+       phy_info = &pi->phy;
+       status = ice_aq_get_link_info(pi, true, NULL, NULL);
+       if (status)
+               goto out;
+
+       if (phy_info->link_info.link_info & ICE_AQ_MEDIA_AVAILABLE) {
+               status = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_SW_CFG,
+                                            pcaps, NULL);
+               if (status)
+                       goto out;
+
+               memcpy(phy_info->link_info.module_type, &pcaps->module_type,
+                      sizeof(phy_info->link_info.module_type));
+       }
+out:
+       devm_kfree(ice_hw_to_dev(hw), pcaps);
+       return status;
+}
+
+/**
+ * ice_set_fc
+ * @pi: port information structure
+ * @aq_failures: pointer to status code, specific to ice_set_fc routine
+ * @atomic_restart: enable automatic link update
+ *
+ * Set the requested flow control mode.
+ */
+enum ice_status
+ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool atomic_restart)
+{
+       struct ice_aqc_set_phy_cfg_data cfg = { 0 };
+       struct ice_aqc_get_phy_caps_data *pcaps;
+       enum ice_status status;
+       u8 pause_mask = 0x0;
+       struct ice_hw *hw;
+
+       if (!pi)
+               return ICE_ERR_PARAM;
+       hw = pi->hw;
+       *aq_failures = ICE_SET_FC_AQ_FAIL_NONE;
+
+       switch (pi->fc.req_mode) {
+       case ICE_FC_FULL:
+               pause_mask |= ICE_AQC_PHY_EN_TX_LINK_PAUSE;
+               pause_mask |= ICE_AQC_PHY_EN_RX_LINK_PAUSE;
+               break;
+       case ICE_FC_RX_PAUSE:
+               pause_mask |= ICE_AQC_PHY_EN_RX_LINK_PAUSE;
+               break;
+       case ICE_FC_TX_PAUSE:
+               pause_mask |= ICE_AQC_PHY_EN_TX_LINK_PAUSE;
+               break;
+       default:
+               break;
+       }
+
+       pcaps = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*pcaps), GFP_KERNEL);
+       if (!pcaps)
+               return ICE_ERR_NO_MEMORY;
+
+       /* Get the current phy config */
+       status = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_SW_CFG, pcaps,
+                                    NULL);
+       if (status) {
+               *aq_failures = ICE_SET_FC_AQ_FAIL_GET;
+               goto out;
+       }
+
+       /* clear the old pause settings */
+       cfg.caps = pcaps->caps & ~(ICE_AQC_PHY_EN_TX_LINK_PAUSE |
+                                  ICE_AQC_PHY_EN_RX_LINK_PAUSE);
+       /* set the new capabilities */
+       cfg.caps |= pause_mask;
+       /* If the capabilities have changed, then set the new config */
+       if (cfg.caps != pcaps->caps) {
+               int retry_count, retry_max = 10;
+
+               /* Auto restart link so settings take effect */
+               if (atomic_restart)
+                       cfg.caps |= ICE_AQ_PHY_ENA_ATOMIC_LINK;
+               /* Copy over all the old settings */
+               cfg.phy_type_low = pcaps->phy_type_low;
+               cfg.low_power_ctrl = pcaps->low_power_ctrl;
+               cfg.eee_cap = pcaps->eee_cap;
+               cfg.eeer_value = pcaps->eeer_value;
+               cfg.link_fec_opt = pcaps->link_fec_options;
+
+               status = ice_aq_set_phy_cfg(hw, pi->lport, &cfg, NULL);
+               if (status) {
+                       *aq_failures = ICE_SET_FC_AQ_FAIL_SET;
+                       goto out;
+               }
+
+               /* Update the link info
+                * It sometimes takes a really long time for link to
+                * come back from the atomic reset. Thus, we wait a
+                * little bit.
+                */
+               for (retry_count = 0; retry_count < retry_max; retry_count++) {
+                       status = ice_update_link_info(pi);
+
+                       if (!status)
+                               break;
+
+                       mdelay(100);
+               }
+
+               if (status)
+                       *aq_failures = ICE_SET_FC_AQ_FAIL_UPDATE;
+       }
+
+out:
+       devm_kfree(ice_hw_to_dev(hw), pcaps);
+       return status;
+}
+
+/**
+ * ice_aq_set_link_restart_an
+ * @pi: pointer to the port information structure
+ * @ena_link: if true: enable link, if false: disable link
+ * @cd: pointer to command details structure or NULL
+ *
+ * Sets up the link and restarts the Auto-Negotiation over the link.
+ */
+enum ice_status
+ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
+                          struct ice_sq_cd *cd)
+{
+       struct ice_aqc_restart_an *cmd;
+       struct ice_aq_desc desc;
+
+       cmd = &desc.params.restart_an;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_restart_an);
+
+       cmd->cmd_flags = ICE_AQC_RESTART_AN_LINK_RESTART;
+       cmd->lport_num = pi->lport;
+       if (ena_link)
+               cmd->cmd_flags |= ICE_AQC_RESTART_AN_LINK_ENABLE;
+       else
+               cmd->cmd_flags &= ~ICE_AQC_RESTART_AN_LINK_ENABLE;
+
+       return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
+}
+
 /**
  * __ice_aq_get_set_rss_lut
  * @hw: pointer to the hardware structure
index 38719ba..c4213fb 100644 (file)
@@ -58,6 +58,11 @@ ice_aq_send_cmd(struct ice_hw *hw, struct ice_aq_desc *desc,
 enum ice_status ice_aq_get_fw_ver(struct ice_hw *hw, struct ice_sq_cd *cd);
 enum ice_status ice_clear_pf_cfg(struct ice_hw *hw);
 enum ice_status
+ice_set_fc(struct ice_port_info *pi, u8 *aq_failures, bool atomic_restart);
+enum ice_status
+ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
+                          struct ice_sq_cd *cd);
+enum ice_status
 ice_aq_get_link_info(struct ice_port_info *pi, bool ena_lse,
                     struct ice_link_status *link, struct ice_sq_cd *cd);
 enum ice_status
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
new file mode 100644 (file)
index 0000000..186764a
--- /dev/null
@@ -0,0 +1,940 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+/* ethtool support for ice */
+
+#include "ice.h"
+
+struct ice_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int sizeof_stat;
+       int stat_offset;
+};
+
+#define ICE_STAT(_type, _name, _stat) { \
+       .stat_string = _name, \
+       .sizeof_stat = FIELD_SIZEOF(_type, _stat), \
+       .stat_offset = offsetof(_type, _stat) \
+}
+
+#define ICE_VSI_STAT(_name, _stat) \
+               ICE_STAT(struct ice_vsi, _name, _stat)
+#define ICE_PF_STAT(_name, _stat) \
+               ICE_STAT(struct ice_pf, _name, _stat)
+
+static int ice_q_stats_len(struct net_device *netdev)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+
+       return ((np->vsi->num_txq + np->vsi->num_rxq) *
+               (sizeof(struct ice_q_stats) / sizeof(u64)));
+}
+
+#define ICE_PF_STATS_LEN       ARRAY_SIZE(ice_gstrings_pf_stats)
+#define ICE_VSI_STATS_LEN      ARRAY_SIZE(ice_gstrings_vsi_stats)
+
+#define ICE_ALL_STATS_LEN(n)   (ICE_PF_STATS_LEN + ICE_VSI_STATS_LEN + \
+                                ice_q_stats_len(n))
+
+static const struct ice_stats ice_gstrings_vsi_stats[] = {
+       ICE_VSI_STAT("tx_unicast", eth_stats.tx_unicast),
+       ICE_VSI_STAT("rx_unicast", eth_stats.rx_unicast),
+       ICE_VSI_STAT("tx_multicast", eth_stats.tx_multicast),
+       ICE_VSI_STAT("rx_multicast", eth_stats.rx_multicast),
+       ICE_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
+       ICE_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
+       ICE_VSI_STAT("tx_bytes", eth_stats.tx_bytes),
+       ICE_VSI_STAT("rx_bytes", eth_stats.rx_bytes),
+       ICE_VSI_STAT("rx_discards", eth_stats.rx_discards),
+       ICE_VSI_STAT("tx_errors", eth_stats.tx_errors),
+       ICE_VSI_STAT("tx_linearize", tx_linearize),
+       ICE_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
+       ICE_VSI_STAT("rx_alloc_fail", rx_buf_failed),
+       ICE_VSI_STAT("rx_pg_alloc_fail", rx_page_failed),
+};
+
+/* These PF_STATs might look like duplicates of some NETDEV_STATs,
+ * but they aren't. This device is capable of supporting multiple
+ * VSIs/netdevs on a single PF. The NETDEV_STATs are for individual
+ * netdevs whereas the PF_STATs are for the physical function that's
+ * hosting these netdevs.
+ *
+ * The PF_STATs are appended to the netdev stats only when ethtool -S
+ * is queried on the base PF netdev.
+ */
+static struct ice_stats ice_gstrings_pf_stats[] = {
+       ICE_PF_STAT("tx_bytes", stats.eth.tx_bytes),
+       ICE_PF_STAT("rx_bytes", stats.eth.rx_bytes),
+       ICE_PF_STAT("tx_unicast", stats.eth.tx_unicast),
+       ICE_PF_STAT("rx_unicast", stats.eth.rx_unicast),
+       ICE_PF_STAT("tx_multicast", stats.eth.tx_multicast),
+       ICE_PF_STAT("rx_multicast", stats.eth.rx_multicast),
+       ICE_PF_STAT("tx_broadcast", stats.eth.tx_broadcast),
+       ICE_PF_STAT("rx_broadcast", stats.eth.rx_broadcast),
+       ICE_PF_STAT("tx_errors", stats.eth.tx_errors),
+       ICE_PF_STAT("tx_size_64", stats.tx_size_64),
+       ICE_PF_STAT("rx_size_64", stats.rx_size_64),
+       ICE_PF_STAT("tx_size_127", stats.tx_size_127),
+       ICE_PF_STAT("rx_size_127", stats.rx_size_127),
+       ICE_PF_STAT("tx_size_255", stats.tx_size_255),
+       ICE_PF_STAT("rx_size_255", stats.rx_size_255),
+       ICE_PF_STAT("tx_size_511", stats.tx_size_511),
+       ICE_PF_STAT("rx_size_511", stats.rx_size_511),
+       ICE_PF_STAT("tx_size_1023", stats.tx_size_1023),
+       ICE_PF_STAT("rx_size_1023", stats.rx_size_1023),
+       ICE_PF_STAT("tx_size_1522", stats.tx_size_1522),
+       ICE_PF_STAT("rx_size_1522", stats.rx_size_1522),
+       ICE_PF_STAT("tx_size_big", stats.tx_size_big),
+       ICE_PF_STAT("rx_size_big", stats.rx_size_big),
+       ICE_PF_STAT("link_xon_tx", stats.link_xon_tx),
+       ICE_PF_STAT("link_xon_rx", stats.link_xon_rx),
+       ICE_PF_STAT("link_xoff_tx", stats.link_xoff_tx),
+       ICE_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
+       ICE_PF_STAT("tx_dropped_link_down", stats.tx_dropped_link_down),
+       ICE_PF_STAT("rx_undersize", stats.rx_undersize),
+       ICE_PF_STAT("rx_fragments", stats.rx_fragments),
+       ICE_PF_STAT("rx_oversize", stats.rx_oversize),
+       ICE_PF_STAT("rx_jabber", stats.rx_jabber),
+       ICE_PF_STAT("rx_csum_bad", hw_csum_rx_error),
+       ICE_PF_STAT("rx_length_errors", stats.rx_len_errors),
+       ICE_PF_STAT("rx_dropped", stats.eth.rx_discards),
+       ICE_PF_STAT("rx_crc_errors", stats.crc_errors),
+       ICE_PF_STAT("illegal_bytes", stats.illegal_bytes),
+       ICE_PF_STAT("mac_local_faults", stats.mac_local_faults),
+       ICE_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
+};
+
+static u32 ice_regs_dump_list[] = {
+       PFGEN_STATE,
+       PRTGEN_STATUS,
+       QRX_CTRL(0),
+       QINT_TQCTL(0),
+       QINT_RQCTL(0),
+       PFINT_OICR_ENA,
+       QRX_ITR(0),
+};
+
+/**
+ * ice_nvm_version_str - format the NVM version strings
+ * @hw: ptr to the hardware info
+ */
+static char *ice_nvm_version_str(struct ice_hw *hw)
+{
+       static char buf[ICE_ETHTOOL_FWVER_LEN];
+       u8 ver, patch;
+       u32 full_ver;
+       u16 build;
+
+       full_ver = hw->nvm.oem_ver;
+       ver = (u8)((full_ver & ICE_OEM_VER_MASK) >> ICE_OEM_VER_SHIFT);
+       build = (u16)((full_ver & ICE_OEM_VER_BUILD_MASK) >>
+                     ICE_OEM_VER_BUILD_SHIFT);
+       patch = (u8)(full_ver & ICE_OEM_VER_PATCH_MASK);
+
+       snprintf(buf, sizeof(buf), "%x.%02x 0x%x %d.%d.%d",
+                (hw->nvm.ver & ICE_NVM_VER_HI_MASK) >> ICE_NVM_VER_HI_SHIFT,
+                (hw->nvm.ver & ICE_NVM_VER_LO_MASK) >> ICE_NVM_VER_LO_SHIFT,
+                hw->nvm.eetrack, ver, build, patch);
+
+       return buf;
+}
+
+static void
+ice_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+
+       strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, ice_drv_ver, sizeof(drvinfo->version));
+       strlcpy(drvinfo->fw_version, ice_nvm_version_str(&pf->hw),
+               sizeof(drvinfo->fw_version));
+       strlcpy(drvinfo->bus_info, pci_name(pf->pdev),
+               sizeof(drvinfo->bus_info));
+}
+
+static int ice_get_regs_len(struct net_device __always_unused *netdev)
+{
+       return ARRAY_SIZE(ice_regs_dump_list);
+}
+
+static void
+ice_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_pf *pf = np->vsi->back;
+       struct ice_hw *hw = &pf->hw;
+       u32 *regs_buf = (u32 *)p;
+       int i;
+
+       regs->version = 1;
+
+       for (i = 0; i < ARRAY_SIZE(ice_regs_dump_list) / sizeof(u32); ++i)
+               regs_buf[i] = rd32(hw, ice_regs_dump_list[i]);
+}
+
+static u32 ice_get_msglevel(struct net_device *netdev)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_pf *pf = np->vsi->back;
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+       if (pf->hw.debug_mask)
+               netdev_info(netdev, "hw debug_mask: 0x%llX\n",
+                           pf->hw.debug_mask);
+#endif /* !CONFIG_DYNAMIC_DEBUG */
+
+       return pf->msg_enable;
+}
+
+static void ice_set_msglevel(struct net_device *netdev, u32 data)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_pf *pf = np->vsi->back;
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+       if (ICE_DBG_USER & data)
+               pf->hw.debug_mask = data;
+       else
+               pf->msg_enable = data;
+#else
+       pf->msg_enable = data;
+#endif /* !CONFIG_DYNAMIC_DEBUG */
+}
+
+static void ice_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       char *p = (char *)data;
+       unsigned int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ICE_VSI_STATS_LEN; i++) {
+                       snprintf(p, ETH_GSTRING_LEN, "%s",
+                                ice_gstrings_vsi_stats[i].stat_string);
+                       p += ETH_GSTRING_LEN;
+               }
+
+               ice_for_each_txq(vsi, i) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "tx-queue-%u.tx_packets", i);
+                       p += ETH_GSTRING_LEN;
+                       snprintf(p, ETH_GSTRING_LEN, "tx-queue-%u.tx_bytes", i);
+                       p += ETH_GSTRING_LEN;
+               }
+
+               ice_for_each_rxq(vsi, i) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "rx-queue-%u.rx_packets", i);
+                       p += ETH_GSTRING_LEN;
+                       snprintf(p, ETH_GSTRING_LEN, "rx-queue-%u.rx_bytes", i);
+                       p += ETH_GSTRING_LEN;
+               }
+
+               if (vsi->type != ICE_VSI_PF)
+                       return;
+
+               for (i = 0; i < ICE_PF_STATS_LEN; i++) {
+                       snprintf(p, ETH_GSTRING_LEN, "port.%s",
+                                ice_gstrings_pf_stats[i].stat_string);
+                       p += ETH_GSTRING_LEN;
+               }
+
+               break;
+       default:
+               break;
+       }
+}
+
+static int ice_get_sset_count(struct net_device *netdev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ICE_ALL_STATS_LEN(netdev);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void
+ice_get_ethtool_stats(struct net_device *netdev,
+                     struct ethtool_stats __always_unused *stats, u64 *data)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+       struct ice_ring *ring;
+       unsigned int j = 0;
+       int i = 0;
+       char *p;
+
+       for (j = 0; j < ICE_VSI_STATS_LEN; j++) {
+               p = (char *)vsi + ice_gstrings_vsi_stats[j].stat_offset;
+               data[i++] = (ice_gstrings_vsi_stats[j].sizeof_stat ==
+                           sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+
+       /* populate per queue stats */
+       rcu_read_lock();
+
+       ice_for_each_txq(vsi, j) {
+               ring = READ_ONCE(vsi->tx_rings[j]);
+               if (!ring)
+                       continue;
+               data[i++] = ring->stats.pkts;
+               data[i++] = ring->stats.bytes;
+       }
+
+       ice_for_each_rxq(vsi, j) {
+               ring = READ_ONCE(vsi->rx_rings[j]);
+               data[i++] = ring->stats.pkts;
+               data[i++] = ring->stats.bytes;
+       }
+
+       rcu_read_unlock();
+
+       if (vsi->type != ICE_VSI_PF)
+               return;
+
+       for (j = 0; j < ICE_PF_STATS_LEN; j++) {
+               p = (char *)pf + ice_gstrings_pf_stats[j].stat_offset;
+               data[i++] = (ice_gstrings_pf_stats[j].sizeof_stat ==
+                            sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+}
+
+static int
+ice_get_link_ksettings(struct net_device *netdev,
+                      struct ethtool_link_ksettings *ks)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_link_status *hw_link_info;
+       struct ice_vsi *vsi = np->vsi;
+       bool link_up;
+
+       hw_link_info = &vsi->port_info->phy.link_info;
+       link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+       ethtool_link_ksettings_add_link_mode(ks, supported,
+                                            10000baseT_Full);
+       ethtool_link_ksettings_add_link_mode(ks, advertising,
+                                            10000baseT_Full);
+
+       /* set speed and duplex */
+       if (link_up) {
+               switch (hw_link_info->link_speed) {
+               case ICE_AQ_LINK_SPEED_100MB:
+                       ks->base.speed = SPEED_100;
+                       break;
+               case ICE_AQ_LINK_SPEED_2500MB:
+                       ks->base.speed = SPEED_2500;
+                       break;
+               case ICE_AQ_LINK_SPEED_5GB:
+                       ks->base.speed = SPEED_5000;
+                       break;
+               case ICE_AQ_LINK_SPEED_10GB:
+                       ks->base.speed = SPEED_10000;
+                       break;
+               case ICE_AQ_LINK_SPEED_25GB:
+                       ks->base.speed = SPEED_25000;
+                       break;
+               case ICE_AQ_LINK_SPEED_40GB:
+                       ks->base.speed = SPEED_40000;
+                       break;
+               default:
+                       ks->base.speed = SPEED_UNKNOWN;
+                       break;
+               }
+
+               ks->base.duplex = DUPLEX_FULL;
+       } else {
+               ks->base.speed = SPEED_UNKNOWN;
+               ks->base.duplex = DUPLEX_UNKNOWN;
+       }
+
+       /* set autoneg settings */
+       ks->base.autoneg = ((hw_link_info->an_info & ICE_AQ_AN_COMPLETED) ?
+                           AUTONEG_ENABLE : AUTONEG_DISABLE);
+
+       /* set media type settings */
+       switch (vsi->port_info->phy.media_type) {
+       case ICE_MEDIA_FIBER:
+               ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
+               ks->base.port = PORT_FIBRE;
+               break;
+       case ICE_MEDIA_BASET:
+               ethtool_link_ksettings_add_link_mode(ks, supported, TP);
+               ethtool_link_ksettings_add_link_mode(ks, advertising, TP);
+               ks->base.port = PORT_TP;
+               break;
+       case ICE_MEDIA_BACKPLANE:
+               ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg);
+               ethtool_link_ksettings_add_link_mode(ks, supported, Backplane);
+               ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg);
+               ethtool_link_ksettings_add_link_mode(ks, advertising,
+                                                    Backplane);
+               ks->base.port = PORT_NONE;
+               break;
+       case ICE_MEDIA_DA:
+               ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);
+               ethtool_link_ksettings_add_link_mode(ks, advertising, FIBRE);
+               ks->base.port = PORT_DA;
+               break;
+       default:
+               ks->base.port = PORT_OTHER;
+               break;
+       }
+
+       /* flow control is symmetric and always supported */
+       ethtool_link_ksettings_add_link_mode(ks, supported, Pause);
+
+       switch (vsi->port_info->fc.req_mode) {
+       case ICE_FC_FULL:
+               ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
+               break;
+       case ICE_FC_TX_PAUSE:
+               ethtool_link_ksettings_add_link_mode(ks, advertising,
+                                                    Asym_Pause);
+               break;
+       case ICE_FC_RX_PAUSE:
+               ethtool_link_ksettings_add_link_mode(ks, advertising, Pause);
+               ethtool_link_ksettings_add_link_mode(ks, advertising,
+                                                    Asym_Pause);
+               break;
+       case ICE_FC_PFC:
+       default:
+               ethtool_link_ksettings_del_link_mode(ks, advertising, Pause);
+               ethtool_link_ksettings_del_link_mode(ks, advertising,
+                                                    Asym_Pause);
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * ice_get_rxnfc - command to get RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ * @rule_locs: buffer to rturn Rx flow classification rules
+ *
+ * Returns Success if the command is supported.
+ */
+static int ice_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
+                        u32 __always_unused *rule_locs)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       int ret = -EOPNOTSUPP;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = vsi->rss_size;
+               ret = 0;
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static void
+ice_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+
+       ring->rx_max_pending = ICE_MAX_NUM_DESC;
+       ring->tx_max_pending = ICE_MAX_NUM_DESC;
+       ring->rx_pending = vsi->rx_rings[0]->count;
+       ring->tx_pending = vsi->tx_rings[0]->count;
+       ring->rx_mini_pending = ICE_MIN_NUM_DESC;
+       ring->rx_mini_max_pending = 0;
+       ring->rx_jumbo_max_pending = 0;
+       ring->rx_jumbo_pending = 0;
+}
+
+static int
+ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
+{
+       struct ice_ring *tx_rings = NULL, *rx_rings = NULL;
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+       int i, timeout = 50, err = 0;
+       u32 new_rx_cnt, new_tx_cnt;
+
+       if (ring->tx_pending > ICE_MAX_NUM_DESC ||
+           ring->tx_pending < ICE_MIN_NUM_DESC ||
+           ring->rx_pending > ICE_MAX_NUM_DESC ||
+           ring->rx_pending < ICE_MIN_NUM_DESC) {
+               netdev_err(netdev, "Descriptors requested (Tx: %d / Rx: %d) out of range [%d-%d]\n",
+                          ring->tx_pending, ring->rx_pending,
+                          ICE_MIN_NUM_DESC, ICE_MAX_NUM_DESC);
+               return -EINVAL;
+       }
+
+       new_tx_cnt = ALIGN(ring->tx_pending, ICE_REQ_DESC_MULTIPLE);
+       new_rx_cnt = ALIGN(ring->rx_pending, ICE_REQ_DESC_MULTIPLE);
+
+       /* if nothing to do return success */
+       if (new_tx_cnt == vsi->tx_rings[0]->count &&
+           new_rx_cnt == vsi->rx_rings[0]->count) {
+               netdev_dbg(netdev, "Nothing to change, descriptor count is same as requested\n");
+               return 0;
+       }
+
+       while (test_and_set_bit(__ICE_CFG_BUSY, pf->state)) {
+               timeout--;
+               if (!timeout)
+                       return -EBUSY;
+               usleep_range(1000, 2000);
+       }
+
+       /* set for the next time the netdev is started */
+       if (!netif_running(vsi->netdev)) {
+               for (i = 0; i < vsi->alloc_txq; i++)
+                       vsi->tx_rings[i]->count = new_tx_cnt;
+               for (i = 0; i < vsi->alloc_rxq; i++)
+                       vsi->rx_rings[i]->count = new_rx_cnt;
+               netdev_dbg(netdev, "Link is down, descriptor count change happens when link is brought up\n");
+               goto done;
+       }
+
+       if (new_tx_cnt == vsi->tx_rings[0]->count)
+               goto process_rx;
+
+       /* alloc updated Tx resources */
+       netdev_info(netdev, "Changing Tx descriptor count from %d to %d\n",
+                   vsi->tx_rings[0]->count, new_tx_cnt);
+
+       tx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_txq,
+                               sizeof(struct ice_ring), GFP_KERNEL);
+       if (!tx_rings) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       for (i = 0; i < vsi->num_txq; i++) {
+               /* clone ring and setup updated count */
+               tx_rings[i] = *vsi->tx_rings[i];
+               tx_rings[i].count = new_tx_cnt;
+               tx_rings[i].desc = NULL;
+               tx_rings[i].tx_buf = NULL;
+               err = ice_setup_tx_ring(&tx_rings[i]);
+               if (err) {
+                       while (i) {
+                               i--;
+                               ice_clean_tx_ring(&tx_rings[i]);
+                       }
+                       devm_kfree(&pf->pdev->dev, tx_rings);
+                       goto done;
+               }
+       }
+
+process_rx:
+       if (new_rx_cnt == vsi->rx_rings[0]->count)
+               goto process_link;
+
+       /* alloc updated Rx resources */
+       netdev_info(netdev, "Changing Rx descriptor count from %d to %d\n",
+                   vsi->rx_rings[0]->count, new_rx_cnt);
+
+       rx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_rxq,
+                               sizeof(struct ice_ring), GFP_KERNEL);
+       if (!rx_rings) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       for (i = 0; i < vsi->num_rxq; i++) {
+               /* clone ring and setup updated count */
+               rx_rings[i] = *vsi->rx_rings[i];
+               rx_rings[i].count = new_rx_cnt;
+               rx_rings[i].desc = NULL;
+               rx_rings[i].rx_buf = NULL;
+               /* this is to allow wr32 to have something to write to
+                * during early allocation of Rx buffers
+                */
+               rx_rings[i].tail = vsi->back->hw.hw_addr + PRTGEN_STATUS;
+
+               err = ice_setup_rx_ring(&rx_rings[i]);
+               if (err)
+                       goto rx_unwind;
+
+               /* allocate Rx buffers */
+               err = ice_alloc_rx_bufs(&rx_rings[i],
+                                       ICE_DESC_UNUSED(&rx_rings[i]));
+rx_unwind:
+               if (err) {
+                       while (i) {
+                               i--;
+                               ice_free_rx_ring(&rx_rings[i]);
+                       }
+                       devm_kfree(&pf->pdev->dev, rx_rings);
+                       err = -ENOMEM;
+                       goto free_tx;
+               }
+       }
+
+process_link:
+       /* Bring interface down, copy in the new ring info, then restore the
+        * interface. if VSI is up, bring it down and then back up
+        */
+       if (!test_and_set_bit(__ICE_DOWN, vsi->state)) {
+               ice_down(vsi);
+
+               if (tx_rings) {
+                       for (i = 0; i < vsi->alloc_txq; i++) {
+                               ice_free_tx_ring(vsi->tx_rings[i]);
+                               *vsi->tx_rings[i] = tx_rings[i];
+                       }
+                       devm_kfree(&pf->pdev->dev, tx_rings);
+               }
+
+               if (rx_rings) {
+                       for (i = 0; i < vsi->alloc_rxq; i++) {
+                               ice_free_rx_ring(vsi->rx_rings[i]);
+                               /* copy the real tail offset */
+                               rx_rings[i].tail = vsi->rx_rings[i]->tail;
+                               /* this is to fake out the allocation routine
+                                * into thinking it has to realloc everything
+                                * but the recycling logic will let us re-use
+                                * the buffers allocated above
+                                */
+                               rx_rings[i].next_to_use = 0;
+                               rx_rings[i].next_to_clean = 0;
+                               rx_rings[i].next_to_alloc = 0;
+                               *vsi->rx_rings[i] = rx_rings[i];
+                       }
+                       devm_kfree(&pf->pdev->dev, rx_rings);
+               }
+
+               ice_up(vsi);
+       }
+       goto done;
+
+free_tx:
+       /* error cleanup if the Rx allocations failed after getting Tx */
+       if (tx_rings) {
+               for (i = 0; i < vsi->alloc_txq; i++)
+                       ice_free_tx_ring(&tx_rings[i]);
+               devm_kfree(&pf->pdev->dev, tx_rings);
+       }
+
+done:
+       clear_bit(__ICE_CFG_BUSY, pf->state);
+       return err;
+}
+
+static int ice_nway_reset(struct net_device *netdev)
+{
+       /* restart autonegotiation */
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_link_status *hw_link_info;
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_port_info *pi;
+       enum ice_status status;
+       bool link_up;
+
+       pi = vsi->port_info;
+       hw_link_info = &pi->phy.link_info;
+       link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+       status = ice_aq_set_link_restart_an(pi, link_up, NULL);
+       if (status) {
+               netdev_info(netdev, "link restart failed, err %d aq_err %d\n",
+                           status, pi->hw->adminq.sq_last_status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * ice_get_pauseparam - Get Flow Control status
+ * @netdev: network interface device structure
+ * @pause: ethernet pause (flow control) parameters
+ */
+static void
+ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_port_info *pi;
+
+       pi = np->vsi->port_info;
+       pause->autoneg =
+               ((pi->phy.link_info.an_info & ICE_AQ_AN_COMPLETED) ?
+                AUTONEG_ENABLE : AUTONEG_DISABLE);
+
+       if (pi->fc.current_mode == ICE_FC_RX_PAUSE) {
+               pause->rx_pause = 1;
+       } else if (pi->fc.current_mode == ICE_FC_TX_PAUSE) {
+               pause->tx_pause = 1;
+       } else if (pi->fc.current_mode == ICE_FC_FULL) {
+               pause->rx_pause = 1;
+               pause->tx_pause = 1;
+       }
+}
+
+/**
+ * ice_set_pauseparam - Set Flow Control parameter
+ * @netdev: network interface device structure
+ * @pause: return tx/rx flow control status
+ */
+static int
+ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_link_status *hw_link_info;
+       struct ice_pf *pf = np->vsi->back;
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_hw *hw = &pf->hw;
+       struct ice_port_info *pi;
+       enum ice_status status;
+       u8 aq_failures;
+       bool link_up;
+       int err = 0;
+
+       pi = vsi->port_info;
+       hw_link_info = &pi->phy.link_info;
+       link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+       /* Changing the port's flow control is not supported if this isn't the
+        * PF VSI
+        */
+       if (vsi->type != ICE_VSI_PF) {
+               netdev_info(netdev, "Changing flow control parameters only supported for PF VSI\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (pause->autoneg != (hw_link_info->an_info & ICE_AQ_AN_COMPLETED)) {
+               netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
+               return -EOPNOTSUPP;
+       }
+
+       /* If we have link and don't have autoneg */
+       if (!test_bit(__ICE_DOWN, pf->state) &&
+           !(hw_link_info->an_info & ICE_AQ_AN_COMPLETED)) {
+               /* Send message that it might not necessarily work*/
+               netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
+       }
+
+       if (pause->rx_pause && pause->tx_pause)
+               pi->fc.req_mode = ICE_FC_FULL;
+       else if (pause->rx_pause && !pause->tx_pause)
+               pi->fc.req_mode = ICE_FC_RX_PAUSE;
+       else if (!pause->rx_pause && pause->tx_pause)
+               pi->fc.req_mode = ICE_FC_TX_PAUSE;
+       else if (!pause->rx_pause && !pause->tx_pause)
+               pi->fc.req_mode = ICE_FC_NONE;
+       else
+               return -EINVAL;
+
+       /* Tell the OS link is going down, the link will go back up when fw
+        * says it is ready asynchronously
+        */
+       ice_print_link_msg(vsi, false);
+       netif_carrier_off(netdev);
+       netif_tx_stop_all_queues(netdev);
+
+       /* Set the FC mode and only restart AN if link is up */
+       status = ice_set_fc(pi, &aq_failures, link_up);
+
+       if (aq_failures & ICE_SET_FC_AQ_FAIL_GET) {
+               netdev_info(netdev, "Set fc failed on the get_phy_capabilities call with err %d aq_err %d\n",
+                           status, hw->adminq.sq_last_status);
+               err = -EAGAIN;
+       } else if (aq_failures & ICE_SET_FC_AQ_FAIL_SET) {
+               netdev_info(netdev, "Set fc failed on the set_phy_config call with err %d aq_err %d\n",
+                           status, hw->adminq.sq_last_status);
+               err = -EAGAIN;
+       } else if (aq_failures & ICE_SET_FC_AQ_FAIL_UPDATE) {
+               netdev_info(netdev, "Set fc failed on the get_link_info call with err %d aq_err %d\n",
+                           status, hw->adminq.sq_last_status);
+               err = -EAGAIN;
+       }
+
+       if (!test_bit(__ICE_DOWN, pf->state)) {
+               /* Give it a little more time to try to come back */
+               msleep(75);
+               if (!test_bit(__ICE_DOWN, pf->state))
+                       return ice_nway_reset(netdev);
+       }
+
+       return err;
+}
+
+/**
+ * ice_get_rxfh_key_size - get the RSS hash key size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ */
+static u32 ice_get_rxfh_key_size(struct net_device __always_unused *netdev)
+{
+       return ICE_VSIQF_HKEY_ARRAY_SIZE;
+}
+
+/**
+ * ice_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ */
+static u32 ice_get_rxfh_indir_size(struct net_device *netdev)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+
+       return np->vsi->rss_table_size;
+}
+
+/**
+ * ice_get_rxfh - get the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key
+ * @hfunc: hash function
+ *
+ * Reads the indirection table directly from the hardware.
+ */
+static int
+ice_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+       int ret = 0, i;
+       u8 *lut;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+
+       if (!indir)
+               return 0;
+
+       if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
+               /* RSS not supported return error here */
+               netdev_warn(netdev, "RSS is not configured on this VSI!\n");
+               return -EIO;
+       }
+
+       lut = devm_kzalloc(&pf->pdev->dev, vsi->rss_table_size, GFP_KERNEL);
+       if (!lut)
+               return -ENOMEM;
+
+       if (ice_get_rss(vsi, key, lut, vsi->rss_table_size)) {
+               ret = -EIO;
+               goto out;
+       }
+
+       for (i = 0; i < vsi->rss_table_size; i++)
+               indir[i] = (u32)(lut[i]);
+
+out:
+       devm_kfree(&pf->pdev->dev, lut);
+       return ret;
+}
+
+/**
+ * ice_set_rxfh - set the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key
+ * @hfunc: hash function
+ *
+ * Returns -EINVAL if the table specifies an invalid queue id, otherwise
+ * returns 0 after programming the table.
+ */
+static int ice_set_rxfh(struct net_device *netdev, const u32 *indir,
+                       const u8 *key, const u8 hfunc)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       struct ice_pf *pf = vsi->back;
+       u8 *seed = NULL;
+
+       if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+               return -EOPNOTSUPP;
+
+       if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
+               /* RSS not supported return error here */
+               netdev_warn(netdev, "RSS is not configured on this VSI!\n");
+               return -EIO;
+       }
+
+       if (key) {
+               if (!vsi->rss_hkey_user) {
+                       vsi->rss_hkey_user =
+                               devm_kzalloc(&pf->pdev->dev,
+                                            ICE_VSIQF_HKEY_ARRAY_SIZE,
+                                            GFP_KERNEL);
+                       if (!vsi->rss_hkey_user)
+                               return -ENOMEM;
+               }
+               memcpy(vsi->rss_hkey_user, key, ICE_VSIQF_HKEY_ARRAY_SIZE);
+               seed = vsi->rss_hkey_user;
+       }
+
+       if (!vsi->rss_lut_user) {
+               vsi->rss_lut_user = devm_kzalloc(&pf->pdev->dev,
+                                                vsi->rss_table_size,
+                                                GFP_KERNEL);
+               if (!vsi->rss_lut_user)
+                       return -ENOMEM;
+       }
+
+       /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+       if (indir) {
+               int i;
+
+               for (i = 0; i < vsi->rss_table_size; i++)
+                       vsi->rss_lut_user[i] = (u8)(indir[i]);
+       } else {
+               ice_fill_rss_lut(vsi->rss_lut_user, vsi->rss_table_size,
+                                vsi->rss_size);
+       }
+
+       if (ice_set_rss(vsi, seed, vsi->rss_lut_user, vsi->rss_table_size))
+               return -EIO;
+
+       return 0;
+}
+
+static const struct ethtool_ops ice_ethtool_ops = {
+       .get_link_ksettings     = ice_get_link_ksettings,
+       .get_drvinfo            = ice_get_drvinfo,
+       .get_regs_len           = ice_get_regs_len,
+       .get_regs               = ice_get_regs,
+       .get_msglevel           = ice_get_msglevel,
+       .set_msglevel           = ice_set_msglevel,
+       .get_link               = ethtool_op_get_link,
+       .get_strings            = ice_get_strings,
+       .get_ethtool_stats      = ice_get_ethtool_stats,
+       .get_sset_count         = ice_get_sset_count,
+       .get_rxnfc              = ice_get_rxnfc,
+       .get_ringparam          = ice_get_ringparam,
+       .set_ringparam          = ice_set_ringparam,
+       .nway_reset             = ice_nway_reset,
+       .get_pauseparam         = ice_get_pauseparam,
+       .set_pauseparam         = ice_set_pauseparam,
+       .get_rxfh_key_size      = ice_get_rxfh_key_size,
+       .get_rxfh_indir_size    = ice_get_rxfh_indir_size,
+       .get_rxfh               = ice_get_rxfh,
+       .set_rxfh               = ice_set_rxfh,
+};
+
+/**
+ * ice_set_ethtool_ops - setup netdev ethtool ops
+ * @netdev: network interface device structure
+ *
+ * setup netdev ethtool ops with ice specific ops
+ */
+void ice_set_ethtool_ops(struct net_device *netdev)
+{
+       netdev->ethtool_ops = &ice_ethtool_ops;
+}
index fc9b0b1..777a1c6 100644 (file)
@@ -94,6 +94,8 @@
 #define PFGEN_CTRL                     0x00091000
 #define PFGEN_CTRL_PFSWR_S             0
 #define PFGEN_CTRL_PFSWR_M             BIT(PFGEN_CTRL_PFSWR_S)
+#define PFGEN_STATE                    0x00088000
+#define PRTGEN_STATUS                  0x000B8100
 #define PFHMC_ERRORDATA                        0x00520500
 #define PFHMC_ERRORINFO                        0x00520400
 #define GLINT_DYN_CTL(_INT)            (0x00160000 + ((_INT) * 4))
 #define QRX_CTRL_QENA_REQ_M            BIT(QRX_CTRL_QENA_REQ_S)
 #define QRX_CTRL_QENA_STAT_S           2
 #define QRX_CTRL_QENA_STAT_M           BIT(QRX_CTRL_QENA_STAT_S)
+#define QRX_ITR(_QRX)                  (0x00292000 + ((_QRX) * 4))
 #define QRX_TAIL(_QRX)                 (0x00290000 + ((_QRX) * 4))
 #define GLNVM_FLA                      0x000B6108
 #define GLNVM_FLA_LOCKED_S             6
 #define PF_FUNC_RID                    0x0009E880
 #define PF_FUNC_RID_FUNC_NUM_S         0
 #define PF_FUNC_RID_FUNC_NUM_M         ICE_M(0x7, PF_FUNC_RID_FUNC_NUM_S)
+#define GLPRT_BPRCH(_i)                        (0x00381384 + ((_i) * 8))
+#define GLPRT_BPRCL(_i)                        (0x00381380 + ((_i) * 8))
+#define GLPRT_BPTCH(_i)                        (0x00381244 + ((_i) * 8))
+#define GLPRT_BPTCL(_i)                        (0x00381240 + ((_i) * 8))
+#define GLPRT_CRCERRS(_i)              (0x00380100 + ((_i) * 8))
+#define GLPRT_GORCH(_i)                        (0x00380004 + ((_i) * 8))
+#define GLPRT_GORCL(_i)                        (0x00380000 + ((_i) * 8))
+#define GLPRT_GOTCH(_i)                        (0x00380B44 + ((_i) * 8))
+#define GLPRT_GOTCL(_i)                        (0x00380B40 + ((_i) * 8))
+#define GLPRT_ILLERRC(_i)              (0x003801C0 + ((_i) * 8))
+#define GLPRT_LXOFFRXC(_i)             (0x003802C0 + ((_i) * 8))
+#define GLPRT_LXOFFTXC(_i)             (0x00381180 + ((_i) * 8))
+#define GLPRT_LXONRXC(_i)              (0x00380280 + ((_i) * 8))
+#define GLPRT_LXONTXC(_i)              (0x00381140 + ((_i) * 8))
+#define GLPRT_MLFC(_i)                 (0x00380040 + ((_i) * 8))
+#define GLPRT_MPRCH(_i)                        (0x00381344 + ((_i) * 8))
+#define GLPRT_MPRCL(_i)                        (0x00381340 + ((_i) * 8))
+#define GLPRT_MPTCH(_i)                        (0x00381204 + ((_i) * 8))
+#define GLPRT_MPTCL(_i)                        (0x00381200 + ((_i) * 8))
+#define GLPRT_MRFC(_i)                 (0x00380080 + ((_i) * 8))
+#define GLPRT_PRC1023H(_i)             (0x00380A04 + ((_i) * 8))
+#define GLPRT_PRC1023L(_i)             (0x00380A00 + ((_i) * 8))
+#define GLPRT_PRC127H(_i)              (0x00380944 + ((_i) * 8))
+#define GLPRT_PRC127L(_i)              (0x00380940 + ((_i) * 8))
+#define GLPRT_PRC1522H(_i)             (0x00380A44 + ((_i) * 8))
+#define GLPRT_PRC1522L(_i)             (0x00380A40 + ((_i) * 8))
+#define GLPRT_PRC255H(_i)              (0x00380984 + ((_i) * 8))
+#define GLPRT_PRC255L(_i)              (0x00380980 + ((_i) * 8))
+#define GLPRT_PRC511H(_i)              (0x003809C4 + ((_i) * 8))
+#define GLPRT_PRC511L(_i)              (0x003809C0 + ((_i) * 8))
+#define GLPRT_PRC64H(_i)               (0x00380904 + ((_i) * 8))
+#define GLPRT_PRC64L(_i)               (0x00380900 + ((_i) * 8))
+#define GLPRT_PRC9522H(_i)             (0x00380A84 + ((_i) * 8))
+#define GLPRT_PRC9522L(_i)             (0x00380A80 + ((_i) * 8))
+#define GLPRT_PTC1023H(_i)             (0x00380C84 + ((_i) * 8))
+#define GLPRT_PTC1023L(_i)             (0x00380C80 + ((_i) * 8))
+#define GLPRT_PTC127H(_i)              (0x00380BC4 + ((_i) * 8))
+#define GLPRT_PTC127L(_i)              (0x00380BC0 + ((_i) * 8))
+#define GLPRT_PTC1522H(_i)             (0x00380CC4 + ((_i) * 8))
+#define GLPRT_PTC1522L(_i)             (0x00380CC0 + ((_i) * 8))
+#define GLPRT_PTC255H(_i)              (0x00380C04 + ((_i) * 8))
+#define GLPRT_PTC255L(_i)              (0x00380C00 + ((_i) * 8))
+#define GLPRT_PTC511H(_i)              (0x00380C44 + ((_i) * 8))
+#define GLPRT_PTC511L(_i)              (0x00380C40 + ((_i) * 8))
+#define GLPRT_PTC64H(_i)               (0x00380B84 + ((_i) * 8))
+#define GLPRT_PTC64L(_i)               (0x00380B80 + ((_i) * 8))
+#define GLPRT_PTC9522H(_i)             (0x00380D04 + ((_i) * 8))
+#define GLPRT_PTC9522L(_i)             (0x00380D00 + ((_i) * 8))
+#define GLPRT_RFC(_i)                  (0x00380AC0 + ((_i) * 8))
+#define GLPRT_RJC(_i)                  (0x00380B00 + ((_i) * 8))
+#define GLPRT_RLEC(_i)                 (0x00380140 + ((_i) * 8))
+#define GLPRT_ROC(_i)                  (0x00380240 + ((_i) * 8))
+#define GLPRT_RUC(_i)                  (0x00380200 + ((_i) * 8))
+#define GLPRT_TDOLD(_i)                        (0x00381280 + ((_i) * 8))
+#define GLPRT_UPRCH(_i)                        (0x00381304 + ((_i) * 8))
+#define GLPRT_UPRCL(_i)                        (0x00381300 + ((_i) * 8))
+#define GLPRT_UPTCH(_i)                        (0x003811C4 + ((_i) * 8))
+#define GLPRT_UPTCL(_i)                        (0x003811C0 + ((_i) * 8))
+#define GLV_BPRCH(_i)                  (0x003B6004 + ((_i) * 8))
+#define GLV_BPRCL(_i)                  (0x003B6000 + ((_i) * 8))
+#define GLV_BPTCH(_i)                  (0x0030E004 + ((_i) * 8))
+#define GLV_BPTCL(_i)                  (0x0030E000 + ((_i) * 8))
+#define GLV_GORCH(_i)                  (0x003B0004 + ((_i) * 8))
+#define GLV_GORCL(_i)                  (0x003B0000 + ((_i) * 8))
+#define GLV_GOTCH(_i)                  (0x00300004 + ((_i) * 8))
+#define GLV_GOTCL(_i)                  (0x00300000 + ((_i) * 8))
+#define GLV_MPRCH(_i)                  (0x003B4004 + ((_i) * 8))
+#define GLV_MPRCL(_i)                  (0x003B4000 + ((_i) * 8))
+#define GLV_MPTCH(_i)                  (0x0030C004 + ((_i) * 8))
+#define GLV_MPTCL(_i)                  (0x0030C000 + ((_i) * 8))
+#define GLV_RDPC(_i)                   (0x00294C04 + ((_i) * 4))
+#define GLV_TEPC(_VSI)                 (0x00312000 + ((_VSI) * 4))
+#define GLV_UPRCH(_i)                  (0x003B2004 + ((_i) * 8))
+#define GLV_UPRCL(_i)                  (0x003B2000 + ((_i) * 8))
+#define GLV_UPTCH(_i)                  (0x0030A004 + ((_i) * 8))
+#define GLV_UPTCL(_i)                  (0x0030A000 + ((_i) * 8))
+#define VSIQF_HKEY_MAX_INDEX           12
 
 #endif /* _ICE_HW_AUTOGEN_H_ */
index 2124e7a..9c18417 100644 (file)
@@ -9,7 +9,7 @@
 
 #define DRV_VERSION    "ice-0.0.1-k"
 #define DRV_SUMMARY    "Intel(R) Ethernet Connection E800 Series Linux Driver"
-static const char ice_drv_ver[] = DRV_VERSION;
+const char ice_drv_ver[] = DRV_VERSION;
 static const char ice_driver_string[] = DRV_SUMMARY;
 static const char ice_copyright[] = "Copyright (c) 2018, Intel Corporation.";
 
@@ -30,6 +30,8 @@ static struct workqueue_struct *ice_wq;
 static const struct net_device_ops ice_netdev_ops;
 
 static int ice_vsi_release(struct ice_vsi *vsi);
+static void ice_update_vsi_stats(struct ice_vsi *vsi);
+static void ice_update_pf_stats(struct ice_pf *pf);
 
 /**
  * ice_get_free_slot - get the next non-NULL location index in array
@@ -214,12 +216,41 @@ static void ice_free_fltr_list(struct device *dev, struct list_head *h)
        }
 }
 
+/**
+ * ice_watchdog_subtask - periodic tasks not using event driven scheduling
+ * @pf: board private structure
+ */
+static void ice_watchdog_subtask(struct ice_pf *pf)
+{
+       int i;
+
+       /* if interface is down do nothing */
+       if (test_bit(__ICE_DOWN, pf->state) ||
+           test_bit(__ICE_CFG_BUSY, pf->state))
+               return;
+
+       /* make sure we don't do these things too often */
+       if (time_before(jiffies,
+                       pf->serv_tmr_prev + pf->serv_tmr_period))
+               return;
+
+       pf->serv_tmr_prev = jiffies;
+
+       /* Update the stats for active netdevs so the network stack
+        * can look at updated numbers whenever it cares to
+        */
+       ice_update_pf_stats(pf);
+       for (i = 0; i < pf->num_alloc_vsi; i++)
+               if (pf->vsi[i] && pf->vsi[i]->netdev)
+                       ice_update_vsi_stats(pf->vsi[i]);
+}
+
 /**
  * ice_print_link_msg - print link up or down message
  * @vsi: the VSI whose link status is being queried
  * @isup: boolean for if the link is now up or down
  */
-static void ice_print_link_msg(struct ice_vsi *vsi, bool isup)
+void ice_print_link_msg(struct ice_vsi *vsi, bool isup)
 {
        const char *speed;
        const char *fc;
@@ -452,6 +483,7 @@ static void ice_service_task(struct work_struct *work)
        unsigned long start_time = jiffies;
 
        /* subtasks */
+       ice_watchdog_subtask(pf);
        ice_clean_adminq_subtask(pf);
 
        /* Clear __ICE_SERVICE_SCHED flag to allow scheduling next event */
@@ -1763,6 +1795,8 @@ static int ice_cfg_netdev(struct ice_vsi *vsi)
        /* setup watchdog timeout value to be 5 second */
        netdev->watchdog_timeo = 5 * HZ;
 
+       ice_set_ethtool_ops(netdev);
+
        netdev->min_mtu = ETH_MIN_MTU;
        netdev->max_mtu = ICE_MAX_MTU;
 
@@ -3459,6 +3493,434 @@ static int ice_up_complete(struct ice_vsi *vsi)
        return err;
 }
 
+/**
+ * ice_up - Bring the connection back up after being down
+ * @vsi: VSI being configured
+ */
+int ice_up(struct ice_vsi *vsi)
+{
+       int err;
+
+       err = ice_vsi_cfg(vsi);
+       if (!err)
+               err = ice_up_complete(vsi);
+
+       return err;
+}
+
+/**
+ * ice_fetch_u64_stats_per_ring - get packets and bytes stats per ring
+ * @ring: Tx or Rx ring to read stats from
+ * @pkts: packets stats counter
+ * @bytes: bytes stats counter
+ *
+ * This function fetches stats from the ring considering the atomic operations
+ * that needs to be performed to read u64 values in 32 bit machine.
+ */
+static void ice_fetch_u64_stats_per_ring(struct ice_ring *ring, u64 *pkts,
+                                        u64 *bytes)
+{
+       unsigned int start;
+       *pkts = 0;
+       *bytes = 0;
+
+       if (!ring)
+               return;
+       do {
+               start = u64_stats_fetch_begin_irq(&ring->syncp);
+               *pkts = ring->stats.pkts;
+               *bytes = ring->stats.bytes;
+       } while (u64_stats_fetch_retry_irq(&ring->syncp, start));
+}
+
+/**
+ * ice_stat_update40 - read 40 bit stat from the chip and update stat values
+ * @hw: ptr to the hardware info
+ * @hireg: high 32 bit HW register to read from
+ * @loreg: low 32 bit HW register to read from
+ * @prev_stat_loaded: bool to specify if previous stats are loaded
+ * @prev_stat: ptr to previous loaded stat value
+ * @cur_stat: ptr to current stat value
+ */
+static void ice_stat_update40(struct ice_hw *hw, u32 hireg, u32 loreg,
+                             bool prev_stat_loaded, u64 *prev_stat,
+                             u64 *cur_stat)
+{
+       u64 new_data;
+
+       new_data = rd32(hw, loreg);
+       new_data |= ((u64)(rd32(hw, hireg) & 0xFFFF)) << 32;
+
+       /* device stats are not reset at PFR, they likely will not be zeroed
+        * when the driver starts. So save the first values read and use them as
+        * offsets to be subtracted from the raw values in order to report stats
+        * that count from zero.
+        */
+       if (!prev_stat_loaded)
+               *prev_stat = new_data;
+       if (likely(new_data >= *prev_stat))
+               *cur_stat = new_data - *prev_stat;
+       else
+               /* to manage the potential roll-over */
+               *cur_stat = (new_data + BIT_ULL(40)) - *prev_stat;
+       *cur_stat &= 0xFFFFFFFFFFULL;
+}
+
+/**
+ * ice_stat_update32 - read 32 bit stat from the chip and update stat values
+ * @hw: ptr to the hardware info
+ * @reg: HW register to read from
+ * @prev_stat_loaded: bool to specify if previous stats are loaded
+ * @prev_stat: ptr to previous loaded stat value
+ * @cur_stat: ptr to current stat value
+ */
+static void ice_stat_update32(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
+                             u64 *prev_stat, u64 *cur_stat)
+{
+       u32 new_data;
+
+       new_data = rd32(hw, reg);
+
+       /* device stats are not reset at PFR, they likely will not be zeroed
+        * when the driver starts. So save the first values read and use them as
+        * offsets to be subtracted from the raw values in order to report stats
+        * that count from zero.
+        */
+       if (!prev_stat_loaded)
+               *prev_stat = new_data;
+       if (likely(new_data >= *prev_stat))
+               *cur_stat = new_data - *prev_stat;
+       else
+               /* to manage the potential roll-over */
+               *cur_stat = (new_data + BIT_ULL(32)) - *prev_stat;
+}
+
+/**
+ * ice_update_eth_stats - Update VSI-specific ethernet statistics counters
+ * @vsi: the VSI to be updated
+ */
+static void ice_update_eth_stats(struct ice_vsi *vsi)
+{
+       struct ice_eth_stats *prev_es, *cur_es;
+       struct ice_hw *hw = &vsi->back->hw;
+       u16 vsi_num = vsi->vsi_num;    /* HW absolute index of a VSI */
+
+       prev_es = &vsi->eth_stats_prev;
+       cur_es = &vsi->eth_stats;
+
+       ice_stat_update40(hw, GLV_GORCH(vsi_num), GLV_GORCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->rx_bytes,
+                         &cur_es->rx_bytes);
+
+       ice_stat_update40(hw, GLV_UPRCH(vsi_num), GLV_UPRCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->rx_unicast,
+                         &cur_es->rx_unicast);
+
+       ice_stat_update40(hw, GLV_MPRCH(vsi_num), GLV_MPRCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->rx_multicast,
+                         &cur_es->rx_multicast);
+
+       ice_stat_update40(hw, GLV_BPRCH(vsi_num), GLV_BPRCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->rx_broadcast,
+                         &cur_es->rx_broadcast);
+
+       ice_stat_update32(hw, GLV_RDPC(vsi_num), vsi->stat_offsets_loaded,
+                         &prev_es->rx_discards, &cur_es->rx_discards);
+
+       ice_stat_update40(hw, GLV_GOTCH(vsi_num), GLV_GOTCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->tx_bytes,
+                         &cur_es->tx_bytes);
+
+       ice_stat_update40(hw, GLV_UPTCH(vsi_num), GLV_UPTCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->tx_unicast,
+                         &cur_es->tx_unicast);
+
+       ice_stat_update40(hw, GLV_MPTCH(vsi_num), GLV_MPTCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->tx_multicast,
+                         &cur_es->tx_multicast);
+
+       ice_stat_update40(hw, GLV_BPTCH(vsi_num), GLV_BPTCL(vsi_num),
+                         vsi->stat_offsets_loaded, &prev_es->tx_broadcast,
+                         &cur_es->tx_broadcast);
+
+       ice_stat_update32(hw, GLV_TEPC(vsi_num), vsi->stat_offsets_loaded,
+                         &prev_es->tx_errors, &cur_es->tx_errors);
+
+       vsi->stat_offsets_loaded = true;
+}
+
+/**
+ * ice_update_vsi_ring_stats - Update VSI stats counters
+ * @vsi: the VSI to be updated
+ */
+static void ice_update_vsi_ring_stats(struct ice_vsi *vsi)
+{
+       struct rtnl_link_stats64 *vsi_stats = &vsi->net_stats;
+       struct ice_ring *ring;
+       u64 pkts, bytes;
+       int i;
+
+       /* reset netdev stats */
+       vsi_stats->tx_packets = 0;
+       vsi_stats->tx_bytes = 0;
+       vsi_stats->rx_packets = 0;
+       vsi_stats->rx_bytes = 0;
+
+       /* reset non-netdev (extended) stats */
+       vsi->tx_restart = 0;
+       vsi->tx_busy = 0;
+       vsi->tx_linearize = 0;
+       vsi->rx_buf_failed = 0;
+       vsi->rx_page_failed = 0;
+
+       rcu_read_lock();
+
+       /* update Tx rings counters */
+       ice_for_each_txq(vsi, i) {
+               ring = READ_ONCE(vsi->tx_rings[i]);
+               ice_fetch_u64_stats_per_ring(ring, &pkts, &bytes);
+               vsi_stats->tx_packets += pkts;
+               vsi_stats->tx_bytes += bytes;
+               vsi->tx_restart += ring->tx_stats.restart_q;
+               vsi->tx_busy += ring->tx_stats.tx_busy;
+               vsi->tx_linearize += ring->tx_stats.tx_linearize;
+       }
+
+       /* update Rx rings counters */
+       ice_for_each_rxq(vsi, i) {
+               ring = READ_ONCE(vsi->rx_rings[i]);
+               ice_fetch_u64_stats_per_ring(ring, &pkts, &bytes);
+               vsi_stats->rx_packets += pkts;
+               vsi_stats->rx_bytes += bytes;
+               vsi->rx_buf_failed += ring->rx_stats.alloc_buf_failed;
+               vsi->rx_page_failed += ring->rx_stats.alloc_page_failed;
+       }
+
+       rcu_read_unlock();
+}
+
+/**
+ * ice_update_vsi_stats - Update VSI stats counters
+ * @vsi: the VSI to be updated
+ */
+static void ice_update_vsi_stats(struct ice_vsi *vsi)
+{
+       struct rtnl_link_stats64 *cur_ns = &vsi->net_stats;
+       struct ice_eth_stats *cur_es = &vsi->eth_stats;
+       struct ice_pf *pf = vsi->back;
+
+       if (test_bit(__ICE_DOWN, vsi->state) ||
+           test_bit(__ICE_CFG_BUSY, pf->state))
+               return;
+
+       /* get stats as recorded by Tx/Rx rings */
+       ice_update_vsi_ring_stats(vsi);
+
+       /* get VSI stats as recorded by the hardware */
+       ice_update_eth_stats(vsi);
+
+       cur_ns->tx_errors = cur_es->tx_errors;
+       cur_ns->rx_dropped = cur_es->rx_discards;
+       cur_ns->tx_dropped = cur_es->tx_discards;
+       cur_ns->multicast = cur_es->rx_multicast;
+
+       /* update some more netdev stats if this is main VSI */
+       if (vsi->type == ICE_VSI_PF) {
+               cur_ns->rx_crc_errors = pf->stats.crc_errors;
+               cur_ns->rx_errors = pf->stats.crc_errors +
+                                   pf->stats.illegal_bytes;
+               cur_ns->rx_length_errors = pf->stats.rx_len_errors;
+       }
+}
+
+/**
+ * ice_update_pf_stats - Update PF port stats counters
+ * @pf: PF whose stats needs to be updated
+ */
+static void ice_update_pf_stats(struct ice_pf *pf)
+{
+       struct ice_hw_port_stats *prev_ps, *cur_ps;
+       struct ice_hw *hw = &pf->hw;
+       u8 pf_id;
+
+       prev_ps = &pf->stats_prev;
+       cur_ps = &pf->stats;
+       pf_id = hw->pf_id;
+
+       ice_stat_update40(hw, GLPRT_GORCH(pf_id), GLPRT_GORCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.rx_bytes,
+                         &cur_ps->eth.rx_bytes);
+
+       ice_stat_update40(hw, GLPRT_UPRCH(pf_id), GLPRT_UPRCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.rx_unicast,
+                         &cur_ps->eth.rx_unicast);
+
+       ice_stat_update40(hw, GLPRT_MPRCH(pf_id), GLPRT_MPRCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.rx_multicast,
+                         &cur_ps->eth.rx_multicast);
+
+       ice_stat_update40(hw, GLPRT_BPRCH(pf_id), GLPRT_BPRCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.rx_broadcast,
+                         &cur_ps->eth.rx_broadcast);
+
+       ice_stat_update40(hw, GLPRT_GOTCH(pf_id), GLPRT_GOTCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.tx_bytes,
+                         &cur_ps->eth.tx_bytes);
+
+       ice_stat_update40(hw, GLPRT_UPTCH(pf_id), GLPRT_UPTCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.tx_unicast,
+                         &cur_ps->eth.tx_unicast);
+
+       ice_stat_update40(hw, GLPRT_MPTCH(pf_id), GLPRT_MPTCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.tx_multicast,
+                         &cur_ps->eth.tx_multicast);
+
+       ice_stat_update40(hw, GLPRT_BPTCH(pf_id), GLPRT_BPTCL(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->eth.tx_broadcast,
+                         &cur_ps->eth.tx_broadcast);
+
+       ice_stat_update32(hw, GLPRT_TDOLD(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->tx_dropped_link_down,
+                         &cur_ps->tx_dropped_link_down);
+
+       ice_stat_update40(hw, GLPRT_PRC64H(pf_id), GLPRT_PRC64L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->rx_size_64,
+                         &cur_ps->rx_size_64);
+
+       ice_stat_update40(hw, GLPRT_PRC127H(pf_id), GLPRT_PRC127L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->rx_size_127,
+                         &cur_ps->rx_size_127);
+
+       ice_stat_update40(hw, GLPRT_PRC255H(pf_id), GLPRT_PRC255L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->rx_size_255,
+                         &cur_ps->rx_size_255);
+
+       ice_stat_update40(hw, GLPRT_PRC511H(pf_id), GLPRT_PRC511L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->rx_size_511,
+                         &cur_ps->rx_size_511);
+
+       ice_stat_update40(hw, GLPRT_PRC1023H(pf_id),
+                         GLPRT_PRC1023L(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_size_1023, &cur_ps->rx_size_1023);
+
+       ice_stat_update40(hw, GLPRT_PRC1522H(pf_id),
+                         GLPRT_PRC1522L(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_size_1522, &cur_ps->rx_size_1522);
+
+       ice_stat_update40(hw, GLPRT_PRC9522H(pf_id),
+                         GLPRT_PRC9522L(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_size_big, &cur_ps->rx_size_big);
+
+       ice_stat_update40(hw, GLPRT_PTC64H(pf_id), GLPRT_PTC64L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->tx_size_64,
+                         &cur_ps->tx_size_64);
+
+       ice_stat_update40(hw, GLPRT_PTC127H(pf_id), GLPRT_PTC127L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->tx_size_127,
+                         &cur_ps->tx_size_127);
+
+       ice_stat_update40(hw, GLPRT_PTC255H(pf_id), GLPRT_PTC255L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->tx_size_255,
+                         &cur_ps->tx_size_255);
+
+       ice_stat_update40(hw, GLPRT_PTC511H(pf_id), GLPRT_PTC511L(pf_id),
+                         pf->stat_prev_loaded, &prev_ps->tx_size_511,
+                         &cur_ps->tx_size_511);
+
+       ice_stat_update40(hw, GLPRT_PTC1023H(pf_id),
+                         GLPRT_PTC1023L(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->tx_size_1023, &cur_ps->tx_size_1023);
+
+       ice_stat_update40(hw, GLPRT_PTC1522H(pf_id),
+                         GLPRT_PTC1522L(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->tx_size_1522, &cur_ps->tx_size_1522);
+
+       ice_stat_update40(hw, GLPRT_PTC9522H(pf_id),
+                         GLPRT_PTC9522L(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->tx_size_big, &cur_ps->tx_size_big);
+
+       ice_stat_update32(hw, GLPRT_LXONRXC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->link_xon_rx, &cur_ps->link_xon_rx);
+
+       ice_stat_update32(hw, GLPRT_LXOFFRXC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->link_xoff_rx, &cur_ps->link_xoff_rx);
+
+       ice_stat_update32(hw, GLPRT_LXONTXC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->link_xon_tx, &cur_ps->link_xon_tx);
+
+       ice_stat_update32(hw, GLPRT_LXOFFTXC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->link_xoff_tx, &cur_ps->link_xoff_tx);
+
+       ice_stat_update32(hw, GLPRT_CRCERRS(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->crc_errors, &cur_ps->crc_errors);
+
+       ice_stat_update32(hw, GLPRT_ILLERRC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->illegal_bytes, &cur_ps->illegal_bytes);
+
+       ice_stat_update32(hw, GLPRT_MLFC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->mac_local_faults,
+                         &cur_ps->mac_local_faults);
+
+       ice_stat_update32(hw, GLPRT_MRFC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->mac_remote_faults,
+                         &cur_ps->mac_remote_faults);
+
+       ice_stat_update32(hw, GLPRT_RLEC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_len_errors, &cur_ps->rx_len_errors);
+
+       ice_stat_update32(hw, GLPRT_RUC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_undersize, &cur_ps->rx_undersize);
+
+       ice_stat_update32(hw, GLPRT_RFC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_fragments, &cur_ps->rx_fragments);
+
+       ice_stat_update32(hw, GLPRT_ROC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_oversize, &cur_ps->rx_oversize);
+
+       ice_stat_update32(hw, GLPRT_RJC(pf_id), pf->stat_prev_loaded,
+                         &prev_ps->rx_jabber, &cur_ps->rx_jabber);
+
+       pf->stat_prev_loaded = true;
+}
+
+/**
+ * ice_get_stats64 - get statistics for network device structure
+ * @netdev: network interface device structure
+ * @stats: main device statistics structure
+ */
+static
+void ice_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct rtnl_link_stats64 *vsi_stats;
+       struct ice_vsi *vsi = np->vsi;
+
+       vsi_stats = &vsi->net_stats;
+
+       if (test_bit(__ICE_DOWN, vsi->state) || !vsi->num_txq || !vsi->num_rxq)
+               return;
+       /* netdev packet/byte stats come from ring counter. These are obtained
+        * by summing up ring counters (done by ice_update_vsi_ring_stats).
+        */
+       ice_update_vsi_ring_stats(vsi);
+       stats->tx_packets = vsi_stats->tx_packets;
+       stats->tx_bytes = vsi_stats->tx_bytes;
+       stats->rx_packets = vsi_stats->rx_packets;
+       stats->rx_bytes = vsi_stats->rx_bytes;
+
+       /* The rest of the stats can be read from the hardware but instead we
+        * just return values that the watchdog task has already obtained from
+        * the hardware.
+        */
+       stats->multicast = vsi_stats->multicast;
+       stats->tx_errors = vsi_stats->tx_errors;
+       stats->tx_dropped = vsi_stats->tx_dropped;
+       stats->rx_errors = vsi_stats->rx_errors;
+       stats->rx_dropped = vsi_stats->rx_dropped;
+       stats->rx_crc_errors = vsi_stats->rx_crc_errors;
+       stats->rx_length_errors = vsi_stats->rx_length_errors;
+}
+
 /**
  * ice_napi_disable_all - Disable NAPI for all q_vectors in the VSI
  * @vsi: VSI having NAPI disabled
@@ -3478,7 +3940,7 @@ static void ice_napi_disable_all(struct ice_vsi *vsi)
  * ice_down - Shutdown the connection
  * @vsi: The VSI being stopped
  */
-static int ice_down(struct ice_vsi *vsi)
+int ice_down(struct ice_vsi *vsi)
 {
        int i, err;
 
@@ -3878,6 +4340,7 @@ static const struct net_device_ops ice_netdev_ops = {
        .ndo_open = ice_open,
        .ndo_stop = ice_stop,
        .ndo_start_xmit = ice_start_xmit,
+       .ndo_get_stats64 = ice_get_stats64,
        .ndo_vlan_rx_add_vid = ice_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid = ice_vlan_rx_kill_vid,
        .ndo_set_features = ice_set_features,
index 991ac56..c7e0de3 100644 (file)
@@ -20,6 +20,7 @@
 #define ICE_DBG_RES            BIT_ULL(17)
 #define ICE_DBG_AQ_MSG         BIT_ULL(24)
 #define ICE_DBG_AQ_CMD         BIT_ULL(27)
+#define ICE_DBG_USER           BIT_ULL(31)
 
 enum ice_aq_res_ids {
        ICE_NVM_RES_ID = 1,
@@ -42,6 +43,13 @@ enum ice_fc_mode {
        ICE_FC_DFLT
 };
 
+enum ice_set_fc_aq_failures {
+       ICE_SET_FC_AQ_FAIL_NONE = 0,
+       ICE_SET_FC_AQ_FAIL_GET,
+       ICE_SET_FC_AQ_FAIL_SET,
+       ICE_SET_FC_AQ_FAIL_UPDATE
+};
+
 /* Various MAC types */
 enum ice_mac_type {
        ICE_MAC_UNKNOWN = 0,
@@ -301,10 +309,72 @@ struct ice_hw {
 
 };
 
+/* Statistics collected by each port, VSI, VEB, and S-channel */
+struct ice_eth_stats {
+       u64 rx_bytes;                   /* gorc */
+       u64 rx_unicast;                 /* uprc */
+       u64 rx_multicast;               /* mprc */
+       u64 rx_broadcast;               /* bprc */
+       u64 rx_discards;                /* rdpc */
+       u64 rx_unknown_protocol;        /* rupp */
+       u64 tx_bytes;                   /* gotc */
+       u64 tx_unicast;                 /* uptc */
+       u64 tx_multicast;               /* mptc */
+       u64 tx_broadcast;               /* bptc */
+       u64 tx_discards;                /* tdpc */
+       u64 tx_errors;                  /* tepc */
+};
+
+/* Statistics collected by the MAC */
+struct ice_hw_port_stats {
+       /* eth stats collected by the port */
+       struct ice_eth_stats eth;
+       /* additional port specific stats */
+       u64 tx_dropped_link_down;       /* tdold */
+       u64 crc_errors;                 /* crcerrs */
+       u64 illegal_bytes;              /* illerrc */
+       u64 error_bytes;                /* errbc */
+       u64 mac_local_faults;           /* mlfc */
+       u64 mac_remote_faults;          /* mrfc */
+       u64 rx_len_errors;              /* rlec */
+       u64 link_xon_rx;                /* lxonrxc */
+       u64 link_xoff_rx;               /* lxoffrxc */
+       u64 link_xon_tx;                /* lxontxc */
+       u64 link_xoff_tx;               /* lxofftxc */
+       u64 rx_size_64;                 /* prc64 */
+       u64 rx_size_127;                /* prc127 */
+       u64 rx_size_255;                /* prc255 */
+       u64 rx_size_511;                /* prc511 */
+       u64 rx_size_1023;               /* prc1023 */
+       u64 rx_size_1522;               /* prc1522 */
+       u64 rx_size_big;                /* prc9522 */
+       u64 rx_undersize;               /* ruc */
+       u64 rx_fragments;               /* rfc */
+       u64 rx_oversize;                /* roc */
+       u64 rx_jabber;                  /* rjc */
+       u64 tx_size_64;                 /* ptc64 */
+       u64 tx_size_127;                /* ptc127 */
+       u64 tx_size_255;                /* ptc255 */
+       u64 tx_size_511;                /* ptc511 */
+       u64 tx_size_1023;               /* ptc1023 */
+       u64 tx_size_1522;               /* ptc1522 */
+       u64 tx_size_big;                /* ptc9522 */
+};
+
 /* Checksum and Shadow RAM pointers */
 #define ICE_SR_NVM_DEV_STARTER_VER     0x18
 #define ICE_SR_NVM_EETRACK_LO          0x2D
 #define ICE_SR_NVM_EETRACK_HI          0x2E
+#define ICE_NVM_VER_LO_SHIFT           0
+#define ICE_NVM_VER_LO_MASK            (0xff << ICE_NVM_VER_LO_SHIFT)
+#define ICE_NVM_VER_HI_SHIFT           12
+#define ICE_NVM_VER_HI_MASK            (0xf << ICE_NVM_VER_HI_SHIFT)
+#define ICE_OEM_VER_PATCH_SHIFT                0
+#define ICE_OEM_VER_PATCH_MASK         (0xff << ICE_OEM_VER_PATCH_SHIFT)
+#define ICE_OEM_VER_BUILD_SHIFT                8
+#define ICE_OEM_VER_BUILD_MASK         (0xffff << ICE_OEM_VER_BUILD_SHIFT)
+#define ICE_OEM_VER_SHIFT              24
+#define ICE_OEM_VER_MASK               (0xff << ICE_OEM_VER_SHIFT)
 #define ICE_SR_SECTOR_SIZE_IN_WORDS    0x800
 #define ICE_SR_WORDS_IN_1KB            512