diff mbox series

[12/15] ice: Add stats and ethtool support

Message ID 20180309172136.9073-13-anirudh.venkataramanan@intel.com
State Changes Requested
Headers show
Series Add ice driver | expand

Commit Message

Anirudh Venkataramanan March 9, 2018, 5:21 p.m. UTC
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
ethtool --phy-statistics devname

Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
---
 drivers/net/ethernet/intel/ice/Makefile         |   3 +-
 drivers/net/ethernet/intel/ice/ice.h            |  31 +-
 drivers/net/ethernet/intel/ice/ice_adminq_cmd.h |  43 ++
 drivers/net/ethernet/intel/ice/ice_common.c     | 195 +++++
 drivers/net/ethernet/intel/ice/ice_common.h     |   5 +
 drivers/net/ethernet/intel/ice/ice_ethtool.c    | 972 ++++++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_hw_autogen.h |  80 ++
 drivers/net/ethernet/intel/ice/ice_main.c       | 470 +++++++++++-
 drivers/net/ethernet/intel/ice/ice_type.h       |  69 ++
 9 files changed, 1863 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/ethernet/intel/ice/ice_ethtool.c

Comments

Andrew Lunn March 9, 2018, 6:14 p.m. UTC | #1
On Fri, Mar 09, 2018 at 09:21:33AM -0800, Anirudh Venkataramanan wrote:
> 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
> ethtool --phy-statistics devname

Maybe i missed it, but i don't see any code for --phy-statistics?

      Andrew
Jakub Kicinski March 9, 2018, 11:14 p.m. UTC | #2
On Fri,  9 Mar 2018 09:21:33 -0800, Anirudh Venkataramanan wrote:
> +static const struct ice_stats ice_net_stats[] = {
> +	ICE_NETDEV_STAT(rx_packets),
> +	ICE_NETDEV_STAT(tx_packets),
> +	ICE_NETDEV_STAT(rx_bytes),
> +	ICE_NETDEV_STAT(tx_bytes),
> +	ICE_NETDEV_STAT(rx_errors),
> +	ICE_NETDEV_STAT(tx_errors),
> +	ICE_NETDEV_STAT(rx_dropped),
> +	ICE_NETDEV_STAT(tx_dropped),
> +	ICE_NETDEV_STAT(multicast),
> +	ICE_NETDEV_STAT(rx_length_errors),
> +	ICE_NETDEV_STAT(rx_crc_errors),
> +};

Please don't duplicate standard netdev stats in ethtool -S.
David Miller March 10, 2018, 2:35 a.m. UTC | #3
From: Jakub Kicinski <kubakici@wp.pl>
Date: Fri, 9 Mar 2018 15:14:28 -0800

> On Fri,  9 Mar 2018 09:21:33 -0800, Anirudh Venkataramanan wrote:
>> +static const struct ice_stats ice_net_stats[] = {
>> +	ICE_NETDEV_STAT(rx_packets),
>> +	ICE_NETDEV_STAT(tx_packets),
>> +	ICE_NETDEV_STAT(rx_bytes),
>> +	ICE_NETDEV_STAT(tx_bytes),
>> +	ICE_NETDEV_STAT(rx_errors),
>> +	ICE_NETDEV_STAT(tx_errors),
>> +	ICE_NETDEV_STAT(rx_dropped),
>> +	ICE_NETDEV_STAT(tx_dropped),
>> +	ICE_NETDEV_STAT(multicast),
>> +	ICE_NETDEV_STAT(rx_length_errors),
>> +	ICE_NETDEV_STAT(rx_crc_errors),
>> +};
> 
> Please don't duplicate standard netdev stats in ethtool -S.

Indeed, unless you are providing per-queue versions of these
stats, don't do this.
Stephen Hemminger March 10, 2018, 4:37 p.m. UTC | #4
On Fri,  9 Mar 2018 09:21:33 -0800
Anirudh Venkataramanan <anirudh.venkataramanan@intel.com> wrote:

> +	/* VSI stats */
> +	struct rtnl_link_stats64 net_stats;
> +	struct rtnl_link_stats64 net_stats_prev;
> +	struct ice_eth_stats eth_stats;
> +	struct ice_eth_stats eth_stats_prev;
> +

