Benjamin Poirier dd418e
From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= <bjorn.topel@intel.com>
Benjamin Poirier dd418e
Date: Wed, 24 May 2017 07:55:34 +0200
Benjamin Poirier dd418e
Subject: i40e: add XDP support for pass and drop actions
Benjamin Poirier dd418e
MIME-Version: 1.0
Benjamin Poirier dd418e
Content-Type: text/plain; charset=UTF-8
Benjamin Poirier dd418e
Content-Transfer-Encoding: 8bit
Benjamin Poirier dd418e
Patch-mainline: v4.13-rc1
Benjamin Poirier dd418e
Git-commit: 0c8493d90b6bb0f5c4fe9217db8f7203f24c0f28
Benjamin Poirier dd418e
References: bsc#1056658 FATE#322188 bsc#1056662 FATE#322186
Benjamin Poirier dd418e
Benjamin Poirier dd418e
This commit adds basic XDP support for i40e derived NICs. All XDP
Benjamin Poirier dd418e
actions will end up in XDP_DROP.
Benjamin Poirier dd418e
Benjamin Poirier dd418e
Signed-off-by: Björn Töpel <bjorn.topel@intel.com>
Benjamin Poirier dd418e
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Benjamin Poirier dd418e
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Benjamin Poirier dd418e
Acked-by: Benjamin Poirier <bpoirier@suse.com>
Benjamin Poirier dd418e
---
Benjamin Poirier dd418e
 drivers/net/ethernet/intel/i40e/i40e.h      |    7 +
Benjamin Poirier dd418e
 drivers/net/ethernet/intel/i40e/i40e_main.c |   87 ++++++++++++++++++
Benjamin Poirier dd418e
 drivers/net/ethernet/intel/i40e/i40e_txrx.c |  130 +++++++++++++++++++++-------
Benjamin Poirier dd418e
 drivers/net/ethernet/intel/i40e/i40e_txrx.h |    1 
Benjamin Poirier dd418e
 4 files changed, 194 insertions(+), 31 deletions(-)
