[PATCH RFC v2 0/8] xdp: Generalize XDP

STALE3475d

Revision v2 of 2 in this series.

31 messages, 8 authors, 2017-02-14 · open the first message on its own page

[PATCH RFC v2 0/8] xdp: Generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-09 00:01:34

This patch set generalizes XDP by making the hooks in drivers to be
generic. This has a number of advantages:

  - Allows alternative users of the XDP hooks other than the original
    BPF
  - Allows a means to pipeline XDP programs together
  - Reduces the amount of code and complexity needed in drivers to
    manage XDP
  - Provides a more structured environment that is extensible to new
    features while being mostly transparent to the drivers

The generic XDP infrastructure is based on an xdp_hook structure that
contains callback functions and private data structure that can be
populated by the user of XDP. The XDP hooks are registered either on a
netdev or a napi (both maintain a list of XDP hooks). Allowing per
netdev hooks makes management of XDP a lot simpler when the intent is
for the hook to apply to the whole device (as is the case with XDP_BPF
so far).  Multiple xdp hooks may be registered on a device or napi
instance, the order of execution is indicated in the priority field of
the xdp_hook structure. Execution of the list contains to the end or
until a program returns something other than XDP_PASS. If both
napi XDP hooks and device hooks are enabled, the NAPI hooks are run
first.

The xdp_hook structure contains a "hookfn" field that is the function
executes a hook. The "priv" structure is private data that is provided
as an argument to hookfn-- in the case of a BPF hook this is simply
the bpf_prog.

Hooks may be registered by xdp_register_dev_hook or
xdp_register_napi_hook, and subsequently they can be unregistered
but xdp_unregister_dev_hook and xdp_unregister_napi_hook. The
identifier for a hook is the pointer to the template hook that was
used to register the hook. xdp_find_dev_hook and
xdp_find_napi_hook will return whether a hook has been registered
and optionally return the contents of the hook. xdp_bpf_check_prog
is called for BPF programs to check if the driver is okay with
running the program (uses the XDP_CHECK_BPF_PROG ndo command
described below).

Driver interface:

Drivers no longer deal with BPF programs for the most part, instead
they call into the XDP interface.

There are two functions of interest for use in the receive data path:
  - xdp_hook_run_needed_check: returns true if there is an XDP
    program registered on the napi instance or its device
  - xdp_hook_run, xdp_hook_run_ret_last: runs the XDP programs for
    the hooks registered for the given napi instance or its device.
    The latter variant returns a pointer to the last XDP hook that
    was run (useful for reporting).

The ndo_xdp defines a new set of commands for this interface. A driver
should implement these commands:
  - XDP_MODE_ON: Initialize device to use XDP. Called when first XDP
		 program is registered on a device (including on a NAPI
		 instance).
  - XDP_MODE_OFF: XDP is finished on the device. Called after the last
		  XDP hook has been unregistered for a device.
  - XDP_CHECK_BPF_PROG: Check if a BPF program is acceptable to a device
		  to run.
  - XDP_OFFLOAD_BPF: Offload the associated BPF program (e.g. Netronome).

A new net feature is added NETIF_F_XDP so that a driver indicates
that is supports XDP.

This patch set:
  - Adds the infrastructure described above include xdp.c and xdp.h files.
  - Modifies mlx4, mlx5, qede, nfp, and virt_net drivers to use the new
    interface. That is mostly removed the management of BPF programs and
    changing to call the new interface.

v2:
  - Eliminate use of nfhooks like lists. Just use use simple array for
    the hooks
  - Modify more drivers that now support XDP

Tested: TBD


Tom Herbert (8):
  xdp: Infrastructure to generalize XDP
  mlx4: Changes to use generic XDP infrastructure
  nfp: Changes to use generic XDP infrastructure
  qede: Changes to use generic XDP infrastructure
  virt_net: Changes to use generic XDP infrastructure
  mlx5: Changes to use generic XDP infrastructure
  bnxt: Changes to use generic XDP infrastructure
  xdp: Cleanup after API changes

 drivers/net/ethernet/broadcom/bnxt/bnxt.c          |  14 -
 drivers/net/ethernet/broadcom/bnxt/bnxt.h          |   2 +-
 drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c      |  46 +--
 drivers/net/ethernet/mellanox/mlx4/en_netdev.c     |  92 ++----
 drivers/net/ethernet/mellanox/mlx4/en_rx.c         |  27 +-
 drivers/net/ethernet/mellanox/mlx4/en_tx.c         |   1 +
 drivers/net/ethernet/mellanox/mlx4/mlx4_en.h       |   1 -
 drivers/net/ethernet/mellanox/mlx5/core/en.h       |   3 +-
 drivers/net/ethernet/mellanox/mlx5/core/en_main.c  | 105 ++-----
 drivers/net/ethernet/mellanox/mlx5/core/en_rx.c    |  12 +-
 drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c   |   1 +
 drivers/net/ethernet/netronome/nfp/nfp_net.h       |   5 +-
 .../net/ethernet/netronome/nfp/nfp_net_common.c    | 170 ++++++-----
 .../net/ethernet/netronome/nfp/nfp_net_ethtool.c   |  12 +-
 drivers/net/ethernet/qlogic/qede/qede.h            |   3 +-
 drivers/net/ethernet/qlogic/qede/qede_ethtool.c    |   2 +-
 drivers/net/ethernet/qlogic/qede/qede_filter.c     |  39 ++-
 drivers/net/ethernet/qlogic/qede/qede_fp.c         |  36 ++-
 drivers/net/ethernet/qlogic/qede/qede_main.c       |  23 +-
 drivers/net/virtio_net.c                           |  98 +++----
 include/linux/filter.h                             |  11 +-
 include/linux/netdev_features.h                    |   3 +-
 include/linux/netdevice.h                          |  27 +-
 include/net/xdp.h                                  | 310 +++++++++++++++++++++
 include/trace/events/xdp.h                         |  16 +-
 net/core/Makefile                                  |   2 +-
 net/core/dev.c                                     |  53 ++--
 net/core/filter.c                                  |   7 +-
 net/core/rtnetlink.c                               |  14 +-
 net/core/xdp.c                                     | 304 ++++++++++++++++++++
 30 files changed, 942 insertions(+), 497 deletions(-)
 create mode 100644 include/net/xdp.h
 create mode 100644 net/core/xdp.c

-- 
2.9.3

[PATCH RFC v2 4/8] qede: Changes to use generic XDP infrastructure

From: Tom Herbert <hidden>
Date: 2017-02-09 00:01:50

Change XDP program management functional interface to correspond to new
XDP API.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/qlogic/qede/qede.h         |  3 +-
 drivers/net/ethernet/qlogic/qede/qede_ethtool.c |  2 +-
 drivers/net/ethernet/qlogic/qede/qede_filter.c  | 39 ++++++++++---------------
 drivers/net/ethernet/qlogic/qede/qede_fp.c      | 36 +++++++++++++----------
 drivers/net/ethernet/qlogic/qede/qede_main.c    | 23 ++++-----------
 5 files changed, 44 insertions(+), 59 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
index b423406..e1baf88 100644
--- a/drivers/net/ethernet/qlogic/qede/qede.h
+++ b/drivers/net/ethernet/qlogic/qede/qede.h
@@ -213,10 +213,9 @@ struct qede_dev {
 	u16				geneve_dst_port;
 
 	bool wol_enabled;
+	bool xdp_enabled;
 
 	struct qede_rdma_dev		rdma_info;
-
-	struct bpf_prog *xdp_prog;
 };
 
 enum QEDE_STATE {
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index baf2642..5559d6e 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -341,7 +341,7 @@ static int qede_get_sset_count(struct net_device *dev, int stringset)
 		num_stats += QEDE_RSS_COUNT(edev) * QEDE_NUM_RQSTATS;
 
 		/* Account for XDP statistics [if needed] */
-		if (edev->xdp_prog)
+		if (edev->xdp_enabled)
 			num_stats += QEDE_RSS_COUNT(edev) * QEDE_NUM_TQSTATS;
 		return num_stats;
 
diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c
index 107c3fd..9c9db44 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_filter.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c
@@ -426,7 +426,7 @@ int qede_set_features(struct net_device *dev, netdev_features_t features)
 		 * aggregations, so no need to actually reload.
 		 */
 		__qede_lock(edev);
-		if (edev->xdp_prog)
+		if (edev->xdp_enabled)
 			args.func(edev, &args);
 		else
 			qede_reload(edev, &args, true);
@@ -506,29 +506,21 @@ void qede_udp_tunnel_del(struct net_device *dev, struct udp_tunnel_info *ti)
 	schedule_delayed_work(&edev->sp_task, 0);
 }
 
-static void qede_xdp_reload_func(struct qede_dev *edev,
-				 struct qede_reload_args *args)
+static int qede_xdp_check_bpf(struct qede_dev *edev, struct bpf_prog *prog)
 {
-	struct bpf_prog *old;
-
-	old = xchg(&edev->xdp_prog, args->u.new_prog);
-	if (old)
-		bpf_prog_put(old);
-}
-
-static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog)
-{
-	struct qede_reload_args args;
-
 	if (prog && prog->xdp_adjust_head) {
 		DP_ERR(edev, "Does not support bpf_xdp_adjust_head()\n");
 		return -EOPNOTSUPP;
 	}
 
-	/* If we're called, there was already a bpf reference increment */
-	args.func = &qede_xdp_reload_func;
-	args.u.new_prog = prog;
-	qede_reload(edev, &args, false);
+	return 0;
+}
+
+static int qede_xdp_init(struct qede_dev *edev, bool enable)
+{
+	edev->xdp_enabled = enable;
+
+	qede_reload(edev, NULL, false);
 
 	return 0;
 }
@@ -538,11 +530,12 @@ int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp)
 	struct qede_dev *edev = netdev_priv(dev);
 
 	switch (xdp->command) {
-	case XDP_SETUP_PROG:
-		return qede_xdp_set(edev, xdp->prog);
-	case XDP_QUERY_PROG:
-		xdp->prog_attached = !!edev->xdp_prog;
-		return 0;
+	case XDP_MODE_OFF:
+		return qede_xdp_init(edev, true);
+	case XDP_MODE_ON:
+		return qede_xdp_init(edev, false);
+	case XDP_CHECK_BPF_PROG:
+		return qede_xdp_check_bpf(edev, xdp->prog);
 	default:
 		return -EINVAL;
 	}
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index 26848ee..af885c3 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -40,6 +40,7 @@
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 #include <net/ip6_checksum.h>
+#include <net/xdp.h>
 
 #include <linux/qed/qed_if.h>
 #include "qede.h"
