Skip to content

Commit 4870a18

Browse files
committed
idpf: fix a race in txq wakeup
JIRA: https://issues.redhat.com/browse/RHEL-79685 commit 7292af0 Author: Brian Vazquez <brianvv@google.com> Date: Thu May 1 17:06:17 2025 +0000 idpf: fix a race in txq wakeup Add a helper function to correctly handle the lockless synchronization when the sender needs to block. The paradigm is if (no_resources()) { stop_queue(); barrier(); if (!no_resources()) restart_queue(); } netif_subqueue_maybe_stop already handles the paradigm correctly, but the code split the check for resources in three parts, the first one (descriptors) followed the protocol, but the other two (completions and tx_buf) were only doing the first part and so race prone. Luckily netif_subqueue_maybe_stop macro already allows you to use a function to evaluate the start/stop conditions so the fix only requires the right helper function to evaluate all the conditions at once. The patch removes idpf_tx_maybe_stop_common since it's no longer needed and instead adjusts separately the conditions for singleq and splitq. Note that idpf_tx_buf_hw_update doesn't need to check for resources since that will be covered in idpf_tx_splitq_frame. To reproduce: Reduce the threshold for pending completions to increase the chances of hitting this pause by changing your kernel: drivers/net/ethernet/intel/idpf/idpf_txrx.h -#define IDPF_TX_COMPLQ_OVERFLOW_THRESH(txcq) ((txcq)->desc_count >> 1) +#define IDPF_TX_COMPLQ_OVERFLOW_THRESH(txcq) ((txcq)->desc_count >> 4) Use pktgen to force the host to push small pkts very aggressively: ./pktgen_sample02_multiqueue.sh -i eth1 -s 100 -6 -d $IP -m $MAC \ -p 10000-10000 -t 16 -n 0 -v -x -c 64 Fixes: 6818c4d ("idpf: add splitq start_xmit") Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Reviewed-by: Madhu Chittim <madhu.chittim@intel.com> Signed-off-by: Josh Hay <joshua.a.hay@intel.com> Signed-off-by: Brian Vazquez <brianvv@google.com> Signed-off-by: Luigi Rizzo <lrizzo@google.com> Reviewed-by: Simon Horman <horms@kernel.org> Tested-by: Samuel Salin <Samuel.salin@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com> Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
1 parent cc52a5e commit 4870a18

File tree

3 files changed

+22
-40
lines changed

3 files changed

+22
-40
lines changed

drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,17 +362,18 @@ netdev_tx_t idpf_tx_singleq_frame(struct sk_buff *skb,
362362
{
363363
struct idpf_tx_offload_params offload = { };
364364
struct idpf_tx_buf *first;
365+
int csum, tso, needed;
365366
unsigned int count;
366367
__be16 protocol;
367-
int csum, tso;
368368

369369
count = idpf_tx_desc_count_required(tx_q, skb);
370370
if (unlikely(!count))
371371
return idpf_tx_drop_skb(tx_q, skb);
372372

373-
if (idpf_tx_maybe_stop_common(tx_q,
374-
count + IDPF_TX_DESCS_PER_CACHE_LINE +
375-
IDPF_TX_DESCS_FOR_CTX)) {
373+
needed = count + IDPF_TX_DESCS_PER_CACHE_LINE + IDPF_TX_DESCS_FOR_CTX;
374+
if (!netif_subqueue_maybe_stop(tx_q->netdev, tx_q->idx,
375+
IDPF_DESC_UNUSED(tx_q),
376+
needed, needed)) {
376377
idpf_tx_buf_hw_update(tx_q, tx_q->next_to_use, false);
377378

378379
u64_stats_update_begin(&tx_q->stats_sync);

drivers/net/ethernet/intel/idpf/idpf_txrx.c

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,19 @@ void idpf_tx_splitq_build_flow_desc(union idpf_tx_flex_desc *desc,
21322132
desc->flow.qw1.compl_tag = cpu_to_le16(params->compl_tag);
21332133
}
21342134

2135+
/* Global conditions to tell whether the txq (and related resources)
2136+
* has room to allow the use of "size" descriptors.
2137+
*/
2138+
static int idpf_txq_has_room(struct idpf_tx_queue *tx_q, u32 size)
2139+
{
2140+
if (IDPF_DESC_UNUSED(tx_q) < size ||
2141+
IDPF_TX_COMPLQ_PENDING(tx_q->txq_grp) >
2142+
IDPF_TX_COMPLQ_OVERFLOW_THRESH(tx_q->txq_grp->complq) ||
2143+
IDPF_TX_BUF_RSV_LOW(tx_q))
2144+
return 0;
2145+
return 1;
2146+
}
2147+
21352148
/**
21362149
* idpf_tx_maybe_stop_splitq - 1st level check for Tx splitq stop conditions
21372150
* @tx_q: the queue to be checked
@@ -2142,29 +2155,11 @@ void idpf_tx_splitq_build_flow_desc(union idpf_tx_flex_desc *desc,
21422155
static int idpf_tx_maybe_stop_splitq(struct idpf_tx_queue *tx_q,
21432156
unsigned int descs_needed)
21442157
{
2145-
if (idpf_tx_maybe_stop_common(tx_q, descs_needed))
2146-
goto out;
2147-
2148-
/* If there are too many outstanding completions expected on the
2149-
* completion queue, stop the TX queue to give the device some time to
2150-
* catch up
2151-
*/
2152-
if (unlikely(IDPF_TX_COMPLQ_PENDING(tx_q->txq_grp) >
2153-
IDPF_TX_COMPLQ_OVERFLOW_THRESH(tx_q->txq_grp->complq)))
2154-
goto splitq_stop;
2155-
2156-
/* Also check for available book keeping buffers; if we are low, stop
2157-
* the queue to wait for more completions
2158-
*/
2159-
if (unlikely(IDPF_TX_BUF_RSV_LOW(tx_q)))
2160-
goto splitq_stop;
2161-
2162-
return 0;
2163-
2164-
splitq_stop:
2165-
netif_stop_subqueue(tx_q->netdev, tx_q->idx);
2158+
if (netif_subqueue_maybe_stop(tx_q->netdev, tx_q->idx,
2159+
idpf_txq_has_room(tx_q, descs_needed),
2160+
1, 1))
2161+
return 0;
21662162

2167-
out:
21682163
u64_stats_update_begin(&tx_q->stats_sync);
21692164
u64_stats_inc(&tx_q->q_stats.q_busy);
21702165
u64_stats_update_end(&tx_q->stats_sync);
@@ -2190,12 +2185,6 @@ void idpf_tx_buf_hw_update(struct idpf_tx_queue *tx_q, u32 val,
21902185
nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx);
21912186
tx_q->next_to_use = val;
21922187

2193-
if (idpf_tx_maybe_stop_common(tx_q, IDPF_TX_DESC_NEEDED)) {
2194-
u64_stats_update_begin(&tx_q->stats_sync);
2195-
u64_stats_inc(&tx_q->q_stats.q_busy);
2196-
u64_stats_update_end(&tx_q->stats_sync);
2197-
}
2198-
21992188
/* Force memory writes to complete before letting h/w
22002189
* know there are new descriptors to fetch. (Only
22012190
* applicable for weak-ordered memory model archs,

drivers/net/ethernet/intel/idpf/idpf_txrx.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,12 +1039,4 @@ bool idpf_rx_singleq_buf_hw_alloc_all(struct idpf_rx_queue *rxq,
10391039
u16 cleaned_count);
10401040
int idpf_tso(struct sk_buff *skb, struct idpf_tx_offload_params *off);
10411041

1042-
static inline bool idpf_tx_maybe_stop_common(struct idpf_tx_queue *tx_q,
1043-
u32 needed)
1044-
{
1045-
return !netif_subqueue_maybe_stop(tx_q->netdev, tx_q->idx,
1046-
IDPF_DESC_UNUSED(tx_q),
1047-
needed, needed);
1048-
}
1049-
10501042
#endif /* !_IDPF_TXRX_H_ */

0 commit comments

Comments
 (0)