Benjamin Poirier dd418e
Benjamin Poirier dd418e
--- a/drivers/net/ethernet/intel/i40e/i40e.h
Benjamin Poirier dd418e
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
Benjamin Poirier dd418e
@@ -645,6 +645,8 @@ struct i40e_vsi {
Benjamin Poirier dd418e
 	u16 max_frame;
Benjamin Poirier dd418e
 	u16 rx_buf_len;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
+	struct bpf_prog *xdp_prog;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 	/* List of q_vectors allocated to this VSI */
Benjamin Poirier dd418e
 	struct i40e_q_vector **q_vectors;
Benjamin Poirier dd418e
 	int num_q_vectors;
Benjamin Poirier dd418e
@@ -972,4 +974,9 @@ i40e_status i40e_get_npar_bw_setting(str
Benjamin Poirier dd418e
 i40e_status i40e_set_npar_bw_setting(struct i40e_pf *pf);
Benjamin Poirier dd418e
 i40e_status i40e_commit_npar_bw_setting(struct i40e_pf *pf);
Benjamin Poirier dd418e
 void i40e_print_link_message(struct i40e_vsi *vsi, bool isup);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+static inline bool i40e_enabled_xdp_vsi(struct i40e_vsi *vsi)
Benjamin Poirier dd418e
+{
Benjamin Poirier dd418e
+	return !!vsi->xdp_prog;
Benjamin Poirier dd418e
+}
Benjamin Poirier dd418e
 #endif /* _I40E_H_ */
Benjamin Poirier dd418e
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
Benjamin Poirier dd418e
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
Benjamin Poirier dd418e
@@ -27,6 +27,7 @@
Benjamin Poirier dd418e
 #include <linux/etherdevice.h>
Benjamin Poirier dd418e
 #include <linux/of_net.h>
Benjamin Poirier dd418e
 #include <linux/pci.h>
Benjamin Poirier dd418e
+#include <linux/bpf.h>
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 /* Local includes */
Benjamin Poirier dd418e
 #include "i40e.h"
Benjamin Poirier dd418e
@@ -2396,6 +2397,18 @@ static void i40e_sync_filters_subtask(st
Benjamin Poirier dd418e
 }
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 /**
Benjamin Poirier dd418e
+ * i40e_max_xdp_frame_size - returns the maximum allowed frame size for XDP
Benjamin Poirier dd418e
+ * @vsi: the vsi
Benjamin Poirier dd418e
+ **/
Benjamin Poirier dd418e
+static int i40e_max_xdp_frame_size(struct i40e_vsi *vsi)
Benjamin Poirier dd418e
+{
Benjamin Poirier dd418e
+	if (PAGE_SIZE >= 8192 || (vsi->back->flags & I40E_FLAG_LEGACY_RX))
Benjamin Poirier dd418e
+		return I40E_RXBUFFER_2048;
Benjamin Poirier dd418e
+	else
Benjamin Poirier dd418e
+		return I40E_RXBUFFER_3072;
Benjamin Poirier dd418e
+}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+/**
Benjamin Poirier dd418e
  * i40e_change_mtu - NDO callback to change the Maximum Transfer Unit
Benjamin Poirier dd418e
  * @netdev: network interface device structure
Benjamin Poirier dd418e
  * @new_mtu: new value for maximum frame size
Benjamin Poirier dd418e
@@ -2408,6 +2421,13 @@ static int i40e_change_mtu(struct net_de
Benjamin Poirier dd418e
 	struct i40e_vsi *vsi = np->vsi;
Benjamin Poirier dd418e
 	struct i40e_pf *pf = vsi->back;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
+	if (i40e_enabled_xdp_vsi(vsi)) {
Benjamin Poirier dd418e
+		int frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+		if (frame_size > i40e_max_xdp_frame_size(vsi))
Benjamin Poirier dd418e
+			return -EINVAL;
Benjamin Poirier dd418e
+	}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 	netdev_info(netdev, "changing MTU from %d to %d\n",
Benjamin Poirier dd418e
 		    netdev->mtu, new_mtu);
Benjamin Poirier dd418e
 	netdev->mtu = new_mtu;
Benjamin Poirier dd418e
@@ -9311,6 +9331,72 @@ out_err:
Benjamin Poirier dd418e
 	return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
Benjamin Poirier dd418e
 }
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
+/**
Benjamin Poirier dd418e
+ * i40e_xdp_setup - add/remove an XDP program
Benjamin Poirier dd418e
+ * @vsi: VSI to changed
Benjamin Poirier dd418e
+ * @prog: XDP program
Benjamin Poirier dd418e
+ **/
Benjamin Poirier dd418e
+static int i40e_xdp_setup(struct i40e_vsi *vsi,
Benjamin Poirier dd418e
+			  struct bpf_prog *prog)
Benjamin Poirier dd418e
+{
Benjamin Poirier dd418e
+	int frame_size = vsi->netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
Benjamin Poirier dd418e
+	struct i40e_pf *pf = vsi->back;
Benjamin Poirier dd418e
+	struct bpf_prog *old_prog;
Benjamin Poirier dd418e
+	bool need_reset;
Benjamin Poirier dd418e
+	int i;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	/* Don't allow frames that span over multiple buffers */
Benjamin Poirier dd418e
+	if (frame_size > vsi->rx_buf_len)
Benjamin Poirier dd418e
+		return -EINVAL;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	if (!i40e_enabled_xdp_vsi(vsi) && !prog)
Benjamin Poirier dd418e
+		return 0;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	/* When turning XDP on->off/off->on we reset and rebuild the rings. */
Benjamin Poirier dd418e
+	need_reset = (i40e_enabled_xdp_vsi(vsi) != !!prog);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	if (need_reset)
Benjamin Poirier dd418e
+		i40e_prep_for_reset(pf, true);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	old_prog = xchg(&vsi->xdp_prog, prog);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	if (need_reset)
Benjamin Poirier dd418e
+		i40e_reset_and_rebuild(pf, true, true);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	for (i = 0; i < vsi->num_queue_pairs; i++)
Benjamin Poirier dd418e
+		WRITE_ONCE(vsi->rx_rings[i]->xdp_prog, vsi->xdp_prog);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	if (old_prog)
Benjamin Poirier dd418e
+		bpf_prog_put(old_prog);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	return 0;
Benjamin Poirier dd418e
+}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+/**
Benjamin Poirier dd418e
+ * i40e_xdp - implements ndo_xdp for i40e
Benjamin Poirier dd418e
+ * @dev: netdevice
Benjamin Poirier dd418e
+ * @xdp: XDP command
Benjamin Poirier dd418e
+ **/
Benjamin Poirier dd418e
+static int i40e_xdp(struct net_device *dev,
Benjamin Poirier dd418e
+		    struct netdev_xdp *xdp)
Benjamin Poirier dd418e
+{
Benjamin Poirier dd418e
+	struct i40e_netdev_priv *np = netdev_priv(dev);
Benjamin Poirier dd418e
+	struct i40e_vsi *vsi = np->vsi;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	if (vsi->type != I40E_VSI_MAIN)
Benjamin Poirier dd418e
+		return -EINVAL;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	switch (xdp->command) {
Benjamin Poirier dd418e
+	case XDP_SETUP_PROG:
Benjamin Poirier dd418e
+		return i40e_xdp_setup(vsi, xdp->prog);
Benjamin Poirier dd418e
+	case XDP_QUERY_PROG:
Benjamin Poirier dd418e
+		xdp->prog_attached = i40e_enabled_xdp_vsi(vsi);
Benjamin Poirier dd418e
+		return 0;
Benjamin Poirier dd418e
+	default:
Benjamin Poirier dd418e
+		return -EINVAL;
Benjamin Poirier dd418e
+	}
Benjamin Poirier dd418e
+}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 static const struct net_device_ops i40e_netdev_ops = {
Benjamin Poirier dd418e
 	.ndo_open		= i40e_open,
Benjamin Poirier dd418e
 	.ndo_stop		= i40e_close,
Benjamin Poirier dd418e
@@ -9343,6 +9429,7 @@ static const struct net_device_ops i40e_
Benjamin Poirier dd418e
 	.ndo_features_check	= i40e_features_check,
Benjamin Poirier dd418e
 	.ndo_bridge_getlink	= i40e_ndo_bridge_getlink,
Benjamin Poirier dd418e
 	.ndo_bridge_setlink	= i40e_ndo_bridge_setlink,
Benjamin Poirier dd418e
+	.ndo_xdp		= i40e_xdp,
Benjamin Poirier dd418e
 };
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 /**
Benjamin Poirier dd418e
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
Benjamin Poirier dd418e
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
Benjamin Poirier dd418e
@@ -26,6 +26,7 @@
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 #include <linux/prefetch.h>
Benjamin Poirier dd418e
 #include <net/busy_poll.h>
Benjamin Poirier dd418e
+#include <linux/bpf_trace.h>
Benjamin Poirier dd418e
 #include "i40e.h"
Benjamin Poirier dd418e
 #include "i40e_trace.h"
Benjamin Poirier dd418e
 #include "i40e_prototype.h"
Benjamin Poirier dd418e
@@ -1195,6 +1196,7 @@ void i40e_clean_rx_ring(struct i40e_ring
Benjamin Poirier dd418e
 void i40e_free_rx_resources(struct i40e_ring *rx_ring)
Benjamin Poirier dd418e
 {
Benjamin Poirier dd418e
 	i40e_clean_rx_ring(rx_ring);
Benjamin Poirier dd418e
+	rx_ring->xdp_prog = NULL;
Benjamin Poirier dd418e
 	kfree(rx_ring->rx_bi);
Benjamin Poirier dd418e
 	rx_ring->rx_bi = NULL;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
@@ -1241,6 +1243,8 @@ int i40e_setup_rx_descriptors(struct i40
Benjamin Poirier dd418e
 	rx_ring->next_to_clean = 0;
Benjamin Poirier dd418e
 	rx_ring->next_to_use = 0;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
+	rx_ring->xdp_prog = rx_ring->vsi->xdp_prog;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 	return 0;
Benjamin Poirier dd418e
 err:
Benjamin Poirier dd418e
 	kfree(rx_ring->rx_bi);
Benjamin Poirier dd418e
@@ -1593,6 +1597,7 @@ void i40e_process_skb_fields(struct i40e
Benjamin Poirier dd418e
  * i40e_cleanup_headers - Correct empty headers
Benjamin Poirier dd418e
  * @rx_ring: rx descriptor ring packet is being transacted on
Benjamin Poirier dd418e
  * @skb: pointer to current skb being fixed
Benjamin Poirier dd418e
+ * @rx_desc: pointer to the EOP Rx descriptor
Benjamin Poirier dd418e
  *
Benjamin Poirier dd418e
  * Also address the case where we are pulling data in on pages only
Benjamin Poirier dd418e
  * and as such no data is present in the skb header.
Benjamin Poirier dd418e
@@ -1602,8 +1607,25 @@ void i40e_process_skb_fields(struct i40e
Benjamin Poirier dd418e
  *
Benjamin Poirier dd418e
  * Returns true if an error was encountered and skb was freed.
Benjamin Poirier dd418e
  **/
Benjamin Poirier dd418e
-static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb)
Benjamin Poirier dd418e
+static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb,
Benjamin Poirier dd418e
+				 union i40e_rx_desc *rx_desc)
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 {
Benjamin Poirier dd418e
+	/* XDP packets use error pointer so abort at this point */
Benjamin Poirier dd418e
+	if (IS_ERR(skb))
Benjamin Poirier dd418e
+		return true;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	/* ERR_MASK will only have valid bits if EOP set, and
Benjamin Poirier dd418e
+	 * what we are doing here is actually checking
Benjamin Poirier dd418e
+	 * I40E_RX_DESC_ERROR_RXE_SHIFT, since it is the zeroth bit in
Benjamin Poirier dd418e
+	 * the error field
Benjamin Poirier dd418e
+	 */
Benjamin Poirier dd418e
+	if (unlikely(i40e_test_staterr(rx_desc,
Benjamin Poirier dd418e
+				       BIT(I40E_RXD_QW1_ERROR_SHIFT)))) {
Benjamin Poirier dd418e
+		dev_kfree_skb_any(skb);
Benjamin Poirier dd418e
+		return true;
Benjamin Poirier dd418e
+	}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 	/* if eth_skb_pad returns an error the skb was freed */
Benjamin Poirier dd418e
 	if (eth_skb_pad(skb))
Benjamin Poirier dd418e
 		return true;
Benjamin Poirier dd418e
@@ -1776,7 +1798,7 @@ static struct i40e_rx_buffer *i40e_get_r
Benjamin Poirier dd418e
  * i40e_construct_skb - Allocate skb and populate it
Benjamin Poirier dd418e
  * @rx_ring: rx descriptor ring to transact packets on
Benjamin Poirier dd418e
  * @rx_buffer: rx buffer to pull data from
Benjamin Poirier dd418e
- * @size: size of buffer to add to skb
Benjamin Poirier dd418e
+ * @xdp: xdp_buff pointing to the data
Benjamin Poirier dd418e
  *
Benjamin Poirier dd418e
  * This function allocates an skb.  It then populates it with the page
Benjamin Poirier dd418e
  * data from the current receive descriptor, taking care to set up the
Benjamin Poirier dd418e
@@ -1784,9 +1806,9 @@ static struct i40e_rx_buffer *i40e_get_r
Benjamin Poirier dd418e
  */
Benjamin Poirier dd418e
 static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring,
Benjamin Poirier dd418e
 					  struct i40e_rx_buffer *rx_buffer,
Benjamin Poirier dd418e
-					  unsigned int size)
Benjamin Poirier dd418e
+					  struct xdp_buff *xdp)
Benjamin Poirier dd418e
 {
Benjamin Poirier dd418e
-	void *va = page_address(rx_buffer->page) + rx_buffer->page_offset;
Benjamin Poirier dd418e
+	unsigned int size = xdp->data_end - xdp->data;
Benjamin Poirier dd418e
 #if (PAGE_SIZE < 8192)
Benjamin Poirier dd418e
 	unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2;
Benjamin Poirier dd418e
 #else
Benjamin Poirier dd418e
@@ -1796,9 +1818,9 @@ static struct sk_buff *i40e_construct_sk
Benjamin Poirier dd418e
 	struct sk_buff *skb;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 	/* prefetch first cache line of first page */
Benjamin Poirier dd418e
-	prefetch(va);
Benjamin Poirier dd418e
+	prefetch(xdp->data);
Benjamin Poirier dd418e
 #if L1_CACHE_BYTES < 128
Benjamin Poirier dd418e
-	prefetch(va + L1_CACHE_BYTES);
Benjamin Poirier dd418e
+	prefetch(xdp->data + L1_CACHE_BYTES);
Benjamin Poirier dd418e
 #endif
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 	/* allocate a skb to store the frags */
Benjamin Poirier dd418e
@@ -1811,10 +1833,11 @@ static struct sk_buff *i40e_construct_sk
Benjamin Poirier dd418e
 	/* Determine available headroom for copy */
Benjamin Poirier dd418e
 	headlen = size;
Benjamin Poirier dd418e
 	if (headlen > I40E_RX_HDR_SIZE)
Benjamin Poirier dd418e
-		headlen = eth_get_headlen(va, I40E_RX_HDR_SIZE);
Benjamin Poirier dd418e
+		headlen = eth_get_headlen(xdp->data, I40E_RX_HDR_SIZE);
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 	/* align pull length to size of long to optimize memcpy performance */
Benjamin Poirier dd418e
-	memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long)));
Benjamin Poirier dd418e
+	memcpy(__skb_put(skb, headlen), xdp->data,
Benjamin Poirier dd418e
+	       ALIGN(headlen, sizeof(long)));
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 	/* update all of the pointers */
Benjamin Poirier dd418e
 	size -= headlen;
