Commit b3873a5b authored by Maxim Mikityanskiy's avatar Maxim Mikityanskiy Committed by Daniel Borkmann
Browse files

net/i40e: Fix concurrency issues between config flow and XSK



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: default avatarMaxim Mikityanskiy <maximmi@mellanox.com>
Signed-off-by: default avatarBjörn Töpel <bjorn.topel@intel.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20191217162023.16011-4-maximmi@mellanox.com
parent 9cf88808
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1152,7 +1152,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);
+7 −3
Original line number Diff line number Diff line
@@ -6823,8 +6823,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]);
@@ -12546,8 +12546,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);
+4 −0
Original line number Diff line number Diff line
@@ -787,8 +787,12 @@ int i40e_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
{
	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;