From: Björn Töpel <bjorn.to...@intel.com>

Previously, when swapping from one XDP program to another, a
reset/rebuild rings was triggered. Now, the XDP program is simply
changed without that requirement.

Acked-by: John Fastabend <john.r.fastab...@intel.com>
Signed-off-by: Björn Töpel <bjorn.to...@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e.h      |  4 +--
 drivers/net/ethernet/intel/i40e/i40e_main.c | 41 ++++++++++++++++++-----------
 drivers/net/ethernet/intel/i40e/i40e_txrx.c | 25 +++++++++++-------
 drivers/net/ethernet/intel/i40e/i40e_txrx.h |  2 +-
 4 files changed, 44 insertions(+), 28 deletions(-)

diff --git a/drivers/net/ethernet/intel/i40e/i40e.h 
b/drivers/net/ethernet/intel/i40e/i40e.h
index adc1f3f32729..9bc2a8cf5c2e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -549,7 +549,7 @@ struct i40e_vsi {
         * regular rings, i.e. alloc_queue_pairs/num_queue_pairs
         */
        struct i40e_ring **xdp_rings;
-       struct bpf_prog *xdp_prog;
+       bool xdp_enabled;
 
        u32  active_filters;
        u32  promisc_threshold;
@@ -920,6 +920,6 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool 
isup);
  **/
 static inline bool i40e_enabled_xdp_vsi(const struct i40e_vsi *vsi)
 {
-       return vsi->xdp_prog;
+       return vsi->xdp_enabled;
 }
 #endif /* _I40E_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c 
b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 9310a5712ae3..7cac13d1c244 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -3116,15 +3116,6 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
        ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q);
        writel(0, ring->tail);
 
-       if (i40e_enabled_xdp_vsi(vsi)) {
-               struct bpf_prog *prog;
-
-               prog = bpf_prog_add(vsi->xdp_prog, 1);
-               if (IS_ERR(prog))
-                       return PTR_ERR(prog);
-               ring->xdp_prog = prog;
-       }
-
        i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring));
 
        return 0;
@@ -9428,7 +9419,9 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi,
 {
        struct i40e_pf *pf = vsi->back;
        struct net_device *netdev = vsi->netdev;
-       int frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       int i, frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       bool need_reset;
+       struct bpf_prog *old_prog;
 
        if (frame_size > I40E_RXBUFFER_2048)
                return -EINVAL;
@@ -9439,13 +9432,29 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi,
        if (!i40e_enabled_xdp_vsi(vsi) && !prog)
                return 0;
 
-       i40e_prep_for_reset(pf);
+       if (prog) {
+               prog = bpf_prog_add(prog, vsi->num_queue_pairs - 1);
+               if (IS_ERR(prog))
+                       return PTR_ERR(prog);
+       }
 
-       if (vsi->xdp_prog)
-               bpf_prog_put(vsi->xdp_prog);
-       vsi->xdp_prog = prog;
+       /* When turning XDP on->off/off->on we reset and rebuild the rings. */
+       need_reset = (i40e_enabled_xdp_vsi(vsi) != !!prog);
 
-       i40e_reset_and_rebuild(pf, true);
+       if (need_reset)
+               i40e_prep_for_reset(pf);
+
+       vsi->xdp_enabled = !!prog;
+
+       if (need_reset)
+               i40e_reset_and_rebuild(pf, true);
+
+       for (i = 0; i < vsi->num_queue_pairs; i++) {
+               old_prog = rtnl_dereference(vsi->rx_rings[i]->xdp_prog);
+               rcu_assign_pointer(vsi->rx_rings[i]->xdp_prog, prog);
+               if (old_prog)
+                       bpf_prog_put(old_prog);
+       }
        return 0;
 }
 
@@ -11740,7 +11749,9 @@ static void i40e_remove(struct pci_dev *pdev)
                pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
        }
 
+       rtnl_lock();
        i40e_fdir_teardown(pf);
+       rtnl_unlock();
 
        /* If there is a switch structure or any orphans, remove them.
         * This will leave only the PF's VSI remaining.
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c 
b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index fccdec7ae102..338b4c4c0199 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -1112,6 +1112,7 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring)
        struct device *dev = rx_ring->dev;
        unsigned long bi_size;
        u16 i;
+       struct bpf_prog *old_prog;
 
        /* ring already cleared, nothing to do */
        if (!rx_ring->rx_bi)
@@ -1145,10 +1146,10 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring)
        rx_ring->next_to_clean = 0;
        rx_ring->next_to_use = 0;
 
-       if (rx_ring->xdp_prog) {
-               bpf_prog_put(rx_ring->xdp_prog);
-               rx_ring->xdp_prog = NULL;
-       }
+       old_prog = rtnl_dereference(rx_ring->xdp_prog);
+       RCU_INIT_POINTER(rx_ring->xdp_prog, NULL);
+       if (old_prog)
+               bpf_prog_put(old_prog);
 }
 
 /**
@@ -1880,6 +1881,7 @@ bool i40e_fetch_rx_buffer(struct i40e_ring *rx_ring,
 {
        struct i40e_rx_buffer *rx_buffer;
        struct page *page;
+       struct bpf_prog *xdp_prog;
 
        rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean];
        page = rx_buffer->page;
@@ -1892,14 +1894,17 @@ bool i40e_fetch_rx_buffer(struct i40e_ring *rx_ring,
                                      I40E_RXBUFFER_2048,
                                      DMA_FROM_DEVICE);
 
-       if (rx_ring->xdp_prog) {
-               bool xdp_consumed;
-
-               xdp_consumed = i40e_run_xdp(rx_ring, rx_buffer,
-                                           rx_desc, rx_ring->xdp_prog);
-               if (xdp_consumed)
+       rcu_read_lock();
+       xdp_prog = rcu_dereference(rx_ring->xdp_prog);
+       if (xdp_prog) {
+               bool xdp_consumed = i40e_run_xdp(rx_ring, rx_buffer,
+                                                rx_desc, xdp_prog);
+               if (xdp_consumed) {
+                       rcu_read_unlock();
                        return true;
+               }
        }
+       rcu_read_unlock();
 
        *skb = rx_buffer->skb;
 
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h 
b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index 4d9459134e69..cfb2c1016242 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -344,7 +344,7 @@ struct i40e_ring {
        struct rcu_head rcu;            /* to avoid race on free */
        u16 next_to_alloc;
 
-       struct bpf_prog *xdp_prog;
+       struct bpf_prog __rcu *xdp_prog;
        struct i40e_ring *xdp_sibling;  /* rx to xdp, and xdp to rx */
        bool xdp_needs_tail_bump;
        u16 curr_in_use;
-- 
2.9.3

Reply via email to