There is already a stats64 in netdev.
You might want to also consider per-cpu statistics since incrementing
a shared counter is cache unfriendly.
Stephen Hemminger March 10, 2018, 4:42 p.m. UTC | #5
On Fri,  9 Mar 2018 09:21:33 -0800
Anirudh Venkataramanan <anirudh.venkataramanan@intel.com> wrote:

> +	/* VSI stats */
> +	struct rtnl_link_stats64 net_stats;
> +	struct rtnl_link_stats64 net_stats_prev;
> +	struct ice_eth_stats eth_stats;
> +	struct ice_eth_stats eth_stats_prev;

You also don't need current and previous as separate copies since previous is only
used while computing the current values.
Anirudh Venkataramanan March 13, 2018, 7:05 p.m. UTC | #6
On Fri, 2018-03-09 at 15:14 -0800, Jakub Kicinski wrote:
> On Fri,  9 Mar 2018 09:21:33 -0800, Anirudh Venkataramanan wrote:
> > +static const struct ice_stats ice_net_stats[] = {
> > +	ICE_NETDEV_STAT(rx_packets),
> > +	ICE_NETDEV_STAT(tx_packets),
> > +	ICE_NETDEV_STAT(rx_bytes),
> > +	ICE_NETDEV_STAT(tx_bytes),
> > +	ICE_NETDEV_STAT(rx_errors),
> > +	ICE_NETDEV_STAT(tx_errors),
> > +	ICE_NETDEV_STAT(rx_dropped),
> > +	ICE_NETDEV_STAT(tx_dropped),
> > +	ICE_NETDEV_STAT(multicast),
> > +	ICE_NETDEV_STAT(rx_length_errors),
> > +	ICE_NETDEV_STAT(rx_crc_errors),
> > +};
> 
> Please don't duplicate standard netdev stats in ethtool -S.

Jacub,

Thanks for the feedback. I am not sure I understand what's being asked
here. Do you mean to say that standard netdev stats should not be
printed when we do ethtool -S or something else?

Thanks!
Ani
Eric Dumazet March 13, 2018, 7:17 p.m. UTC | #7
On 03/13/2018 12:05 PM, Venkataramanan, Anirudh wrote:
> On Fri, 2018-03-09 at 15:14 -0800, Jakub Kicinski wrote:
>> On Fri,  9 Mar 2018 09:21:33 -0800, Anirudh Venkataramanan wrote:
>>> +static const struct ice_stats ice_net_stats[] = {
>>> +	ICE_NETDEV_STAT(rx_packets),
>>> +	ICE_NETDEV_STAT(tx_packets),
>>> +	ICE_NETDEV_STAT(rx_bytes),
>>> +	ICE_NETDEV_STAT(tx_bytes),
>>> +	ICE_NETDEV_STAT(rx_errors),
>>> +	ICE_NETDEV_STAT(tx_errors),
>>> +	ICE_NETDEV_STAT(rx_dropped),
>>> +	ICE_NETDEV_STAT(tx_dropped),
>>> +	ICE_NETDEV_STAT(multicast),
>>> +	ICE_NETDEV_STAT(rx_length_errors),
>>> +	ICE_NETDEV_STAT(rx_crc_errors),
>>> +};
>>
>> Please don't duplicate standard netdev stats in ethtool -S.
> 
> Jacub,
> 
> Thanks for the feedback. I am not sure I understand what's being asked
> here. Do you mean to say that standard netdev stats should not be
> printed when we do ethtool -S or something else?
> 
> Thanks!
> Ani
> 

Yes, this is a recurring mistake