@@ -987,13 +988,14 @@ static bool qede_pkt_is_ip_fragmented(struct eth_fast_path_rx_reg_cqe *cqe,
 static bool qede_rx_xdp(struct qede_dev *edev,
 			struct qede_fastpath *fp,
 			struct qede_rx_queue *rxq,
-			struct bpf_prog *prog,
 			struct sw_rx_data *bd,
 			struct eth_fast_path_rx_reg_cqe *cqe)
 {
 	u16 len = le16_to_cpu(cqe->len_on_first_bd);
 	struct xdp_buff xdp;
 	enum xdp_action act;
+	struct xdp_hook *last_hook;
+	bool retval = false;
 
 	xdp.data = page_address(bd->data) + cqe->placement_offset;
 	xdp.data_end = xdp.data + len;
@@ -1003,11 +1005,13 @@ static bool qede_rx_xdp(struct qede_dev *edev,
 	 * side for map helpers.
 	 */
 	rcu_read_lock();
-	act = bpf_prog_run_xdp(prog, &xdp);
-	rcu_read_unlock();
 
-	if (act == XDP_PASS)
-		return true;
+	act = xdp_hook_run_ret_last(&fp->napi, &xdp, &last_hook);
+
+	if (act == XDP_PASS) {
+		retval = true;
+		goto out;
+	}
 
 	/* Count number of packets not to be passed to stack */
 	rxq->xdp_no_pass++;
@@ -1017,8 +1021,8 @@ static bool qede_rx_xdp(struct qede_dev *edev,
 		/* We need the replacement buffer before transmit. */
 		if (qede_alloc_rx_buffer(rxq, true)) {
 			qede_recycle_rx_bd_ring(rxq, 1);
-			trace_xdp_exception(edev->ndev, prog, act);
-			return false;
+			trace_xdp_hook_exception(edev->ndev, last_hook, act);
+			goto out;
 		}
 
 		/* Now if there's a transmission problem, we'd still have to
@@ -1028,22 +1032,25 @@ static bool qede_rx_xdp(struct qede_dev *edev,
 			dma_unmap_page(rxq->dev, bd->mapping,
 				       PAGE_SIZE, DMA_BIDIRECTIONAL);
 			__free_page(bd->data);
-			trace_xdp_exception(edev->ndev, prog, act);
+			trace_xdp_hook_exception(edev->ndev, last_hook, act);
 		}
 
 		/* Regardless, we've consumed an Rx BD */
 		qede_rx_bd_ring_consume(rxq);
-		return false;
+		goto out;
 
 	default:
-		bpf_warn_invalid_xdp_action(act);
+		xdp_warn_invalid_action(act);
 	case XDP_ABORTED:
-		trace_xdp_exception(edev->ndev, prog, act);
+		trace_xdp_hook_exception(edev->ndev, last_hook, act);
 	case XDP_DROP:
 		qede_recycle_rx_bd_ring(rxq, cqe->bd_num);
 	}
 
-	return false;
+out:
+	rcu_read_unlock();
+
+	return retval;
 }
 
 static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev,
@@ -1188,7 +1195,6 @@ static int qede_rx_process_cqe(struct qede_dev *edev,
 			       struct qede_fastpath *fp,
 			       struct qede_rx_queue *rxq)
 {
-	struct bpf_prog *xdp_prog = READ_ONCE(rxq->xdp_prog);
 	struct eth_fast_path_rx_reg_cqe *fp_cqe;
 	u16 len, pad, bd_cons_idx, parse_flag;
 	enum eth_rx_cqe_type cqe_type;
@@ -1226,8 +1232,8 @@ static int qede_rx_process_cqe(struct qede_dev *edev,
 	pad = fp_cqe->placement_offset;
 
 	/* Run eBPF program if one is attached */
-	if (xdp_prog)
-		if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe))
+	if (xdp_hook_run_needed_check(edev->ndev, &fp->napi))
+		if (!qede_rx_xdp(edev, fp, rxq, bd, fp_cqe))
 			return 1;
 
 	/* If this is an error packet then drop it */
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index 40a76a1..91babcc 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -560,7 +560,7 @@ static void qede_init_ndev(struct qede_dev *edev)
 {
 	struct net_device *ndev = edev->ndev;
 	struct pci_dev *pdev = edev->pdev;
-	u32 hw_features;
+	netdev_features_t hw_features;
 
 	pci_set_drvdata(pdev, ndev);
 
@@ -580,7 +580,7 @@ static void qede_init_ndev(struct qede_dev *edev)
 	/* user-changeble features */
 	hw_features = NETIF_F_GRO | NETIF_F_SG |
 		      NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
-		      NETIF_F_TSO | NETIF_F_TSO6;
+		      NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_XDP;
 
 	/* Encap features*/
 	hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL |
@@ -709,7 +709,7 @@ static int qede_alloc_fp_array(struct qede_dev *edev)
 			if (!fp->rxq)
 				goto err;
 
-			if (edev->xdp_prog) {
+			if (edev->xdp_enabled) {
 				fp->xdp_tx = kzalloc(sizeof(*fp->xdp_tx),
 						     GFP_KERNEL);
 				if (!fp->xdp_tx)
@@ -913,10 +913,6 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
 
 	pci_set_drvdata(pdev, NULL);
 
-	/* Release edev's reference to XDP's bpf if such exist */
-	if (edev->xdp_prog)
-		bpf_prog_put(edev->xdp_prog);
-
 	free_netdev(ndev);
 
 	/* Use global ops since we've freed edev */
@@ -1069,7 +1065,7 @@ static int qede_alloc_sge_mem(struct qede_dev *edev, struct qede_rx_queue *rxq)
 	int i;
 
 	/* Don't perform FW aggregations in case of XDP */
-	if (edev->xdp_prog)
+	if (edev->xdp_enabled)
 		edev->gro_disable = 1;
 
 	if (edev->gro_disable)
@@ -1127,7 +1123,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
 	/* Segment size to spilt a page in multiple equal parts,
 	 * unless XDP is used in which case we'd use the entire page.
 	 */
-	if (!edev->xdp_prog)
+	if (!edev->xdp_enabled)
 		rxq->rx_buf_seg_size = roundup_pow_of_two(rxq->rx_buf_size);
 	else
 		rxq->rx_buf_seg_size = PAGE_SIZE;
@@ -1580,8 +1576,6 @@ static int qede_stop_queues(struct qede_dev *edev)
 			rc = qede_stop_txq(edev, fp->xdp_tx, i);
 			if (rc)
 				return rc;
-
-			bpf_prog_put(fp->rxq->xdp_prog);
 		}
 	}
 
@@ -1724,13 +1718,6 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
 			rc = qede_start_txq(edev, fp, fp->xdp_tx, i, XDP_PI);
 			if (rc)
 				goto out;
-
-			fp->rxq->xdp_prog = bpf_prog_add(edev->xdp_prog, 1);
-			if (IS_ERR(fp->rxq->xdp_prog)) {
-				rc = PTR_ERR(fp->rxq->xdp_prog);
-				fp->rxq->xdp_prog = NULL;
-				goto out;
-			}
 		}
 
 		if (fp->type & QEDE_FASTPATH_TX) {
-- 
2.9.3

[PATCH RFC v2 6/8] mlx5: Changes to use generic XDP infrastructure

From: Tom Herbert <hidden>
Date: 2017-02-09 00:20:25

Change XDP program management functional interface to correspond to new
XDP API.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/mellanox/mlx5/core/en.h      |   3 +-
 drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 105 ++++++----------------
 drivers/net/ethernet/mellanox/mlx5/core/en_rx.c   |  12 +--
 3 files changed, 33 insertions(+), 87 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 95ca03c..0255423 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -381,7 +381,6 @@ struct mlx5e_rq {
 	u16                    rx_headroom;
 
 	struct mlx5e_rx_am     am; /* Adaptive Moderation */
-	struct bpf_prog       *xdp_prog;
 
 	/* control */
 	struct mlx5_wq_ctrl    wq_ctrl;
@@ -695,7 +694,7 @@ struct mlx5e_priv {
 	/* priv data path fields - start */
 	struct mlx5e_sq            **txq_to_sq_map;
 	int channeltc_to_txq_map[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC];
-	struct bpf_prog *xdp_prog;
+	bool			   xdp_enabled;
 	/* priv data path fields - end */
 
 	unsigned long              state;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 3cce628..da91cf52 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -36,6 +36,7 @@
 #include <linux/mlx5/fs.h>
 #include <net/vxlan.h>
 #include <linux/bpf.h>
+#include <net/xdp.h>
 #include "en.h"
 #include "en_tc.h"
 #include "eswitch.h"
@@ -113,7 +114,7 @@ static void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type)
 static void mlx5e_set_rq_priv_params(struct mlx5e_priv *priv)
 {
 	u8 rq_type = mlx5e_check_fragmented_striding_rq_cap(priv->mdev) &&
-		    !priv->xdp_prog ?
+		    !priv->xdp_enabled ?
 		    MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ :
 		    MLX5_WQ_TYPE_LINKED_LIST;
 	mlx5e_set_rq_type_params(priv, rq_type);
@@ -568,14 +569,12 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
 	rq->ix      = c->ix;
 	rq->priv    = c->priv;
 
-	rq->xdp_prog = priv->xdp_prog ? bpf_prog_inc(priv->xdp_prog) : NULL;
-	if (IS_ERR(rq->xdp_prog)) {
-		err = PTR_ERR(rq->xdp_prog);
-		rq->xdp_prog = NULL;
-		goto err_rq_wq_destroy;
-	}
-
-	if (rq->xdp_prog) {
+	if (priv->xdp_enabled) {
+		/* Note XDP is checked whether it is enabled for the device. If
+		 * XDP programs are set per ring as opposed to setting program
+		 * across the device this could be adjusted to account for
+		 * that.
+		 */
 		rq->buff.map_dir = DMA_BIDIRECTIONAL;
 		rq->rx_headroom = XDP_PACKET_HEADROOM;
 	} else {
@@ -662,8 +661,6 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
 	mlx5_core_destroy_mkey(mdev, &rq->umr_mkey);
 
 err_rq_wq_destroy:
-	if (rq->xdp_prog)
-		bpf_prog_put(rq->xdp_prog);
 	mlx5_wq_destroy(&rq->wq_ctrl);
 
 	return err;
@@ -673,9 +670,6 @@ static void mlx5e_destroy_rq(struct mlx5e_rq *rq)
 {
 	int i;
 
-	if (rq->xdp_prog)
-		bpf_prog_put(rq->xdp_prog);
-
 	switch (rq->wq_type) {
 	case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ:
 		mlx5e_rq_free_mpwqe_info(rq);
@@ -1547,7 +1541,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
 	c->netdev   = priv->netdev;
 	c->mkey_be  = cpu_to_be32(priv->mdev->mlx5e_res.mkey.key);
 	c->num_tc   = priv->params.num_tc;
-	c->xdp      = !!priv->xdp_prog;
+	c->xdp      = priv->xdp_enabled;
 
 	if (priv->params.rx_am_enabled)
 		rx_cq_profile = mlx5e_am_get_def_profile(priv->params.rx_cq_period_mode);
@@ -3196,96 +3190,52 @@ static void mlx5e_tx_timeout(struct net_device *dev)
 		schedule_work(&priv->tx_timeout_work);
 }
 
-static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog)
+static int mlx5e_xdp_init(struct net_device *netdev, bool enable)
 {
 	struct mlx5e_priv *priv = netdev_priv(netdev);
-	struct bpf_prog *old_prog;
 	int err = 0;
-	bool reset, was_opened;
-	int i;
+	bool was_opened;
 
 	mutex_lock(&priv->state_lock);
 
-	if ((netdev->features & NETIF_F_LRO) && prog) {
+	if (priv->xdp_enabled == enable)
+		goto unlock;
+
+	if ((netdev->features & NETIF_F_LRO) && enable) {
 		netdev_warn(netdev, "can't set XDP while LRO is on, disable LRO first\n");
 		err = -EINVAL;
 		goto unlock;
 	}
 
 	was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
-	/* no need for full reset when exchanging programs */
-	reset = (!priv->xdp_prog || !prog);
 
-	if (was_opened && reset)
+	if (was_opened)
 		mlx5e_close_locked(netdev);
-	if (was_opened && !reset) {
-		/* num_channels is invariant here, so we can take the
-		 * batched reference right upfront.
-		 */
-		prog = bpf_prog_add(prog, priv->params.num_channels);
-		if (IS_ERR(prog)) {
-			err = PTR_ERR(prog);
-			goto unlock;
-		}
-	}
 
-	/* exchange programs, extra prog reference we got from caller
-	 * as long as we don't fail from this point onwards.
-	 */
-	old_prog = xchg(&priv->xdp_prog, prog);
-	if (old_prog)
-		bpf_prog_put(old_prog);
-
-	if (reset) /* change RQ type according to priv->xdp_prog */
-		mlx5e_set_rq_priv_params(priv);
+	mlx5e_set_rq_priv_params(priv);
 
-	if (was_opened && reset)
+	if (was_opened)
 		mlx5e_open_locked(netdev);
 
-	if (!test_bit(MLX5E_STATE_OPENED, &priv->state) || reset)
-		goto unlock;
-
-	/* exchanging programs w/o reset, we update ref counts on behalf
-	 * of the channels RQs here.
-	 */
-	for (i = 0; i < priv->params.num_channels; i++) {
-		struct mlx5e_channel *c = priv->channel[i];
-
-		clear_bit(MLX5E_RQ_STATE_ENABLED, &c->rq.state);
-		napi_synchronize(&c->napi);
-		/* prevent mlx5e_poll_rx_cq from accessing rq->xdp_prog */
-
-		old_prog = xchg(&c->rq.xdp_prog, prog);
-
-		set_bit(MLX5E_RQ_STATE_ENABLED, &c->rq.state);
-		/* napi_schedule in case we have missed anything */
-		set_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags);
-		napi_schedule(&c->napi);
-
-		if (old_prog)
-			bpf_prog_put(old_prog);
-	}
-
 unlock:
 	mutex_unlock(&priv->state_lock);
 	return err;
 }
 
-static bool mlx5e_xdp_attached(struct net_device *dev)
+static int mlx5_xdp_check_bpf(struct net_device *dev, struct bpf_prog *prog)
 {
-	struct mlx5e_priv *priv = netdev_priv(dev);
-
-	return !!priv->xdp_prog;
+	return 0;
 }
 
 static int mlx5e_xdp(struct net_device *dev, struct netdev_xdp *xdp)
 {
 	switch (xdp->command) {
-	case XDP_SETUP_PROG:
-		return mlx5e_xdp_set(dev, xdp->prog);
-	case XDP_QUERY_PROG:
-		xdp->prog_attached = mlx5e_xdp_attached(dev);
-		return 0;
+	case XDP_MODE_ON:
+		return mlx5e_xdp_init(dev, true);
+	case XDP_MODE_OFF:
+		return mlx5e_xdp_init(dev, false);
+	case XDP_CHECK_BPF_PROG:
+		return mlx5_xdp_check_bpf(dev, xdp->prog);
 	default:
 		return -EINVAL;
 	}
@@ -3706,9 +3656,6 @@ static void mlx5e_nic_init(struct mlx5_core_dev *mdev,
 static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
 {
 	mlx5e_vxlan_cleanup(priv);
-
-	if (priv->xdp_prog)
-		bpf_prog_put(priv->xdp_prog);
 }
 
 static int mlx5e_init_nic_rx(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index b039b87..50ab4b9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -721,18 +721,18 @@ static inline int mlx5e_xdp_handle(struct mlx5e_rq *rq,
 				   struct mlx5e_dma_info *di,
 				   void *va, u16 *rx_headroom, u32 *len)
 {
-	const struct bpf_prog *prog = READ_ONCE(rq->xdp_prog);
 	struct xdp_buff xdp;
+	struct xdp_hook *last_hook;
 	u32 act;
 
-	if (!prog)
+	if (!xdp_hook_run_needed_check(rq->netdev, rq->cq.napi))
 		return false;
 
 	xdp.data = va + *rx_headroom;
 	xdp.data_end = xdp.data + *len;
 	xdp.data_hard_start = va;
 
-	act = bpf_prog_run_xdp(prog, &xdp);
+	act = xdp_hook_run_ret_last(rq->cq.napi, &xdp, &last_hook);
 	switch (act) {
 	case XDP_PASS:
 		*rx_headroom = xdp.data - xdp.data_hard_start;
@@ -740,12 +740,12 @@ static inline int mlx5e_xdp_handle(struct mlx5e_rq *rq,
 		return false;
 	case XDP_TX:
 		if (unlikely(!mlx5e_xmit_xdp_frame(rq, di, &xdp)))
-			trace_xdp_exception(rq->netdev, prog, act);
+			trace_xdp_hook_exception(rq->netdev, last_hook, act);
 		return true;
 	default:
-		bpf_warn_invalid_xdp_action(act);
+		xdp_warn_invalid_action(act);
 	case XDP_ABORTED:
-		trace_xdp_exception(rq->netdev, prog, act);
+		trace_xdp_hook_exception(rq->netdev, last_hook, act);
 	case XDP_DROP:
 		rq->stats.xdp_drop++;
 		mlx5e_page_release(rq, di, true);
-- 
2.9.3

[PATCH RFC v2 3/8] nfp: Changes to use generic XDP infrastructure

From: Tom Herbert <hidden>
Date: 2017-02-09 00:25:45

Change XDP program management functional interface to correspond to new
XDP API.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/netronome/nfp/nfp_net.h       |   5 +-
 .../net/ethernet/netronome/nfp/nfp_net_common.c    | 172 ++++++++++-----------
 .../net/ethernet/netronome/nfp/nfp_net_ethtool.c   |  12 +-
 3 files changed, 87 insertions(+), 102 deletions(-)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index 2115f44..09a315e 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -504,14 +504,13 @@ struct nfp_net {
 	unsigned fw_loaded:1;
 	unsigned bpf_offload_skip_sw:1;
 	unsigned bpf_offload_xdp:1;
+	unsigned xdp_enabled:1;
 
 	u32 ctrl;
 	u32 fl_bufsz;
 
 	u32 rx_offset;
 
-	struct bpf_prog *xdp_prog;
-
 	struct nfp_net_tx_ring *tx_rings;
 	struct nfp_net_rx_ring *rx_rings;
 
@@ -792,7 +791,7 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
 int nfp_net_irqs_alloc(struct nfp_net *nn);
 void nfp_net_irqs_disable(struct nfp_net *nn);
 int
-nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
+nfp_net_ring_reconfig(struct nfp_net *nn,
 		      struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx);
 
 #ifdef CONFIG_NFP_NET_DEBUG
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 6ac43ab..2dee867 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -65,6 +65,7 @@
 
 #include <net/pkt_cls.h>
 #include <net/vxlan.h>
+#include <net/xdp.h>
 
 #include "nfp_net_ctrl.h"
 #include "nfp_net.h"
@@ -1166,10 +1167,10 @@ nfp_net_napi_alloc_one(struct nfp_net *nn, int direction, dma_addr_t *dma_addr)
 {
 	void *frag;
 
-	if (!nn->xdp_prog)
-		frag = napi_alloc_frag(nn->fl_bufsz);
-	else
+	if (nn->xdp_enabled)
 		frag = page_address(alloc_page(GFP_ATOMIC | __GFP_COLD));
+	else
+		frag = napi_alloc_frag(nn->fl_bufsz);
 	if (!frag) {
 		nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n");
 		return NULL;
@@ -1177,7 +1178,7 @@ nfp_net_napi_alloc_one(struct nfp_net *nn, int direction, dma_addr_t *dma_addr)
 
 	*dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, direction);
 	if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) {
-		nfp_net_free_frag(frag, nn->xdp_prog);
+		nfp_net_free_frag(frag, nn->xdp_enabled);
 		nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n");
 		return NULL;
 	}
@@ -1248,17 +1249,15 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring)
  * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring
  * @nn:		NFP Net device
  * @rx_ring:	RX ring to remove buffers from
- * @xdp:	Whether XDP is enabled
  *
  * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1)
  * entries.  After device is disabled nfp_net_rx_ring_reset() must be called
  * to restore required ring geometry.
  */
 static void
-nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
-			  bool xdp)
+nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring)
 {
-	int direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
+	int direction = nn->xdp_enabled ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
 	unsigned int i;
 
 	for (i = 0; i < rx_ring->cnt - 1; i++) {
@@ -1271,7 +1270,7 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
 
 		nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[i].dma_addr,
 				     rx_ring->bufsz, direction);
-		nfp_net_free_frag(rx_ring->rxbufs[i].frag, xdp);
+		nfp_net_free_frag(rx_ring->rxbufs[i].frag, nn->xdp_enabled);
 		rx_ring->rxbufs[i].dma_addr = 0;
 		rx_ring->rxbufs[i].frag = NULL;
 	}
@@ -1284,8 +1283,7 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
  * @xdp:	Whether XDP is enabled
  */
 static int
-nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
-			   bool xdp)
+nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring)
 {
 	struct nfp_net_rx_buf *rxbufs;
 	unsigned int i;
@@ -1295,9 +1293,9 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
 	for (i = 0; i < rx_ring->cnt - 1; i++) {
 		rxbufs[i].frag =
 			nfp_net_rx_alloc_one(rx_ring, &rxbufs[i].dma_addr,
-					     rx_ring->bufsz, xdp);
+					     rx_ring->bufsz, nn->xdp_enabled);
 		if (!rxbufs[i].frag) {
-			nfp_net_rx_ring_bufs_free(nn, rx_ring, xdp);
+			nfp_net_rx_ring_bufs_free(nn, rx_ring);
 			return -ENOMEM;
 		}
 	}
@@ -1513,16 +1511,6 @@ nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
 	return true;
 }
 
-static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len)
-{
-	struct xdp_buff xdp;
-
-	xdp.data = data;
-	xdp.data_end = data + len;
-
-	return bpf_prog_run_xdp(prog, &xdp);
-}
-
 /**
  * nfp_net_rx() - receive up to @budget packets on @rx_ring
  * @rx_ring:   RX ring to receive from
@@ -1539,19 +1527,20 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 	struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
 	struct nfp_net *nn = r_vec->nfp_net;
 	struct nfp_net_tx_ring *tx_ring;
-	struct bpf_prog *xdp_prog;
 	unsigned int true_bufsz;
 	struct sk_buff *skb;
+	bool run_xdp;
 	int pkts_polled = 0;
 	int rx_dma_map_dir;
 	int idx;
 
 	rcu_read_lock();
-	xdp_prog = READ_ONCE(nn->xdp_prog);
-	rx_dma_map_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
-	true_bufsz = xdp_prog ? PAGE_SIZE : nn->fl_bufsz;
+	rx_dma_map_dir = nn->xdp_enabled ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
+	true_bufsz = nn->xdp_enabled ? PAGE_SIZE : nn->fl_bufsz;
 	tx_ring = r_vec->xdp_ring;
 
+	run_xdp = xdp_hook_run_needed_check(nn->netdev, &r_vec->napi);
+
 	while (pkts_polled < budget) {
 		unsigned int meta_len, data_len, data_off, pkt_len, pkt_off;
 		struct nfp_net_rx_buf *rxbuf;
@@ -1602,15 +1591,21 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 		r_vec->rx_bytes += pkt_len;
 		u64_stats_update_end(&r_vec->rx_sync);
 
-		if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF &&
-				  nn->bpf_offload_xdp)) {
+		if (run_xdp && !(rxd->rxd.flags & PCIE_DESC_RX_BPF &&
+				 nn->bpf_offload_xdp)) {
 			int act;
+			struct xdp_buff xdp;
+			struct xdp_hook *last_hook;
 
 			dma_sync_single_for_cpu(&nn->pdev->dev,
 						rxbuf->dma_addr + pkt_off,
 						pkt_len, DMA_FROM_DEVICE);
-			act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off,
-					      pkt_len);
+
+			xdp.data = rxbuf->frag + data_off;
+			xdp.data_end = xdp.data + pkt_len;
+
+			act = xdp_hook_run_ret_last(&r_vec->napi, &xdp,
+						    &last_hook);
 			switch (act) {
 			case XDP_PASS:
 				break;
@@ -1618,12 +1613,15 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 				if (unlikely(!nfp_net_tx_xdp_buf(nn, rx_ring,
 								 tx_ring, rxbuf,
 								 pkt_off, pkt_len)))
-					trace_xdp_exception(nn->netdev, xdp_prog, act);
+					trace_xdp_hook_exception(nn->netdev,
+								 last_hook,
+								 act);
 				continue;
 			default:
-				bpf_warn_invalid_xdp_action(act);
+				xdp_warn_invalid_action(act);
 			case XDP_ABORTED:
-				trace_xdp_exception(nn->netdev, xdp_prog, act);
+				trace_xdp_hook_exception(nn->netdev, last_hook,
+							 act);
 			case XDP_DROP:
 				nfp_net_rx_give_one(rx_ring, rxbuf->frag,
 						    rxbuf->dma_addr);
@@ -1676,7 +1674,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 		napi_gro_receive(&rx_ring->r_vec->napi, skb);
 	}
 
-	if (xdp_prog && tx_ring->wr_ptr_add)
+	if (run_xdp && tx_ring->wr_ptr_add)
 		nfp_net_tx_xmit_more_flush(tx_ring);
 	rcu_read_unlock();
 
@@ -1907,8 +1905,7 @@ nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring, unsigned int fl_bufsz,
 }
 
 static struct nfp_net_rx_ring *
-nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s,
-			    bool xdp)
+nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s)
 {
 	unsigned int fl_bufsz =	nfp_net_calc_fl_bufsz(nn, s->mtu);
 	struct nfp_net_rx_ring *rings;
@@ -1924,7 +1921,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s,
 		if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt))
 			goto err_free_prev;
 
-		if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r], xdp))
+		if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r]))
 			goto err_free_ring;
 	}
 
@@ -1932,7 +1929,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s,
 
 err_free_prev:
 	while (r--) {
-		nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp);
+		nfp_net_rx_ring_bufs_free(nn, &rings[r]);
 err_free_ring:
 		nfp_net_rx_ring_free(&rings[r]);
 	}
@@ -1958,14 +1955,13 @@ nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s)
 }
 
 static void
-nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s,
-			 bool xdp)
+nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s)
 {
 	struct nfp_net_rx_ring *rings = s->rings;
 	unsigned int r;
 
 	for (r = 0; r < s->n_rings; r++) {
-		nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp);
+		nfp_net_rx_ring_bufs_free(nn, &rings[r]);
 		nfp_net_rx_ring_free(&rings[r]);
 	}
 
@@ -2302,7 +2298,7 @@ static int nfp_net_netdev_open(struct net_device *netdev)
 			goto err_cleanup_vec_p;
 	}
 
-	nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx, nn->xdp_prog);
+	nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx);
 	if (!nn->rx_rings) {
 		err = -ENOMEM;
 		goto err_cleanup_vec;
@@ -2350,7 +2346,7 @@ static int nfp_net_netdev_open(struct net_device *netdev)
 err_free_rings:
 	nfp_net_tx_ring_set_free(nn, &tx);
 err_free_rx_rings:
-	nfp_net_rx_ring_set_free(nn, &rx, nn->xdp_prog);
+	nfp_net_rx_ring_set_free(nn, &rx);
 err_cleanup_vec:
 	r = nn->num_r_vecs;
 err_cleanup_vec_p:
@@ -2391,7 +2387,7 @@ static void nfp_net_close_free_all(struct nfp_net *nn)
 	unsigned int r;
 
 	for (r = 0; r < nn->num_rx_rings; r++) {
-		nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r], nn->xdp_prog);
+		nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r]);
 		nfp_net_rx_ring_free(&nn->rx_rings[r]);
 	}
 	for (r = 0; r < nn->num_tx_rings; r++)
@@ -2472,7 +2468,6 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 static int
 nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs,
 			 unsigned int *stack_tx_rings,
-			 struct bpf_prog **xdp_prog,
 			 struct nfp_net_ring_set *rx,
 			 struct nfp_net_ring_set *tx)
 {
@@ -2486,7 +2481,6 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs,
 
 	swap(*num_vecs, nn->num_r_vecs);
 	swap(*stack_tx_rings, nn->num_stack_tx_rings);
-	*xdp_prog = xchg(&nn->xdp_prog, *xdp_prog);
 
 	for (r = 0; r <	nn->max_r_vecs; r++)
 		nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r);
@@ -2510,11 +2504,11 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs,
 }
 
 static int
-nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog,
+nfp_net_check_config(struct nfp_net *nn,
 		     struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx)
 {
 	/* XDP-enabled tests */
-	if (!xdp_prog)
+	if (!nn->xdp_enabled)
 		return 0;
 	if (rx && nfp_net_calc_fl_bufsz(nn, rx->mtu) > PAGE_SIZE) {
 		nn_warn(nn, "MTU too large w/ XDP enabled\n");
@@ -2529,7 +2523,7 @@ nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog,
 }
 
 static void
-nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog,
+nfp_net_ring_reconfig_down(struct nfp_net *nn,
 			   struct nfp_net_ring_set *rx,
 			   struct nfp_net_ring_set *tx,
 			   unsigned int stack_tx_rings, unsigned int num_vecs)
@@ -2542,31 +2536,30 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 	nn->num_tx_rings = tx ? tx->n_rings : nn->num_tx_rings;
 	nn->num_stack_tx_rings = stack_tx_rings;
 	nn->num_r_vecs = num_vecs;
-	*xdp_prog = xchg(&nn->xdp_prog, *xdp_prog);
 
 	if (!netif_is_rxfh_configured(nn->netdev))
 		nfp_net_rss_init_itbl(nn);
 }
 
 int
-nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
+nfp_net_ring_reconfig(struct nfp_net *nn,
 		      struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx)
 {
 	unsigned int stack_tx_rings, num_vecs, r;
 	int err;
 
 	stack_tx_rings = tx ? tx->n_rings : nn->num_tx_rings;
-	if (*xdp_prog)
+	if (nn->xdp_enabled)
 		stack_tx_rings -= rx ? rx->n_rings : nn->num_rx_rings;
 
 	num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings, stack_tx_rings);
 
-	err = nfp_net_check_config(nn, *xdp_prog, rx, tx);
+	err = nfp_net_check_config(nn, rx, tx);
 	if (err)
 		return err;
 
 	if (!netif_running(nn->netdev)) {
-		nfp_net_ring_reconfig_down(nn, xdp_prog, rx, tx,
+		nfp_net_ring_reconfig_down(nn, rx, tx,
 					   stack_tx_rings, num_vecs);
 		return 0;
 	}
@@ -2580,7 +2573,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 		}
 	}
 	if (rx) {
-		if (!nfp_net_rx_ring_set_prepare(nn, rx, *xdp_prog)) {
+		if (!nfp_net_rx_ring_set_prepare(nn, rx)) {
 			err = -ENOMEM;
 			goto err_cleanup_vecs;
 		}
@@ -2597,7 +2590,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 	nfp_net_clear_config_and_disable(nn);
 
 	err = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings,
-				       xdp_prog, rx, tx);
+				       rx, tx);
 	if (err) {
 		int err2;
 
@@ -2605,7 +2598,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 
 		/* Try with old configuration and old rings */
 		err2 = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings,
-						xdp_prog, rx, tx);
+						rx, tx);
 		if (err2)
 			nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n",
 			       err, err2);
@@ -2614,7 +2607,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 		nfp_net_cleanup_vector(nn, &nn->r_vecs[r]);
 
 	if (rx)
-		nfp_net_rx_ring_set_free(nn, rx, *xdp_prog);
+		nfp_net_rx_ring_set_free(nn, rx);
 	if (tx)
 		nfp_net_tx_ring_set_free(nn, tx);
 
@@ -2624,7 +2617,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 
 err_free_rx:
 	if (rx)
-		nfp_net_rx_ring_set_free(nn, rx, *xdp_prog);
+		nfp_net_rx_ring_set_free(nn, rx);
 err_cleanup_vecs:
 	for (r = num_vecs - 1; r >= nn->num_r_vecs; r--)
 		nfp_net_cleanup_vector(nn, &nn->r_vecs[r]);
@@ -2640,7 +2633,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
 		.dcnt = nn->rxd_cnt,
 	};
 
-	return nfp_net_ring_reconfig(nn, &nn->xdp_prog, &rx, NULL);
+	return nfp_net_ring_reconfig(nn, &rx, NULL);
 }
 
 static void nfp_net_stat64(struct net_device *netdev,
@@ -2936,7 +2929,17 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog)
 	return ret;
 }
 
