net/ixgbe: Fix concurrency issues between config flow and XSK
authorMaxim Mikityanskiy <maximmi@mellanox.com>
Tue, 17 Dec 2019 16:20:47 +0000 (16:20 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 12 Jan 2020 11:21:42 +0000 (12:21 +0100)
[ Upstream commit c0fdccfd226a1424683d3000d9e08384391210a2 ]

Use synchronize_rcu to wait until the XSK wakeup function finishes
before destroying the resources it uses:

1. ixgbe_down already calls synchronize_rcu after setting __IXGBE_DOWN.

2. After switching the XDP program, call synchronize_rcu to let
ixgbe_xsk_wakeup exit before the XDP program is freed.

3. Changing the number of channels brings the interface down.

4. Disabling UMEM sets __IXGBE_TX_DISABLED before closing hardware
resources and resetting xsk_umem. Check that bit in ixgbe_xsk_wakeup to
avoid using the XDP ring when it's already destroyed. synchronize_rcu is
called from ixgbe_txrx_ring_disable.

Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com>
Signed-off-by: Björn Töpel <bjorn.topel@intel.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20191217162023.16011-5-maximmi@mellanox.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c

index 1a7203f..c6404ab 100644 (file)
@@ -10248,7 +10248,12 @@ static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog)
 
        /* If transitioning XDP modes reconfigure rings */
        if (need_reset) {
-               int err = ixgbe_setup_tc(dev, adapter->hw_tcs);
+               int err;
+
+               if (!prog)
+                       /* Wait until ndo_xsk_wakeup completes. */
+                       synchronize_rcu();
+               err = ixgbe_setup_tc(dev, adapter->hw_tcs);
 
                if (err) {
                        rcu_assign_pointer(adapter->xdp_prog, old_prog);
index d6feaac..b43be9f 100644 (file)
@@ -709,10 +709,14 @@ int ixgbe_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
        if (qid >= adapter->num_xdp_queues)
                return -ENXIO;
 
-       if (!adapter->xdp_ring[qid]->xsk_umem)
+       ring = adapter->xdp_ring[qid];
+
+       if (test_bit(__IXGBE_TX_DISABLED, &ring->state))
+               return -ENETDOWN;
+
+       if (!ring->xsk_umem)
                return -ENXIO;
 
-       ring = adapter->xdp_ring[qid];
        if (!napi_if_scheduled_mark_missed(&ring->q_vector->napi)) {
                u64 eics = BIT_ULL(ring->q_vector->v_idx);