Skip to content

Commit cb83b55

Browse files
jahay1anguy11
authored andcommitted
idpf: add support for Tx refillqs in flow scheduling mode
In certain production environments, it is possible for completion tags to collide, meaning N packets with the same completion tag are in flight at the same time. In this environment, any given Tx queue is effectively used to send both slower traffic and higher throughput traffic simultaneously. This is the result of a customer's specific configuration in the device pipeline, the details of which Intel cannot provide. This configuration results in a small number of out-of-order completions, i.e., a small number of packets in flight. The existing guardrails in the driver only protect against a large number of packets in flight. The slower flow completions are delayed which causes the out-of-order completions. The fast flow will continue sending traffic and generating tags. Because tags are generated on the fly, the fast flow eventually uses the same tag for a packet that is still in flight from the slower flow. The driver has no idea which packet it should clean when it processes the completion with that tag, but it will look for the packet on the buffer ring before the hash table. If the slower flow packet completion is processed first, it will end up cleaning the fast flow packet on the ring prematurely. This leaves the descriptor ring in a bad state resulting in a crash or Tx timeout. In summary, generating a tag when a packet is sent can lead to the same tag being associated with multiple packets. This can lead to resource leaks, crashes, and/or Tx timeouts. Before we can replace the tag generation, we need a new mechanism for the send path to know what tag to use next. The driver will allocate and initialize a refillq for each TxQ with all of the possible free tag values. During send, the driver grabs the next free tag from the refillq from next_to_clean. While cleaning the packet, the clean routine posts the tag back to the refillq's next_to_use to indicate that it is now free to use. This mechanism works exactly the same way as the existing Rx refill queues, which post the cleaned buffer IDs back to the buffer queue to be reposted to HW. Since we're using the refillqs for both Rx and Tx now, genericize some of the existing refillq support. Note: the refillqs will not be used yet. This is only demonstrating how they will be used to pass free tags back to the send path. Signed-off-by: Joshua Hay <[email protected]> Reviewed-by: Madhu Chittim <[email protected]> Tested-by: Samuel Salin <[email protected]> Signed-off-by: Tony Nguyen <[email protected]>
1 parent 1b78236 commit cb83b55

File tree

2 files changed

+91
-10
lines changed

2 files changed

+91
-10
lines changed

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

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ static void idpf_tx_desc_rel(struct idpf_tx_queue *txq)
139139
if (!txq->desc_ring)
140140
return;
141141