-static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
+static int nfp_xdp_check_bpf(struct nfp_net *nn, struct bpf_prog *prog)
+{
+	if (prog && prog->xdp_adjust_head) {
+		nn_err(nn, "Does not support bpf_xdp_adjust_head()\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int nfp_net_xdp_init(struct nfp_net *nn, bool enable)
 {
 	struct nfp_net_ring_set rx = {
 		.n_rings = nn->num_rx_rings,
@@ -2949,33 +2952,17 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
 	};
 	int err;
 
-	if (prog && prog->xdp_adjust_head) {
-		nn_err(nn, "Does not support bpf_xdp_adjust_head()\n");
-		return -EOPNOTSUPP;
-	}
-	if (!prog && !nn->xdp_prog)
-		return 0;
-	if (prog && nn->xdp_prog) {
-		prog = xchg(&nn->xdp_prog, prog);
-		bpf_prog_put(prog);
-		nfp_net_xdp_offload(nn, nn->xdp_prog);
+	if (nn->xdp_enabled == enable)
 		return 0;
-	}
-
-	tx.n_rings += prog ? nn->num_rx_rings : -nn->num_rx_rings;
 
-	/* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */
-	err = nfp_net_ring_reconfig(nn, &prog, &rx, &tx);
-	if (err)
-		return err;
+	nn->xdp_enabled = enable;
 
-	/* @prog got swapped and is now the old one */
-	if (prog)
-		bpf_prog_put(prog);
+	tx.n_rings += enable ? nn->num_rx_rings : -nn->num_rx_rings;
 
-	nfp_net_xdp_offload(nn, nn->xdp_prog);
+	/* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */
+	err = nfp_net_ring_reconfig(nn, &rx, &tx);
 
-	return 0;
+	return err;
 }
 
 static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp)
@@ -2983,11 +2970,14 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp)
 	struct nfp_net *nn = netdev_priv(netdev);
 
 	switch (xdp->command) {
-	case XDP_SETUP_PROG:
-		return nfp_net_xdp_setup(nn, xdp->prog);
-	case XDP_QUERY_PROG:
-		xdp->prog_attached = !!nn->xdp_prog;
-		return 0;
+	case XDP_MODE_ON:
+		return nfp_net_xdp_init(nn, true);
+	case XDP_MODE_OFF:
+		return nfp_net_xdp_init(nn, false);
+	case XDP_CHECK_BPF_PROG:
+		return nfp_xdp_check_bpf(nn, xdp->prog);
+	case XDP_OFFLOAD_BPF:
+		return nfp_net_xdp_offload(nn, xdp->prog);
 	default:
 		return -EINVAL;
 	}
@@ -3173,7 +3163,7 @@ int nfp_net_netdev_init(struct net_device *netdev)
 	 * and netdev->hw_features advertises which features are
 	 * supported.  By default we enable most features.
 	 */
-	netdev->hw_features = NETIF_F_HIGHDMA;
+	netdev->hw_features = NETIF_F_HIGHDMA | NETIF_F_XDP;
 	if (nn->cap & NFP_NET_CFG_CTRL_RXCSUM) {
 		netdev->hw_features |= NETIF_F_RXCSUM;
 		nn->ctrl |= NFP_NET_CFG_CTRL_RXCSUM;
@@ -3272,8 +3262,6 @@ void nfp_net_netdev_clean(struct net_device *netdev)
 {
 	struct nfp_net *nn = netdev_priv(netdev);
 
-	if (nn->xdp_prog)
-		bpf_prog_put(nn->xdp_prog);
 	if (nn->bpf_offload_xdp)
 		nfp_net_xdp_offload(nn, NULL);
 	unregister_netdev(nn->netdev);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
index 1b26e96..ca3ddd5 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -176,8 +176,7 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt)
 	if (nn->txd_cnt != txd_cnt)
 		reconfig_tx = &tx;
 
-	return nfp_net_ring_reconfig(nn, &nn->xdp_prog,
-				     reconfig_rx, reconfig_tx);
+	return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx);
 }
 
 static int nfp_net_set_ringparam(struct net_device *netdev,
@@ -643,7 +642,7 @@ static void nfp_net_get_channels(struct net_device *netdev,
 	unsigned int num_tx_rings;
 
 	num_tx_rings = nn->num_tx_rings;
-	if (nn->xdp_prog)
+	if (nn->xdp_enabled)
 		num_tx_rings -= nn->num_rx_rings;
 
 	channel->max_rx = min(nn->max_rx_rings, nn->max_r_vecs);
@@ -673,15 +672,14 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx,
 	if (nn->num_rx_rings != total_rx)
 		reconfig_rx = &rx;
 	if (nn->num_stack_tx_rings != total_tx ||
-	    (nn->xdp_prog && reconfig_rx))
+	    (nn->xdp_enabled && reconfig_rx))
 		reconfig_tx = &tx;
 
 	/* nfp_net_check_config() will catch tx.n_rings > nn->max_tx_rings */
-	if (nn->xdp_prog)
+	if (nn->xdp_enabled)
 		tx.n_rings += total_rx;
 
-	return nfp_net_ring_reconfig(nn, &nn->xdp_prog,
-				     reconfig_rx, reconfig_tx);
+	return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx);
 }
 
 static int nfp_net_set_channels(struct net_device *netdev,
-- 
2.9.3

[PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-09 00:27:52

This patch creates an infrastructure for registering and running code at
XDP hooks in drivers. This extends and generalizes the original XDP/BPF
interface. Specifically, it defines a generic xdp_hook structure and a
set of hooks that can be assigned to devices or napi instances.  These
hooks are also generic to allow for XDP/BPF programs as well as non-BPF
code (e.g. kernel code can be written in a module).

An XDP hook is defined by the xdp_hook structure. A pointer to this
structure is passed into the XDP register function to set up a hook.
The XDP register function mallocs its own xdp_hook structure and copies
the values from the xdp_hook passed in. The register function also saves
the pointer value of the xdp_hook argument; this pointer is used in
subsequently calls to XDP to identify the registered hook.

The interface is defined in net/xdp.h. This includes the definition of
xdp_hook, functions to register and unregister hooks on a device
or individual instances of napi, and xdp_hook_run that is called by
drivers to run the hooks.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c |   1 +
 include/linux/filter.h                           |  10 +-
 include/linux/netdev_features.h                  |   3 +-
 include/linux/netdevice.h                        |  16 ++
 include/net/xdp.h                                | 310 +++++++++++++++++++++++
 include/trace/events/xdp.h                       |  31 +++
 kernel/bpf/core.c                                |   1 +
 net/core/Makefile                                |   2 +-
 net/core/dev.c                                   |  53 ++--
 net/core/filter.c                                |   1 +
 net/core/rtnetlink.c                             |  14 +-
 net/core/xdp.c                                   | 304 ++++++++++++++++++++++
 12 files changed, 711 insertions(+), 35 deletions(-)
 create mode 100644 include/net/xdp.h
 create mode 100644 net/core/xdp.c
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c
index 335beb8..d294fb2 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c
@@ -38,6 +38,7 @@
 #include <linux/filter.h>
 #include <linux/pkt_cls.h>
 #include <linux/unistd.h>
+#include <net/xdp.h>
 
 #include "nfp_asm.h"
 #include "nfp_bpf.h"
diff --git a/include/linux/filter.h b/include/linux/filter.h
index e4eb254..bb9f2f2 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -428,7 +428,7 @@ struct sk_filter {
 	struct bpf_prog	*prog;
 };
 
-#define BPF_PROG_RUN(filter, ctx)  (*filter->bpf_func)(ctx, filter->insnsi)
+#define BPF_PROG_RUN(filter, ctx)  (*(filter)->bpf_func)(ctx, (filter)->insnsi)
 
 #define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN
 
@@ -437,12 +437,6 @@ struct bpf_skb_data_end {
 	void *data_end;
 };
 
-struct xdp_buff {
-	void *data;
-	void *data_end;
-	void *data_hard_start;
-};
-
 /* compute the linear packet data range [data, data_end) which
  * will be accessed by cls_bpf, act_bpf and lwt programs
  */
@@ -504,6 +498,8 @@ static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog,
 	return BPF_PROG_RUN(prog, skb);
 }
 
+struct xdp_buff;
+
 static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog,
 					    struct xdp_buff *xdp)
 {
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index 9a04195..f22d379 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -71,8 +71,8 @@ enum {
 	NETIF_F_HW_VLAN_STAG_RX_BIT,	/* Receive VLAN STAG HW acceleration */
 	NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */
 	NETIF_F_HW_L2FW_DOFFLOAD_BIT,	/* Allow L2 Forwarding in Hardware */
-
 	NETIF_F_HW_TC_BIT,		/* Offload TC infrastructure */
+	NETIF_F_XDP_BIT,		/* Support XDP interface */
 
 	/*
 	 * Add your fresh new feature above and remember to update
@@ -134,6 +134,7 @@ enum {
 #define NETIF_F_HW_VLAN_STAG_TX	__NETIF_F(HW_VLAN_STAG_TX)
 #define NETIF_F_HW_L2FW_DOFFLOAD	__NETIF_F(HW_L2FW_DOFFLOAD)
 #define NETIF_F_HW_TC		__NETIF_F(HW_TC)
+#define NETIF_F_XDP		__NETIF_F(XDP)
 
 #define for_each_netdev_feature(mask_addr, bit)	\
 	for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 58afbd1..2404e76 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -324,6 +324,7 @@ struct napi_struct {
 	struct sk_buff		*skb;
 	struct hrtimer		timer;
 	struct list_head	dev_list;
+	struct xdp_hook_set __rcu *xdp_hooks;
 	struct hlist_node	napi_hash_node;
 	unsigned int		napi_id;
 };
@@ -821,12 +822,25 @@ enum xdp_netdev_command {
 	 * return true if a program is currently attached and running.
 	 */
 	XDP_QUERY_PROG,
+	/* Initialize device to use XDP. Called when first XDP program is
+	 * registered on a device (including on a NAPI instance).
+	 */
+	XDP_MODE_ON,
+	/* XDP is finished on the device. Called after the last XDP hook
+	 * has been removed from a device.
+	 */
+	XDP_MODE_OFF,
+	/* Check if device is okay with the proposed BPF program to be loaded */
+	XDP_CHECK_BPF_PROG,
+	/* Offload a BPF program to the device */
+	XDP_OFFLOAD_BPF,
 };
 
 struct netdev_xdp {
 	enum xdp_netdev_command command;
 	union {
 		/* XDP_SETUP_PROG */
+		/* XDP_CHECK_BPF_PROG */
 		struct bpf_prog *prog;
 		/* XDP_QUERY_PROG */
 		bool prog_attached;
@@ -1666,6 +1680,8 @@ struct net_device {
 	struct list_head	close_list;
 	struct list_head	ptype_all;
 	struct list_head	ptype_specific;
+	struct xdp_hook_set __rcu *xdp_hooks;
+	unsigned int		xdp_hook_cnt;
 
 	struct {
 		struct list_head upper;
diff --git a/include/net/xdp.h b/include/net/xdp.h
new file mode 100644
index 0000000..ba025d0
--- /dev/null
+++ b/include/net/xdp.h
@@ -0,0 +1,310 @@
+/*
+ * eXpress Data Path (XDP)
+ *
+ * Copyright (c) 2017 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __NET_XDP_H_
+#define __NET_XDP_H_
+
+#include <linux/filter.h>
+#include <linux/netdevice.h>
+#include <linux/static_key.h>
+
+/* XDP data structure.
+ *
+ * Fields:
+ *   data - pointer to first byte of data
+ *   data_end - pointer to last byte
+ *   data_hard_start - point to first possible byte
+ *
+ * Length is deduced by xdp->data_end - xdp->data.
+ */
+struct xdp_buff {
+	void *data;
+	void *data_end;
+	void *data_hard_start;
+};
+
+typedef unsigned int xdp_hookfn(const void *priv, struct xdp_buff *xdp);
+typedef void xdp_put_privfn(const void *priv);
+
+#define XDP_TAG_SIZE	8 /* Should be at least BPF_TAG_SIZE */
+
+/* xdp_hook struct
+ *
+ * This structure contains the ops and data for an XDP hook. A pointer
+ * to this structure providing the definitiona of a hook is passed into
+ * the XDP register function to set up a hook. The XDP register function
+ * mallocs its own xdp_hook structure and copies the values from the
+ * xdp_hook definition. The register function also saves the pointer value
+ * of the xdp_hook definition argument; this pointer is used in subsequent
+ * calls to XDP to find or unregister the hook.
+ *
+ * Fields:
+ *
+ *   priority - priority for insertion into set. The set is ordered lowest to
+ *	highest priority.
+ *   is_bpf - indicates that the hook is a BPF program (priv refers to a
+ *	bpf_prog structure. This allows calling the BPF program directly
+ *	from xdp_run without a extra level of indirection.
+ *   hookfn - function to call when hook are run.
+ *   priv - private data associated with hook. This is passed as an argument
+ *	to the hook function (in the case of BPF this is a bpf_prog structure).
+ *   put_priv - function call when XDP is done with private data.
+ *   def - point to definitions of xdp_hook. The pointer value is saved as
+ *      a refernce the instance of hook loaded (used to find and unregister a
+ *      hook).
+ *   tag - readable tag for reporting purposes
+ */
+struct xdp_hook {
+	int priority;
+	bool is_bpf;
+	xdp_hookfn *hookfn;
+	void __rcu *priv;
+	xdp_put_privfn *put_priv;
+	const struct xdp_hook *template;
+	u8 tag[XDP_TAG_SIZE];
+};
+
+/* xdp_hook_set
+ *
+ * This structure holds a set of XDP hooks in an array of size num. This
+ * structure is used in netdevice to refer to the XDP hooks for a whole
+ * device or in the napi structure to contain the hooks for an individual
+ * RX queue.
+ */
+struct xdp_hook_set {
+	unsigned int num;
+	struct rcu_head rcu;
+	struct xdp_hook hooks[0];
+};
+
+#define XDP_SET_SIZE(_num) (sizeof(struct xdp_hook_set) + ((_num) * \
+	sizeof(struct xdp_hook)))
+
+extern struct xdp_hook xdp_bpf_hook;
+
+extern struct static_key_false xdp_napi_hooks_needed;
+extern struct static_key_false xdp_dev_hooks_needed;
+
+/* Check if XDP hooks are set for a napi or its device */
+static inline bool xdp_hook_run_needed_check(struct net_device *dev,
+					     struct napi_struct *napi)
+{
+	return ((static_branch_unlikely(&xdp_dev_hooks_needed) &&
+		dev->xdp_hooks) ||
+		(static_branch_unlikely(&xdp_napi_hooks_needed) &&
+		 napi->xdp_hooks));
+}
+
+static inline int __xdp_run_one_hook(struct xdp_hook *hook,
+				     struct xdp_buff *xdp)
+{
+	void *priv = rcu_dereference(hook->priv);
+
+	if (hook->is_bpf) {
+		/* Run BPF programs directly do avoid one layer of
+		 * indirection.
+		 */
+		return BPF_PROG_RUN((struct bpf_prog *)priv, (void *)xdp);
+	} else {
+		return hook->hookfn(priv, xdp);
+	}
+}
+
+/* Core function to run the XDP hooks. This must be as fast as possible */
+static inline int __xdp_hook_run(struct xdp_hook_set *hook_set,
+				 struct xdp_buff *xdp,
+				 struct xdp_hook **last_hook)
+{
+	struct xdp_hook *hook;
+	int i, ret;
+
+	if (unlikely(!hook_set))
+		return XDP_PASS;
+
+	hook = &hook_set->hooks[0];
+	ret = __xdp_run_one_hook(hook, xdp);
+	*last_hook = hook;
+
+	for (i = 1; i < hook_set->num; i++) {
+		if (ret != XDP_PASS)
+			break;
+		hook = &hook_set->hooks[i];
+		ret = __xdp_run_one_hook(hook, xdp);
+	}
+
+	return ret;
+}
+
+/* Run the XDP hooks for a napi device and return a reference to the last
+ * hook processed. Called from a driver's receive routine. RCU
+ * read lock must be held.
+ */
+static inline int xdp_hook_run_ret_last(struct napi_struct *napi,
+					struct xdp_buff *xdp,
+					struct xdp_hook **last_hook)
+{
+	struct net_device *dev = napi->dev;
+	struct xdp_hook_set *hook_set;
+	int ret = XDP_PASS;
+
+	if (static_branch_unlikely(&xdp_napi_hooks_needed)) {
+		/* Run hooks in napi first */
+		hook_set = rcu_dereference(napi->xdp_hooks);
+		ret = __xdp_hook_run(hook_set, xdp, last_hook);
+
+		/* Check for dev hooks now taking into account that
+		 * we need to check for XDP_PASS having been
+		 * returned only if they are need (this is why
+		 * we don't do a fall through).
+		 */
+		if (static_branch_unlikely(&xdp_dev_hooks_needed)) {
+			if (ret != XDP_PASS)
+				return ret;
+			hook_set = rcu_dereference(dev->xdp_hooks);
+			ret = __xdp_hook_run(hook_set, xdp, last_hook);
+		}
+	} else if (static_branch_unlikely(&xdp_dev_hooks_needed)) {
+		/* Now run device hooks */
+		hook_set = rcu_dereference(dev->xdp_hooks);
+		ret = __xdp_hook_run(hook_set, xdp, last_hook);
+	}
+
+	return ret;
+}
+
+/* Run the XDP hooks for a napi device. Called from a driver's receive
+ * routine. RCU read lock must be held.
+ */
+static inline int xdp_hook_run(struct napi_struct *napi,
+			       struct xdp_buff *xdp)
+{
+	struct xdp_hook *last_hook;
+
+	return xdp_hook_run_ret_last(napi, xdp, &last_hook);
+}
+
+/* Register an XDP hook
+ *    dev: Assoicated net_device
+ *    hook_set: Hook set
+ *    def: Definition of the hook. The values are copied from this to a
+ *	   malloc'ed structure. The base_def pointer is saved as a
+ *	   reference to the hook to manage it
+ *    change: Change hook if it exists
+ *    dev_hook: Is a hook on a net_device (as oppsed to a napi instance)
+ */
+int __xdp_register_hook(struct net_device *dev,
+			struct xdp_hook_set __rcu **hook_set,
+			const struct xdp_hook *base_def,
+			bool change, bool dev_hook);
+
+/* Register an XDP hook on a device */
+static inline int xdp_register_dev_hook(struct net_device *dev,
+					const struct xdp_hook *def)
+{
+	return __xdp_register_hook(dev, &dev->xdp_hooks, def, false, true);
+}
+
+/* Register an XDP hook on a napi instance */
+static inline int xdp_register_napi_hook(struct napi_struct *napi,
+					 const struct xdp_hook *def)
+{
+	return __xdp_register_hook(napi->dev, &napi->xdp_hooks, def, false,
+				   false);
+}
+
+/* Change an XDP hook.
+ *
+ *    - If the hook does not exist (xdp_hook_ops does not match a hook set on
+ *      the device), then attempt to register the hook.
+ *    - Else, change the private data (priv field in xdp_hook_ops) in the
+ *      existing hook to be the new one (in reg). All the other fields in
+ *      xdp_hook_ops are ignored in that case.
+ */
+
+/* Change a device XDP hook */
+static inline int xdp_change_dev_hook(struct net_device *dev,
+				      const struct xdp_hook *reg)
+{
+	return __xdp_register_hook(dev, &dev->xdp_hooks, reg, true, true);
+}
+
+/* Change a napi XDP hook */
+static inline int xdp_change_napi_hook(struct napi_struct *napi,
+				       const struct xdp_hook *reg)
+{
+	return __xdp_register_hook(napi->dev, &napi->xdp_hooks, reg, true,
+				   false);
+}
+
+int __xdp_unregister_hook(struct net_device *dev,
+			  struct xdp_hook_set __rcu **hook_set,
+			  const struct xdp_hook *template, bool dev_hook);
+
+/* Unregister device XDP hook */
+static inline int xdp_unregister_dev_hook(struct net_device *dev,
+					   const struct xdp_hook *template)
+{
+	return __xdp_unregister_hook(dev, &dev->xdp_hooks, template, true);
+}
+
+/* Unregister a napi XDP hook */
+static inline int xdp_unregister_napi_hook(struct napi_struct *napi,
+					    const struct xdp_hook *template)
+{
+	return __xdp_unregister_hook(napi->dev, &napi->xdp_hooks, template,
+				     false);
+}
+
+/* Unregister all XDP hooks associated with a device (both the device hooks
+ * and hooks on all napi instances). This function is called when the netdev
+ * is being freed.
+ */
+void xdp_unregister_all_hooks(struct net_device *dev);
+
+/* Unregister all XDP hooks for a given xdp_hook_ops in a net. This walks
+ * all devices in net and napis for each device to unregister matching hooks.
+ * This can be called when a module that had registered some number of hooks
+ * is being unloaded.
+ */
+void xdp_unregister_net_hooks(struct net *net, struct xdp_hook *template);
+
+/* Find a registered device hook.
+ *   - If hook is found *ret is set to the values in the registered hook and
+ *     true is returned.
+ *   - Else false is returned.
+ */
+bool __xdp_find_hook(struct xdp_hook_set **hook_set,
+		     const struct xdp_hook *template,
+		     struct xdp_hook *ret);
+
+/* Find a device XDP hook. */
+static inline bool xdp_find_dev_hook(struct net_device *dev,
+				     const struct xdp_hook *template,
+				     struct xdp_hook *ret)
+{
+	return __xdp_find_hook(&dev->xdp_hooks, template, ret);
+}
+
+/* Find a napi XDP hook. */
+static inline bool xdp_find_napi_hook(struct napi_struct *napi,
+				      const struct xdp_hook *template,
+				      struct xdp_hook *ret)
+{
+	return __xdp_find_hook(&napi->xdp_hooks, template, ret);
+}
+
+int xdp_bpf_check_prog(struct net_device *dev, struct bpf_prog *prog);
+
+static inline void xdp_warn_invalid_action(u32 act)
+{
+	WARN_ONCE(1, "Illegal XDP return value %u, expect packet loss\n", act);
+}
+
+#endif /* __NET_XDP_H_ */
diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h
index 1b61357..3a40c6e 100644
--- a/include/trace/events/xdp.h
+++ b/include/trace/events/xdp.h
@@ -7,6 +7,7 @@
 #include <linux/netdevice.h>
 #include <linux/filter.h>
 #include <linux/tracepoint.h>
+#include <net/xdp.h>
 
 #define __XDP_ACT_MAP(FN)	\
 	FN(ABORTED)		\
@@ -48,6 +49,36 @@ TRACE_EVENT(xdp_exception,
 		  __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB))
 );
 
+/* Temporaray trace function. This will be renamed to xdp_exception after all
+ * the calling drivers have been patched.
+ */
+TRACE_EVENT(xdp_hook_exception,
+
+	TP_PROTO(const struct net_device *dev,
+		 const struct xdp_hook *hook, u32 act),
+
+	TP_ARGS(dev, hook, act),
+
+	TP_STRUCT__entry(
+		__string(name, dev->name)
+		__array(u8, prog_tag, 8)
+		__field(u32, act)
+	),
+
+	TP_fast_assign(
+		BUILD_BUG_ON(sizeof(__entry->prog_tag) !=
+						sizeof(hook->tag));
+		memcpy(__entry->prog_tag, hook->tag, sizeof(hook->tag));
+			__assign_str(name, dev->name);
+			__entry->act = act;
+		),
+
+	TP_printk("prog=%s device=%s action=%s",
+		  __print_hex_str(__entry->prog_tag, 8),
+		  __get_str(name),
+		  __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB))
+);
+
 #endif /* _TRACE_XDP_H */
 
 #include <trace/define_trace.h>
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index fddd76b..b80a589 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1179,6 +1179,7 @@ int __weak skb_copy_bits(const struct sk_buff *skb, int offset, void *to,
 #include <linux/bpf_trace.h>
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception);
+EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_hook_exception);
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(bpf_prog_get_type);
 EXPORT_TRACEPOINT_SYMBOL_GPL(bpf_prog_put_rcu);
