* [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