142+
if (txq->refillq)
143+
kfree(txq->refillq->ring);
144+
142145
dmam_free_coherent(txq->dev, txq->size, txq->desc_ring, txq->dma);
143146
txq->desc_ring = NULL;
144147
txq->next_to_use = 0;
@@ -244,6 +247,7 @@ static int idpf_tx_desc_alloc(const struct idpf_vport *vport,
244247
struct idpf_tx_queue *tx_q)
245248
{
246249
struct device *dev = tx_q->dev;
250+
struct idpf_sw_queue *refillq;
247251
int err;
248252

249253
err = idpf_tx_buf_alloc_all(tx_q);
@@ -267,6 +271,29 @@ static int idpf_tx_desc_alloc(const struct idpf_vport *vport,
267271
tx_q->next_to_clean = 0;
268272
idpf_queue_set(GEN_CHK, tx_q);
269273

274+
if (!idpf_queue_has(FLOW_SCH_EN, tx_q))
275+
return 0;
276+
277+
refillq = tx_q->refillq;
278+
refillq->desc_count = tx_q->desc_count;
279+
refillq->ring = kcalloc(refillq->desc_count, sizeof(u32),
280+
GFP_KERNEL);
281+
if (!refillq->ring) {
282+
err = -ENOMEM;
283+
goto err_alloc;
284+
}
285+
286+
for (unsigned int i = 0; i < refillq->desc_count; i++)
287+
refillq->ring[i] =
288+
FIELD_PREP(IDPF_RFL_BI_BUFID_M, i) |
289+
FIELD_PREP(IDPF_RFL_BI_GEN_M,
290+
idpf_queue_has(GEN_CHK, refillq));
291+
292+
/* Go ahead and flip the GEN bit since this counts as filling
293+
* up the ring, i.e. we already ring wrapped.
294+
*/
295+
idpf_queue_change(GEN_CHK, refillq);
296+
270297
return 0;
271298

272299
err_alloc:
@@ -603,18 +630,18 @@ static int idpf_rx_hdr_buf_alloc_all(struct idpf_buf_queue *bufq)
603630
}
604631

605632
/**
606-
* idpf_rx_post_buf_refill - Post buffer id to refill queue
633+
* idpf_post_buf_refill - Post buffer id to refill queue
607634
* @refillq: refill queue to post to
608635
* @buf_id: buffer id to post
609636
*/
610-
static void idpf_rx_post_buf_refill(struct idpf_sw_queue *refillq, u16 buf_id)
637+
static void idpf_post_buf_refill(struct idpf_sw_queue *refillq, u16 buf_id)
611638
{
612639
u32 nta = refillq->next_to_use;
613640

614641
/* store the buffer ID and the SW maintained GEN bit to the refillq */
615642
refillq->ring[nta] =
616-
FIELD_PREP(IDPF_RX_BI_BUFID_M, buf_id) |
617-
FIELD_PREP(IDPF_RX_BI_GEN_M,
643+
FIELD_PREP(IDPF_RFL_BI_BUFID_M, buf_id) |
644+
FIELD_PREP(IDPF_RFL_BI_GEN_M,
618645
idpf_queue_has(GEN_CHK, refillq));
619646

620647
if (unlikely(++nta == refillq->desc_count)) {
@@ -995,6 +1022,11 @@ static void idpf_txq_group_rel(struct idpf_vport *vport)
9951022
struct idpf_txq_group *txq_grp = &vport->txq_grps[i];
9961023

9971024
for (j = 0; j < txq_grp->num_txq; j++) {
1025+
if (flow_sch_en) {
1026+
kfree(txq_grp->txqs[j]->refillq);
1027+
txq_grp->txqs[j]->refillq = NULL;
1028+
}
1029+
9981030
kfree(txq_grp->txqs[j]);
9991031
txq_grp->txqs[j] = NULL;
10001032
}
@@ -1414,6 +1446,13 @@ static int idpf_txq_group_alloc(struct idpf_vport *vport, u16 num_txq)
14141446
}
14151447

14161448
idpf_queue_set(FLOW_SCH_EN, q);
1449+
1450+
q->refillq = kzalloc(sizeof(*q->refillq), GFP_KERNEL);
1451+
if (!q->refillq)
1452+
goto err_alloc;
1453+
1454+
idpf_queue_set(GEN_CHK, q->refillq);
1455+
idpf_queue_set(RFL_GEN_CHK, q->refillq);
14171456
}
14181457

14191458
if (!split)
@@ -2005,6 +2044,8 @@ static void idpf_tx_handle_rs_completion(struct idpf_tx_queue *txq,
20052044

20062045
compl_tag = le16_to_cpu(desc->q_head_compl_tag.compl_tag);
20072046

2047+
idpf_post_buf_refill(txq->refillq, compl_tag);
2048+
20082049
/* If we didn't clean anything on the ring, this packet must be
20092050
* in the hash table. Go clean it there.
20102051
*/
@@ -2364,6 +2405,37 @@ static unsigned int idpf_tx_splitq_bump_ntu(struct idpf_tx_queue *txq, u16 ntu)
23642405
return ntu;
23652406
}
23662407

2408+
/**
2409+
* idpf_tx_get_free_buf_id - get a free buffer ID from the refill queue
2410+
* @refillq: refill queue to get buffer ID from
2411+
* @buf_id: return buffer ID
2412+
*
2413+
* Return: true if a buffer ID was found, false if not
2414+
*/
2415+
static bool idpf_tx_get_free_buf_id(struct idpf_sw_queue *refillq,
2416+
u16 *buf_id)
2417+
{
2418+
u32 ntc = refillq->next_to_clean;
2419+
u32 refill_desc;
2420+
2421+
refill_desc = refillq->ring[ntc];
2422+
2423+
if (unlikely(idpf_queue_has(RFL_GEN_CHK, refillq) !=
2424+
!!(refill_desc & IDPF_RFL_BI_GEN_M)))
2425+
return false;
2426+
2427+
*buf_id = FIELD_GET(IDPF_RFL_BI_BUFID_M, refill_desc);
2428+
2429+
if (unlikely(++ntc == refillq->desc_count)) {
2430+
idpf_queue_change(RFL_GEN_CHK, refillq);
2431+
ntc = 0;
2432+
}
2433+
2434+
refillq->next_to_clean = ntc;
2435+
2436+
return true;
2437+
}
2438+
23672439
/**
23682440
* idpf_tx_splitq_map - Build the Tx flex descriptor
23692441
* @tx_q: queue to send buffer on
@@ -2912,6 +2984,13 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
29122984
}
29132985

29142986
if (idpf_queue_has(FLOW_SCH_EN, tx_q)) {
2987+
if (unlikely(!idpf_tx_get_free_buf_id(tx_q->refillq,
2988+
&tx_params.compl_tag))) {
2989+
u64_stats_update_begin(&tx_q->stats_sync);
2990+
u64_stats_inc(&tx_q->q_stats.q_busy);
2991+
u64_stats_update_end(&tx_q->stats_sync);
2992+
}
2993+
29152994
tx_params.dtype = IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
29162995
tx_params.eop_cmd = IDPF_TXD_FLEX_FLOW_CMD_EOP;
29172996
/* Set the RE bit to catch any packets that may have not been
@@ -3472,7 +3551,7 @@ static int idpf_rx_splitq_clean(struct idpf_rx_queue *rxq, int budget)
34723551
skip_data:
34733552
rx_buf->netmem = 0;
34743553

3475-
idpf_rx_post_buf_refill(refillq, buf_id);
3554+
idpf_post_buf_refill(refillq, buf_id);
34763555
IDPF_RX_BUMP_NTC(rxq, ntc);
34773556

34783557
/* skip if it is non EOP desc */
@@ -3580,10 +3659,10 @@ static void idpf_rx_clean_refillq(struct idpf_buf_queue *bufq,
35803659
bool failure;
35813660

35823661
if (idpf_queue_has(RFL_GEN_CHK, refillq) !=
3583-
!!(refill_desc & IDPF_RX_BI_GEN_M))
3662+
!!(refill_desc & IDPF_RFL_BI_GEN_M))
35843663
break;
35853664

3586-
buf_id = FIELD_GET(IDPF_RX_BI_BUFID_M, refill_desc);
3665+
buf_id = FIELD_GET(IDPF_RFL_BI_BUFID_M, refill_desc);
35873666
failure = idpf_rx_update_bufq_desc(bufq, buf_id, buf_desc);
35883667
if (failure)
35893668
break;

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ do { \
108108
*/
109109
#define IDPF_TX_SPLITQ_RE_MIN_GAP 64
110110

111-
#define IDPF_RX_BI_GEN_M BIT(16)
112-
#define IDPF_RX_BI_BUFID_M GENMASK(15, 0)
111+
#define IDPF_RFL_BI_GEN_M BIT(16)
112+
#define IDPF_RFL_BI_BUFID_M GENMASK(15, 0)
113113

114114
#define IDPF_RXD_EOF_SPLITQ VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_M
115115
#define IDPF_RXD_EOF_SINGLEQ VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_M
@@ -622,6 +622,7 @@ libeth_cacheline_set_assert(struct idpf_rx_queue, 64,
622622
* @cleaned_pkts: Number of packets cleaned for the above said case
623623
* @tx_max_bufs: Max buffers that can be transmitted with scatter-gather
624624
* @stash: Tx buffer stash for Flow-based scheduling mode
625+
* @refillq: Pointer to refill queue
625626
* @compl_tag_bufid_m: Completion tag buffer id mask
626627
* @compl_tag_cur_gen: Used to keep track of current completion tag generation
627628
* @compl_tag_gen_max: To determine when compl_tag_cur_gen should be reset
@@ -671,6 +672,7 @@ struct idpf_tx_queue {
671672

672673
u16 tx_max_bufs;
673674
struct idpf_txq_stash *stash;
675+
struct idpf_sw_queue *refillq;
674676

675677
u16 compl_tag_bufid_m;
676678
u16 compl_tag_cur_gen;
@@ -692,7 +694,7 @@ struct idpf_tx_queue {
692694
__cacheline_group_end_aligned(cold);
693695
};
694696
libeth_cacheline_set_assert(struct idpf_tx_queue, 64,
695-
112 + sizeof(struct u64_stats_sync),
697+
120 + sizeof(struct u64_stats_sync),
696698
24);
697699

698700
/**

0 commit comments

Comments
 (0)