Merge branch 'net-ethernet-cortina-fix-rx-budget-accounting'

Linus Walleij says:

====================
net: ethernet: cortina: Fix RX budget accounting

Finish RX updates before releasing NAPI ownership, report actual NAPI
work, charge dropped frames to the poll budget, and drive free-queue
refills from consumed RX descriptors.

Track RX drop state across descriptor chains so discarded frames are
counted exactly once.

Tested on the D-Link DIR-685.

Hi Sashiko, yes there are more latent issues I will get to them, but
my LLM thinks those are on the top of the list.

Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
====================

Link: https://patch.msgid.link/20260903-gemini-ethernet-fixes-v2-0-2bbbd598ca6e@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-09-08 12:35:23 +02:00
commit e0554c6276

View File

@ -124,6 +124,7 @@ struct gemini_ethernet_port {
unsigned int rx_coalesce_nsecs;
struct sk_buff *rx_skb;
unsigned int rx_frag_nr;
bool rx_dropping;
unsigned int freeq_refill;
struct gmac_txq txq[TX_QUEUE_NUM];
@ -1439,7 +1440,8 @@ static struct sk_buff *gmac_skb_if_good_frame(struct gemini_ethernet_port *port,
return skb;
}
static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget,
unsigned int *freeq_consumed)
{
struct gemini_ethernet_port *port = netdev_priv(netdev);
unsigned short m = (1 << port->rxq_order) - 1;
@ -1447,9 +1449,12 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
void __iomem *ptr_reg = port->rxq_rwptr;
unsigned int frag_nr = port->rx_frag_nr;
struct sk_buff *skb = port->rx_skb;
unsigned int consumed = 0;
unsigned int frame_len, frag_len;
struct gmac_rxdesc *rx = NULL;
struct gmac_queue_page *gpage;
unsigned int received = 0;
bool dropping = port->rx_dropping;
union gmac_rxdesc_0 word0;
union gmac_rxdesc_1 word1;
union gmac_rxdesc_3 word3;
@ -1471,6 +1476,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
w = rw.bits.wptr;
while (budget && w != r) {
page = NULL;
rx = port->rxq_ring + r;
word0 = rx->word0;
word1 = rx->word1;
@ -1479,11 +1485,22 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
r++;
r &= m;
consumed++;
frag_len = word0.bits.buffer_size;
frame_len = word1.bits.byte_count;
page_offs = mapping & ~PAGE_MASK;
if (word3.bits32 & SOF_BIT) {
if (skb) {
napi_free_frags(&port->napi);
port->stats.rx_dropped++;
skb = NULL;
frag_nr = 0;
}
dropping = false;
}
if (!mapping) {
netdev_err(netdev,
"rxq[%u]: HW BUG: zero DMA desc\n", r);
@ -1494,24 +1511,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE);
if (!gpage) {
dev_err(geth->dev, "could not find mapping\n");
port->stats.rx_dropped++;
if (skb) {
napi_free_frags(&port->napi);
skb = NULL;
frag_nr = 0;
}
continue;
goto err_drop;
}
page = gpage->page;
if (word3.bits32 & SOF_BIT) {
if (skb) {
napi_free_frags(&port->napi);
port->stats.rx_dropped++;
skb = NULL;
frag_nr = 0;
}
skb = gmac_skb_if_good_frame(port, word0, frame_len);
if (!skb)
goto err_drop;
@ -1521,8 +1525,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
frag_nr = 0;
} else if (!skb) {
put_page(page);
continue;
goto err_drop;
}
if (word3.bits32 & EOF_BIT)
@ -1545,9 +1548,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
napi_gro_frags(&port->napi);
skb = NULL;
frag_nr = 0;
--budget;
}
continue;
goto next_desc;
err_drop:
if (skb) {
@ -1556,16 +1558,29 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
frag_nr = 0;
}
if (mapping)
if (page)
put_page(page);
port->stats.rx_dropped++;
if (!dropping) {
port->stats.rx_dropped++;
dropping = true;
}
next_desc:
/* Final or single-descriptor fragment, advance things */
if (word3.bits32 & EOF_BIT) {
budget--;
received++;
dropping = false;
}
}
port->rx_skb = skb;
port->rx_frag_nr = frag_nr;
port->rx_dropping = dropping;
*freeq_consumed = consumed;
writew(r, ptr_reg);
return budget;
return received;
}
static int gmac_napi_poll(struct napi_struct *napi, int budget)
@ -1573,26 +1588,27 @@ static int gmac_napi_poll(struct napi_struct *napi, int budget)
struct gemini_ethernet_port *port = netdev_priv(napi->dev);
struct gemini_ethernet *geth = port->geth;
unsigned int freeq_threshold;
unsigned int freeq_consumed;
unsigned int received;
freeq_threshold = 1 << (geth->freeq_order - 1);
u64_stats_update_begin(&port->rx_stats_syncp);
received = gmac_rx(napi->dev, budget);
if (received < budget) {
napi_gro_flush(napi, false);
napi_complete_done(napi, received);
gmac_enable_rx_irq(napi->dev, 1);
received = gmac_rx(napi->dev, budget, &freeq_consumed);
if (received < budget)
++port->rx_napi_exits;
}
port->freeq_refill += (budget - received);
u64_stats_update_end(&port->rx_stats_syncp);
port->freeq_refill += freeq_consumed;
if (port->freeq_refill > freeq_threshold) {
port->freeq_refill -= freeq_threshold;
geth_fill_freeq(geth, true);
}
u64_stats_update_end(&port->rx_stats_syncp);
if (received < budget && napi_complete_done(napi, received))
gmac_enable_rx_irq(napi->dev, 1);
return received;
}
@ -1893,6 +1909,7 @@ static int gmac_stop(struct net_device *netdev)
napi_disable(&port->napi);
port->rx_skb = NULL;
port->rx_frag_nr = 0;
port->rx_dropping = false;
gmac_enable_irq(netdev, 0);
gmac_cleanup_rxq(netdev);