diff --git a/net/core/Makefile b/net/core/Makefile
index f6761b6..64d98d4 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
 
 obj-y		     += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
 			neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
-			sock_diag.o dev_ioctl.o tso.o sock_reuseport.o
+			sock_diag.o dev_ioctl.o tso.o sock_reuseport.o xdp.o
 
 obj-$(CONFIG_XFRM) += flow.o
 obj-y += net-sysfs.o
diff --git a/net/core/dev.c b/net/core/dev.c
index 0921609..35549dd 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -140,6 +140,8 @@
 #include <linux/hrtimer.h>
 #include <linux/netfilter_ingress.h>
 #include <linux/crash_dump.h>
+#include <linux/filter.h>
+#include <net/xdp.h>
 
 #include "net-sysfs.h"
 
@@ -6598,6 +6600,27 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)
 }
 EXPORT_SYMBOL(dev_change_proto_down);
 
+/* Run a BPF/XDP program. RCU read lock must be held */
+static u32 dev_bpf_prog_run_xdp(const void *priv,
+				struct xdp_buff *xdp)
+{
+	const struct bpf_prog *prog = (const struct bpf_prog *)priv;
+
+	return BPF_PROG_RUN(prog, (void *)xdp);
+}
+
+static void dev_bpf_prog_put_xdp(const void *priv)
+{
+	bpf_prog_put((struct bpf_prog *)priv);
+}
+
+struct xdp_hook xdp_bpf_hook = {
+	.hookfn = dev_bpf_prog_run_xdp,
+	.put_priv = dev_bpf_prog_put_xdp,
+	.priority = 0,
+	.is_bpf = true
+};
+
 /**
  *	dev_change_xdp_fd - set or clear a bpf program for a device rx path
  *	@dev: device
@@ -6610,7 +6633,6 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags)
 {
 	const struct net_device_ops *ops = dev->netdev_ops;
 	struct bpf_prog *prog = NULL;
-	struct netdev_xdp xdp;
 	int err;
 
 	ASSERT_RTNL();
@@ -6618,29 +6640,25 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags)
 	if (!ops->ndo_xdp)
 		return -EOPNOTSUPP;
 	if (fd >= 0) {
-		if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) {
-			memset(&xdp, 0, sizeof(xdp));
-			xdp.command = XDP_QUERY_PROG;
-
-			err = ops->ndo_xdp(dev, &xdp);
-			if (err < 0)
-				return err;
-			if (xdp.prog_attached)
-				return -EBUSY;
-		}
+		if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) &&
+		    xdp_find_dev_hook(dev, &xdp_bpf_hook, NULL))
+			return -EBUSY;
 
 		prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP);
 		if (IS_ERR(prog))
 			return PTR_ERR(prog);
 	}
 
-	memset(&xdp, 0, sizeof(xdp));
-	xdp.command = XDP_SETUP_PROG;
-	xdp.prog = prog;
+	if (prog) {
+		err = xdp_bpf_check_prog(dev, prog);
+		if (err >= 0)
+			err = xdp_register_dev_hook(dev, &xdp_bpf_hook);
 
-	err = ops->ndo_xdp(dev, &xdp);
-	if (err < 0 && prog)
-		bpf_prog_put(prog);
+		if (err < 0)
+			bpf_prog_put(prog);
+	} else {
+		err = xdp_unregister_dev_hook(dev, &xdp_bpf_hook);
+	}
 
 	return err;
 }
@@ -7679,6 +7697,7 @@ void free_netdev(struct net_device *dev)
 	struct napi_struct *p, *n;
 
 	might_sleep();
+	xdp_unregister_all_hooks(dev);
 	netif_free_tx_queues(dev);
 #ifdef CONFIG_SYSFS
 	kvfree(dev->_rx);
diff --git a/net/core/filter.c b/net/core/filter.c
index 0b753cb..8c01157 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -52,6 +52,7 @@
 #include <net/dst_metadata.h>
 #include <net/dst.h>
 #include <net/sock_reuseport.h>
+#include <net/xdp.h>
 
 /**
  *	sk_filter_trim_cap - run a packet through a socket filter
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index adfb54b..8a697c60 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -56,6 +56,7 @@
 #include <net/fib_rules.h>
 #include <net/rtnetlink.h>
 #include <net/net_namespace.h>
+#include <net/xdp.h>
 
 struct rtnl_link {
 	rtnl_doit_func		doit;
@@ -903,7 +904,7 @@ static size_t rtnl_xdp_size(const struct net_device *dev)
 	size_t xdp_size = nla_total_size(0) +	/* nest IFLA_XDP */
 			  nla_total_size(1);	/* XDP_ATTACHED */
 
-	if (!dev->netdev_ops->ndo_xdp)
+	if (!(dev->features & NETIF_F_XDP))
 		return 0;
 	else
 		return xdp_size;
@@ -1253,20 +1254,15 @@ static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev)
 
 static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev)
 {
-	struct netdev_xdp xdp_op = {};
 	struct nlattr *xdp;
 	int err;
 
-	if (!dev->netdev_ops->ndo_xdp)
-		return 0;
 	xdp = nla_nest_start(skb, IFLA_XDP);
 	if (!xdp)
 		return -EMSGSIZE;
-	xdp_op.command = XDP_QUERY_PROG;
-	err = dev->netdev_ops->ndo_xdp(dev, &xdp_op);
-	if (err)
-		goto err_cancel;
-	err = nla_put_u8(skb, IFLA_XDP_ATTACHED, xdp_op.prog_attached);
+
+	err = nla_put_u8(skb, IFLA_XDP_ATTACHED,
+			 xdp_find_dev_hook(dev, &xdp_bpf_hook, NULL));
 	if (err)
 		goto err_cancel;
 
diff --git a/net/core/xdp.c b/net/core/xdp.c
new file mode 100644
index 0000000..efe8826
--- /dev/null
+++ b/net/core/xdp.c
@@ -0,0 +1,304 @@
+/*
+ * eXpress Data Path
+ *
+ * Copyright (c) 2017 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+#include <net/xdp.h>
+
+DEFINE_STATIC_KEY_FALSE(xdp_dev_hooks_needed);
+EXPORT_SYMBOL(xdp_dev_hooks_needed);
+
+DEFINE_STATIC_KEY_FALSE(xdp_napi_hooks_needed);
+EXPORT_SYMBOL(xdp_napi_hooks_needed);
+
+static DEFINE_MUTEX(xdp_hook_mutex);
+
+int __xdp_register_hook(struct net_device *dev,
+			struct xdp_hook_set __rcu **xdp_hooks,
+			const struct xdp_hook *template,
+			bool change, bool dev_hook)
+{
+	struct xdp_hook_set *new_hooks = NULL, *old_hooks;
+	struct xdp_hook *hook;
+	int index, targindex = 0;
+	int i, err;
+
+	mutex_lock(&xdp_hook_mutex);
+
+	old_hooks = rcu_dereference(*xdp_hooks);
+
+	if (old_hooks) {
+		/* Walk over hooks, see if hook is already registered and
+		 * determine insertion point.
+		 */
+
+		for (index = 0; index < old_hooks->num; index++) {
+			hook = &old_hooks->hooks[index];
+			if (hook->template != template) {
+				if (template->priority < hook->priority)
+					targindex = index;
+				continue;
+			}
+
+			if (change) {
+				void *old_priv;
+
+				/* Only allow changing priv field in an existing
+				 * hook.
+				 */
+				old_priv = rcu_dereference_protected(hook->priv,
+					lockdep_is_held(&xdp_hook_mutex));
+				rcu_assign_pointer(hook->priv, template->priv);
+				if (old_priv && hook->put_priv)
+					hook->put_priv(old_priv);
+				goto out;
+			} else {
+				/* Already registered */
+				err = -EALREADY;
+				goto err;
+			}
+		}
+	}
+
+	/* Need to add new hook set. index holds number of entries in hooks
+	 * set (zero if hooks set is NULL). targindex holds index to insert
+	 * new hook.
+	 */
+	new_hooks = kzalloc(XDP_SET_SIZE(index + 1), GFP_KERNEL);
+	if (!new_hooks) {
+		err = -ENOMEM;
+		goto err;
+	}
+
+	/* Initialize XDP in driver */
+	if (!dev->xdp_hook_cnt && dev->netdev_ops->ndo_xdp) {
+		struct netdev_xdp xdp_op = {};
+
+		xdp_op.command = XDP_MODE_ON;
+		err = dev->netdev_ops->ndo_xdp(dev, &xdp_op);
+		if (err)
+			goto err;
+	}
+
+	if (old_hooks) {
+		for (i = 0; i < targindex; i++)
+			new_hooks->hooks[i] = old_hooks->hooks[i];
+
+		for (i++; i < index + 1; i++)
+			new_hooks->hooks[i] = old_hooks->hooks[i - 1];
+	}
+
+	new_hooks->hooks[targindex] = *template;
+	new_hooks->num = index + 1;
+	rcu_assign_pointer(*xdp_hooks, new_hooks);
+
+	if (old_hooks)
+		kfree_rcu(old_hooks, rcu);
+
+	if (dev_hook)
+		static_branch_inc(&xdp_dev_hooks_needed);
+	else
+		static_branch_inc(&xdp_napi_hooks_needed);
+
+	dev->xdp_hook_cnt++;
+
+out:
+	mutex_unlock(&xdp_hook_mutex);
+
+	return 0;
+
+err:
+	mutex_unlock(&xdp_hook_mutex);
+	kfree(new_hooks);
+	return err;
+}
+EXPORT_SYMBOL_GPL(__xdp_register_hook);
+
+int __xdp_unregister_hook(struct net_device *dev,
+			  struct xdp_hook_set __rcu **xdp_hooks,
+			  const struct xdp_hook *template,
+			  bool dev_hook)
+{
+	struct xdp_hook_set *old_hooks, *new_hooks = NULL;
+	struct xdp_hook *hook;
+	int i, index;
+	int err = 0;
+
+	old_hooks = rcu_dereference(*xdp_hooks);
+
+	mutex_lock(&xdp_hook_mutex);
+
+	for (index = 0; index < old_hooks->num; index++) {
+		hook = &old_hooks->hooks[index];
+		if (hook->template != template)
+			continue;
+
+		if (old_hooks->num > 1) {
+			new_hooks = kzalloc(XDP_SET_SIZE(
+				old_hooks->num  - 1), GFP_KERNEL);
+
+			if (!new_hooks) {
+				err = -ENOMEM;
+				goto out;
+			}
+			for (i = 0; i < index; i++)
+				new_hooks->hooks[i] = old_hooks->hooks[i];
+			for (i++; i < index; i++)
+				new_hooks->hooks[i - 1] = old_hooks->hooks[i];
+
+			new_hooks->num = old_hooks->num - 1;
+		}
+
+		break;
+	}
+
+	if (index >= old_hooks->num)
+		goto out;
+
+	rcu_assign_pointer(*xdp_hooks, new_hooks);
+
+	if (old_hooks)
+		kfree_rcu(old_hooks, rcu);
+
+	dev->xdp_hook_cnt--;
+
+	if (dev_hook)
+		static_branch_dec(&xdp_dev_hooks_needed);
+	else
+		static_branch_dec(&xdp_napi_hooks_needed);
+
+	if (hook->priv && hook->put_priv)
+		hook->put_priv(hook->priv);
+
+	if (!dev->xdp_hook_cnt && dev->netdev_ops->ndo_xdp) {
+		struct netdev_xdp xdp_op = {};
+
+		xdp_op.command = XDP_MODE_OFF;
+		dev->netdev_ops->ndo_xdp(dev, &xdp_op);
+	}
+
+out:
+	mutex_unlock(&xdp_hook_mutex);
+	synchronize_net();
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(__xdp_unregister_hook);
+
+static void __xdp_unregister_hooks(struct net_device *dev,
+				   struct xdp_hook_set __rcu **xdp_hooks,
+				   bool dev_hook)
+{
+	struct xdp_hook_set *old_hooks;
+	int i;
+
+	mutex_lock(&xdp_hook_mutex);
+
+	old_hooks = rcu_dereference(*xdp_hooks);
+
+	if (!old_hooks) {
+		mutex_unlock(&xdp_hook_mutex);
+		return;
+	}
+
+	for (i = 0; i < old_hooks->num; i++) {
+		if (dev_hook)
+			static_branch_dec(&xdp_dev_hooks_needed);
+		else
+			static_branch_dec(&xdp_napi_hooks_needed);
+		dev->xdp_hook_cnt--;
+	}
+
+	rcu_assign_pointer(*xdp_hooks, NULL);
+
+	if (!dev->xdp_hook_cnt && dev->netdev_ops->ndo_xdp) {
+		struct netdev_xdp xdp_op = {};
+
+		xdp_op.command = XDP_MODE_OFF;
+		dev->netdev_ops->ndo_xdp(dev, &xdp_op);
+	}
+
+	mutex_unlock(&xdp_hook_mutex);
+
+	kfree_rcu(old_hooks, rcu);
+}
+
+void xdp_unregister_all_hooks(struct net_device *dev)
+{
+	struct napi_struct *napi;
+
+	/* Unregister NAPI hooks for device */
+	list_for_each_entry(napi, &dev->napi_list, dev_list)
+		__xdp_unregister_hooks(dev, &napi->xdp_hooks, false);
+
+	/* Unregister device hooks */
+	__xdp_unregister_hooks(dev, &dev->xdp_hooks, true);
+}
+EXPORT_SYMBOL_GPL(xdp_unregister_all_hooks);
+
+void xdp_unregister_net_hooks(struct net *net, struct xdp_hook *template)
+{
+	struct net_device *dev;
+	struct napi_struct *napi;
+
+	list_for_each_entry_rcu(dev, &net->dev_base_head, dev_list) {
+		list_for_each_entry(napi, &dev->napi_list, dev_list)
+			xdp_unregister_napi_hook(napi, template);
+
+		xdp_unregister_dev_hook(dev, template);
+	}
+}
+EXPORT_SYMBOL_GPL(xdp_unregister_net_hooks);
+
+bool __xdp_find_hook(struct xdp_hook_set __rcu **xdp_hooks,
+		     const struct xdp_hook *template,
+		     struct xdp_hook *ret)
+{
+	struct xdp_hook_set *old_hooks;
+	struct xdp_hook *hook;
+	bool retval = false;
+	int index;
+
+	rcu_read_lock();
+
+	old_hooks = rcu_dereference(*xdp_hooks);
+
+	if (!old_hooks)
+		goto out;
+
+	for (index = 0; index < old_hooks->num; index++) {
+		hook = &old_hooks->hooks[index];
+		if (hook->template != template)
+			continue;
+
+		if (ret)
+			*ret = *hook;
+		retval = true;
+		goto out;
+	}
+
+out:
+	rcu_read_unlock();
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(__xdp_find_hook);
+
+int xdp_bpf_check_prog(struct net_device *dev, struct bpf_prog *prog)
+{
+	if (dev->netdev_ops->ndo_xdp) {
+		struct netdev_xdp xdp_op = {};
+
+		xdp_op.command = XDP_CHECK_BPF_PROG;
+		xdp_op.prog = prog;
+
+		return dev->netdev_ops->ndo_xdp(dev, &xdp_op);
+	} else {
+		return -EOPNOTSUPP;
+	}
+}
+EXPORT_SYMBOL_GPL(xdp_bpf_check_prog);
-- 
2.9.3

[PATCH RFC v2 8/8] xdp: Cleanup after API changes

From: Tom Herbert <hidden>
Date: 2017-02-09 00:36:23

This patch:
  - Change trace_xdp_hook_exception to trace_xdp_exception
  - Remove XDP_SETUP_PROG and XDP_QUERY_PROG constants
  - Remove bpf_warn_invalid_xdp_action

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c      |  4 +--
 drivers/net/ethernet/mellanox/mlx4/en_rx.c         |  4 +--
 drivers/net/ethernet/mellanox/mlx5/core/en_rx.c    |  4 +--
 .../net/ethernet/netronome/nfp/nfp_net_common.c    |  8 +++---
 drivers/net/ethernet/qlogic/qede/qede_fp.c         |  6 ++---
 drivers/net/virtio_net.c                           |  8 +++---
 include/linux/filter.h                             |  1 -
 include/linux/netdevice.h                          | 15 -----------
 include/trace/events/xdp.h                         | 29 ----------------------
 kernel/bpf/core.c                                  |  1 -
 net/core/filter.c                                  |  6 -----
 11 files changed, 16 insertions(+), 70 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
index 3cfdc94..e894b67 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
@@ -134,7 +134,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 
 	case XDP_TX:
 		if (tx_avail < 2) {
-			trace_xdp_hook_exception(bp->dev, last_hook, act);
+			trace_xdp_exception(bp->dev, last_hook, act);
 			bnxt_reuse_rx_data(rxr, cons, page);
 			return true;
 		}
@@ -150,7 +150,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 		xdp_warn_invalid_action(act);
 		/* Fall thru */
 	case XDP_ABORTED:
-		trace_xdp_hook_exception(bp->dev, last_hook, act);
+		trace_xdp_exception(bp->dev, last_hook, act);
 		/* Fall thru */
 	case XDP_DROP:
 		bnxt_reuse_rx_data(rxr, cons, page);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index a8fddc0..d8648fe 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -927,12 +927,12 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 							length, cq->ring,
 							&doorbell_pending)))
 					goto consumed;
