diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index ac11005..72e11b2 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -328,8 +328,18 @@ enum i40e_port_netdev_type {
 	I40E_PORT_NETDEV_VF
 };
 
+struct port_netdev_pcpu_stats {
+	u64			tx_packets;
+	u64			tx_bytes;
+	u64			tx_drops;
+	u64			rx_packets;
+	u64			rx_bytes;
+	struct u64_stats_sync	syncp;
+};
+
 /* Port representor netdev private structure */
 struct i40e_port_netdev_priv {
+	struct port_netdev_pcpu_stats __percpu *stats;
 	enum i40e_port_netdev_type type;	/* type - PF or VF */
 	struct metadata_dst *dst;		/* port id */
 	void *f;				/* ptr to PF or VF struct */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index e9c5c6b..4f0eebc 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -11001,10 +11001,123 @@ static int i40e_port_netdev_stop(struct net_device *dev)
 	return err;
 }
 
+/**
+ * i40e_port_netdev_get_stats64
+ * @dev: network interface device structure
+ * @stats: netlink stats structure
+ *
+ * Fills the hw statistics from the VSI corresponding to the associated port
+ * netdev
+ **/
+static void
+i40e_port_netdev_get_stats64(struct net_device *netdev,
+			     struct rtnl_link_stats64 *stats)
+{
+	struct i40e_port_netdev_priv *priv = netdev_priv(netdev);
+	struct i40e_vf *vf;
+	struct i40e_pf *pf;
+	struct i40e_vsi *vsi;
+	struct i40e_eth_stats *estats;
+
+	switch (priv->type) {
+	case I40E_PORT_NETDEV_VF:
+		vf = (struct i40e_vf *)priv->f;
+		pf = vf->pf;
+		vsi = pf->vsi[vf->lan_vsi_idx];
+		break;
+	case I40E_PORT_NETDEV_PF:
+		pf = (struct i40e_pf *)priv->f;
+		vsi = pf->vsi[pf->lan_vsi];
+		break;
+	default:
+		return;
+	}
+
+	i40e_update_stats(vsi);
+	estats = &vsi->eth_stats;
+
+	/* TX and RX stats are flipped as we are returning the stats as seen
+	 * at the switch port corresponding to the VF.
+	 */
+	stats->rx_packets = estats->tx_unicast + estats->tx_multicast +
+			    estats->tx_broadcast;
+	stats->tx_packets = estats->rx_unicast + estats->rx_multicast +
+			    estats->rx_broadcast;
+	stats->rx_bytes = estats->tx_bytes;
+	stats->tx_bytes = estats->rx_bytes;
+	stats->rx_dropped = estats->tx_discards;
+	stats->tx_dropped = estats->rx_discards;
+}
+
+/**
+ * i40e_port_netdev_get_cpu_hit_stats64
+ * @dev: network interface device structure
+ * @stats: netlink stats structure
+ *
+ * stats are filled from the priv structure. correspond to the packets
+ * that are seen by the cpu and sent/received via port netdev.
+ **/
+static int
+i40e_port_netdev_get_cpu_hit_stats64(const struct net_device *dev,
+				     struct rtnl_link_stats64 *stats)
+{
+	struct i40e_port_netdev_priv *priv = netdev_priv(dev);
+	int i;
+
+	for_each_possible_cpu(i) {
+		struct port_netdev_pcpu_stats *port_netdev_stats;
+		u64 tbytes, tpkts, tdrops, rbytes, rpkts;
+		unsigned int start;
+
+		port_netdev_stats = per_cpu_ptr(priv->stats, i);
+		do {
+			start = u64_stats_fetch_begin_irq(&port_netdev_stats->syncp);
+			tbytes = port_netdev_stats->tx_bytes;
+			tpkts = port_netdev_stats->tx_packets;
+			tdrops = port_netdev_stats->tx_drops;
+			rbytes = port_netdev_stats->rx_bytes;
+			rpkts = port_netdev_stats->rx_packets;
+		} while (u64_stats_fetch_retry_irq(&port_netdev_stats->syncp, start));
+		stats->tx_bytes += tbytes;
+		stats->tx_packets += tpkts;
+		stats->tx_dropped += tdrops;
+		stats->rx_bytes += rbytes;
+		stats->rx_packets += rpkts;
+	}
+
+	return 0;
+}
+
+static bool
+i40e_port_netdev_has_offload_stats(const struct net_device *dev, int attr_id)
+{
+	switch (attr_id) {
+	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
+		return true;
+	}
+
+	return false;
+}
+
+static int
+i40e_port_netdev_get_offload_stats(int attr_id, const struct net_device *dev,
+				   void *sp)
+{
+	switch (attr_id) {
+	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
+		return i40e_port_netdev_get_cpu_hit_stats64(dev, sp);
+	}
+
+	return -EINVAL;
+}
+
 static const struct net_device_ops i40e_port_netdev_ops = {
 	.ndo_open		= i40e_port_netdev_open,
 	.ndo_stop		= i40e_port_netdev_stop,
 	.ndo_start_xmit		= i40e_port_netdev_start_xmit,
+	.ndo_get_stats64	= i40e_port_netdev_get_stats64,
+	.ndo_has_offload_stats	= i40e_port_netdev_has_offload_stats,
+	.ndo_get_offload_stats	= i40e_port_netdev_get_offload_stats,
 };
 
 /**
@@ -11077,6 +11190,16 @@ int i40e_alloc_port_netdev(void *f, enum i40e_port_netdev_type type)
 		return -EINVAL;
 	}
 
+	priv->stats = netdev_alloc_pcpu_stats(struct port_netdev_pcpu_stats);
+	if (!priv->stats) {
+		dev_err(&pf->pdev->dev,
+			"alloc_pcpu_stats failed for port netdev: %s\n",
+			port_netdev->name);
+		dst_release((struct dst_entry *)priv->dst);
+		free_netdev(port_netdev);
+		return -ENOMEM;
+	}
+
 	port_netdev->netdev_ops = &i40e_port_netdev_ops;
 	eth_hw_addr_random(port_netdev);
 
@@ -11144,6 +11267,7 @@ void i40e_free_port_netdev(void *f, enum i40e_port_netdev_type type)
 			 pf->port_netdev->name);
 		priv = netdev_priv(pf->port_netdev);
 		dst_release((struct dst_entry *)priv->dst);
+		free_percpu(priv->stats);
 		unregister_netdev(pf->port_netdev);
 		free_netdev(pf->port_netdev);
 		pf->port_netdev = NULL;
@@ -11163,6 +11287,7 @@ void i40e_free_port_netdev(void *f, enum i40e_port_netdev_type type)
 			 vf->port_netdev->name);
 		priv = netdev_priv(vf->port_netdev);
 		dst_release((struct dst_entry *)priv->dst);
+		free_percpu(priv->stats);
 		unregister_netdev(vf->port_netdev);
 		free_netdev(vf->port_netdev);
 		vf->port_netdev = NULL;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 86d2510..449a35c 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -1309,7 +1309,9 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring,
 static void i40e_handle_lpbk_skb(struct i40e_ring *rx_ring, struct sk_buff *skb)
 {
 	struct i40e_q_vector *q_vector = rx_ring->q_vector;
+	struct port_netdev_pcpu_stats *port_netdev_stats;
 	struct i40e_pf *pf = rx_ring->vsi->back;
+	struct i40e_port_netdev_priv *priv;
 	struct sk_buff *nskb;
 	struct i40e_vf *vf;
 	struct ethhdr *eth;
@@ -1334,6 +1336,12 @@ static void i40e_handle_lpbk_skb(struct i40e_ring *rx_ring, struct sk_buff *skb)
 				break;
 			nskb->offload_fwd_mark = 1;
 			nskb->dev = vf->port_netdev;
+			priv = netdev_priv(vf->port_netdev);
+			port_netdev_stats = this_cpu_ptr(priv->stats);
+			u64_stats_update_begin(&port_netdev_stats->syncp);
+			port_netdev_stats->rx_packets++;
+			port_netdev_stats->rx_bytes += nskb->len;
+			u64_stats_update_end(&port_netdev_stats->syncp);
 			napi_gro_receive(&q_vector->napi, nskb);
 			break;
 		}
@@ -3283,6 +3291,7 @@ netdev_tx_t i40e_port_netdev_start_xmit(struct sk_buff *skb,
 	struct i40e_vsi *vsi;
 	struct i40e_pf *pf;
 	struct i40e_vf *vf;
+	int ret;
 
 	switch (priv->type) {
 	case I40E_PORT_NETDEV_VF:
@@ -3302,5 +3311,18 @@ netdev_tx_t i40e_port_netdev_start_xmit(struct sk_buff *skb,
 	skb_dst_set(skb, &priv->dst->dst);
 	skb->dev = vsi->netdev;
 
-	return dev_queue_xmit(skb);
+	ret = dev_queue_xmit(skb);
+	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
+		struct port_netdev_pcpu_stats *port_netdev_stats;
+
+		port_netdev_stats = this_cpu_ptr(priv->stats);
+		u64_stats_update_begin(&port_netdev_stats->syncp);
+		port_netdev_stats->tx_packets++;
+		port_netdev_stats->tx_bytes += skb->len;
+		u64_stats_update_end(&port_netdev_stats->syncp);
+	} else {
+		this_cpu_inc(priv->stats->tx_drops);
+	}
+
+	return ret;
 }
