| From a436b6a090299080c9974bf851e847d3c8c8c62a Mon Sep 17 00:00:00 2001 |
| From: Maxim Mikityanskiy <maximmi@mellanox.com> |
| Date: Tue, 17 Dec 2019 16:20:45 +0000 |
| Subject: [PATCH] net/i40e: Fix concurrency issues between config flow and XSK |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| commit b3873a5be757b44d51af542a50a6f2a3b6f95284 upstream. |
| |
| Use synchronize_rcu to wait until the XSK wakeup function finishes |
| before destroying the resources it uses: |
| |
| 1. i40e_down already calls synchronize_rcu. On i40e_down either |
| __I40E_VSI_DOWN or __I40E_CONFIG_BUSY is set. Check the latter in |
| i40e_xsk_wakeup (the former is already checked there). |
| |
| 2. After switching the XDP program, call synchronize_rcu to let |
| i40e_xsk_wakeup exit before the XDP program is freed. |
| |
| 3. Changing the number of channels brings the interface down (see |
| i40e_prep_for_reset and i40e_pf_quiesce_all_vsi). |
| |
| 4. Disabling UMEM sets __I40E_CONFIG_BUSY, too. |
| |
| 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-4-maximmi@mellanox.com |
| [PG: note async_xmit ---> wakeup rename in mainline 9116e5e2b1ff.] |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h |
| index 7ce42040b851..9ef2d3f86aff 100644 |
| --- a/drivers/net/ethernet/intel/i40e/i40e.h |
| +++ b/drivers/net/ethernet/intel/i40e/i40e.h |
| @@ -1125,7 +1125,7 @@ void i40e_set_fec_in_flags(u8 fec_cfg, u32 *flags); |
| |
| static inline bool i40e_enabled_xdp_vsi(struct i40e_vsi *vsi) |
| { |
| - return !!vsi->xdp_prog; |
| + return !!READ_ONCE(vsi->xdp_prog); |
| } |
| |
| int i40e_create_queue_channel(struct i40e_vsi *vsi, struct i40e_channel *ch); |
| diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c |
| index f86d66139ecc..6bfd0560a7e7 100644 |
| --- a/drivers/net/ethernet/intel/i40e/i40e_main.c |
| +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c |
| @@ -6789,8 +6789,8 @@ void i40e_down(struct i40e_vsi *vsi) |
| for (i = 0; i < vsi->num_queue_pairs; i++) { |
| i40e_clean_tx_ring(vsi->tx_rings[i]); |
| if (i40e_enabled_xdp_vsi(vsi)) { |
| - /* Make sure that in-progress ndo_xdp_xmit |
| - * calls are completed. |
| + /* Make sure that in-progress ndo_xdp_xmit and |
| + * ndo_xsk_wakeup calls are completed. |
| */ |
| synchronize_rcu(); |
| i40e_clean_tx_ring(vsi->xdp_rings[i]); |
| @@ -12015,8 +12015,12 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi, |
| |
| old_prog = xchg(&vsi->xdp_prog, prog); |
| |
| - if (need_reset) |
| + if (need_reset) { |
| + if (!prog) |
| + /* Wait until ndo_xsk_wakeup completes. */ |
| + synchronize_rcu(); |
| i40e_reset_and_rebuild(pf, true, true); |
| + } |
| |
| for (i = 0; i < vsi->num_queue_pairs; i++) |
| WRITE_ONCE(vsi->rx_rings[i]->xdp_prog, vsi->xdp_prog); |
| diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c b/drivers/net/ethernet/intel/i40e/i40e_xsk.c |
| index 1b17486543ac..30e731272df4 100644 |
| --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c |
| +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c |
| @@ -772,8 +772,12 @@ int i40e_xsk_async_xmit(struct net_device *dev, u32 queue_id) |
| { |
| struct i40e_netdev_priv *np = netdev_priv(dev); |
| struct i40e_vsi *vsi = np->vsi; |
| + struct i40e_pf *pf = vsi->back; |
| struct i40e_ring *ring; |
| |
| + if (test_bit(__I40E_CONFIG_BUSY, pf->state)) |
| + return -ENETDOWN; |
| + |
| if (test_bit(__I40E_VSI_DOWN, vsi->state)) |
| return -ENETDOWN; |
| |
| -- |
| 2.7.4 |
| |