* [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions
@ 2026-06-30 4:16 Rosen Penev
2026-06-30 4:16 ` [PATCH 01/10] net: emac: remove emac_xaht_base() Rosen Penev
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
This series targets the IBM EMAC (Ethernet Media Access Controller)
driver used on PowerPC 4xx SoCs. It removes unused infrastructure,
fixes sparse warnings, replaces legacy helpers, streamlines
synchronization, fixes DMA API usage, and adds BQL support along
with ndo_get_stats64 conversion.
Rosen Penev (10):
net: emac: remove emac_xaht_base()
net: emac: fix sparse __iomem warnings in IAHT register access
net: emac: use DMA-specific and SMP memory barriers
net: emac: mal: replace of_get_property with of_property_read_u32
net: emac: mal: replace busy-wait in mal_poll_disable with wait_event
net: emac: batch stats, eliminate modulo, tighten barrier in RX poll
net: emac: fix DMA API mapping and unmapping correctness
net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED()
net: emac: add Byte Queue Limits (BQL) support
net: emac: use ndo_get_stats64 instead of ndo_get_stats
drivers/net/ethernet/ibm/emac/core.c | 253 ++++++++++++++++----------
drivers/net/ethernet/ibm/emac/core.h | 17 +-
drivers/net/ethernet/ibm/emac/mal.c | 37 ++--
drivers/net/ethernet/ibm/emac/mal.h | 3 +
drivers/net/ethernet/ibm/emac/rgmii.c | 2 +-
drivers/net/ethernet/ibm/emac/tah.c | 2 +-
drivers/net/ethernet/ibm/emac/zmii.c | 4 +-
7 files changed, 188 insertions(+), 130 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 01/10] net: emac: remove emac_xaht_base()
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 02/10] net: emac: fix sparse __iomem warnings in IAHT register access Rosen Penev
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Unused function. It's also missing __iomem.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.h | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 89fa1683ec3c..46c5512c8e00 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -420,14 +420,6 @@ static inline u32 __iomem *emac_gaht_base(struct emac_instance *dev)
return emac_xaht_base(dev) + EMAC_XAHT_REGS(dev);
}
-static inline u32 *emac_iaht_base(struct emac_instance *dev)
-{
- /* IAHT registers always come before an identical number of
- * GAHT registers.
- */
- return emac_xaht_base(dev);
-}
-
/* Ethtool get_regs complex data.
* We want to get not just EMAC registers, but also MAL, ZMII, RGMII, TAH
* when available.
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 02/10] net: emac: fix sparse __iomem warnings in IAHT register access
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
2026-06-30 4:16 ` [PATCH 01/10] net: emac: remove emac_xaht_base() Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 03/10] net: emac: use DMA-specific and SMP memory barriers Rosen Penev
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Annotate iaht1/iaht2 in the EMAC4 register union with __iomem so
sparse does not warn about address-space mismatches, and simplify
emac_xaht_base() to return &p->u1.emac4sync.iaht1 (or the EMAC4
variant) directly instead of computing the offset by hand.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.h | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 46c5512c8e00..296da4bf3781 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -399,17 +399,13 @@ static inline int emac_has_feature(struct emac_instance *dev,
static inline u32 __iomem *emac_xaht_base(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
- int offset;
/* The first IAHT entry always is the base of the block of
* IAHT and GAHT registers.
*/
if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC))
- offset = offsetof(struct emac_regs, u1.emac4sync.iaht1);
- else
- offset = offsetof(struct emac_regs, u0.emac4.iaht1);
-
- return (u32 __iomem *)((__force ptrdiff_t)p + offset);
+ return &p->u1.emac4sync.iaht1;
+ return &p->u0.emac4.iaht1;
}
static inline u32 __iomem *emac_gaht_base(struct emac_instance *dev)
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 03/10] net: emac: use DMA-specific and SMP memory barriers
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
2026-06-30 4:16 ` [PATCH 01/10] net: emac: remove emac_xaht_base() Rosen Penev
2026-06-30 4:16 ` [PATCH 02/10] net: emac: fix sparse __iomem warnings in IAHT register access Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 04/10] net: emac: mal: replace of_get_property with of_property_read_u32 Rosen Penev
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Replace generic wmb()/mb() barriers with more specific variants:
- dma_wmb() for ordering descriptor field writes before hardware
ownership bit (ctrl) hand-off in TX/RX paths
- dma_rmb() for ordering descriptor ctrl read after ownership
observation in the RX poll path
- smp_wmb() for CPU-to-CPU ordering (link_polling flag,
platform_set_drvdata visibility)
- dma_rmb() for the RX descriptor ownership transfer read
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 16 ++++++++--------
drivers/net/ethernet/ibm/emac/mal.c | 2 +-
drivers/net/ethernet/ibm/emac/rgmii.c | 2 +-
drivers/net/ethernet/ibm/emac/tah.c | 2 +-
drivers/net/ethernet/ibm/emac/zmii.c | 4 ++--
5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 1d46cf6c2c12..5e7b85d28bde 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1185,7 +1185,7 @@ __emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot)
dev->rx_desc[slot].data_ptr =
dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
dev->rx_sync_size, DMA_FROM_DEVICE) + NET_IP_ALIGN;
- wmb();
+ dma_wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
@@ -1263,7 +1263,7 @@ static int emac_open(struct net_device *ndev)
link_poll_interval = PHY_POLL_LINK_OFF;
}
dev->link_polling = 1;
- wmb();
+ smp_wmb();
schedule_delayed_work(&dev->link_work, link_poll_interval);
emac_print_link_status(dev);
} else
@@ -1464,7 +1464,7 @@ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb->data, len,
DMA_TO_DEVICE);
dev->tx_desc[slot].data_len = (u16) len;
- wmb();
+ dma_wmb();
dev->tx_desc[slot].ctrl = ctrl;
return emac_xmit_finish(dev, len);
@@ -1560,7 +1560,7 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
/* Send the packet out */
if (dev->tx_slot == NUM_TX_BUFF - 1)
ctrl |= MAL_TX_CTRL_WRAP;
- wmb();
+ dma_wmb();
dev->tx_desc[dev->tx_slot].ctrl = ctrl;
dev->tx_slot = (slot + 1) % NUM_TX_BUFF;
@@ -1671,7 +1671,7 @@ static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
DMA_FROM_DEVICE);
dev->rx_desc[slot].data_len = 0;
- wmb();
+ dma_wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
}
@@ -1754,7 +1754,7 @@ static int emac_poll_rx(void *param, int budget)
break;
skb = dev->rx_skb[slot];
- mb();
+ dma_rmb();
len = dev->rx_desc[slot].data_len;
if (unlikely(!MAL_IS_SINGLE_RX(ctrl)))
@@ -1845,7 +1845,7 @@ static int emac_poll_rx(void *param, int budget)
}
if (unlikely(budget && test_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags))) {
- mb();
+ dma_rmb();
if (!(dev->rx_desc[slot].ctrl & MAL_RX_CTRL_EMPTY)) {
DBG2(dev, "rx restart" NL);
received = 0;
@@ -3167,7 +3167,7 @@ static int emac_probe(struct platform_device *ofdev)
/* Set our drvdata last as we don't want them visible until we are
* fully initialized
*/
- wmb();
+ smp_wmb();
platform_set_drvdata(ofdev, dev);
printk(KERN_INFO "%s: EMAC-%d %pOF, MAC %pM\n",
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 4025bc36ae16..99615c8a6c3e 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -691,7 +691,7 @@ static int mal_probe(struct platform_device *ofdev)
mal->num_tx_chans, mal->num_rx_chans);
/* Advertise this instance to the rest of the world */
- wmb();
+ smp_wmb();
platform_set_drvdata(ofdev, mal);
return 0;
diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c
index b544dd8633b7..093aa4f129e3 100644
--- a/drivers/net/ethernet/ibm/emac/rgmii.c
+++ b/drivers/net/ethernet/ibm/emac/rgmii.c
@@ -255,7 +255,7 @@ static int rgmii_probe(struct platform_device *ofdev)
ofdev->dev.of_node,
(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out");
- wmb();
+ smp_wmb();
platform_set_drvdata(ofdev, dev);
return 0;
diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c
index ed07532aaf85..077da56fa449 100644
--- a/drivers/net/ethernet/ibm/emac/tah.c
+++ b/drivers/net/ethernet/ibm/emac/tah.c
@@ -112,7 +112,7 @@ static int tah_probe(struct platform_device *ofdev)
tah_reset(ofdev);
printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node);
- wmb();
+ smp_wmb();
return 0;
}
diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c
index a3839cf02ec4..5144ee94a7d2 100644
--- a/drivers/net/ethernet/ibm/emac/zmii.c
+++ b/drivers/net/ethernet/ibm/emac/zmii.c
@@ -258,8 +258,8 @@ static int zmii_probe(struct platform_device *ofdev)
/* Disable all inputs by default */
out_be32(&dev->base->fer, 0);
- printk(KERN_INFO "ZMII %pOF initialized\n", ofdev->dev.of_node);
- wmb();
+ dev_info(&ofdev->dev, "ZMII initialized\n");
+ smp_wmb();
platform_set_drvdata(ofdev, dev);
return 0;
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 04/10] net: emac: mal: replace of_get_property with of_property_read_u32
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (2 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 03/10] net: emac: use DMA-specific and SMP memory barriers Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 05/10] net: emac: mal: replace busy-wait in mal_poll_disable with wait_event Rosen Penev
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Replace the deprecated of_get_property() calls with the typed
of_property_read_u32() helper in mal_probe(). This is both safer
and more idiomatic for modern DT API usage.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/mal.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 99615c8a6c3e..82d502d576ee 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -519,7 +519,7 @@ static int mal_probe(struct platform_device *ofdev)
int err = 0, i, bd_size;
int index = mal_count++;
unsigned int dcr_base;
- const u32 *prop;
+ u32 val;
u32 cfg;
unsigned long irqflags;
irq_handler_t hdlr_serr, hdlr_txde, hdlr_rxde;
@@ -535,23 +535,23 @@ static int mal_probe(struct platform_device *ofdev)
MAL_DBG(mal, "probe" NL);
- prop = of_get_property(ofdev->dev.of_node, "num-tx-chans", NULL);
- if (prop == NULL) {
+ err = of_property_read_u32(ofdev->dev.of_node, "num-tx-chans", &val);
+ if (err) {
printk(KERN_ERR
"mal%d: can't find MAL num-tx-chans property!\n",
index);
return -ENODEV;
}
- mal->num_tx_chans = prop[0];
+ mal->num_tx_chans = val;
- prop = of_get_property(ofdev->dev.of_node, "num-rx-chans", NULL);
- if (prop == NULL) {
+ err = of_property_read_u32(ofdev->dev.of_node, "num-rx-chans", &val);
+ if (err) {
printk(KERN_ERR
"mal%d: can't find MAL num-rx-chans property!\n",
index);
return -ENODEV;
}
- mal->num_rx_chans = prop[0];
+ mal->num_rx_chans = val;
dcr_base = dcr_resource_start(ofdev->dev.of_node, 0);
if (dcr_base == 0) {
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 05/10] net: emac: mal: replace busy-wait in mal_poll_disable with wait_event
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (3 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 04/10] net: emac: mal: replace of_get_property with of_property_read_u32 Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 06/10] net: emac: batch stats, eliminate modulo, tighten barrier in RX poll Rosen Penev
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Replace the msleep(1) busy-wait loop in mal_poll_disable() with
a proper wait_event/wake_up mechanism. Add wait_queue_head_t to
struct mal_commac, initialize it in mal_poll_add(), and wake
waiters in mal_poll_enable().
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/mal.c | 7 +++++--
drivers/net/ethernet/ibm/emac/mal.h | 3 +++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 82d502d576ee..d12a376f69fd 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -176,6 +176,8 @@ void mal_poll_add(struct mal_instance *mal, struct mal_commac *commac)
MAL_DBG(mal, "poll_add(%p)" NL, commac);
+ init_waitqueue_head(&commac->poll_wait);
+
/* starts disabled */
set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
@@ -371,8 +373,8 @@ static irqreturn_t mal_int(int irq, void *dev_instance)
void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac)
{
/* Spinlock-type semantics: only one caller disable poll at a time */
- while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags))
- msleep(1);
+ wait_event(commac->poll_wait,
+ !test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags));
/* Synchronize with the MAL NAPI poller */
napi_synchronize(&mal->napi);
@@ -382,6 +384,7 @@ void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
{
smp_wmb();
clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
+ wake_up(&commac->poll_wait);
/* Feels better to trigger a poll here to catch up with events that
* may have happened on this channel while disabled. It will most
diff --git a/drivers/net/ethernet/ibm/emac/mal.h b/drivers/net/ethernet/ibm/emac/mal.h
index e0ddc41186a2..bd52bb41adee 100644
--- a/drivers/net/ethernet/ibm/emac/mal.h
+++ b/drivers/net/ethernet/ibm/emac/mal.h
@@ -19,6 +19,8 @@
#ifndef __IBM_NEWEMAC_MAL_H
#define __IBM_NEWEMAC_MAL_H
+#include <linux/wait.h>
+
/*
* There are some variations on the MAL, we express them in this driver as
* MAL Version 1 and 2 though that doesn't match any IBM terminology.
@@ -172,6 +174,7 @@ struct mal_commac {
void *dev;
struct list_head poll_list;
long flags;
+ wait_queue_head_t poll_wait;
#define MAL_COMMAC_RX_STOPPED 0
#define MAL_COMMAC_POLL_DISABLED 1
u32 tx_chan_mask;
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 06/10] net: emac: batch stats, eliminate modulo, tighten barrier in RX poll
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (4 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 05/10] net: emac: mal: replace busy-wait in mal_poll_disable with wait_event Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 07/10] net: emac: fix DMA API mapping and unmapping correctness Rosen Penev
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Three small hot-path changes in emac_poll_rx():
- Batch per-packet 64-bit stat updates into local accumulators and
write dev->stats once after the poll loop, avoiding expensive
load-linked/store-conditional sequences on 32-bit PPC for every
received packet.
- Replace slot = (slot + 1) % NUM_RX_BUFF with a simple
if (++slot == NUM_RX_BUFF) branch, avoiding a div/mul by a
non-power-of-2 constant.
- Use dma_rmb() instead of mb() when ordering the ctrl vs. data_len
read of the coherent RX descriptor. The device writes the
descriptor fields in-order and clears MAL_RX_CTRL_EMPTY last;
a read barrier is sufficient.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index 5e7b85d28bde..ced9690cddc3 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -1741,6 +1741,7 @@ static int emac_poll_rx(void *param, int budget)
{
struct emac_instance *dev = param;
int slot = dev->rx_slot, received = 0;
+ u64 packets = 0, bytes = 0;
DBG2(dev, "poll_rx(%d)" NL, budget);
@@ -1797,10 +1798,11 @@ static int emac_poll_rx(void *param, int budget)
napi_gro_receive(&dev->mal->napi, skb);
next:
- ++dev->stats.rx_packets;
+ ++packets;
skip:
- dev->stats.rx_bytes += len;
- slot = (slot + 1) % NUM_RX_BUFF;
+ bytes += len;
+ if (++slot == NUM_RX_BUFF)
+ slot = 0;
--budget;
++received;
continue;
@@ -1864,6 +1866,9 @@ static int emac_poll_rx(void *param, int budget)
emac_rx_enable(dev);
dev->rx_slot = 0;
}
+
+ dev->stats.rx_packets += packets;
+ dev->stats.rx_bytes += bytes;
return received;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 07/10] net: emac: fix DMA API mapping and unmapping correctness
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (5 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 06/10] net: emac: batch stats, eliminate modulo, tighten barrier in RX poll Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 08/10] net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED() Rosen Penev
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Add missing dma_mapping_error() checks after every dma_map_single()
and skb_frag_dma_map() call. Without these, CONFIG_DMA_API_DEBUG
emits:
DMA-API: emac ... device driver failed to check map error
Also fix emac_recycle_rx_skb() which called dma_map_single() but
discarded the returned DMA address -- the descriptor kept a stale
(unmapped) address after the old mapping was freed.
The RX descriptors are allocated in coherent (uncached) DMA memory.
Add a shadow rx_dma[] array in regular cached memory to store the
raw DMA address of each RX slot, avoiding a slow uncached read of
dev->rx_desc[slot].data_ptr on the per-packet hot path. This
prevents a measurable throughput regression on non-coherent PowerPC
platforms where the original fix added such a read.
In emac_recycle_rx_skb(), use dma_sync_single_for_device() with the
actual received length (cache-line-aligned) instead of destroying
and recreating the mapping. The mapping is long-lived; only the
bytes touched by skb_copy_from_linear_data_offset() need
synchronization.
- __emac_prepare_rx_skb: check map error, free skb on failure
- emac_resize_rx_ring: check map error, invalidate slot on failure
- emac_recycle_rx_skb: map first, check error, then unmap old;
use dma_sync_single_for_device() with SKB_DATA_ALIGN(len + NET_IP_ALIGN)
- emac_start_xmit: check map error, free skb on failure
- emac_start_xmit_sg: check map error on both data and frag maps,
undo partial descriptor setup on frag failure
- emac_poll_rx: use rx_dma[] shadow array, unmap old skb before
passing to napi_gro_receive
- emac_clean_rx_ring: use rx_dma[] shadow array for consistency
There's a small performance decrease tested with iperf3
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 574 MBytes 481 Mbits/sec 1 sender
[ 5] 0.00-10.00 sec 572 MBytes 479 Mbits/sec receiver
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 558 MBytes 468 Mbits/sec 0 sender
[ 5] 0.00-10.00 sec 556 MBytes 466 Mbits/sec receiver
but probably worth it. For whatever reason after this patch, ath9k
stopped throwing DMA errors with CONFIG_DMA_API_DEBUG.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 92 +++++++++++++++++++++++-----
drivers/net/ethernet/ibm/emac/core.h | 1 +
2 files changed, 77 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index ced9690cddc3..aed1ad21e2ea 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -52,7 +52,15 @@
#include "core.h"
/*
- * Lack of dma_unmap_???? calls is intentional.
+ * Note on dma_unmap calls:
+ *
+ * RX buffers are properly unmapped before being remapped or passed to the
+ * network stack. See emac_recycle_rx_skb() and emac_poll_rx().
+ *
+ * TX buffers still lack dma_unmap calls for the reasons explained in the
+ * original note below (a single skb may be split across multiple BDs on
+ * TAH-equipped EMACs, making per-fragment tracking complex).
+ * The original rationale is kept for the TX path only:
*
* API-correct usage requires additional support state information to be
* maintained for every RX and TX buffer descriptor (BD). Unfortunately, due to
@@ -1058,6 +1066,7 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
/* Second pass, allocate new skbs */
for (i = 0; i < NUM_RX_BUFF; ++i) {
struct sk_buff *skb;
+ dma_addr_t dma;
skb = netdev_alloc_skb_ip_align(dev->ndev, rx_skb_size);
if (!skb) {
@@ -1066,12 +1075,24 @@ static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
}
BUG_ON(!dev->rx_skb[i]);
+ dma_unmap_single(&dev->ofdev->dev,
+ dev->rx_dma[i],
+ dev->rx_sync_size, DMA_FROM_DEVICE);
dev_kfree_skb(dev->rx_skb[i]);
- dev->rx_desc[i].data_ptr =
- dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
- rx_sync_size, DMA_FROM_DEVICE)
- + NET_IP_ALIGN;
+ dma = dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+ rx_sync_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, dma)) {
+ dev_kfree_skb(skb);
+ dev->rx_skb[i] = NULL;
+ dev->rx_dma[i] = 0;
+ dev->rx_desc[i].data_ptr = 0;
+ dev->rx_desc[i].ctrl = 0;
+ ret = -ENOMEM;
+ goto oom;
+ }
+ dev->rx_desc[i].data_ptr = dma + NET_IP_ALIGN;
+ dev->rx_dma[i] = dma;
dev->rx_skb[i] = skb;
}
skip:
@@ -1150,9 +1171,13 @@ static void emac_clean_rx_ring(struct emac_instance *dev)
for (i = 0; i < NUM_RX_BUFF; ++i)
if (dev->rx_skb[i]) {
+ dma_unmap_single(&dev->ofdev->dev,
+ dev->rx_dma[i],
+ dev->rx_sync_size, DMA_FROM_DEVICE);
dev->rx_desc[i].ctrl = 0;
dev_kfree_skb(dev->rx_skb[i]);
dev->rx_skb[i] = NULL;
+ dev->rx_dma[i] = 0;
dev->rx_desc[i].data_ptr = 0;
}
@@ -1176,15 +1201,23 @@ static void emac_clear_mal_desc(struct mal_descriptor *desc, int count)
static int
__emac_prepare_rx_skb(struct sk_buff *skb, struct emac_instance *dev, int slot)
{
+ dma_addr_t dma;
+
if (unlikely(!skb))
return -ENOMEM;
dev->rx_skb[slot] = skb;
dev->rx_desc[slot].data_len = 0;
- dev->rx_desc[slot].data_ptr =
- dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
- dev->rx_sync_size, DMA_FROM_DEVICE) + NET_IP_ALIGN;
+ dma = dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
+ dev->rx_sync_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, dma)) {
+ dev->rx_skb[slot] = NULL;
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+ dev->rx_desc[slot].data_ptr = dma + NET_IP_ALIGN;
+ dev->rx_dma[slot] = dma;
dma_wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
@@ -1463,6 +1496,12 @@ static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
dev->tx_desc[slot].data_ptr = dma_map_single(&dev->ofdev->dev,
skb->data, len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev,
+ dev->tx_desc[slot].data_ptr)) {
+ dev->tx_skb[slot] = NULL;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
dev->tx_desc[slot].data_len = (u16) len;
dma_wmb();
dev->tx_desc[slot].ctrl = ctrl;
@@ -1530,8 +1569,12 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
/* skb data */
dev->tx_skb[slot] = NULL;
chunk = min(len, MAL_MAX_TX_SIZE);
- dev->tx_desc[slot].data_ptr = pd =
- dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE);
+ pd = dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, pd)) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ dev->tx_desc[slot].data_ptr = pd;
dev->tx_desc[slot].data_len = (u16) chunk;
len -= chunk;
if (unlikely(len))
@@ -1547,6 +1590,18 @@ emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
pd = skb_frag_dma_map(&dev->ofdev->dev, frag, 0, len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->ofdev->dev, pd)) {
+ /* Undo partial descriptor setup and drop packet */
+ while (slot != dev->tx_slot) {
+ dev->tx_desc[slot].ctrl = 0;
+ --dev->tx_cnt;
+ if (--slot < 0)
+ slot = NUM_TX_BUFF - 1;
+ }
+ ++dev->estats.tx_undo;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
slot = emac_xmit_split(dev, slot, pd, len, i == nr_frags - 1,
ctrl);
@@ -1661,14 +1716,14 @@ static void emac_poll_tx(void *param)
static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
int len)
{
- struct sk_buff *skb = dev->rx_skb[slot];
-
DBG2(dev, "recycle %d %d" NL, slot, len);
- if (len)
- dma_map_single(&dev->ofdev->dev, skb->data - NET_IP_ALIGN,
- SKB_DATA_ALIGN(len + NET_IP_ALIGN),
- DMA_FROM_DEVICE);
+ if (len) {
+ dma_sync_single_for_device(&dev->ofdev->dev,
+ dev->rx_dma[slot],
+ SKB_DATA_ALIGN(len + NET_IP_ALIGN),
+ DMA_FROM_DEVICE);
+ }
dev->rx_desc[slot].data_len = 0;
dma_wmb();
@@ -1808,12 +1863,17 @@ static int emac_poll_rx(void *param, int budget)
continue;
sg:
if (ctrl & MAL_RX_CTRL_FIRST) {
+ dma_addr_t old_dma = dev->rx_dma[slot];
+
BUG_ON(dev->rx_sg_skb);
if (unlikely(emac_alloc_rx_skb_napi(dev, slot))) {
DBG(dev, "rx OOM %d" NL, slot);
++dev->estats.rx_dropped_oom;
emac_recycle_rx_skb(dev, slot, 0);
} else {
+ dma_unmap_single(&dev->ofdev->dev, old_dma,
+ dev->rx_sync_size,
+ DMA_FROM_DEVICE);
dev->rx_sg_skb = skb;
skb_put(skb, len);
}
diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
index 296da4bf3781..0719f98f3325 100644
--- a/drivers/net/ethernet/ibm/emac/core.h
+++ b/drivers/net/ethernet/ibm/emac/core.h
@@ -246,6 +246,7 @@ struct emac_instance {
struct sk_buff *tx_skb[NUM_TX_BUFF];
struct sk_buff *rx_skb[NUM_RX_BUFF];
+ dma_addr_t rx_dma[NUM_RX_BUFF];
/* Stats
*/
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 08/10] net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED()
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (6 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 07/10] net: emac: fix DMA API mapping and unmapping correctness Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 09/10] net: emac: add Byte Queue Limits (BQL) support Rosen Penev
2026-06-30 4:16 ` [PATCH 10/10] net: emac: use ndo_get_stats64 instead of ndo_get_stats Rosen Penev
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Convert compile-time #ifdef blocks to IS_ENABLED() conditionals
for better compile coverage and more idiomatic kernel code.
Affected functions: emac_rx_clk_tx, emac_rx_clk_default,
emac_reset, emac_init_phy in core.c, and mal_txeob/mal_rxeob
in mal.c.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 41 ++++++++++++----------------
drivers/net/ethernet/ibm/emac/mal.c | 14 ++++------
2 files changed, 23 insertions(+), 32 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index aed1ad21e2ea..dba3cdfea340 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -139,20 +139,18 @@ static inline void emac_report_timeout_error(struct emac_instance *dev,
*/
static inline void emac_rx_clk_tx(struct emac_instance *dev)
{
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR,
0, SDR0_MFR_ECS >> dev->cell_index);
-#endif
}
static inline void emac_rx_clk_default(struct emac_instance *dev)
{
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR,
SDR0_MFR_ECS >> dev->cell_index, 0);
-#endif
}
/* PHY polling intervals */
@@ -339,7 +337,7 @@ static int emac_reset(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
int n = 20;
- bool __maybe_unused try_internal_clock = false;
+ bool try_internal_clock = false;
DBG(dev, "reset" NL);
@@ -351,8 +349,6 @@ static int emac_reset(struct emac_instance *dev)
emac_tx_disable(dev);
}
-#ifdef CONFIG_PPC_DCR_NATIVE
-do_retry:
/*
* PPC460EX/GT Embedded Processor Advanced User's Manual
* section 28.10.1 Mode Register 0 (EMACx_MR0) states:
@@ -370,7 +366,9 @@ static int emac_reset(struct emac_instance *dev)
* driver will temporarily switch to the internal clock, after
* the first reset fails.
*/
- if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
+retry:
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
if (try_internal_clock || (dev->phy_address == 0xffffffff &&
dev->phy_map == 0xffffffff)) {
/* No PHY: select internal loop clock before reset */
@@ -382,19 +380,18 @@ static int emac_reset(struct emac_instance *dev)
SDR0_ETH_CFG_ECS << dev->cell_index, 0);
}
}
-#endif
out_be32(&p->mr0, EMAC_MR0_SRST);
while ((in_be32(&p->mr0) & EMAC_MR0_SRST) && n)
--n;
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_460EX_PHY_CLK_FIX)) {
if (!n && !try_internal_clock) {
/* first attempt has timed out. */
n = 20;
try_internal_clock = true;
- goto do_retry;
+ goto retry;
}
if (try_internal_clock || (dev->phy_address == 0xffffffff &&
@@ -404,7 +401,6 @@ static int emac_reset(struct emac_instance *dev)
SDR0_ETH_CFG_ECS << dev->cell_index, 0);
}
}
-#endif
if (n) {
dev->reset_failed = 0;
@@ -2754,18 +2750,16 @@ static int emac_init_phy(struct emac_instance *dev)
dev->phy.mdio_write = emac_mdio_write;
/* Enable internal clock source */
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR, 0, SDR0_MFR_ECS);
-#endif
/* PHY clock workaround */
emac_rx_clk_tx(dev);
/* Enable internal clock source on 440GX*/
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR, 0, SDR0_MFR_ECS);
-#endif
/* Configure EMAC with defaults so we can at least use MDIO
* This is needed mostly for 440GX
*/
@@ -2825,10 +2819,9 @@ static int emac_init_phy(struct emac_instance *dev)
}
/* Enable external clock source */
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX))
dcri_clrset(SDR0, SDR0_MFR, SDR0_MFR_ECS, 0);
-#endif
mutex_unlock(&emac_phy_map_lock);
if (i == 0x20) {
printk(KERN_WARNING "%pOF: can't find PHY!\n", np);
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index d12a376f69fd..2adfd9d9bdb1 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -282,11 +282,10 @@ static irqreturn_t mal_txeob(int irq, void *dev_instance)
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_TXEOBISR, r);
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
- (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX));
-#endif
+ (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICTX));
return IRQ_HANDLED;
}
@@ -302,11 +301,10 @@ static irqreturn_t mal_rxeob(int irq, void *dev_instance)
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_RXEOBISR, r);
-#ifdef CONFIG_PPC_DCR_NATIVE
- if (mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
+ if (IS_ENABLED(CONFIG_PPC_DCR_NATIVE) &&
+ mal_has_feature(mal, MAL_FTR_CLEAR_ICINTSTAT))
mtdcri(SDR0, DCRN_SDR_ICINTSTAT,
- (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX));
-#endif
+ (mfdcri(SDR0, DCRN_SDR_ICINTSTAT) | ICINTSTAT_ICRX));
return IRQ_HANDLED;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 09/10] net: emac: add Byte Queue Limits (BQL) support
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (7 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 08/10] net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED() Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
2026-06-30 4:16 ` [PATCH 10/10] net: emac: use ndo_get_stats64 instead of ndo_get_stats Rosen Penev
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Add BQL to the TX path to improve tail latency under high throughput:
- Call netdev_tx_sent_queue() before ringing the TX doorbell
- Call netdev_tx_completed_queue() with byte/packet counts after
TX completions
- Call netdev_reset_queue() on close and full TX reset
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index dba3cdfea340..da5f3d436aa3 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -751,6 +751,7 @@ static void emac_full_tx_reset(struct emac_instance *dev)
mal_disable_tx_channel(dev->mal, dev->mal_tx_chan);
emac_clean_tx_ring(dev);
dev->tx_cnt = dev->tx_slot = dev->ack_slot = 0;
+ netdev_reset_queue(dev->ndev);
emac_configure(dev);
@@ -1428,6 +1429,7 @@ static int emac_close(struct net_device *ndev)
emac_clean_tx_ring(dev);
emac_clean_rx_ring(dev);
+ netdev_reset_queue(ndev);
netif_carrier_off(ndev);
return 0;
@@ -1448,6 +1450,9 @@ static inline netdev_tx_t emac_xmit_finish(struct emac_instance *dev, int len)
{
struct emac_regs __iomem *p = dev->emacp;
struct net_device *ndev = dev->ndev;
+ struct netdev_queue *txq = netdev_get_tx_queue(ndev, 0);
+
+ netdev_tx_sent_queue(txq, len);
/* Send the packet out. If the if makes a significant perf
* difference, then we can store the TMR0 value in "dev"
@@ -1666,6 +1671,7 @@ static void emac_parse_tx_error(struct emac_instance *dev, u16 ctrl)
static void emac_poll_tx(void *param)
{
struct emac_instance *dev = param;
+ struct netdev_queue *txq = netdev_get_tx_queue(dev->ndev, 0);
u32 bad_mask;
DBG2(dev, "poll_tx, %d %d" NL, dev->tx_cnt, dev->ack_slot);
@@ -1679,6 +1685,7 @@ static void emac_poll_tx(void *param)
if (dev->tx_cnt) {
u16 ctrl;
int slot = dev->ack_slot, n = 0;
+ unsigned int bytes = 0;
again:
ctrl = dev->tx_desc[slot].ctrl;
if (!(ctrl & MAL_TX_CTRL_READY)) {
@@ -1686,6 +1693,7 @@ static void emac_poll_tx(void *param)
++n;
if (skb) {
+ bytes += skb->len;
dev_kfree_skb(skb);
dev->tx_skb[slot] = NULL;
}
@@ -1699,6 +1707,7 @@ static void emac_poll_tx(void *param)
}
if (n) {
dev->ack_slot = slot;
+ netdev_tx_completed_queue(txq, n, bytes);
if (netif_queue_stopped(dev->ndev) &&
dev->tx_cnt < EMAC_TX_WAKEUP_THRESH)
netif_wake_queue(dev->ndev);
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 10/10] net: emac: use ndo_get_stats64 instead of ndo_get_stats
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
` (8 preceding siblings ...)
2026-06-30 4:16 ` [PATCH 09/10] net: emac: add Byte Queue Limits (BQL) support Rosen Penev
@ 2026-06-30 4:16 ` Rosen Penev
9 siblings, 0 replies; 11+ messages in thread
From: Rosen Penev @ 2026-06-30 4:16 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, open list
Replace the legacy emac_stats() callback with emac_get_stats64()
that fills struct rtnl_link_stats64 directly from the driver's
u64 counters, avoiding truncation of 64-bit values on 32-bit
architectures.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/ethernet/ibm/emac/core.c | 84 ++++++++++++++--------------
1 file changed, 41 insertions(+), 43 deletions(-)
diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
index da5f3d436aa3..c62abc8aa471 100644
--- a/drivers/net/ethernet/ibm/emac/core.c
+++ b/drivers/net/ethernet/ibm/emac/core.c
@@ -2026,57 +2026,55 @@ static irqreturn_t emac_irq(int irq, void *dev_instance)
return IRQ_HANDLED;
}
-static struct net_device_stats *emac_stats(struct net_device *ndev)
+static void emac_get_stats64(struct net_device *ndev,
+ struct rtnl_link_stats64 *stats)
{
struct emac_instance *dev = netdev_priv(ndev);
struct emac_stats *st = &dev->stats;
struct emac_error_stats *est = &dev->estats;
- struct net_device_stats *nst = &ndev->stats;
unsigned long flags;
DBG2(dev, "stats" NL);
- /* Compute "legacy" statistics */
spin_lock_irqsave(&dev->lock, flags);
- nst->rx_packets = (unsigned long)st->rx_packets;
- nst->rx_bytes = (unsigned long)st->rx_bytes;
- nst->tx_packets = (unsigned long)st->tx_packets;
- nst->tx_bytes = (unsigned long)st->tx_bytes;
- nst->rx_dropped = (unsigned long)(est->rx_dropped_oom +
- est->rx_dropped_error +
- est->rx_dropped_resize +
- est->rx_dropped_mtu);
- nst->tx_dropped = (unsigned long)est->tx_dropped;
-
- nst->rx_errors = (unsigned long)est->rx_bd_errors;
- nst->rx_fifo_errors = (unsigned long)(est->rx_bd_overrun +
- est->rx_fifo_overrun +
- est->rx_overrun);
- nst->rx_frame_errors = (unsigned long)(est->rx_bd_alignment_error +
- est->rx_alignment_error);
- nst->rx_crc_errors = (unsigned long)(est->rx_bd_bad_fcs +
- est->rx_bad_fcs);
- nst->rx_length_errors = (unsigned long)(est->rx_bd_runt_packet +
- est->rx_bd_short_event +
- est->rx_bd_packet_too_long +
- est->rx_bd_out_of_range +
- est->rx_bd_in_range +
- est->rx_runt_packet +
- est->rx_short_event +
- est->rx_packet_too_long +
- est->rx_out_of_range +
- est->rx_in_range);
-
- nst->tx_errors = (unsigned long)(est->tx_bd_errors + est->tx_errors);
- nst->tx_fifo_errors = (unsigned long)(est->tx_bd_underrun +
- est->tx_underrun);
- nst->tx_carrier_errors = (unsigned long)est->tx_bd_carrier_loss;
- nst->collisions = (unsigned long)(est->tx_bd_excessive_deferral +
- est->tx_bd_excessive_collisions +
- est->tx_bd_late_collision +
- est->tx_bd_multple_collisions);
+ stats->rx_packets = st->rx_packets;
+ stats->rx_bytes = st->rx_bytes;
+ stats->tx_packets = st->tx_packets;
+ stats->tx_bytes = st->tx_bytes;
+ stats->rx_dropped = est->rx_dropped_oom +
+ est->rx_dropped_error +
+ est->rx_dropped_resize +
+ est->rx_dropped_mtu;
+ stats->tx_dropped = est->tx_dropped;
+
+ stats->rx_errors = est->rx_bd_errors;
+ stats->rx_fifo_errors = est->rx_bd_overrun +
+ est->rx_fifo_overrun +
+ est->rx_overrun;
+ stats->rx_frame_errors = est->rx_bd_alignment_error +
+ est->rx_alignment_error;
+ stats->rx_crc_errors = est->rx_bd_bad_fcs +
+ est->rx_bad_fcs;
+ stats->rx_length_errors = est->rx_bd_runt_packet +
+ est->rx_bd_short_event +
+ est->rx_bd_packet_too_long +
+ est->rx_bd_out_of_range +
+ est->rx_bd_in_range +
+ est->rx_runt_packet +
+ est->rx_short_event +
+ est->rx_packet_too_long +
+ est->rx_out_of_range +
+ est->rx_in_range;
+
+ stats->tx_errors = est->tx_bd_errors + est->tx_errors;
+ stats->tx_fifo_errors = est->tx_bd_underrun +
+ est->tx_underrun;
+ stats->tx_carrier_errors = est->tx_bd_carrier_loss;
+ stats->collisions = est->tx_bd_excessive_deferral +
+ est->tx_bd_excessive_collisions +
+ est->tx_bd_late_collision +
+ est->tx_bd_multple_collisions;
spin_unlock_irqrestore(&dev->lock, flags);
- return nst;
}
static struct mal_commac_ops emac_commac_ops = {
@@ -3040,7 +3038,7 @@ static int emac_init_config(struct emac_instance *dev)
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
- .ndo_get_stats = emac_stats,
+ .ndo_get_stats64 = emac_get_stats64,
.ndo_set_rx_mode = emac_set_multicast_list,
.ndo_eth_ioctl = emac_ioctl,
.ndo_tx_timeout = emac_tx_timeout,
@@ -3052,7 +3050,7 @@ static const struct net_device_ops emac_netdev_ops = {
static const struct net_device_ops emac_gige_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
- .ndo_get_stats = emac_stats,
+ .ndo_get_stats64 = emac_get_stats64,
.ndo_set_rx_mode = emac_set_multicast_list,
.ndo_eth_ioctl = emac_ioctl,
.ndo_tx_timeout = emac_tx_timeout,
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-30 4:16 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-30 4:16 [PATCH 00/10] net: emac: various cleanups, fixes, and feature additions Rosen Penev
2026-06-30 4:16 ` [PATCH 01/10] net: emac: remove emac_xaht_base() Rosen Penev
2026-06-30 4:16 ` [PATCH 02/10] net: emac: fix sparse __iomem warnings in IAHT register access Rosen Penev
2026-06-30 4:16 ` [PATCH 03/10] net: emac: use DMA-specific and SMP memory barriers Rosen Penev
2026-06-30 4:16 ` [PATCH 04/10] net: emac: mal: replace of_get_property with of_property_read_u32 Rosen Penev
2026-06-30 4:16 ` [PATCH 05/10] net: emac: mal: replace busy-wait in mal_poll_disable with wait_event Rosen Penev
2026-06-30 4:16 ` [PATCH 06/10] net: emac: batch stats, eliminate modulo, tighten barrier in RX poll Rosen Penev
2026-06-30 4:16 ` [PATCH 07/10] net: emac: fix DMA API mapping and unmapping correctness Rosen Penev
2026-06-30 4:16 ` [PATCH 08/10] net: emac: replace #ifdef CONFIG_PPC_DCR_NATIVE with IS_ENABLED() Rosen Penev
2026-06-30 4:16 ` [PATCH 09/10] net: emac: add Byte Queue Limits (BQL) support Rosen Penev
2026-06-30 4:16 ` [PATCH 10/10] net: emac: use ndo_get_stats64 instead of ndo_get_stats Rosen Penev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox