ice: xsk: use Rx ring's XDP ring when picking NAPI context

Ice driver allocates per cpu XDP queues so that redirect path can safely
use smp_processor_id() as an index to the array. At the same time
though, XDP rings are used to pick NAPI context to call napi_schedule()
or set NAPIF_STATE_MISSED. When user reduces queue count, say to 8, and
num_possible_cpus() of underlying platform is 44, then this means queue
vectors with correlated NAPI contexts will carry several XDP queues.

This in turn can result in a broken behavior where NAPI context of
interest will never be scheduled and AF_XDP socket will not process any
traffic.

To fix this, let us change the way how XDP rings are assigned to Rx
rings and use this information later on when setting
ice_tx_ring::xsk_pool pointer. For each Rx ring, grab the associated
queue vector and walk through Tx ring's linked list. Once we stumble
upon XDP ring in it, assign this ring to ice_rx_ring::xdp_ring.

Previous [0] approach of fixing this issue was for txonly scenario
because of the described grouping of XDP rings across queue vectors. So,
relying on Rx ring meant that NAPI context could be scheduled with a
queue vector without XDP ring with associated XSK pool.

[0]: https://lore.kernel.org/netdev/20220707161128.54215-1-maciej.fijalkowski@intel.com/

Fixes: 2d4238f556 ("ice: Add support for AF_XDP")
Fixes: 22bf877e52 ("ice: introduce XDP_TX fallback path")
Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Tested-by: George Kuruvinakunnel <george.kuruvinakunnel@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
Maciej Fijalkowski 2022-08-11 20:21:49 +02:00 committed by Tony Nguyen
parent 5a42f112d3
commit 9ead7e74bf
4 changed files with 48 additions and 29 deletions

View File