-				trace_xdp_hook_exception(dev, last_hook, act);
+				trace_xdp_exception(dev, last_hook, act);
 				goto xdp_drop_no_cnt; /* Drop on xmit failure */
 			default:
 				xdp_warn_invalid_action(act);
 			case XDP_ABORTED:
-				trace_xdp_hook_exception(dev, last_hook, act);
+				trace_xdp_exception(dev, last_hook, act);
 			case XDP_DROP:
 				ring->xdp_drop++;
 xdp_drop_no_cnt:
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 50ab4b9..1be1eef 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -740,12 +740,12 @@ static inline int mlx5e_xdp_handle(struct mlx5e_rq *rq,
 		return false;
 	case XDP_TX:
 		if (unlikely(!mlx5e_xmit_xdp_frame(rq, di, &xdp)))
-			trace_xdp_hook_exception(rq->netdev, last_hook, act);
+			trace_xdp_exception(rq->netdev, last_hook, act);
 		return true;
 	default:
 		xdp_warn_invalid_action(act);
 	case XDP_ABORTED:
-		trace_xdp_hook_exception(rq->netdev, last_hook, act);
+		trace_xdp_exception(rq->netdev, last_hook, act);
 	case XDP_DROP:
 		rq->stats.xdp_drop++;
 		mlx5e_page_release(rq, di, true);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 2dee867..381f6be 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -1613,15 +1613,13 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 				if (unlikely(!nfp_net_tx_xdp_buf(nn, rx_ring,
 								 tx_ring, rxbuf,
 								 pkt_off, pkt_len)))
-					trace_xdp_hook_exception(nn->netdev,
-								 last_hook,
-								 act);
+					trace_xdp_exception(nn->netdev,
+							    last_hook, act);
 				continue;
 			default:
 				xdp_warn_invalid_action(act);
 			case XDP_ABORTED:
-				trace_xdp_hook_exception(nn->netdev, last_hook,
-							 act);
+				trace_xdp_exception(nn->netdev, last_hook, act);
 			case XDP_DROP:
 				nfp_net_rx_give_one(rx_ring, rxbuf->frag,
 						    rxbuf->dma_addr);
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index af885c3..f2800e3 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -1021,7 +1021,7 @@ static bool qede_rx_xdp(struct qede_dev *edev,
 		/* We need the replacement buffer before transmit. */
 		if (qede_alloc_rx_buffer(rxq, true)) {
 			qede_recycle_rx_bd_ring(rxq, 1);
-			trace_xdp_hook_exception(edev->ndev, last_hook, act);
+			trace_xdp_exception(edev->ndev, last_hook, act);
 			goto out;
 		}
 
@@ -1032,7 +1032,7 @@ static bool qede_rx_xdp(struct qede_dev *edev,
 			dma_unmap_page(rxq->dev, bd->mapping,
 				       PAGE_SIZE, DMA_BIDIRECTIONAL);
 			__free_page(bd->data);
-			trace_xdp_hook_exception(edev->ndev, last_hook, act);
+			trace_xdp_exception(edev->ndev, last_hook, act);
 		}
 
 		/* Regardless, we've consumed an Rx BD */
@@ -1042,7 +1042,7 @@ static bool qede_rx_xdp(struct qede_dev *edev,
 	default:
 		xdp_warn_invalid_action(act);
 	case XDP_ABORTED:
-		trace_xdp_hook_exception(edev->ndev, last_hook, act);
+		trace_xdp_exception(edev->ndev, last_hook, act);
 	case XDP_DROP:
 		qede_recycle_rx_bd_ring(rxq, cqe->bd_num);
 	}
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index e8b1747..bd746e1 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -440,13 +440,13 @@ static struct sk_buff *receive_small(struct net_device *dev,
 			break;
 		case XDP_TX:
 			if (unlikely(!virtnet_xdp_xmit(vi, rq, &xdp, skb)))
-				trace_xdp_hook_exception(vi->dev, last_hook, act);
+				trace_xdp_exception(vi->dev, last_hook, act);
 			rcu_read_unlock();
 			goto xdp_xmit;
 		default:
 			xdp_warn_invalid_action(act);
 		case XDP_ABORTED:
-			trace_xdp_hook_exception(vi->dev, last_hook, act);
+			trace_xdp_exception(vi->dev, last_hook, act);
 		case XDP_DROP:
 			goto err_xdp;
 		}
@@ -620,7 +620,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 			break;
 		case XDP_TX:
 			if (unlikely(!virtnet_xdp_xmit(vi, rq, &xdp, data)))
-				trace_xdp_hook_exception(vi->dev, last_hook, act);
+				trace_xdp_exception(vi->dev, last_hook, act);
 			ewma_pkt_len_add(&rq->mrg_avg_pkt_len, len);
 			if (unlikely(xdp_page != page))
 				goto err_xdp;
@@ -629,7 +629,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 		default:
 			xdp_warn_invalid_action(act);
 		case XDP_ABORTED:
-			trace_xdp_hook_exception(vi->dev, last_hook, act);
+			trace_xdp_exception(vi->dev, last_hook, act);
 		case XDP_DROP:
 			if (unlikely(xdp_page != page))
 				__free_pages(xdp_page, 0);
diff --git a/include/linux/filter.h b/include/linux/filter.h
index bb9f2f2..cb98f61 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -607,7 +607,6 @@ bool bpf_helper_changes_pkt_data(void *func);
 
 struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
 				       const struct bpf_insn *patch, u32 len);
-void bpf_warn_invalid_xdp_action(u32 act);
 
 #ifdef CONFIG_BPF_JIT
 extern int bpf_jit_enable;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 2404e76..63c16aa 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -810,18 +810,6 @@ struct tc_to_netdev {
  * to the netdevice through the xdp op.
  */
 enum xdp_netdev_command {
-	/* Set or clear a bpf program used in the earliest stages of packet
-	 * rx. The prog will have been loaded as BPF_PROG_TYPE_XDP. The callee
-	 * is responsible for calling bpf_prog_put on any old progs that are
-	 * stored. In case of error, the callee need not release the new prog
-	 * reference, but on success it takes ownership and must bpf_prog_put
-	 * when it is no longer used.
-	 */
-	XDP_SETUP_PROG,
-	/* Check if a bpf program is set on the device.  The callee should
-	 * return true if a program is currently attached and running.
-	 */
-	XDP_QUERY_PROG,
 	/* Initialize device to use XDP. Called when first XDP program is
 	 * registered on a device (including on a NAPI instance).
 	 */
@@ -839,11 +827,8 @@ enum xdp_netdev_command {
 struct netdev_xdp {
 	enum xdp_netdev_command command;
 	union {
-		/* XDP_SETUP_PROG */
 		/* XDP_CHECK_BPF_PROG */
 		struct bpf_prog *prog;
-		/* XDP_QUERY_PROG */
-		bool prog_attached;
 	};
 };
 
diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h
index 3a40c6e..d225de6 100644
--- a/include/trace/events/xdp.h
+++ b/include/trace/events/xdp.h
@@ -26,35 +26,6 @@ __XDP_ACT_MAP(__XDP_ACT_TP_FN)
 TRACE_EVENT(xdp_exception,
 
 	TP_PROTO(const struct net_device *dev,
-		 const struct bpf_prog *xdp, u32 act),
-
-	TP_ARGS(dev, xdp, act),
-
-	TP_STRUCT__entry(
-		__string(name, dev->name)
-		__array(u8, prog_tag, 8)
-		__field(u32, act)
-	),
-
-	TP_fast_assign(
-		BUILD_BUG_ON(sizeof(__entry->prog_tag) != sizeof(xdp->tag));
-		memcpy(__entry->prog_tag, xdp->tag, sizeof(xdp->tag));
-		__assign_str(name, dev->name);
-		__entry->act = act;
-	),
-
-	TP_printk("prog=%s device=%s action=%s",
-		  __print_hex_str(__entry->prog_tag, 8),
-		  __get_str(name),
-		  __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB))
-);
-
-/* Temporaray trace function. This will be renamed to xdp_exception after all
- * the calling drivers have been patched.
- */
-TRACE_EVENT(xdp_hook_exception,
-
-	TP_PROTO(const struct net_device *dev,
 		 const struct xdp_hook *hook, u32 act),
 
 	TP_ARGS(dev, hook, act),
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index b80a589..fddd76b 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1179,7 +1179,6 @@ int __weak skb_copy_bits(const struct sk_buff *skb, int offset, void *to,
 #include <linux/bpf_trace.h>
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception);
-EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_hook_exception);
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(bpf_prog_get_type);
 EXPORT_TRACEPOINT_SYMBOL_GPL(bpf_prog_put_rcu);
diff --git a/net/core/filter.c b/net/core/filter.c
index 8c01157..d7735dd 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2991,12 +2991,6 @@ static bool xdp_is_valid_access(int off, int size,
 	return __is_valid_xdp_access(off, size);
 }
 
-void bpf_warn_invalid_xdp_action(u32 act)
-{
-	WARN_ONCE(1, "Illegal XDP return value %u, expect packet loss\n", act);
-}
-EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action);
-
 static u32 bpf_convert_ctx_access(enum bpf_access_type type,
 				  const struct bpf_insn *si,
 				  struct bpf_insn *insn_buf,
-- 
2.9.3

[PATCH RFC v2 5/8] virt_net: Changes to use generic XDP infrastructure

From: Tom Herbert <hidden>
Date: 2017-02-09 00:58:17

Change XDP program management functional interface to correspond to new
XDP API.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/virtio_net.c | 98 +++++++++++++++++++-----------------------------
 1 file changed, 38 insertions(+), 60 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 11e2853..e8b1747 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -93,8 +93,6 @@ struct receive_queue {
 
 	struct napi_struct napi;
 
-	struct bpf_prog __rcu *xdp_prog;
-
 	/* Chain pages by the private ptr. */
 	struct page *pages;
 
@@ -140,6 +138,9 @@ struct virtnet_info {
 	/* Host can handle any s/g split between our header and packet data */
 	bool any_header_sg;
 
+	/* XDP has been enabled in device */
+	bool xdp_enabled;
+
 	/* Packet virtio header size */
 	u8 hdr_len;
 
@@ -414,13 +415,12 @@ static struct sk_buff *receive_small(struct net_device *dev,
 				     void *buf, unsigned int len)
 {
 	struct sk_buff * skb = buf;
-	struct bpf_prog *xdp_prog;
+	struct xdp_hook *last_hook;
 
 	len -= vi->hdr_len;
 
 	rcu_read_lock();
-	xdp_prog = rcu_dereference(rq->xdp_prog);
-	if (xdp_prog) {
+	if (xdp_hook_run_needed_check(dev, &rq->napi)) {
 		struct virtio_net_hdr_mrg_rxbuf *hdr = buf;
 		struct xdp_buff xdp;
 		u32 act;
@@ -431,8 +431,7 @@ static struct sk_buff *receive_small(struct net_device *dev,
 		xdp.data_hard_start = skb->data;
 		xdp.data = skb->data + VIRTIO_XDP_HEADROOM;
 		xdp.data_end = xdp.data + len;
-		act = bpf_prog_run_xdp(xdp_prog, &xdp);
-
+		act = xdp_hook_run_ret_last(&rq->napi, &xdp, &last_hook);
 		switch (act) {
 		case XDP_PASS:
 			/* Recalculate length in case bpf program changed it */
@@ -441,13 +440,13 @@ static struct sk_buff *receive_small(struct net_device *dev,
 			break;
 		case XDP_TX:
 			if (unlikely(!virtnet_xdp_xmit(vi, rq, &xdp, skb)))
-				trace_xdp_exception(vi->dev, xdp_prog, act);
+				trace_xdp_hook_exception(vi->dev, last_hook, act);
 			rcu_read_unlock();
 			goto xdp_xmit;
 		default:
-			bpf_warn_invalid_xdp_action(act);
+			xdp_warn_invalid_action(act);
 		case XDP_ABORTED:
-			trace_xdp_exception(vi->dev, xdp_prog, act);
+			trace_xdp_hook_exception(vi->dev, last_hook, act);
 		case XDP_DROP:
 			goto err_xdp;
 		}
@@ -559,16 +558,15 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 	struct page *page = virt_to_head_page(buf);
 	int offset = buf - page_address(page);
 	struct sk_buff *head_skb, *curr_skb;
-	struct bpf_prog *xdp_prog;
 	unsigned int truesize;
 
 	head_skb = NULL;
 
 	rcu_read_lock();
-	xdp_prog = rcu_dereference(rq->xdp_prog);
-	if (xdp_prog) {
+	if (xdp_hook_run_needed_check(dev, &rq->napi)) {
 		struct page *xdp_page;
 		struct xdp_buff xdp;
+		struct xdp_hook *last_hook;
 		void *data;
 		u32 act;
 
@@ -599,7 +597,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 		xdp.data_hard_start = data - VIRTIO_XDP_HEADROOM + vi->hdr_len;
 		xdp.data = data + vi->hdr_len;
 		xdp.data_end = xdp.data + (len - vi->hdr_len);
-		act = bpf_prog_run_xdp(xdp_prog, &xdp);
+		act = xdp_hook_run_ret_last(&rq->napi, &xdp, &last_hook);
 
 		switch (act) {
 		case XDP_PASS:
@@ -622,16 +620,16 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
 			break;
 		case XDP_TX:
 			if (unlikely(!virtnet_xdp_xmit(vi, rq, &xdp, data)))
-				trace_xdp_exception(vi->dev, xdp_prog, act);
+				trace_xdp_hook_exception(vi->dev, last_hook, act);
 			ewma_pkt_len_add(&rq->mrg_avg_pkt_len, len);
 			if (unlikely(xdp_page != page))
 				goto err_xdp;
 			rcu_read_unlock();
 			goto xdp_xmit;
 		default:
-			bpf_warn_invalid_xdp_action(act);
+			xdp_warn_invalid_action(act);
 		case XDP_ABORTED:
-			trace_xdp_exception(vi->dev, xdp_prog, act);
+			trace_xdp_hook_exception(vi->dev, last_hook, act);
 		case XDP_DROP:
 			if (unlikely(xdp_page != page))
 				__free_pages(xdp_page, 0);
@@ -1599,7 +1597,7 @@ static int virtnet_set_channels(struct net_device *dev,
 	 * also when XDP is loaded all RX queues have XDP programs so we only
 	 * need to check a single RX queue.
 	 */
-	if (vi->rq[0].xdp_prog)
+	if (vi->xdp_enabled)
 		return -EINVAL;
 
 	get_online_cpus();
@@ -1770,13 +1768,22 @@ static int virtnet_reset(struct virtnet_info *vi)
 	return ret;
 }
 
-static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
+static int virtnet_xdp_check_bpf(struct net_device *dev, struct bpf_prog *prog)
+{
+	if (prog && prog->xdp_adjust_head) {
+		netdev_warn(dev, "Does not support bpf_xdp_adjust_head()\n");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int virtnet_xdp_init(struct net_device *dev, bool enable)
 {
 	unsigned long int max_sz = PAGE_SIZE - sizeof(struct padded_vnet_hdr);
 	struct virtnet_info *vi = netdev_priv(dev);
-	struct bpf_prog *old_prog;
 	u16 oxdp_qp, xdp_qp = 0, curr_qp;
-	int i, err;
+	int err;
 
 	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) ||
 	    virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) ||
@@ -1797,7 +1804,7 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 	}
 
 	curr_qp = vi->curr_queue_pairs - vi->xdp_queue_pairs;
-	if (prog)
+	if (enable)
 		xdp_qp = nr_cpu_ids;
 
 	/* XDP requires extra queues for XDP_TX */
@@ -1807,12 +1814,6 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 		return -ENOMEM;
 	}
 
-	if (prog) {
-		prog = bpf_prog_add(prog, vi->max_queue_pairs - 1);
-		if (IS_ERR(prog))
-			return PTR_ERR(prog);
-	}
-
 	err = _virtnet_set_queues(vi, curr_qp + xdp_qp);
 	if (err) {
 		dev_warn(&dev->dev, "XDP Device queue allocation failure.\n");
@@ -1835,12 +1836,7 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 
 	netif_set_real_num_rx_queues(dev, curr_qp + xdp_qp);
 
-	for (i = 0; i < vi->max_queue_pairs; i++) {
-		old_prog = rtnl_dereference(vi->rq[i].xdp_prog);
-		rcu_assign_pointer(vi->rq[i].xdp_prog, prog);
-		if (old_prog)
-			bpf_prog_put(old_prog);
-	}
+	vi->xdp_enabled = enable;
 
 	return 0;
 
@@ -1855,31 +1851,18 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 	/* On queue set error we can unwind bpf ref count and user space can
 	 * retry this is most likely an allocation failure.
 	 */
-	if (prog)
-		bpf_prog_sub(prog, vi->max_queue_pairs - 1);
 	return err;
 }
 
-static bool virtnet_xdp_query(struct net_device *dev)
-{
-	struct virtnet_info *vi = netdev_priv(dev);
-	int i;
-
-	for (i = 0; i < vi->max_queue_pairs; i++) {
-		if (vi->rq[i].xdp_prog)
-			return true;
-	}
-	return false;
-}
-
 static int virtnet_xdp(struct net_device *dev, struct netdev_xdp *xdp)
 {
 	switch (xdp->command) {
-	case XDP_SETUP_PROG:
-		return virtnet_xdp_set(dev, xdp->prog);
-	case XDP_QUERY_PROG:
-		xdp->prog_attached = virtnet_xdp_query(dev);
-		return 0;
+	case XDP_MODE_ON:
+		return virtnet_xdp_init(dev, true);
+	case XDP_MODE_OFF:
+		return virtnet_xdp_init(dev, false);
+	case XDP_CHECK_BPF_PROG:
+		return virtnet_xdp_check_bpf(dev, xdp->prog);
 	default:
 		return -EINVAL;
 	}
@@ -1960,17 +1943,11 @@ static void virtnet_free_queues(struct virtnet_info *vi)
 
 static void _free_receive_bufs(struct virtnet_info *vi)
 {
-	struct bpf_prog *old_prog;
 	int i;
 
 	for (i = 0; i < vi->max_queue_pairs; i++) {
 		while (vi->rq[i].pages)
 			__free_pages(get_a_page(&vi->rq[i], GFP_KERNEL), 0);
-
-		old_prog = rtnl_dereference(vi->rq[i].xdp_prog);
-		RCU_INIT_POINTER(vi->rq[i].xdp_prog, NULL);
-		if (old_prog)
-			bpf_prog_put(old_prog);
 	}
 }
 
@@ -2283,7 +2260,8 @@ static int virtnet_probe(struct virtio_device *vdev)
 	/* Do we support "hardware" checksums? */
 	if (virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) {
 		/* This opens up the world of extra features. */
-		dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_SG;
+		dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_SG |
+				    NETIF_F_XDP;
 		if (csum)
 			dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG;
 
-- 
2.9.3

[PATCH RFC v2 2/8] mlx4: Changes to use generic XDP infrastructure

From: Tom Herbert <hidden>
Date: 2017-02-09 01:06:38

Change XDP program management functional interface to correspond to new
XDP API.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 92 +++++---------------------
 drivers/net/ethernet/mellanox/mlx4/en_rx.c     | 27 ++++----
 drivers/net/ethernet/mellanox/mlx4/en_tx.c     |  1 +
 drivers/net/ethernet/mellanox/mlx4/mlx4_en.h   |  1 -
 4 files changed, 29 insertions(+), 92 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 748e9f6..613786a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -42,6 +42,7 @@
 #include <net/busy_poll.h>
 #include <net/vxlan.h>
 #include <net/devlink.h>
+#include <net/xdp.h>
 
 #include <linux/mlx4/driver.h>
 #include <linux/mlx4/device.h>
@@ -2195,8 +2196,7 @@ int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
 				struct mlx4_en_port_profile *prof,
 				bool carry_xdp_prog)
 {
-	struct bpf_prog *xdp_prog;
-	int i, t;
+	int t;
 
 	mlx4_en_copy_priv(tmp, priv, prof);
 
@@ -2211,22 +2211,6 @@ int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
 		return -ENOMEM;
 	}
 
-	/* All rx_rings has the same xdp_prog.  Pick the first one. */
-	xdp_prog = rcu_dereference_protected(
-		priv->rx_ring[0]->xdp_prog,
-		lockdep_is_held(&priv->mdev->state_lock));
-
-	if (xdp_prog && carry_xdp_prog) {
-		xdp_prog = bpf_prog_add(xdp_prog, tmp->rx_ring_num);
-		if (IS_ERR(xdp_prog)) {
-			mlx4_en_free_resources(tmp);
-			return PTR_ERR(xdp_prog);
-		}
-		for (i = 0; i < tmp->rx_ring_num; i++)
-			rcu_assign_pointer(tmp->rx_ring[i]->xdp_prog,
-					   xdp_prog);
-	}
-
 	return 0;
 }
 
@@ -2713,42 +2697,20 @@ static int mlx4_en_set_tx_maxrate(struct net_device *dev, int queue_index, u32 m
 	return err;
 }
 
-static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog)
+static int mlx4_xdp_init(struct net_device *dev, bool enable)
 {
 	struct mlx4_en_priv *priv = netdev_priv(dev);
 	struct mlx4_en_dev *mdev = priv->mdev;
 	struct mlx4_en_port_profile new_prof;
-	struct bpf_prog *old_prog;
 	struct mlx4_en_priv *tmp;
 	int tx_changed = 0;
-	int xdp_ring_num;
 	int port_up = 0;
-	int err;
-	int i;
+	int xdp_ring_num, err;
 
-	xdp_ring_num = prog ? priv->rx_ring_num : 0;
+	xdp_ring_num = enable ? ALIGN(priv->rx_ring_num, MLX4_EN_NUM_UP) : 0;
 
-	/* No need to reconfigure buffers when simply swapping the
-	 * program for a new one.
-	 */
-	if (priv->tx_ring_num[TX_XDP] == xdp_ring_num) {
-		if (prog) {
-			prog = bpf_prog_add(prog, priv->rx_ring_num - 1);
-			if (IS_ERR(prog))
-				return PTR_ERR(prog);
-		}
-		mutex_lock(&mdev->state_lock);
-		for (i = 0; i < priv->rx_ring_num; i++) {
-			old_prog = rcu_dereference_protected(
-					priv->rx_ring[i]->xdp_prog,
-					lockdep_is_held(&mdev->state_lock));
-			rcu_assign_pointer(priv->rx_ring[i]->xdp_prog, prog);
-			if (old_prog)
-				bpf_prog_put(old_prog);
-		}
-		mutex_unlock(&mdev->state_lock);
+	if (priv->tx_ring_num[TX_XDP] == xdp_ring_num)
 		return 0;
-	}
 
 	if (!mlx4_en_check_xdp_mtu(dev, dev->mtu))
 		return -EOPNOTSUPP;
@@ -2757,14 +2719,6 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 	if (!tmp)
 		return -ENOMEM;
 
-	if (prog) {
-		prog = bpf_prog_add(prog, priv->rx_ring_num - 1);
-		if (IS_ERR(prog)) {
-			err = PTR_ERR(prog);
-			goto out;
-		}
-	}
-
 	mutex_lock(&mdev->state_lock);
 	memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
 	new_prof.tx_ring_num[TX_XDP] = xdp_ring_num;
@@ -2777,11 +2731,8 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 	}
 
 	err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, false);
-	if (err) {
-		if (prog)
-			bpf_prog_sub(prog, priv->rx_ring_num - 1);
+	if (err)
 		goto unlock_out;
-	}
 
 	if (priv->port_up) {
 		port_up = 1;
@@ -2792,15 +2743,6 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 	if (tx_changed)
 		netif_set_real_num_tx_queues(dev, priv->tx_ring_num[TX]);
 
-	for (i = 0; i < priv->rx_ring_num; i++) {
-		old_prog = rcu_dereference_protected(
-					priv->rx_ring[i]->xdp_prog,
-					lockdep_is_held(&mdev->state_lock));
-		rcu_assign_pointer(priv->rx_ring[i]->xdp_prog, prog);
-		if (old_prog)
-			bpf_prog_put(old_prog);
-	}
-
 	if (port_up) {
 		err = mlx4_en_start_port(dev);
 		if (err) {
@@ -2812,26 +2754,24 @@ static int mlx4_xdp_set(struct net_device *dev, struct bpf_prog *prog)
 
 unlock_out:
 	mutex_unlock(&mdev->state_lock);
-out:
 	kfree(tmp);
 	return err;
 }
 
-static bool mlx4_xdp_attached(struct net_device *dev)
+static int mlx4_xdp_check_bpf(struct net_device *dev, struct bpf_prog *prog)
 {
-	struct mlx4_en_priv *priv = netdev_priv(dev);
-
-	return !!priv->tx_ring_num[TX_XDP];
+	return 0;
 }
 
 static int mlx4_xdp(struct net_device *dev, struct netdev_xdp *xdp)
 {
 	switch (xdp->command) {
-	case XDP_SETUP_PROG:
-		return mlx4_xdp_set(dev, xdp->prog);
-	case XDP_QUERY_PROG:
-		xdp->prog_attached = mlx4_xdp_attached(dev);
-		return 0;
+	case XDP_MODE_ON:
+		return mlx4_xdp_init(dev, true);
+	case XDP_MODE_OFF:
+		return mlx4_xdp_init(dev, false);
+	case XDP_CHECK_BPF_PROG:
+		return mlx4_xdp_check_bpf(dev, xdp->prog);
 	default:
 		return -EINVAL;
 	}
@@ -3331,7 +3271,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
 
 	dev->vlan_features = dev->hw_features;
 
-	dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_RXHASH;
+	dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_RXHASH | NETIF_F_XDP;
 	dev->features = dev->hw_features | NETIF_F_HIGHDMA |
 			NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
 			NETIF_F_HW_VLAN_CTAG_FILTER;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index d85e644..a8fddc0 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -43,6 +43,7 @@
 #include <linux/if_vlan.h>
 #include <linux/vmalloc.h>
 #include <linux/irq.h>
+#include <net/xdp.h>
 
 #if IS_ENABLED(CONFIG_IPV6)
 #include <net/ip6_checksum.h>
@@ -547,13 +548,7 @@ void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
 {
 	struct mlx4_en_dev *mdev = priv->mdev;
 	struct mlx4_en_rx_ring *ring = *pring;
-	struct bpf_prog *old_prog;
 
-	old_prog = rcu_dereference_protected(
-					ring->xdp_prog,
-					lockdep_is_held(&mdev->state_lock));
-	if (old_prog)
-		bpf_prog_put(old_prog);
 	mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE);
 	vfree(ring->rx_info);
 	ring->rx_info = NULL;
@@ -802,7 +797,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 	struct mlx4_en_rx_ring *ring = priv->rx_ring[cq->ring];
 	struct mlx4_en_rx_alloc *frags;
 	struct mlx4_en_rx_desc *rx_desc;
-	struct bpf_prog *xdp_prog;
 	int doorbell_pending;
 	struct sk_buff *skb;
 	int index;
@@ -813,6 +807,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 	int factor = priv->cqe_factor;
 	u64 timestamp;
 	bool l2_tunnel;
+	bool run_xdp;
 
 	if (unlikely(!priv->port_up))
 		return 0;
@@ -820,9 +815,9 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 	if (unlikely(budget <= 0))
 		return polled;
 
-	/* Protect accesses to: ring->xdp_prog, priv->mac_hash list */
+	/* Protect accesses to: XDP hooks, priv->mac_hash list */
 	rcu_read_lock();
-	xdp_prog = rcu_dereference(ring->xdp_prog);
+	run_xdp = xdp_hook_run_needed_check(dev, &cq->napi);
 	doorbell_pending = 0;
 
 	/* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx
@@ -895,13 +890,14 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 		l2_tunnel = (dev->hw_enc_features & NETIF_F_RXCSUM) &&
 			(cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL));
 
-		/* A bpf program gets first chance to drop the packet. It may
+		/* An xdp program gets first chance to drop the packet. It may
 		 * read bytes but not past the end of the frag.
 		 */
-		if (xdp_prog) {
+		if (run_xdp) {
 			struct xdp_buff xdp;
 			dma_addr_t dma;
 			void *orig_data;
+			struct xdp_hook *last_hook;
 			u32 act;
 
 			dma = be64_to_cpu(rx_desc->data[0].addr);
@@ -914,7 +910,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 			xdp.data_end = xdp.data + length;
 			orig_data = xdp.data;
 
-			act = bpf_prog_run_xdp(xdp_prog, &xdp);
+			act = xdp_hook_run_ret_last(&cq->napi, &xdp,
+						    &last_hook);
 
 			if (xdp.data != orig_data) {
 				length = xdp.data_end - xdp.data;
@@ -930,12 +927,12 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
 							length, cq->ring,
 							&doorbell_pending)))
 					goto consumed;
-				trace_xdp_exception(dev, xdp_prog, act);
+				trace_xdp_hook_exception(dev, last_hook, act);
 				goto xdp_drop_no_cnt; /* Drop on xmit failure */
 			default:
-				bpf_warn_invalid_xdp_action(act);
+				xdp_warn_invalid_action(act);
 			case XDP_ABORTED:
-				trace_xdp_exception(dev, xdp_prog, act);
+				trace_xdp_hook_exception(dev, last_hook, act);
 			case XDP_DROP:
 				ring->xdp_drop++;
 xdp_drop_no_cnt:
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 3ed4219..870acb7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -43,6 +43,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/moduleparam.h>
+#include <net/xdp.h>
 
 #include "mlx4_en.h"
 
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index cec59bc..fbd1764 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -347,7 +347,6 @@ struct mlx4_en_rx_ring {
 	u8  fcs_del;
 	void *buf;
 	void *rx_info;
-	struct bpf_prog __rcu *xdp_prog;
 	struct mlx4_en_page_cache page_cache;
 	unsigned long bytes;
 	unsigned long packets;
-- 
2.9.3

[PATCH RFC v2 7/8] bnxt: Changes to use generic XDP infrastructure

From: Tom Herbert <hidden>
Date: 2017-02-09 01:16:52

Change XDP program management functional interface to correspond to new
XDP API.

Signed-off-by: Tom Herbert <redacted>
---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c     | 14 --------
 drivers/net/ethernet/broadcom/bnxt/bnxt.h     |  2 +-
 drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 46 +++++++++++++++------------
 3 files changed, 27 insertions(+), 35 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index cda1c78..ce311fb 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -2091,9 +2091,6 @@ static void bnxt_free_rx_rings(struct bnxt *bp)
 		struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
 		struct bnxt_ring_struct *ring;
 
-		if (rxr->xdp_prog)
-			bpf_prog_put(rxr->xdp_prog);
-
 		kfree(rxr->rx_tpa);
 		rxr->rx_tpa = NULL;
 
@@ -2381,15 +2378,6 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
 	ring = &rxr->rx_ring_struct;
 	bnxt_init_rxbd_pages(ring, type);
 
-	if (BNXT_RX_PAGE_MODE(bp) && bp->xdp_prog) {
-		rxr->xdp_prog = bpf_prog_add(bp->xdp_prog, 1);
-		if (IS_ERR(rxr->xdp_prog)) {
-			int rc = PTR_ERR(rxr->xdp_prog);
-
-			rxr->xdp_prog = NULL;
-			return rc;
-		}
-	}
 	prod = rxr->rx_prod;
 	for (i = 0; i < bp->rx_ring_size; i++) {
 		if (bnxt_alloc_rx_data(bp, rxr, prod, GFP_KERNEL) != 0) {
@@ -7157,8 +7145,6 @@ static void bnxt_remove_one(struct pci_dev *pdev)
 	pci_iounmap(pdev, bp->bar0);
 	kfree(bp->edev);
 	bp->edev = NULL;
-	if (bp->xdp_prog)
-		bpf_prog_put(bp->xdp_prog);
 	free_netdev(dev);
 
 	pci_release_regions(pdev);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 9f07b9c..3efe7af 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -1175,7 +1175,7 @@ struct bnxt {
 	u8			num_leds;
 	struct bnxt_led_info	leds[BNXT_MAX_LED];
 
-	struct bpf_prog		*xdp_prog;
+	bool			xdp_enabled;
 };
 
 #define BNXT_RX_STATS_OFFSET(counter)			\
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
index 899c30f..3cfdc94 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
@@ -85,18 +85,18 @@ void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts)
 bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 		 struct page *page, u8 **data_ptr, unsigned int *len, u8 *event)
 {
-	struct bpf_prog *xdp_prog = READ_ONCE(rxr->xdp_prog);
 	struct bnxt_tx_ring_info *txr;
 	struct bnxt_sw_rx_bd *rx_buf;
 	struct pci_dev *pdev;
 	struct xdp_buff xdp;
+	struct xdp_hook *last_hook;
 	dma_addr_t mapping;
 	void *orig_data;
 	u32 tx_avail;
 	u32 offset;
 	u32 act;
 
-	if (!xdp_prog)
+	if (!xdp_hook_run_needed_check(bp->dev, &rxr->bnapi->napi))
 		return false;
 
 	pdev = bp->pdev;
@@ -113,7 +113,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 	dma_sync_single_for_cpu(&pdev->dev, mapping + offset, *len, bp->rx_dir);
 
 	rcu_read_lock();
-	act = bpf_prog_run_xdp(xdp_prog, &xdp);
+	act = xdp_hook_run_ret_last(&rxr->bnapi->napi, &xdp, &last_hook);
 	rcu_read_unlock();
 
 	tx_avail = bnxt_tx_avail(bp, txr);
@@ -134,7 +134,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 
 	case XDP_TX:
 		if (tx_avail < 2) {
-			trace_xdp_exception(bp->dev, xdp_prog, act);
+			trace_xdp_hook_exception(bp->dev, last_hook, act);
 			bnxt_reuse_rx_data(rxr, cons, page);
 			return true;
 		}
@@ -147,10 +147,10 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 		bnxt_reuse_rx_data(rxr, cons, page);
 		return true;
 	default:
-		bpf_warn_invalid_xdp_action(act);
+		xdp_warn_invalid_action(act);
 		/* Fall thru */
 	case XDP_ABORTED:
-		trace_xdp_exception(bp->dev, xdp_prog, act);
+		trace_xdp_hook_exception(bp->dev, last_hook, act);
 		/* Fall thru */
 	case XDP_DROP:
 		bnxt_reuse_rx_data(rxr, cons, page);
@@ -160,13 +160,15 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
 }
 
 /* Under rtnl_lock */
-static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
+static int bnxt_xdp_init(struct bnxt *bp, bool enable)
 {
 	struct net_device *dev = bp->dev;
 	int tx_xdp = 0, rc, tc;
-	struct bpf_prog *old;
 
-	if (prog && bp->dev->mtu > BNXT_MAX_PAGE_MODE_MTU) {
+	if (bp->xdp_enabled == enable)
+		return 0;
+
+	if (enable && bp->dev->mtu > BNXT_MAX_PAGE_MODE_MTU) {
 		netdev_warn(dev, "MTU %d larger than largest XDP supported MTU %d.\n",
 			    bp->dev->mtu, BNXT_MAX_PAGE_MODE_MTU);
 		return -EOPNOTSUPP;
@@ -175,7 +177,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
 		netdev_warn(dev, "ethtool rx/tx channels must be combined to support XDP.\n");
 		return -EOPNOTSUPP;
 	}
-	if (prog)
+	if (enable)
 		tx_xdp = bp->rx_nr_rings;
 
 	tc = netdev_get_num_tc(dev);
@@ -190,11 +192,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
 	if (netif_running(dev))
 		bnxt_close_nic(bp, true, false);
 
-	old = xchg(&bp->xdp_prog, prog);
-	if (old)
-		bpf_prog_put(old);
-
-	if (prog) {
+	if (enable) {
 		bnxt_set_rx_skb_mode(bp, true);
 	} else {
 		int rx, tx;
@@ -210,6 +208,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
 	bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp;
 	bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings);
 	bp->num_stat_ctxs = bp->cp_nr_rings;
+	bp->xdp_enabled = enable;
 	bnxt_set_tpa_flags(bp);
 	bnxt_set_ring_params(bp);
 
@@ -219,18 +218,25 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
 	return 0;
 }
 
+static int bnxt_xdp_check_bpf(struct net_device *dev, struct bpf_prog *prog)
+{
+	return 0;
+}
+
 int bnxt_xdp(struct net_device *dev, struct netdev_xdp *xdp)
 {
 	struct bnxt *bp = netdev_priv(dev);
 	int rc;
 
 	switch (xdp->command) {
-	case XDP_SETUP_PROG:
-		rc = bnxt_xdp_set(bp, xdp->prog);
+	case XDP_MODE_ON:
+		rc = bnxt_xdp_init(bp, true);
+		break;
+	case XDP_MODE_OFF:
+		rc = bnxt_xdp_init(bp, false);
 		break;
-	case XDP_QUERY_PROG:
-		xdp->prog_attached = !!bp->xdp_prog;
-		rc = 0;
+	case XDP_CHECK_BPF_PROG:
+		rc = bnxt_xdp_check_bpf(dev, xdp->prog);
 		break;
 	default:
 		rc = -EINVAL;
-- 
2.9.3

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Jiri Pirko <jiri@resnulli.us>
Date: 2017-02-09 07:58:39

Thu, Feb 09, 2017 at 12:41:20AM CET, tom@herbertland.com wrote:
This patch creates an infrastructure for registering and running code at
XDP hooks in drivers. This extends and generalizes the original XDP/BPF
interface. Specifically, it defines a generic xdp_hook structure and a
set of hooks that can be assigned to devices or napi instances.  These
hooks are also generic to allow for XDP/BPF programs as well as non-BPF
code (e.g. kernel code can be written in a module).

An XDP hook is defined by the xdp_hook structure. A pointer to this
structure is passed into the XDP register function to set up a hook.
The XDP register function mallocs its own xdp_hook structure and copies
the values from the xdp_hook passed in. The register function also saves
the pointer value of the xdp_hook argument; this pointer is used in
subsequently calls to XDP to identify the registered hook.

The interface is defined in net/xdp.h. This includes the definition of
xdp_hook, functions to register and unregister hooks on a device
or individual instances of napi, and xdp_hook_run that is called by
drivers to run the hooks.

Signed-off-by: Tom Herbert <redacted>
---
drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c |   1 +
include/linux/filter.h                           |  10 +-
include/linux/netdev_features.h                  |   3 +-
include/linux/netdevice.h                        |  16 ++
include/net/xdp.h                                | 310 +++++++++++++++++++++++
include/trace/events/xdp.h                       |  31 +++
kernel/bpf/core.c                                |   1 +
net/core/Makefile                                |   2 +-
net/core/dev.c                                   |  53 ++--
net/core/filter.c                                |   1 +
net/core/rtnetlink.c                             |  14 +-
net/core/xdp.c                                   | 304 ++++++++++++++++++++++
12 files changed, 711 insertions(+), 35 deletions(-)
create mode 100644 include/net/xdp.h
create mode 100644 net/core/xdp.c
[...]
quoted hunk
@@ -48,6 +49,36 @@ TRACE_EVENT(xdp_exception,
		  __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB))
);

+/* Temporaray trace function. This will be renamed to xdp_exception after all
typo
+ * the calling drivers have been patched.
+ */

RE: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Mintz, Yuval <hidden>
Date: 2017-02-09 14:23:22

+ * Fields:
+ *
+ *   priority - priority for insertion into set. The set is ordered lowest to
+ *	highest priority.
+ *   is_bpf - indicates that the hook is a BPF program (priv refers to a
+ *	bpf_prog structure. This allows calling the BPF program directly
+ *	from xdp_run without a extra level of indirection.
+ *   hookfn - function to call when hook are run.
+ *   priv - private data associated with hook. This is passed as an argument
+ *	to the hook function (in the case of BPF this is a bpf_prog structure).
+ *   put_priv - function call when XDP is done with private data.
+ *   def - point to definitions of xdp_hook. The pointer value is saved as
def->template
+ *      a refernce the instance of hook loaded (used to find and unregister a
+ *      hook).
+ *   tag - readable tag for reporting purposes
+ */
+struct xdp_hook {
+	int priority;
+	bool is_bpf;
+	xdp_hookfn *hookfn;
+	void __rcu *priv;
+	xdp_put_privfn *put_priv;
+	const struct xdp_hook *template;
+	u8 tag[XDP_TAG_SIZE];
+};
...
+static inline int __xdp_run_one_hook(struct xdp_hook *hook,
+				     struct xdp_buff *xdp)
+{
+	void *priv = rcu_dereference(hook->priv);
+
+	if (hook->is_bpf) {
Shouldn't this branch be 'likely' [until we have other flavors of xdp]?
+		/* Run BPF programs directly do avoid one layer of
+		 * indirection.
+		 */
+		return BPF_PROG_RUN((struct bpf_prog *)priv, (void *)xdp);
+	} else {
+		return hook->hookfn(priv, xdp);
+	}
+}
+
+/* Core function to run the XDP hooks. This must be as fast as possible
+*/ static inline int __xdp_hook_run(struct xdp_hook_set *hook_set,
+				 struct xdp_buff *xdp,
+				 struct xdp_hook **last_hook)
+{
+	struct xdp_hook *hook;
+	int i, ret;
+
+	if (unlikely(!hook_set))
+		return XDP_PASS;
+
+	hook = &hook_set->hooks[0];
+	ret = __xdp_run_one_hook(hook, xdp);
+	*last_hook = hook;
Not setting last_hook in loop; Probably failing the 'last' intention.
+	for (i = 1; i < hook_set->num; i++) {
+		if (ret != XDP_PASS)
+			break;
+		hook = &hook_set->hooks[i];
+		ret = __xdp_run_one_hook(hook, xdp);
+	}
+
+	return ret;
+}
...
+/* Run a BPF/XDP program. RCU read lock must be held */ static u32
+dev_bpf_prog_run_xdp(const void *priv,
+				struct xdp_buff *xdp)
+{
+	const struct bpf_prog *prog = (const struct bpf_prog *)priv;
+
+	return BPF_PROG_RUN(prog, (void *)xdp); }
+
+static void dev_bpf_prog_put_xdp(const void *priv) {
+	bpf_prog_put((struct bpf_prog *)priv); }
+
+struct xdp_hook xdp_bpf_hook = {
+	.hookfn = dev_bpf_prog_run_xdp,
+	.put_priv = dev_bpf_prog_put_xdp,
+	.priority = 0,
+	.is_bpf = true
+};
What's the purpose of populating hookfn,
if for performance you've chosen the function based on 'is_bpf'?

...
-	if (!dev->netdev_ops->ndo_xdp)
+	if (!(dev->features & NETIF_F_XDP))
 		return 0;
This should probably go in the cleanup patch.

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: David Miller <davem@davemloft.net>
Date: 2017-02-09 22:18:16

From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.

Tom if you're going to do a cleanup that makes it so that drivers
need less code to support XDP, that is awesome but please do only
that.

Don't combine it with more controversial changes.

Thank you.

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-09 22:27:02

On Thu, Feb 9, 2017 at 2:17 PM, David Miller [off-list ref] wrote:
From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
quoted
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.
Kernel modules go through extensive netdev review before they are
taken into the kernel, for BPF programs we just allow what any user
gives us without any peer review even implied. For this reason, I
simply don't believe that BPF is magically more robust code than what
is in a kernel module. Or to put it another way, do you think DPDK is
going to put any restrictions on what a user can do over raw queues in
userspace? If we put on artificial limits on XDP like it can only ever
be BPF then we are just closing the door to its full potential and
given more fodder for the userpace stacks to claim superiority.
Tom if you're going to do a cleanup that makes it so that drivers
need less code to support XDP, that is awesome but please do only
that.

Don't combine it with more controversial changes.
We need this for TXDP; I have no interest in rewriting the TCP stack in BPF :-)

Tom
Thank you.

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: David Miller <davem@davemloft.net>
Date: 2017-02-09 22:40:57

From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:26:50 -0800
On Thu, Feb 9, 2017 at 2:17 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
quoted
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.
Kernel modules go through extensive netdev review before they are
taken into the kernel, for BPF programs we just allow what any user
gives us without any peer review even implied.
We can actually control what externally written XDP eBPF programs can
do, for kernel modules we have no such control or influence.  This
hook runs right in the driver and bypasses the entire stack, it has to
execute in a hardened thing that cannot crash and it will not as long
as BPF verifier is correct.

And you're going to make it even more complicated what XDP offload in
hardware actually means.  With eBPF it is very clearly defined what
the necessary execution engine is.

Tom I'm strongly against being allowed to run arbitrary module code
from the XDP hook, sorry.

It is as important as the distinction between full stack offload and
partial offload in those nice charts in your talks. :-)

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-09 22:46:09

On Thu, Feb 9, 2017 at 2:34 PM, David Miller [off-list ref] wrote:
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:26:50 -0800
quoted
On Thu, Feb 9, 2017 at 2:17 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
quoted
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.
Kernel modules go through extensive netdev review before they are
taken into the kernel, for BPF programs we just allow what any user
gives us without any peer review even implied.
We can actually control what externally written XDP eBPF programs can
do, for kernel modules we have no such control or influence.  This
hook runs right in the driver and bypasses the entire stack, it has to
execute in a hardened thing that cannot crash and it will not as long
as BPF verifier is correct.

And you're going to make it even more complicated what XDP offload in
hardware actually means.  With eBPF it is very clearly defined what
the necessary execution engine is.

Tom I'm strongly against being allowed to run arbitrary module code
from the XDP hook, sorry.

It is as important as the distinction between full stack offload and
partial offload in those nice charts in your talks. :-)
Yes it is. And the relevant principle that I would draw from that is
the "offload" means offloading functionality from the kernel **to**
the device. Restricting what we implement in the kernel on the basis
of whether or not it can be offloaded to a device is completely
backwards in this regard.

Tom

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-09 23:17:01

On Thu, Feb 9, 2017 at 2:34 PM, David Miller [off-list ref] wrote:
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:26:50 -0800
quoted
On Thu, Feb 9, 2017 at 2:17 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
quoted
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.
Kernel modules go through extensive netdev review before they are
taken into the kernel, for BPF programs we just allow what any user
gives us without any peer review even implied.
We can actually control what externally written XDP eBPF programs can
do, for kernel modules we have no such control or influence.  This
hook runs right in the driver and bypasses the entire stack, it has to
execute in a hardened thing that cannot crash and it will not as long
as BPF verifier is correct.

And you're going to make it even more complicated what XDP offload in
hardware actually means.  With eBPF it is very clearly defined what
the necessary execution engine is.

Tom I'm strongly against being allowed to run arbitrary module code
from the XDP hook, sorry.
Okay, how about this... I'll add a configuration option like
XDP_ALLOW_OTHER_HOOKS. The default will be to disallow setting any
hook other than a BPF. If it is set, then we'll accept other hooks to
be run. This way mostly restrict the interface by default, but still
allow experimentation with other hook types like I need with TXDP or
maybe the netfilter guys might want to fastpath netfilter etc. When we
we bring a working robust implementation to netdev that show clear
benefits then we can add those to BPF as the "allowed" hooks at that
time. So this strictly controls the interfaces, but still also allows
room for innovation.

Tom
It is as important as the distinction between full stack offload and
partial offload in those nice charts in your talks. :-)

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-10 02:30:34

On Thu, Feb 9, 2017 at 5:48 PM, David Miller [off-list ref] wrote:
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 15:08:22 -0800
quoted
Okay, how about this... I'll add a configuration option like
XDP_ALLOW_OTHER_HOOKS. The default will be to disallow setting any
hook other than a BPF. If it is set, then we'll accept other hooks
to be run. This way mostly restrict the interface by default, but
still allow experimentation with other hook types like I need with
TXDP or maybe the netfilter guys might want to fastpath netfilter
etc. When we we bring a working robust implementation to netdev that
show clear benefits then we can add those to BPF as the "allowed"
hooks at that time. So this strictly controls the interfaces, but
still also allows room for innovation.
Anyone is allowed to "innovate" in their own private kernel tree.

But I'm not unleashing that upstream.

The only reason I accepted XDP is entirely because it is limited
in scope to eBPF.  All eBPF programs execute in finite time,
cannot loop, cannot deadlock, cannot access arbitrary pieces
of kernel memory and datastructures.

It is a well defined, constrained, and incredibly tightly controlled
execution environment for implementing policy, monitoring and control.
And it's also incredibly invasive in the core data path of drivers.
TBH it is not clear to me that the narrow use cases for XDP justifies
adding this complexity being added to every driver.

In any case, I withdraw the patch set.

Tom

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-10 02:30:34

On Thu, Feb 9, 2017 at 5:42 PM, David Miller [off-list ref] wrote:
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:45:04 -0800
quoted
On Thu, Feb 9, 2017 at 2:34 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:26:50 -0800
quoted
On Thu, Feb 9, 2017 at 2:17 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
quoted
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.
Kernel modules go through extensive netdev review before they are
taken into the kernel, for BPF programs we just allow what any user
gives us without any peer review even implied.
We can actually control what externally written XDP eBPF programs can
do, for kernel modules we have no such control or influence.  This
hook runs right in the driver and bypasses the entire stack, it has to
execute in a hardened thing that cannot crash and it will not as long
as BPF verifier is correct.

And you're going to make it even more complicated what XDP offload in
hardware actually means.  With eBPF it is very clearly defined what
the necessary execution engine is.

Tom I'm strongly against being allowed to run arbitrary module code
from the XDP hook, sorry.

It is as important as the distinction between full stack offload and
partial offload in those nice charts in your talks. :-)
Yes it is. And the relevant principle that I would draw from that is
the "offload" means offloading functionality from the kernel **to**
the device. Restricting what we implement in the kernel on the basis
of whether or not it can be offloaded to a device is completely
backwards in this regard.
I didn't say that's the reason I'm against it.

I said it's because eBPF is constrained, and there is a very
well understood universe of operations it can perform and what
memory it can access.

Whereas modules can touch any piece of kernel memory, loop, crash,
deadlock, you name it.  None of which is possible with eBPF.
So we have thousands or LOC coming into drivers every day anyway with
all those properties anyway, so this "restricted" environment solves
at best 1% of the problem.


I must admit though that "loops" in code is now considered to be evil
at the same level as deadlocks and crashes is amusing :-)

Tom

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: David Miller <davem@davemloft.net>
Date: 2017-02-10 02:35:57

From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:45:04 -0800
On Thu, Feb 9, 2017 at 2:34 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 14:26:50 -0800
quoted
On Thu, Feb 9, 2017 at 2:17 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Wed, 8 Feb 2017 15:41:20 -0800
quoted
These hooks are also generic to allow for XDP/BPF programs as well
as non-BPF code (e.g. kernel code can be written in a module).
I don't think we should even remotely consider surrendering the XDP
hook to module code.

We restrict it to eBPF for a reason, because that framework is
restricted in what it can do, what it can access, and how it can do
so.
Kernel modules go through extensive netdev review before they are
taken into the kernel, for BPF programs we just allow what any user
gives us without any peer review even implied.
We can actually control what externally written XDP eBPF programs can
do, for kernel modules we have no such control or influence.  This
hook runs right in the driver and bypasses the entire stack, it has to
execute in a hardened thing that cannot crash and it will not as long
as BPF verifier is correct.

And you're going to make it even more complicated what XDP offload in
hardware actually means.  With eBPF it is very clearly defined what
the necessary execution engine is.

Tom I'm strongly against being allowed to run arbitrary module code
from the XDP hook, sorry.

It is as important as the distinction between full stack offload and
partial offload in those nice charts in your talks. :-)
Yes it is. And the relevant principle that I would draw from that is
the "offload" means offloading functionality from the kernel **to**
the device. Restricting what we implement in the kernel on the basis
of whether or not it can be offloaded to a device is completely
backwards in this regard.
I didn't say that's the reason I'm against it.

I said it's because eBPF is constrained, and there is a very
well understood universe of operations it can perform and what
memory it can access.

Whereas modules can touch any piece of kernel memory, loop, crash,
deadlock, you name it.  None of which is possible with eBPF.

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: David Miller <davem@davemloft.net>
Date: 2017-02-10 03:34:08

From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 18:29:54 -0800
So we have thousands or LOC coming into drivers every day anyway with
all those properties anyway, so this "restricted" environment solves
at best 1% of the problem.
What you must understand is that no matter what someone outside of
upstream writes into an eBPF program, it's safe, and we can absolutely
prove this with the verifier and the invariants of the execution
environment.

Real kernel modules have no such restricted scope.

This is the fundamental issue.

Even if I agreed with you, it's tremendously frustrating that we
haven't even touched the surface of what eBPF XDP can do, and yet
you're openning the floodgates to something we cannot even prove
we need or is required yet.

XDP via eBPF in it's current form needs more work and it needs to be
fully fleshed out and more user friendly.  That's where the effort
and engineering resources belong right now.

After that you can say "Ok, now we have that just about feature
complete, here is the thing that's not possible and that's why we need
X" You think you can answer that right now, and I know that it's not
true.  There is so much that eBPF XDP can do with the right mix of
care and helper functions.  I actually really see no fundamental limit
to what it is capable of doing with the proper design.

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: David Miller <davem@davemloft.net>
Date: 2017-02-10 04:16:25

From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 15:08:22 -0800
Okay, how about this... I'll add a configuration option like
XDP_ALLOW_OTHER_HOOKS. The default will be to disallow setting any
hook other than a BPF. If it is set, then we'll accept other hooks
to be run. This way mostly restrict the interface by default, but
still allow experimentation with other hook types like I need with
TXDP or maybe the netfilter guys might want to fastpath netfilter
etc. When we we bring a working robust implementation to netdev that
show clear benefits then we can add those to BPF as the "allowed"
hooks at that time. So this strictly controls the interfaces, but
still also allows room for innovation.
Anyone is allowed to "innovate" in their own private kernel tree.

But I'm not unleashing that upstream.

The only reason I accepted XDP is entirely because it is limited
in scope to eBPF.  All eBPF programs execute in finite time,
cannot loop, cannot deadlock, cannot access arbitrary pieces
of kernel memory and datastructures.

It is a well defined, constrained, and incredibly tightly controlled
execution environment for implementing policy, monitoring and control.

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-10 05:27:15

On Thu, Feb 9, 2017 at 7:33 PM, David Miller [off-list ref] wrote:
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 18:29:54 -0800
quoted
So we have thousands or LOC coming into drivers every day anyway with
all those properties anyway, so this "restricted" environment solves
at best 1% of the problem.
What you must understand is that no matter what someone outside of
upstream writes into an eBPF program, it's safe, and we can absolutely
prove this with the verifier and the invariants of the execution
environment.
This is the exact same argument the userspace stack proponents will
use-- put your stack in userspace and you can't crash the host. But
just like eBPF that does not at all mean the logic of the program is
correct. Getting into a mode where we drop every packet, or checksums
are miscomputed, or a protocol field is miswritten is entirely
possible. The value of coding in the Linux kernel, maybe the only
truly relevant point compared to userspace stacks, is the scrutiny,
the testing, the debugging, and the eyes of experts we get to look at
every line going into the kernel to avoid such problems. Even though
there's the possibility of crash or deadloacking the system, I would
absolutely put the quality of kernel code over _any_ piece of
userspace code _any_ day of the week. Maybe some day we'll see a
process for XDP/BPF for reviewing and accepting code and you along
with several of established experts on netdev will be earnestly
reviewing such code, but until then I am more inclined to stick with
writing kernel code for anything other than simple things that are
amenable to BPF. The problem with kernel bypass is not just that it
bypasses the well-written and well-tested kernel code, but that it
also bypasses the process.

Tom

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Jason Wang <hidden>
Date: 2017-02-10 06:37:48


On 2017年02月10日 10:30, Tom Herbert wrote:
On Thu, Feb 9, 2017 at 5:48 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 15:08:22 -0800
quoted
Okay, how about this... I'll add a configuration option like
XDP_ALLOW_OTHER_HOOKS. The default will be to disallow setting any
hook other than a BPF. If it is set, then we'll accept other hooks
to be run. This way mostly restrict the interface by default, but
still allow experimentation with other hook types like I need with
TXDP or maybe the netfilter guys might want to fastpath netfilter
etc. When we we bring a working robust implementation to netdev that
show clear benefits then we can add those to BPF as the "allowed"
hooks at that time. So this strictly controls the interfaces, but
still also allows room for innovation.
Anyone is allowed to "innovate" in their own private kernel tree.

But I'm not unleashing that upstream.

The only reason I accepted XDP is entirely because it is limited
in scope to eBPF.  All eBPF programs execute in finite time,
cannot loop, cannot deadlock, cannot access arbitrary pieces
of kernel memory and datastructures.

It is a well defined, constrained, and incredibly tightly controlled
execution environment for implementing policy, monitoring and control.
And it's also incredibly invasive in the core data path of drivers.
TBH it is not clear to me that the narrow use cases for XDP justifies
adding this complexity being added to every driver.
XDP is valuable for fast userspace forwarding (e.g macvtap passthrough 
mode). I hope we can leave a window for this. Or we may need introduce 
other similar hooks.

Thanks
In any case, I withdraw the patch set.

Tom

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: David Miller <davem@davemloft.net>
Date: 2017-02-10 17:06:50

From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 20:55:34 -0800
On Thu, Feb 9, 2017 at 7:33 PM, David Miller [off-list ref] wrote:
quoted
From: Tom Herbert <redacted>
Date: Thu, 9 Feb 2017 18:29:54 -0800
quoted
So we have thousands or LOC coming into drivers every day anyway with
all those properties anyway, so this "restricted" environment solves
at best 1% of the problem.
What you must understand is that no matter what someone outside of
upstream writes into an eBPF program, it's safe, and we can absolutely
prove this with the verifier and the invariants of the execution
environment.
This is the exact same argument the userspace stack proponents will
use-- put your stack in userspace and you can't crash the host.
Sounds like we can therefore meet that requirement and keep them in
the kernel networking path, which supports all of our values and goals
precisely.

[lkp-robot] [xdp] 543d41bf78: INFO:suspicious_RCU_usage

From: kernel test robot <hidden>
Date: 2017-02-13 02:42:04

FYI, we noticed the following commit:

commit: 543d41bf78792e858e6f6598945d307ff808b7fc ("xdp: Infrastructure to generalize XDP")
url: https://github.com/0day-ci/linux/commits/Tom-Herbert/xdp-Generalize-XDP/20170209-092238


in testcase: trinity
with following parameters:

	runtime: 300s

test-description: Trinity is a linux system call fuzz tester.
test-url: http://codemonkey.org.uk/projects/trinity/


on test machine: qemu-system-i386 -enable-kvm -smp 2 -m 320M

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):


+-----------------------------------------------------+------------+------------+
|                                                     | df6dd79be8 | 543d41bf78 |
+-----------------------------------------------------+------------+------------+
| boot_successes                                      | 10         | 0          |
| boot_failures                                       | 2          | 12         |
| WARNING:at_arch/x86/mm/dump_pagetables.c:#note_page | 2          | 2          |
| INFO:suspicious_RCU_usage                           | 0          | 12         |
+-----------------------------------------------------+------------+------------+



[    6.814497] [ INFO: suspicious RCU usage. ]
[    6.814497] [ INFO: suspicious RCU usage. ]
[    6.814990] 4.10.0-rc7-01379-g543d41b #1 Not tainted
[    6.814990] 4.10.0-rc7-01379-g543d41b #1 Not tainted
[    6.815618] -------------------------------
[    6.815618] -------------------------------
[    6.816107] net/core/xdp.c:201 suspicious rcu_dereference_check() usage!
[    6.816107] net/core/xdp.c:201 suspicious rcu_dereference_check() usage!
[    6.817090] 
[    6.817090] other info that might help us debug this:
[    6.817090] 
[    6.817090] 
[    6.817090] other info that might help us debug this:
[    6.817090] 
[    6.818000] 
[    6.818000] rcu_scheduler_active = 2, debug_locks = 0
[    6.818000] 
[    6.818000] rcu_scheduler_active = 2, debug_locks = 0
[    6.818778] 1 lock held by swapper/1:
[    6.818778] 1 lock held by swapper/1:
[    6.819213]  #0:  (xdp_hook_mutex){+.+...}, at: [<c146ed6c>] __xdp_unregister_hooks+0x1c/0x185
[    6.819213]  #0:  (xdp_hook_mutex){+.+...}, at: [<c146ed6c>] __xdp_unregister_hooks+0x1c/0x185
[    6.820199] 
[    6.820199] stack backtrace:
[    6.820199] 
[    6.820199] stack backtrace:
[    6.820710] CPU: 0 PID: 1 Comm: swapper Not tainted 4.10.0-rc7-01379-g543d41b #1
[    6.820710] CPU: 0 PID: 1 Comm: swapper Not tainted 4.10.0-rc7-01379-g543d41b #1
[    6.821530] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.9.3-20161025_171302-gandalf 04/01/2014
[    6.821530] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.9.3-20161025_171302-gandalf 04/01/2014
[    6.822747] Call Trace:
[    6.822747] Call Trace:
[    6.823052]  dump_stack+0x16/0x18
[    6.823052]  dump_stack+0x16/0x18
[    6.823434]  lockdep_rcu_suspicious+0xdb/0xee
[    6.823434]  lockdep_rcu_suspicious+0xdb/0xee
[    6.823908]  __xdp_unregister_hooks+0x171/0x185
[    6.823908]  __xdp_unregister_hooks+0x171/0x185
[    6.824421]  ? __might_sleep+0x2d/0x86
[    6.824421]  ? __might_sleep+0x2d/0x86
[    6.824848]  xdp_unregister_all_hooks+0x3a/0x3f
[    6.824848]  xdp_unregister_all_hooks+0x3a/0x3f
[    6.825398]  free_netdev+0x25/0xca
[    6.825398]  free_netdev+0x25/0xca
[    6.825801]  lance_probe+0x115/0x122
[    6.825801]  lance_probe+0x115/0x122
[    6.826191]  probe_list2+0x20/0x41
[    6.826191]  probe_list2+0x20/0x41
[    6.826586]  net_olddevs_init+0x42/0x4e
[    6.826586]  net_olddevs_init+0x42/0x4e
[    6.827037]  ? probe_list2+0x41/0x41
[    6.827037]  ? probe_list2+0x41/0x41
[    6.827448]  do_one_initcall+0x3c/0x184
[    6.827448]  do_one_initcall+0x3c/0x184
[    6.827866]  ? repair_env_string+0x12/0x54
[    6.827866]  ? repair_env_string+0x12/0x54
[    6.828326]  ? parse_args+0x24e/0x402
[    6.828326]  ? parse_args+0x24e/0x402
[    6.828785]  ? trace_hardirqs_on+0xb/0xd
[    6.828785]  ? trace_hardirqs_on+0xb/0xd
[    6.829235]  kernel_init_freeable+0xe1/0x15c
[    6.829235]  kernel_init_freeable+0xe1/0x15c
[    6.829729]  ? rest_init+0x10e/0x10e
[    6.829729]  ? rest_init+0x10e/0x10e
[    6.830134]  kernel_init+0xb/0xe5
[    6.830134]  kernel_init+0xb/0xe5
[    6.830515]  ? schedule_tail+0xc/0x4a
[    6.830515]  ? schedule_tail+0xc/0x4a
[    6.830925]  ? rest_init+0x10e/0x10e
[    6.830925]  ? rest_init+0x10e/0x10e
[    6.831343]  ret_from_fork+0x21/0x2c
[    6.831343]  ret_from_fork+0x21/0x2c
[    6.832026] libphy: Fixed MDIO Bus: probed
[    6.832026] libphy: Fixed MDIO Bus: probed
[    6.832650] arcnet: arcnet loaded
[    6.832650] arcnet: arcnet loaded
[    6.833011] arcnet:rfc1201: RFC1201 "standard" (`a') encapsulation support loaded
[    6.833011] arcnet:rfc1201: RFC1201 "standard" (`a') encapsulation support loaded
[    6.833856] arcnet:arc_rawmode: raw mode (`r') encapsulation support loaded
[    6.833856] arcnet:arc_rawmode: raw mode (`r') encapsulation support loaded
[    6.834641] arcnet:com90xx: COM90xx chipset support
[    6.834641] arcnet:com90xx: COM90xx chipset support
[    7.135493] S3: No ARCnet cards found.
[    7.135493] S3: No ARCnet cards found.
[    7.136044] PPP generic driver version 2.4.2
[    7.136044] PPP generic driver version 2.4.2
[    7.136573] PPP BSD Compression module registered
[    7.136573] PPP BSD Compression module registered
[    7.137113] PPP MPPE Compression module registered
[    7.137113] PPP MPPE Compression module registered
[    7.137655] NET: Registered protocol family 24
[    7.137655] NET: Registered protocol family 24
[    7.138165] SLIP: version 0.8.4-NET3.019-NEWTTY (dynamic channels, max=256).
[    7.138165] SLIP: version 0.8.4-NET3.019-NEWTTY (dynamic channels, max=256).
[    7.138990] SLIP linefill/keepalive option.
[    7.138990] SLIP linefill/keepalive option.
[    7.139543] i8042: PNP: PS/2 Controller [PNP0303:KBD,PNP0f13:MOU] at 0x60,0x64 irq 1,12
[    7.139543] i8042: PNP: PS/2 Controller [PNP0303:KBD,PNP0f13:MOU] at 0x60,0x64 irq 1,12
[    7.140865] serio: i8042 KBD port at 0x60,0x64 irq 1
[    7.140865] serio: i8042 KBD port at 0x60,0x64 irq 1
[    7.141421] serio: i8042 AUX port at 0x60,0x64 irq 12
[    7.141421] serio: i8042 AUX port at 0x60,0x64 irq 12


To reproduce:

        git clone git://git.kernel.org/pub/scm/linux/kernel/git/wfg/lkp-tests.git
        cd lkp-tests
        bin/lkp qemu -k <bzImage> job-script  # job-script is attached in this email



Thanks,
Xiaolong

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Jesper Dangaard Brouer <hidden>
Date: 2017-02-14 20:32:02

On Wed, 8 Feb 2017 15:41:20 -0800
Tom Herbert [off-list ref] wrote:
+static inline int __xdp_run_one_hook(struct xdp_hook *hook,
+				     struct xdp_buff *xdp)
+{
+	void *priv = rcu_dereference(hook->priv);
+
+	if (hook->is_bpf) {
+		/* Run BPF programs directly do avoid one layer of
+		 * indirection.
+		 */
+		return BPF_PROG_RUN((struct bpf_prog *)priv, (void *)xdp);
+	} else {
+		return hook->hookfn(priv, xdp);
+	}
+}
+
+/* Core function to run the XDP hooks. This must be as fast as possible */
+static inline int __xdp_hook_run(struct xdp_hook_set *hook_set,
+				 struct xdp_buff *xdp,
+				 struct xdp_hook **last_hook)
+{
+	struct xdp_hook *hook;
+	int i, ret;
+
+	if (unlikely(!hook_set))
+		return XDP_PASS;
+
+	hook = &hook_set->hooks[0];
+	ret = __xdp_run_one_hook(hook, xdp);
+	*last_hook = hook;
+
+	for (i = 1; i < hook_set->num; i++) {
+		if (ret != XDP_PASS)
+			break;
+		hook = &hook_set->hooks[i];
+		ret = __xdp_run_one_hook(hook, xdp);
+	}
+
+	return ret;
+}
There is one basic problem with this approach.  There is no bulking and
no reuse of instruction cache.  There is no revolution in this approach.
We will end-up with the same known performance problems when more hook
users get added.

Calling N-number of hooks per every packet, will just end-up flushing
the instruction cache (like the issues we have today).

Instead take N-packets, and then call the hooks by turn (store action
verdicts in packet-vector).  Such an architecture would be inline with
that VPP, Snabb and DPDK is doing.  Optimizing icache usage, and opens
up for smarter prefetching of lookup tables.  Imagine, having hook-1
identify lookup bucket and start prefetch, hook-2 access the bucket and
prefetch table data, and hook-3 read data.  This is what DPDK is doing
see[1], and VPP is doing similar tricks to get it to scale to large
route lookup tables.

[1] http://dpdk.org/doc/guides/prog_guide/packet_framework.html#figure-figure35

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-14 20:47:20

On Tue, Feb 14, 2017 at 12:31 PM, Jesper Dangaard Brouer
[off-list ref] wrote:
On Wed, 8 Feb 2017 15:41:20 -0800
Tom Herbert [off-list ref] wrote:
quoted
+static inline int __xdp_run_one_hook(struct xdp_hook *hook,
+                                  struct xdp_buff *xdp)
+{
+     void *priv = rcu_dereference(hook->priv);
+
+     if (hook->is_bpf) {
+             /* Run BPF programs directly do avoid one layer of
+              * indirection.
+              */
+             return BPF_PROG_RUN((struct bpf_prog *)priv, (void *)xdp);
+     } else {
+             return hook->hookfn(priv, xdp);
+     }
+}
+
+/* Core function to run the XDP hooks. This must be as fast as possible */
+static inline int __xdp_hook_run(struct xdp_hook_set *hook_set,
+                              struct xdp_buff *xdp,
+                              struct xdp_hook **last_hook)
+{
+     struct xdp_hook *hook;
+     int i, ret;
+
+     if (unlikely(!hook_set))
+             return XDP_PASS;
+
+     hook = &hook_set->hooks[0];
+     ret = __xdp_run_one_hook(hook, xdp);
+     *last_hook = hook;
+
+     for (i = 1; i < hook_set->num; i++) {
+             if (ret != XDP_PASS)
+                     break;
+             hook = &hook_set->hooks[i];
+             ret = __xdp_run_one_hook(hook, xdp);
+     }
+
+     return ret;
+}
There is one basic problem with this approach.  There is no bulking and
no reuse of instruction cache.  There is no revolution in this approach.
We will end-up with the same known performance problems when more hook
users get added.

Calling N-number of hooks per every packet, will just end-up flushing
the instruction cache (like the issues we have today).

Instead take N-packets, and then call the hooks by turn (store action
verdicts in packet-vector).  Such an architecture would be inline with
that VPP, Snabb and DPDK is doing.  Optimizing icache usage, and opens
up for smarter prefetching of lookup tables.  Imagine, having hook-1
identify lookup bucket and start prefetch, hook-2 access the bucket and
prefetch table data, and hook-3 read data.  This is what DPDK is doing
see[1], and VPP is doing similar tricks to get it to scale to large
route lookup tables.
Conceptually, that's a good extension, basically provide VPP-like
interface in the kernel. Unfortunately though, we need the return
codes to be processed in the driver so that API and driver model would
have to change more. If we do this I suggest it's done sooner than
later, as more drivers adopt XDP changing all the drivers for that API
become much harder (as I saw with just the second version of this
patch).

Tom

[1] http://dpdk.org/doc/guides/prog_guide/packet_framework.html#figure-figure35

--
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-14 21:07:36

On Tue, Feb 14, 2017 at 12:47 PM, Tom Herbert [off-list ref] wrote:
On Tue, Feb 14, 2017 at 12:31 PM, Jesper Dangaard Brouer
[off-list ref] wrote:
quoted
On Wed, 8 Feb 2017 15:41:20 -0800
Tom Herbert [off-list ref] wrote:
quoted
+static inline int __xdp_run_one_hook(struct xdp_hook *hook,
+                                  struct xdp_buff *xdp)
+{
+     void *priv = rcu_dereference(hook->priv);
+
+     if (hook->is_bpf) {
+             /* Run BPF programs directly do avoid one layer of
+              * indirection.
+              */
+             return BPF_PROG_RUN((struct bpf_prog *)priv, (void *)xdp);
+     } else {
+             return hook->hookfn(priv, xdp);
+     }
+}
+
+/* Core function to run the XDP hooks. This must be as fast as possible */
+static inline int __xdp_hook_run(struct xdp_hook_set *hook_set,
+                              struct xdp_buff *xdp,
+                              struct xdp_hook **last_hook)
+{
+     struct xdp_hook *hook;
+     int i, ret;
+
+     if (unlikely(!hook_set))
+             return XDP_PASS;
+
+     hook = &hook_set->hooks[0];
+     ret = __xdp_run_one_hook(hook, xdp);
+     *last_hook = hook;
+
+     for (i = 1; i < hook_set->num; i++) {
+             if (ret != XDP_PASS)
+                     break;
+             hook = &hook_set->hooks[i];
+             ret = __xdp_run_one_hook(hook, xdp);
+     }
+
+     return ret;
+}
There is one basic problem with this approach.  There is no bulking and
no reuse of instruction cache.  There is no revolution in this approach.
We will end-up with the same known performance problems when more hook
users get added.

Calling N-number of hooks per every packet, will just end-up flushing
the instruction cache (like the issues we have today).

Instead take N-packets, and then call the hooks by turn (store action
verdicts in packet-vector).  Such an architecture would be inline with
that VPP, Snabb and DPDK is doing.  Optimizing icache usage, and opens
up for smarter prefetching of lookup tables.  Imagine, having hook-1
identify lookup bucket and start prefetch, hook-2 access the bucket and
prefetch table data, and hook-3 read data.  This is what DPDK is doing
see[1], and VPP is doing similar tricks to get it to scale to large
route lookup tables.
Conceptually, that's a good extension, basically provide VPP-like
interface in the kernel. Unfortunately though, we need the return
codes to be processed in the driver so that API and driver model would
have to change more. If we do this I suggest it's done sooner than
later, as more drivers adopt XDP changing all the drivers for that API
become much harder (as I saw with just the second version of this
patch).
Off the top of my head... I'd say may we might be able to have a
minimally invasive interface with something like:

XDP_RUN(hook, xdp, drv_xdp_handle_action)

This replaces xdp_run and return codes are processed in the called
functions. Its a macro so that xdp_handle_action can be inlined.
Batching could then be done in the backend XDP so that it would be
transparent to the driver. We'd also probably want another call like
so the driver can flush the queued packets when exiting the rx loop.
Something like:

XDP_FLUSH(xdp, drv_xdp_handle_action)

The hook then contains a function callback that gives an array of
pages to a function and returns an array of actions.

Tom

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Edward Cree <hidden>
Date: 2017-02-14 22:08:45

On 14/02/17 21:07, Tom Herbert wrote:
Off the top of my head... I'd say may we might be able to have a
minimally invasive interface with something like:

XDP_RUN(hook, xdp, drv_xdp_handle_action)

This replaces xdp_run and return codes are processed in the called
functions. Its a macro so that xdp_handle_action can be inlined.
I don't see why callbacks are needed, since XDP programs (I assume)
 aren't supposed to block.  This XDP_RUN ends up looking a lot like
 NF_HOOK, for no good reason that I can see (unlike NF hooks, we never
 do things like NF_QUEUE).
Batching could then be done in the backend XDP so that it would be
transparent to the driver.
I also don't see how you can transparently batch and still allow the
 handler to be inlined - you'd have to stash a function pointer that
 you could call later when you decide to dispatch a batch of packets.

To me, the sensible interface (which makes the batching explicit to
 the driver, which I think is necessary) is to have an int (or maybe
 unsigned int, which is the return type of xdp_hookfn, I'm not sure
 which is intended) member in struct xdp_buff.
Then the driver can call something like
	XDP_RUN_ARRAY(napi, xdp_array, array_len);
which is semantically equivalent to
	unsigned int i;
	for (i = 0; i < array_len; i++)
		xdp_array[i].ret = xdp_hook_run(napi, xdp_array + i);
except that it may run the hooks in 'row-major order'.
No callbacks needed, the driver can just loop over xdp_array reading
 the .ret and applying the relevant action to each packet.

This also has the advantage that the driver knows how many packets it
 might have to process in a single batch (i.e. NAPI_POLL_WEIGHT) and
 can allocate the array statically, whereas an XDP hook that tried to
 transparently be 'helpful' would have to guess and/or use kmalloc.

-Ed

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Tom Herbert <hidden>
Date: 2017-02-14 22:28:26

On Tue, Feb 14, 2017 at 2:08 PM, Edward Cree [off-list ref] wrote:
On 14/02/17 21:07, Tom Herbert wrote:
quoted
Off the top of my head... I'd say may we might be able to have a
minimally invasive interface with something like:

XDP_RUN(hook, xdp, drv_xdp_handle_action)

This replaces xdp_run and return codes are processed in the called
functions. Its a macro so that xdp_handle_action can be inlined.
I don't see why callbacks are needed, since XDP programs (I assume)
 aren't supposed to block.  This XDP_RUN ends up looking a lot like
 NF_HOOK, for no good reason that I can see (unlike NF hooks, we never
 do things like NF_QUEUE).
quoted
Batching could then be done in the backend XDP so that it would be
transparent to the driver.
I also don't see how you can transparently batch and still allow the
 handler to be inlined - you'd have to stash a function pointer that
 you could call later when you decide to dispatch a batch of packets.
That would be handled either in XDP_RUN or XDP flush. There should be
no need to save pointer.
To me, the sensible interface (which makes the batching explicit to
 the driver, which I think is necessary) is to have an int (or maybe
 unsigned int, which is the return type of xdp_hookfn, I'm not sure
 which is intended) member in struct xdp_buff.
Then the driver can call something like
        XDP_RUN_ARRAY(napi, xdp_array, array_len);
which is semantically equivalent to
        unsigned int i;
        for (i = 0; i < array_len; i++)
                xdp_array[i].ret = xdp_hook_run(napi, xdp_array + i);
except that it may run the hooks in 'row-major order'.
No callbacks needed, the driver can just loop over xdp_array reading
 the .ret and applying the relevant action to each packet.

This also has the advantage that the driver knows how many packets it
 might have to process in a single batch (i.e. NAPI_POLL_WEIGHT) and
 can allocate the array statically, whereas an XDP hook that tried to
 transparently be 'helpful' would have to guess and/or use kmalloc.
But that has the disadvantage of requiring drivers to implement yet
another loop in drivers for which they each will need to choose bounds
and this makes the complexity of batching explicit in the driver.
Probably the biggest issues with XDP is the potential impact in the
core data path on every driver for something that is a narrow use case
feature. Minimizing the impact on drivers is a high order goal I
believe, even if it might be at the expense of having complete
flexibility.

Tom
-Ed

Re: [PATCH RFC v2 1/8] xdp: Infrastructure to generalize XDP

From: Jesper Dangaard Brouer <hidden>
Date: 2017-02-14 22:29:39

On Tue, 14 Feb 2017 22:08:10 +0000
Edward Cree [off-list ref] wrote:
To me, the sensible interface (which makes the batching explicit to
 the driver, which I think is necessary) is to have an int (or maybe
 unsigned int, which is the return type of xdp_hookfn, I'm not sure
 which is intended) member in struct xdp_buff.

Then the driver can call something like
	XDP_RUN_ARRAY(napi, xdp_array, array_len);
which is semantically equivalent to
	unsigned int i;
	for (i = 0; i < array_len; i++)
		xdp_array[i].ret = xdp_hook_run(napi, xdp_array + i);
Yes, exactly.

I imagined the xdp_array[i].ret would be the XDP action return code.
except that it may run the hooks in 'row-major order'.
No callbacks needed, the driver can just loop over xdp_array reading
 the .ret and applying the relevant action to each packet.

This also has the advantage that the driver knows how many packets it
 might have to process in a single batch (i.e. NAPI_POLL_WEIGHT) and
 can allocate the array statically, whereas an XDP hook that tried to
 transparently be 'helpful' would have to guess and/or use kmalloc.
I also think the driver need to be explicit about batching.

This related to the RX stages I'm talking about.  Saeed is working on
implementing that for mlx5, I got some PoC patches today and I'll soon
test that.

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help