See commit
bf909456f6a89654cb65c01fe83a4f9b133bf978 Revert "net: hns3: Add packet 
statistics of netdev"
Anirudh Venkataramanan March 13, 2018, 8:42 p.m. UTC | #8
On Sat, 2018-03-10 at 08:42 -0800, Stephen Hemminger wrote:
> On Fri,  9 Mar 2018 09:21:33 -0800
> Anirudh Venkataramanan <anirudh.venkataramanan@intel.com> wrote:
> 
> > +	/* VSI stats */
> > +	struct rtnl_link_stats64 net_stats;
> > +	struct rtnl_link_stats64 net_stats_prev;
> > +	struct ice_eth_stats eth_stats;
> > +	struct ice_eth_stats eth_stats_prev;
> 
> You also don't need current and previous as separate copies since
> previous is only
> used while computing the current values.

Thanks for the feedback, Stephen.

eth_stats_prev is used in ice_update_eth_stats when we update
eth_stats.

While looking into this though, I found that net_stats_prev field in
struct ice_vsi (and consequently *prev_ns and *prev_es pointers in
ice_update_vsi_stats) may not be needed. Is this what you meant?

Thanks!
Ani
David Miller March 13, 2018, 8:54 p.m. UTC | #9
From: "Venkataramanan, Anirudh" <anirudh.venkataramanan@intel.com>
Date: Tue, 13 Mar 2018 19:05:19 +0000

> Thanks for the feedback. I am not sure I understand what's being asked
> here. Do you mean to say that standard netdev stats should not be
> printed when we do ethtool -S or something else?

Yes, that is what he is saying.

It is duplicating information already available with a normal
netdev statistics dump.
Jesse Brandeburg March 13, 2018, 9:14 p.m. UTC | #10
On Tue, 13 Mar 2018 12:17:10 -0700
Eric Dumazet <eric.dumazet@gmail.com> wrote:
> 
> Yes, this is a recurring mistake
> 
> See commit
> bf909456f6a89654cb65c01fe83a4f9b133bf978 Revert "net: hns3: Add packet 
> statistics of netdev"

Thanks for the pointer, that was a useful thread to review.  I
understand the point that was made about not having the netdev stats
shown in ethtool -S.  We definitely do provide per-queue stats as well
as these regular stats in ethtool -S.

I do remember from the past discussions that it *is* useless for the
driver to keep internally any stats that were already stored via the
get_stats NDO, and we missed it in the internal review that this driver
was doing that, so that will be fixed.

Maybe it's just that I've been doing this too long, but I regularly
(and many other customers/users do as well) depend on the ethtool stats
being atomically updated w.r.t. each other.  This means that if I'm
getting the over rx_packets, as well as the per-queue rx_packets, and I
read them all at once from the driver with ethtool, then I can check
that things are working as expected.

If I have to gather the netdev stats from /proc/net/dev (which iproute2
tool shows the /proc/net/dev stats?) and somehow atomically gather the
ethtool -S stats.  What's the user to do in this brave new world where
ethtool doesn't at least have rx/tx bytes and packets?
Eric Dumazet March 13, 2018, 9:44 p.m. UTC | #11
On 03/13/2018 02:14 PM, Jesse Brandeburg wrote:
> On Tue, 13 Mar 2018 12:17:10 -0700
> Eric Dumazet <eric.dumazet@gmail.com> wrote:
>>
>> Yes, this is a recurring mistake
>>
>> See commit
>> bf909456f6a89654cb65c01fe83a4f9b133bf978 Revert "net: hns3: Add packet
>> statistics of netdev"
> 
> Thanks for the pointer, that was a useful thread to review.  I
> understand the point that was made about not having the netdev stats
> shown in ethtool -S.  We definitely do provide per-queue stats as well
> as these regular stats in ethtool -S.
> 
> I do remember from the past discussions that it *is* useless for the
> driver to keep internally any stats that were already stored via the
> get_stats NDO, and we missed it in the internal review that this driver
> was doing that, so that will be fixed.
> 
> Maybe it's just that I've been doing this too long, but I regularly
> (and many other customers/users do as well) depend on the ethtool stats
> being atomically updated w.r.t. each other.  This means that if I'm
> getting the over rx_packets, as well as the per-queue rx_packets, and I
> read them all at once from the driver with ethtool, then I can check
> that things are working as expected.
> 
> If I have to gather the netdev stats from /proc/net/dev (which iproute2
> tool shows the /proc/net/dev stats?) and somehow atomically gather the
> ethtool -S stats.  What's the user to do in this brave new world where
> ethtool doesn't at least have rx/tx bytes and packets?