Benjamin Poirier dd418e
@@ -1841,16 +1864,16 @@ static struct sk_buff *i40e_construct_sk
Benjamin Poirier dd418e
  * i40e_build_skb - Build skb around an existing buffer
Benjamin Poirier dd418e
  * @rx_ring: Rx descriptor ring to transact packets on
Benjamin Poirier dd418e
  * @rx_buffer: Rx buffer to pull data from
Benjamin Poirier dd418e
- * @size: size of buffer to add to skb
Benjamin Poirier dd418e
+ * @xdp: xdp_buff pointing to the data
Benjamin Poirier dd418e
  *
Benjamin Poirier dd418e
  * This function builds an skb around an existing Rx buffer, taking care
Benjamin Poirier dd418e
  * to set up the skb correctly and avoid any memcpy overhead.
Benjamin Poirier dd418e
  */
Benjamin Poirier dd418e
 static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring,
Benjamin Poirier dd418e
 				      struct i40e_rx_buffer *rx_buffer,
Benjamin Poirier dd418e
-				      unsigned int size)
Benjamin Poirier dd418e
+				      struct xdp_buff *xdp)
Benjamin Poirier dd418e
 {
Benjamin Poirier dd418e
-	void *va = page_address(rx_buffer->page) + rx_buffer->page_offset;
Benjamin Poirier dd418e
+	unsigned int size = xdp->data_end - xdp->data;
Benjamin Poirier dd418e
 #if (PAGE_SIZE < 8192)
Benjamin Poirier dd418e
 	unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2;
Benjamin Poirier dd418e
 #else
Benjamin Poirier dd418e
@@ -1860,12 +1883,12 @@ static struct sk_buff *i40e_build_skb(st
Benjamin Poirier dd418e
 	struct sk_buff *skb;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 	/* prefetch first cache line of first page */
Benjamin Poirier dd418e
-	prefetch(va);
Benjamin Poirier dd418e
+	prefetch(xdp->data);
Benjamin Poirier dd418e
 #if L1_CACHE_BYTES < 128
Benjamin Poirier dd418e
-	prefetch(va + L1_CACHE_BYTES);
Benjamin Poirier dd418e
+	prefetch(xdp->data + L1_CACHE_BYTES);
Benjamin Poirier dd418e
 #endif
Benjamin Poirier dd418e
 	/* build an skb around the page buffer */
Benjamin Poirier dd418e
-	skb = build_skb(va - I40E_SKB_PAD, truesize);
Benjamin Poirier dd418e
+	skb = build_skb(xdp->data_hard_start, truesize);
Benjamin Poirier dd418e
 	if (unlikely(!skb))
Benjamin Poirier dd418e
 		return NULL;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
@@ -1944,6 +1967,46 @@ static bool i40e_is_non_eop(struct i40e_
Benjamin Poirier dd418e
 	return true;
Benjamin Poirier dd418e
 }
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
+#define I40E_XDP_PASS 0
Benjamin Poirier dd418e
+#define I40E_XDP_CONSUMED 1
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+/**
Benjamin Poirier dd418e
+ * i40e_run_xdp - run an XDP program
Benjamin Poirier dd418e
+ * @rx_ring: Rx ring being processed
Benjamin Poirier dd418e
+ * @xdp: XDP buffer containing the frame
Benjamin Poirier dd418e
+ **/
Benjamin Poirier dd418e
+static struct sk_buff *i40e_run_xdp(struct i40e_ring *rx_ring,
Benjamin Poirier dd418e
+				    struct xdp_buff *xdp)
Benjamin Poirier dd418e
+{
Benjamin Poirier dd418e
+	int result = I40E_XDP_PASS;
Benjamin Poirier dd418e
+	struct bpf_prog *xdp_prog;
Benjamin Poirier dd418e
+	u32 act;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	rcu_read_lock();
Benjamin Poirier dd418e
+	xdp_prog = READ_ONCE(rx_ring->xdp_prog);
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	if (!xdp_prog)
Benjamin Poirier dd418e
+		goto xdp_out;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+	act = bpf_prog_run_xdp(xdp_prog, xdp);
Benjamin Poirier dd418e
+	switch (act) {
Benjamin Poirier dd418e
+	case XDP_PASS:
Benjamin Poirier dd418e
+		break;
Benjamin Poirier dd418e
+	default:
Benjamin Poirier dd418e
+		bpf_warn_invalid_xdp_action(act);
Benjamin Poirier dd418e
+	case XDP_TX:
Benjamin Poirier dd418e
+	case XDP_ABORTED:
Benjamin Poirier dd418e
+		trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
Benjamin Poirier dd418e
+		/* fallthrough -- handle aborts by dropping packet */
Benjamin Poirier dd418e
+	case XDP_DROP:
Benjamin Poirier dd418e
+		result = I40E_XDP_CONSUMED;
Benjamin Poirier dd418e
+		break;
Benjamin Poirier dd418e
+	}
Benjamin Poirier dd418e
+xdp_out:
Benjamin Poirier dd418e
+	rcu_read_unlock();
Benjamin Poirier dd418e
+	return ERR_PTR(-result);
Benjamin Poirier dd418e
+}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
 /**
Benjamin Poirier dd418e
  * i40e_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf
Benjamin Poirier dd418e
  * @rx_ring: rx descriptor ring to transact packets on
Benjamin Poirier dd418e
@@ -1966,6 +2029,7 @@ static int i40e_clean_rx_irq(struct i40e
Benjamin Poirier dd418e
 	while (likely(total_rx_packets < budget)) {
Benjamin Poirier dd418e
 		struct i40e_rx_buffer *rx_buffer;
Benjamin Poirier dd418e
 		union i40e_rx_desc *rx_desc;
Benjamin Poirier dd418e
+		struct xdp_buff xdp;
Benjamin Poirier dd418e
 		unsigned int size;
Benjamin Poirier dd418e
 		u16 vlan_tag;
Benjamin Poirier dd418e
 		u8 rx_ptype;
Benjamin Poirier dd418e
@@ -2006,12 +2070,27 @@ static int i40e_clean_rx_irq(struct i40e
Benjamin Poirier dd418e
 		rx_buffer = i40e_get_rx_buffer(rx_ring, size);
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 		/* retrieve a buffer from the ring */
Benjamin Poirier dd418e
-		if (skb)
Benjamin Poirier dd418e
+		if (!skb) {
Benjamin Poirier dd418e
+			xdp.data = page_address(rx_buffer->page) +
Benjamin Poirier dd418e
+				   rx_buffer->page_offset;
Benjamin Poirier dd418e
+			xdp.data_hard_start = xdp.data -
Benjamin Poirier dd418e
+					      i40e_rx_offset(rx_ring);
Benjamin Poirier dd418e
+			xdp.data_end = xdp.data + size;
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+			skb = i40e_run_xdp(rx_ring, &xdp;;
Benjamin Poirier dd418e
+		}
Benjamin Poirier dd418e
+
Benjamin Poirier dd418e
+		if (IS_ERR(skb)) {
Benjamin Poirier dd418e
+			total_rx_bytes += size;
Benjamin Poirier dd418e
+			total_rx_packets++;
Benjamin Poirier dd418e
+			rx_buffer->pagecnt_bias++;
Benjamin Poirier dd418e
+		} else if (skb) {
Benjamin Poirier dd418e
 			i40e_add_rx_frag(rx_ring, rx_buffer, skb, size);
Benjamin Poirier dd418e
-		else if (ring_uses_build_skb(rx_ring))
Benjamin Poirier dd418e
-			skb = i40e_build_skb(rx_ring, rx_buffer, size);
Benjamin Poirier dd418e
-		else
Benjamin Poirier dd418e
-			skb = i40e_construct_skb(rx_ring, rx_buffer, size);
Benjamin Poirier dd418e
+		} else if (ring_uses_build_skb(rx_ring)) {
Benjamin Poirier dd418e
+			skb = i40e_build_skb(rx_ring, rx_buffer, &xdp;;
Benjamin Poirier dd418e
+		} else {
Benjamin Poirier dd418e
+			skb = i40e_construct_skb(rx_ring, rx_buffer, &xdp;;
Benjamin Poirier dd418e
+		}
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
 		/* exit if we failed to retrieve a buffer */
Benjamin Poirier dd418e
 		if (!skb) {
Benjamin Poirier dd418e
@@ -2026,18 +2105,7 @@ static int i40e_clean_rx_irq(struct i40e
Benjamin Poirier dd418e
 		if (i40e_is_non_eop(rx_ring, rx_desc, skb))
Benjamin Poirier dd418e
 			continue;
Benjamin Poirier dd418e
 
Benjamin Poirier dd418e
-		/* ERR_MASK will only have valid bits if EOP set, and
Benjamin Poirier dd418e
-		 * what we are doing here is actually checking
Benjamin Poirier dd418e
-		 * I40E_RX_DESC_ERROR_RXE_SHIFT, since it is the zeroth bit in
Benjamin Poirier dd418e
-		 * the error field
Benjamin Poirier dd418e
-		 */
Benjamin Poirier dd418e
-		if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) {
Benjamin Poirier dd418e
-			dev_kfree_skb_any(skb);
Benjamin Poirier dd418e
-			skb = NULL;
Benjamin Poirier dd418e
-			continue;
Benjamin Poirier dd418e
-		}
Benjamin Poirier dd418e
-
Benjamin Poirier dd418e
-		if (i40e_cleanup_headers(rx_ring, skb)) {
Benjamin Poirier dd418e
+		if (i40e_cleanup_headers(rx_ring, skb, rx_desc)) {
Benjamin Poirier dd418e
 			skb = NULL;
Benjamin Poirier dd418e
 			continue;
Benjamin Poirier dd418e
 		}
Benjamin Poirier dd418e
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
Benjamin Poirier dd418e
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
Benjamin Poirier dd418e
@@ -360,6 +360,7 @@ struct i40e_ring {
Benjamin Poirier dd418e
 	void *desc;			/* Descriptor ring memory */
Benjamin Poirier dd418e
 	struct device *dev;		/* Used for DMA mapping */
Benjamin Poirier dd418e
 	struct net_device *netdev;	/* netdev ring maps to */
Benjamin Poirier dd418e
+	struct bpf_prog *xdp_prog;
Benjamin Poirier dd418e
 	union {
Benjamin Poirier dd418e
 		struct i40e_tx_buffer *tx_bi;
Benjamin Poirier dd418e
 		struct i40e_rx_buffer *rx_bi;