summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Walleij <linusw@kernel.org>2026-09-03 23:45:32 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-09-08 12:35:22 +0200
commit6520198c430c81bcc367f0dd5e32f2fb740b9d51 (patch)
tree24045deebc0303ac61638703596bac811e2e2093
parentb856c552f556bc0341c1dbe0bf88e630fd1dc4b7 (diff)
downloadlinux-next-6520198c430c81bcc367f0dd5e32f2fb740b9d51.tar.gz
linux-next-6520198c430c81bcc367f0dd5e32f2fb740b9d51.zip
net: ethernet: cortina: Count RX drops once per frame
The absence of a partial skb means either that the driver is not assembling a frame or that the current frame was already dropped. Consequently, repeated descriptor errors can increment rx_dropped more than once, while an orphaned descriptor chain can reach EOF without being counted at all. Track the dropping state across NAPI polls. Clear it at frame boundaries and route mapping failures and orphaned continuations through the common drop path so each discarded frame is counted exactly once. Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") Reported-by: Joe Damato <joe@dama.to> Closes: https://lore.kernel.org/netdev/apdK5aMmvYssz35F@devvm20253.cco0.facebook.com/ Assisted-by: LLM Signed-off-by: Linus Walleij <linusw@kernel.org> Link: https://patch.msgid.link/20260903-gemini-ethernet-fixes-v2-4-2bbbd598ca6e@kernel.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--drivers/net/ethernet/cortina/gemini.c41
1 files changed, 23 insertions, 18 deletions
diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c
index 33e9763b32fe..9ba8524fa371 100644
--- a/drivers/net/ethernet/cortina/gemini.c
+++ b/drivers/net/ethernet/cortina/gemini.c
@@ -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];
@@ -1451,6 +1452,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
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;
@@ -1472,6 +1474,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;
@@ -1485,6 +1488,16 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
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);
@@ -1495,24 +1508,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;
- }
- goto next_desc;
+ 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;
@@ -1522,8 +1522,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
frag_nr = 0;
} else if (!skb) {
- put_page(page);
- goto next_desc;
+ goto err_drop;
}
if (word3.bits32 & EOF_BIT)
@@ -1556,21 +1555,26 @@ err_drop:
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;
writew(r, ptr_reg);
return received;
}
@@ -1900,6 +1904,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);