I do not believe ethtool -S provides an atomic snapshot of counters anyway.

Maybe some drivers/NIC implement such a feature, but it is not documented.

In any case, that could be implemented generically (by core networking 
stack), instead of being copied/pasted in many drivers, if this would be 
really needed.

core networking stack would append (or insert) ndo_stats.
(The atomicity rule if really needed would need to pass an extra struct 
rtnl_link_stats64 pointer to get_ethtool_stats() method)
Toshiaki Makita March 14, 2018, 1:30 a.m. UTC | #12
On 2018/03/14 6:14, Jesse Brandeburg wrote:
> which iproute2 tool shows the /proc/net/dev stats?

ip -s -s link show
Stephen Hemminger March 14, 2018, 3:13 p.m. UTC | #13
On Tue, 13 Mar 2018 14:14:14 -0700
Jesse Brandeburg <jesse.brandeburg@intel.com> wrote:

> Maybe it's just that I've been doing this too long, but I regularly
> (and many other customers/users do as well) depend on the ethtool stats
> being atomically updated w.r.t. each other.  This means that if I'm
> getting the over rx_packets, as well as the per-queue rx_packets, and I
> read them all at once from the driver with ethtool, then I can check
> that things are working as expected.

Any application depending on this behavior is really fragile.
It is unportable to other devices (which I guess is to Intel's benefit)
and relying on guarantees not provided by current Linux network devices.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 0abeb20c006d..643d63016624 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -30,4 +30,5 @@  ice-y := ice_main.o	\
 	 ice_nvm.o	\
 	 ice_switch.o	\
 	 ice_sched.o	\
-	 ice_txrx.o
+	 ice_txrx.o	\
+	 ice_ethtool.o
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 96964d99032d..c09921d43f7a 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -27,12 +27,14 @@ 
 #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>
@@ -48,10 +50,14 @@ 
 #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_MAX_VSI_ALLOC	130
@@ -69,6 +75,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 - \
@@ -115,6 +123,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 */
 };
@@ -131,8 +140,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;
@@ -154,8 +168,15 @@  struct ice_vsi {
 
 	struct ice_aqc_vsi_props info;	 /* VSI properties */
 
+	/* VSI stats */
+	struct rtnl_link_stats64 net_stats;
+	struct rtnl_link_stats64 net_stats_prev;
+	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] */
@@ -218,8 +239,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];
 };
 