@ -684,8 +684,8 @@ static inline void ice_set_ring_xdp(struct ice_tx_ring *ring)
* ice_xsk_pool - get XSK buffer pool bound to a ring
* @ring: Rx ring to use
*
* Returns a pointer to xdp_umem structure if there is a buffer pool present,
* NULL otherwise.
* Returns a pointer to xsk_buff_pool structure if there is a buffer pool
* present, NULL otherwise.
*/
static inline struct xsk_buff_pool *ice_xsk_pool(struct ice_rx_ring *ring)
{
@ -699,23 +699,33 @@ static inline struct xsk_buff_pool *ice_xsk_pool(struct ice_rx_ring *ring)
}
/**
* ice_tx_xsk_pool - get XSK buffer pool bound to a ring
* @ring: Tx ring to use
* ice_tx_xsk_pool - assign XSK buff pool to XDP ring
* @vsi: pointer to VSI
* @qid: index of a queue to look at XSK buff pool presence
*
* Returns a pointer to xdp_umem structure if there is a buffer pool present,
* NULL otherwise. Tx equivalent of ice_xsk_pool.
* Sets XSK buff pool pointer on XDP ring.
*
* XDP ring is picked from Rx ring, whereas Rx ring is picked based on provided
* queue id. Reason for doing so is that queue vectors might have assigned more
* than one XDP ring, e.g. when user reduced the queue count on netdev; Rx ring
* carries a pointer to one of these XDP rings for its own purposes, such as
* handling XDP_TX action, therefore we can piggyback here on the
* rx_ring->xdp_ring assignment that was done during XDP rings initialization.
*/
static inline struct xsk_buff_pool *ice_tx_xsk_pool(struct ice_tx_ring *ring)
static inline void ice_tx_xsk_pool(struct ice_vsi *vsi, u16 qid)
{
struct ice_vsi *vsi = ring->vsi;
u16 qid;
struct ice_tx_ring *ring;
qid = ring->q_index - vsi->alloc_txq;
ring = vsi->rx_rings[qid]->xdp_ring;
if (!ring)
return;
if (!ice_is_xdp_ena_vsi(vsi) || !test_bit(qid, vsi->af_xdp_zc_qps))
return NULL;
if (!ice_is_xdp_ena_vsi(vsi) || !test_bit(qid, vsi->af_xdp_zc_qps)) {
ring->xsk_pool = NULL;
return;
}
return xsk_get_pool_from_qid(vsi->netdev, qid);
ring->xsk_pool = xsk_get_pool_from_qid(vsi->netdev, qid);
}
/**

View File

@ -1986,8 +1986,8 @@ int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi)
if (ret)
return ret;
ice_for_each_xdp_txq(vsi, i)
vsi->xdp_rings[i]->xsk_pool = ice_tx_xsk_pool(vsi->xdp_rings[i]);
ice_for_each_rxq(vsi, i)
ice_tx_xsk_pool(vsi, i);
return ret;
}

View File

@ -2581,7 +2581,6 @@ static int ice_xdp_alloc_setup_rings(struct ice_vsi *vsi)
if (ice_setup_tx_ring(xdp_ring))
goto free_xdp_rings;
ice_set_ring_xdp(xdp_ring);
xdp_ring->xsk_pool = ice_tx_xsk_pool(xdp_ring);
spin_lock_init(&xdp_ring->tx_lock);
for (j = 0; j < xdp_ring->count; j++) {
tx_desc = ICE_TX_DESC(xdp_ring, j);
@ -2589,13 +2588,6 @@ static int ice_xdp_alloc_setup_rings(struct ice_vsi *vsi)
}
}
ice_for_each_rxq(vsi, i) {
if (static_key_enabled(&ice_xdp_locking_key))
vsi->rx_rings[i]->xdp_ring = vsi->xdp_rings[i % vsi->num_xdp_txq];
else
vsi->rx_rings[i]->xdp_ring = vsi->xdp_rings[i];
}
return 0;
free_xdp_rings:
@ -2685,6 +2677,23 @@ int ice_prepare_xdp_rings(struct ice_vsi *vsi, struct bpf_prog *prog)
xdp_rings_rem -= xdp_rings_per_v;
}
ice_for_each_rxq(vsi, i) {
if (static_key_enabled(&ice_xdp_locking_key)) {
vsi->rx_rings[i]->xdp_ring = vsi->xdp_rings[i % vsi->num_xdp_txq];
} else {
struct ice_q_vector *q_vector = vsi->rx_rings[i]->q_vector;
struct ice_tx_ring *ring;
ice_for_each_tx_ring(ring, q_vector->tx) {
if (ice_ring_is_xdp(ring)) {
vsi->rx_rings[i]->xdp_ring = ring;
break;
}
}
}
ice_tx_xsk_pool(vsi, i);
}
/* omit the scheduler update if in reset path; XDP queues will be
* taken into account at the end of ice_vsi_rebuild, where
* ice_cfg_vsi_lan is being called

View File

@ -243,7 +243,7 @@ static int ice_qp_ena(struct ice_vsi *vsi, u16 q_idx)
if (err)
goto free_buf;
ice_set_ring_xdp(xdp_ring);
xdp_ring->xsk_pool = ice_tx_xsk_pool(xdp_ring);
ice_tx_xsk_pool(vsi, q_idx);
}
err = ice_vsi_cfg_rxq(rx_ring);
@ -359,7 +359,7 @@ xsk_pool_if_up:
if (if_running) {
ret = ice_qp_ena(vsi, qid);
if (!ret && pool_present)
napi_schedule(&vsi->xdp_rings[qid]->q_vector->napi);
napi_schedule(&vsi->rx_rings[qid]->xdp_ring->q_vector->napi);
else if (ret)
netdev_err(vsi->netdev, "ice_qp_ena error = %d\n", ret);
}
@ -950,13 +950,13 @@ ice_xsk_wakeup(struct net_device *netdev, u32 queue_id,
if (!ice_is_xdp_ena_vsi(vsi))
return -EINVAL;
if (queue_id >= vsi->num_txq)
if (queue_id >= vsi->num_txq || queue_id >= vsi->num_rxq)
return -EINVAL;
if (!vsi->xdp_rings[queue_id]->xsk_pool)
return -EINVAL;
ring = vsi->rx_rings[queue_id]->xdp_ring;
ring = vsi->xdp_rings[queue_id];
if (!ring->xsk_pool)
return -EINVAL;
/* The idea here is that if NAPI is running, mark a miss, so
* it will run again. If not, trigger an interrupt and