diff options
author | Jakub Kicinski <kuba@kernel.org> | 2024-11-03 11:36:18 -0800 |
---|---|---|
committer | Jakub Kicinski <kuba@kernel.org> | 2024-11-03 11:36:18 -0800 |
commit | 34d15f6ffb72212ca28e7c1283ae9be1cdfd40f6 (patch) | |
tree | 04ec21fb1d87c80a350d4e7280fca432e17dd2bc | |
parent | 9b4b2e02c1e19e6a983cf1b3f082315239d38cb0 (diff) | |
parent | 0c729f53b8c33b9e5eadc2d5e673759e3510501e (diff) |
Merge branch 'simplify-tx-napi-logic-in-airoha_eth-driver'
Lorenzo Bianconi says:
====================
Simplify Tx napi logic in airoha_eth driver
Simplify Tx napi logic relying on the packet index provided by
completion queue indicating the completed packet that can be removed
from the Tx DMA ring.
Read completion queue head and pending entry in airoha_qdma_tx_napi_poll().
====================
Link: https://patch.msgid.link/20241029-airoha-en7581-tx-napi-work-v1-0-96ad1686b946@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r-- | drivers/net/ethernet/mediatek/airoha_eth.c | 102 |
1 files changed, 53 insertions, 49 deletions
diff --git a/drivers/net/ethernet/mediatek/airoha_eth.c b/drivers/net/ethernet/mediatek/airoha_eth.c index f463a505f5ba..6c683a12d5aa 100644 --- a/drivers/net/ethernet/mediatek/airoha_eth.c +++ b/drivers/net/ethernet/mediatek/airoha_eth.c @@ -752,11 +752,9 @@ struct airoha_tx_irq_queue { struct airoha_qdma *qdma; struct napi_struct napi; - u32 *q; int size; - int queued; - u16 head; + u32 *q; }; struct airoha_hw_stats { @@ -1656,30 +1654,38 @@ static int airoha_qdma_init_rx(struct airoha_qdma *qdma) static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) { struct airoha_tx_irq_queue *irq_q; + int id, done = 0, irq_queued; struct airoha_qdma *qdma; struct airoha_eth *eth; - int id, done = 0; + u32 status, head; irq_q = container_of(napi, struct airoha_tx_irq_queue, napi); qdma = irq_q->qdma; id = irq_q - &qdma->q_tx_irq[0]; eth = qdma->eth; - while (irq_q->queued > 0 && done < budget) { - u32 qid, last, val = irq_q->q[irq_q->head]; + status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(id)); + head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); + head = head % irq_q->size; + irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); + + while (irq_queued > 0 && done < budget) { + u32 qid, val = irq_q->q[head]; + struct airoha_qdma_desc *desc; + struct airoha_queue_entry *e; struct airoha_queue *q; + u32 index, desc_ctrl; + struct sk_buff *skb; if (val == 0xff) break; - irq_q->q[irq_q->head] = 0xff; /* mark as done */ - irq_q->head = (irq_q->head + 1) % irq_q->size; - irq_q->queued--; + irq_q->q[head] = 0xff; /* mark as done */ + head = (head + 1) % irq_q->size; + irq_queued--; done++; - last = FIELD_GET(IRQ_DESC_IDX_MASK, val); qid = FIELD_GET(IRQ_RING_IDX_MASK, val); - if (qid >= ARRAY_SIZE(qdma->q_tx)) continue; @@ -1687,46 +1693,53 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) if (!q->ndesc) continue; + index = FIELD_GET(IRQ_DESC_IDX_MASK, val); + if (index >= q->ndesc) + continue; + spin_lock_bh(&q->lock); - while (q->queued > 0) { - struct airoha_qdma_desc *desc = &q->desc[q->tail]; - struct airoha_queue_entry *e = &q->entry[q->tail]; - u32 desc_ctrl = le32_to_cpu(desc->ctrl); - struct sk_buff *skb = e->skb; - u16 index = q->tail; + if (!q->queued) + goto unlock; - if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && - !(desc_ctrl & QDMA_DESC_DROP_MASK)) - break; + desc = &q->desc[index]; + desc_ctrl = le32_to_cpu(desc->ctrl); - q->tail = (q->tail + 1) % q->ndesc; - q->queued--; + if (!(desc_ctrl & QDMA_DESC_DONE_MASK) && + !(desc_ctrl & QDMA_DESC_DROP_MASK)) + goto unlock; - dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, - DMA_TO_DEVICE); + e = &q->entry[index]; + skb = e->skb; - WRITE_ONCE(desc->msg0, 0); - WRITE_ONCE(desc->msg1, 0); + dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); + memset(e, 0, sizeof(*e)); + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + q->queued--; - if (skb) { - u16 queue = skb_get_queue_mapping(skb); - struct netdev_queue *txq; + /* completion ring can report out-of-order indexes if hw QoS + * is enabled and packets with different priority are queued + * to same DMA ring. Take into account possible out-of-order + * reports incrementing DMA ring tail pointer + */ + while (q->tail != q->head && !q->entry[q->tail].dma_addr) + q->tail = (q->tail + 1) % q->ndesc; - txq = netdev_get_tx_queue(skb->dev, queue); - netdev_tx_completed_queue(txq, 1, skb->len); - if (netif_tx_queue_stopped(txq) && - q->ndesc - q->queued >= q->free_thr) - netif_tx_wake_queue(txq); + if (skb) { + u16 queue = skb_get_queue_mapping(skb); + struct netdev_queue *txq; - dev_kfree_skb_any(skb); - e->skb = NULL; - } + txq = netdev_get_tx_queue(skb->dev, queue); + netdev_tx_completed_queue(txq, 1, skb->len); + if (netif_tx_queue_stopped(txq) && + q->ndesc - q->queued >= q->free_thr) + netif_tx_wake_queue(txq); - if (index == last) - break; + dev_kfree_skb_any(skb); } - +unlock: spin_unlock_bh(&q->lock); } @@ -2026,20 +2039,11 @@ static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) if (intr[0] & INT_TX_MASK) { for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { - struct airoha_tx_irq_queue *irq_q = &qdma->q_tx_irq[i]; - u32 status, head; - if (!(intr[0] & TX_DONE_INT_MASK(i))) continue; airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0, TX_DONE_INT_MASK(i)); - - status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(i)); - head = FIELD_GET(IRQ_HEAD_IDX_MASK, status); - irq_q->head = head % irq_q->size; - irq_q->queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status); - napi_schedule(&qdma->q_tx_irq[i].napi); } } |