@@ -252,8 +275,14 @@  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);
+void ice_update_pf_stats(struct ice_pf *pf);
+void ice_update_vsi_stats(struct ice_vsi *vsi);
+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_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 49102817f0a9..2c8d8533f87d 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -873,6 +873,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;
@@ -1151,6 +1190,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;
@@ -1236,6 +1277,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 */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index d677d5f6d808..b97c0e27196b 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -1272,6 +1272,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
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index 5de509394e21..bc52b7bcc78c 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -72,6 +72,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
index 000000000000..275917ecd7af
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -0,0 +1,972 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/* Intel(R) Ethernet Connection E800 Series Linux Driver
+ * Copyright (c) 2018, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ */
+
+/* ethtool support for ice */
+
+#include "ice.h"
+
+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_STAT(_type, _name, _stat) { \
+	.stat_string = _name, \
+	.sizeof_stat = FIELD_SIZEOF(_type, _stat), \
+	.stat_offset = offsetof(_type, _stat) \
+}
+
+#define ICE_NETDEV_STAT(_net_stat) \
+		ICE_STAT(struct rtnl_link_stats64, #_net_stat, _net_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)
+
+#define ICE_GLOBAL_STATS_LEN	ARRAY_SIZE(ice_gstrings_stats)
+#define ICE_NETDEV_STATS_LEN	ARRAY_SIZE(ice_net_stats)
+#define ICE_MISC_STATS_LEN	ARRAY_SIZE(ice_gstrings_misc_stats)
+#define ICE_VSI_STATS_LEN(n)	(ICE_NETDEV_STATS_LEN + ICE_MISC_STATS_LEN + \
+				 ice_q_stats_len(n))
+#define ICE_PF_STATS_LEN(n)	(ICE_GLOBAL_STATS_LEN + ICE_VSI_STATS_LEN((n)))
+
+struct ice_stats {
+	char stat_string[ETH_GSTRING_LEN];
+	int sizeof_stat;
+	int stat_offset;
+};
+
+static const struct ice_stats ice_net_stats[] = {
+	ICE_NETDEV_STAT(rx_packets),
+	ICE_NETDEV_STAT(tx_packets),
+	ICE_NETDEV_STAT(rx_bytes),
+	ICE_NETDEV_STAT(tx_bytes),
+	ICE_NETDEV_STAT(rx_errors),
+	ICE_NETDEV_STAT(tx_errors),
+	ICE_NETDEV_STAT(rx_dropped),
+	ICE_NETDEV_STAT(tx_dropped),
+	ICE_NETDEV_STAT(multicast),
+	ICE_NETDEV_STAT(rx_length_errors),
+	ICE_NETDEV_STAT(rx_crc_errors),
+};
+
+static const struct ice_stats ice_gstrings_misc_stats[] = {
+	ICE_VSI_STAT("tx_bytes", eth_stats.tx_bytes),
+	ICE_VSI_STAT("rx_bytes", eth_stats.rx_bytes),
+	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_errors", eth_stats.tx_errors),
+	ICE_VSI_STAT("rx_discards", eth_stats.rx_discards),
+	ICE_VSI_STAT("tx_linearize", tx_linearize),
+	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_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 sizeof(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 < sizeof(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_NETDEV_STATS_LEN; i++) {
+			snprintf(p, ETH_GSTRING_LEN, "%s",
+				 ice_net_stats[i].stat_string);
+			p += ETH_GSTRING_LEN;
+		}
+		for (i = 0; i < ICE_MISC_STATS_LEN; i++) {
+			snprintf(p, ETH_GSTRING_LEN, "%s",
+				 ice_gstrings_misc_stats[i].stat_string);
+			p += ETH_GSTRING_LEN;
+		}
+		ice_for_each_txq(vsi, i) {
+			snprintf(p, ETH_GSTRING_LEN, "tx-%u.tx_packets", i);
+			p += ETH_GSTRING_LEN;
+			snprintf(p, ETH_GSTRING_LEN, "tx-%u.tx_bytes", i);
+			p += ETH_GSTRING_LEN;
+		}
+		ice_for_each_rxq(vsi, i) {
+			snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_packets", i);
+			p += ETH_GSTRING_LEN;
+			snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i);
+			p += ETH_GSTRING_LEN;
+		}
+		if (vsi->type != ICE_VSI_PF)
+			return;
+		for (i = 0; i < ICE_GLOBAL_STATS_LEN; i++) {
+			snprintf(p, ETH_GSTRING_LEN, "port.%s",
+				 ice_gstrings_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_PF_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 rtnl_link_stats64 *net_stats;
+	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;
+
+	net_stats = &vsi->net_stats;
+	ice_update_pf_stats(pf);
+	ice_update_vsi_stats(vsi);
+
+	for (j = 0; j < ICE_NETDEV_STATS_LEN; j++) {
+		p = (char *)net_stats + ice_net_stats[j].stat_offset;
+		data[i++] = (ice_net_stats[j].sizeof_stat == sizeof(u64)) ?
+			    *(u64 *)p : *(u32 *)p;
+	}
+	for (j = 0; j < ICE_MISC_STATS_LEN; j++) {
+		p = (char *)vsi + ice_gstrings_misc_stats[j].stat_offset;
+		data[i++] = (ice_gstrings_misc_stats[j].sizeof_stat ==
+			    sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+	}
+	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_GLOBAL_STATS_LEN; j++) {
+		p = (char *)pf + ice_gstrings_stats[j].stat_offset;
+		data[i++] = (ice_gstrings_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;
+		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;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
index 6303489866a4..0d24ec3ca975 100644
--- a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
+++ b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
@@ -108,6 +108,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))
@@ -179,6 +181,7 @@ 
 #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
@@ -194,5 +197,82 @@ 
 #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_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 6fbc783b0d67..644e88f64939 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -23,7 +23,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.";
 
@@ -228,12 +228,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;
@@ -466,6 +495,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 */
@@ -1775,6 +1805,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;
 
@@ -3470,6 +3502,437 @@  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_stats - Update VSI stats counters
+ * @vsi: the VSI to be updated
+ */
+void ice_update_vsi_stats(struct ice_vsi *vsi)
+{
+	struct rtnl_link_stats64 *prev_ns, *cur_ns;  /* netdev stats */
+	struct ice_eth_stats *prev_es, *cur_es;      /* device eth stats */
+	struct ice_pf *pf = vsi->back;
+	struct ice_ring *ring;
+	u64 pkts, bytes;
+	int i;
+
+	if (test_bit(__ICE_DOWN, vsi->state) ||
+	    test_bit(__ICE_CFG_BUSY, pf->state))
+		return;
+
+	/* load VSI's current/previous netdev stats and eth stats */
+	cur_ns = &vsi->net_stats;
+	prev_ns = &vsi->net_stats_prev;
+	cur_es = &vsi->eth_stats;
+	prev_es = &vsi->eth_stats_prev;
+
+	/* reset the stats counters for current netdev and VSI */
+	cur_ns->tx_packets = 0;
+	cur_ns->tx_bytes = 0;
+	vsi->tx_restart = 0;
+	vsi->tx_busy = 0;
+	vsi->tx_linearize = 0;
+	cur_ns->rx_packets = 0;
+	cur_ns->rx_bytes = 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);
+		cur_ns->tx_packets += pkts;
+		cur_ns->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);
+		cur_ns->rx_packets += pkts;
+		cur_ns->rx_bytes += bytes;
+		vsi->rx_buf_failed += ring->rx_stats.alloc_page_failed;
+		vsi->rx_page_failed += ring->rx_stats.alloc_buf_failed;
+	}
+	rcu_read_unlock();
+
+	/* update eth stats from register reads and load into netdev stats
+	 * There are a few instances where we store the same stat in a couple
+	 * of different structs. This is partly because we have the netdev
+	 * stats that need to be filled out, which is slightly different from
+	 * the "eth_stats" defined by the chip and used in VF communications.
+	 */
+	ice_update_eth_stats(vsi);
+	prev_ns->tx_errors = prev_es->tx_errors;
+	cur_ns->tx_errors = cur_es->tx_errors;
+	prev_ns->rx_dropped = prev_es->rx_discards;
+	cur_ns->rx_dropped = cur_es->rx_discards;
+	prev_ns->tx_dropped = prev_es->tx_discards;
+	cur_ns->tx_dropped = cur_es->tx_discards;
+	prev_ns->multicast = prev_es->rx_multicast;
+	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
+ */
+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;
+	struct ice_ring *ring;
+	u64 pkts, bytes;
+	int i;
+
+	vsi_stats = &vsi->net_stats;
+
+	if (test_bit(__ICE_DOWN, vsi->state) || !vsi->num_txq || !vsi->num_rxq)
+		return;
+
+	rcu_read_lock();
+	ice_for_each_txq(vsi, i) {
+		ring = READ_ONCE(vsi->tx_rings[i]);
+		ice_fetch_u64_stats_per_ring(ring, &pkts, &bytes);
+		stats->tx_packets += pkts;
+		stats->tx_bytes += bytes;
+	}
+	ice_for_each_rxq(vsi, i) {
+		ring = READ_ONCE(vsi->rx_rings[i]);
+		ice_fetch_u64_stats_per_ring(ring, &pkts, &bytes);
+		stats->rx_packets += pkts;
+		stats->rx_bytes += bytes;
+	}
+	rcu_read_unlock();
+
+	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
@@ -3489,7 +3952,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;
 
@@ -3889,6 +4352,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,
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 2f1ad8cb2456..66143d1fd2b5 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -34,6 +34,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,
@@ -56,6 +57,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,
@@ -315,10 +323,71 @@  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 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