* [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
@ 2025-01-16 23:29 Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 1/6] net: xilinx: axienet: Add some symbolic constants for IRQ delay timer Sean Anderson
` (7 more replies)
0 siblings, 8 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson, Heng Qi
To improve performance without sacrificing latency under low load,
enable DIM. While I appreciate not having to write the library myself, I
do think there are many unusual aspects to DIM, as detailed in the last
patch.
Changes in v4:
- Fix incorrect function name in doc comment for axienet_coalesce_params
- Rebase onto net-next/master
Changes in v3:
- Fix mismatched parameter name documentation for axienet_calc_cr
- Integrate some cleanups originally included in
https://lore.kernel.org/netdev/20240909230908.1319982-1-sean.anderson@linux.dev/
- Move spin (un)locking in IRQs inside the if condition of
napi_schedule_prep. This lets us hold the lock just for the rmw.
- Fix function name in doc comments for axienet_update_coalesce_rx/tx
- Adjust axienet_local doc comment order to match the members
- Rebase onto net-next/master
Changes in v2:
- Add some symbolic constants for IRQ delay timer
- Report an error for bad coalesce settings
- Don't use spin_lock_irqsave when we know the context
- Split the CR calculation refactor from runtime coalesce settings
adjustment support for easier review.
- Have axienet_update_coalesce_rx/tx take the cr value/mask instead of
calculating it with axienet_calc_cr. This will make it easier to add
partial updates in the next few commits.
- Get coalesce parameters from driver state
- Don't take the RTNL in axienet_rx_dim_work to avoid deadlock. Instead,
calculate a partial cr update that axienet_update_coalesce_rx can
perform under a spin lock.
- Use READ/WRITE_ONCE when accessing/modifying rx_irqs
Sean Anderson (6):
net: xilinx: axienet: Add some symbolic constants for IRQ delay timer
net: xilinx: axienet: Report an error for bad coalesce settings
net: xilinx: axienet: Combine CR calculation
net: xilinx: axienet: Support adjusting coalesce settings while
running
net: xilinx: axienet: Get coalesce parameters from driver state
net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
drivers/net/ethernet/xilinx/Kconfig | 1 +
drivers/net/ethernet/xilinx/xilinx_axienet.h | 32 +-
.../net/ethernet/xilinx/xilinx_axienet_main.c | 330 ++++++++++++++----
3 files changed, 280 insertions(+), 83 deletions(-)
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH net-next v4 1/6] net: xilinx: axienet: Add some symbolic constants for IRQ delay timer
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
@ 2025-01-16 23:29 ` Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 2/6] net: xilinx: axienet: Report an error for bad coalesce settings Sean Anderson
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson
Instead of using literals, add some symbolic constants for the IRQ delay
timer calculation.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed by: Shannon Nelson <shannon.nelson@amd.com>
---
(no changes since v2)
Changes in v2:
- New
drivers/net/ethernet/xilinx/xilinx_axienet.h | 3 +++
drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 7 ++-----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index d64b8abcf018..a3f4f3e42587 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -120,6 +120,9 @@
#define XAXIDMA_IRQ_ERROR_MASK 0x00004000 /* Error interrupt */
#define XAXIDMA_IRQ_ALL_MASK 0x00007000 /* All interrupts */
+/* Constant to convert delay counts to microseconds */
+#define XAXIDMA_DELAY_SCALE (125ULL * USEC_PER_SEC)
+
/* Default TX/RX Threshold and delay timer values for SGDMA mode */
#define XAXIDMA_DFT_TX_THRESHOLD 24
#define XAXIDMA_DFT_TX_USEC 50
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 07850ad331dd..0532dc94ee93 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -238,11 +238,8 @@ static u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec)
/* 1 Timeout Interval = 125 * (clock period of SG clock) */
result = DIV64_U64_ROUND_CLOSEST((u64)coalesce_usec * clk_rate,
- (u64)125000000);
- if (result > 255)
- result = 255;
-
- return result;
+ XAXIDMA_DELAY_SCALE);
+ return min(result, FIELD_MAX(XAXIDMA_DELAY_MASK));
}
/**
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next v4 2/6] net: xilinx: axienet: Report an error for bad coalesce settings
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 1/6] net: xilinx: axienet: Add some symbolic constants for IRQ delay timer Sean Anderson
@ 2025-01-16 23:29 ` Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 3/6] net: xilinx: axienet: Combine CR calculation Sean Anderson
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson
Instead of silently ignoring invalid/unsupported settings, report an
error. Additionally, relax the check for non-zero usecs to apply only
when it will be used (i.e. when frames != 1).
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed by: Shannon Nelson <shannon.nelson@amd.com>
---
(no changes since v2)
Changes in v2:
- New
.../net/ethernet/xilinx/xilinx_axienet_main.c | 27 +++++++++++++------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 0532dc94ee93..9e7fa012e4fa 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -2059,14 +2059,25 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
return -EINVAL;
}
- if (ecoalesce->rx_max_coalesced_frames)
- lp->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames;
- if (ecoalesce->rx_coalesce_usecs)
- lp->coalesce_usec_rx = ecoalesce->rx_coalesce_usecs;
- if (ecoalesce->tx_max_coalesced_frames)
- lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames;
- if (ecoalesce->tx_coalesce_usecs)
- lp->coalesce_usec_tx = ecoalesce->tx_coalesce_usecs;
+ if (!ecoalesce->rx_max_coalesced_frames ||
+ !ecoalesce->tx_max_coalesced_frames) {
+ NL_SET_ERR_MSG(extack, "frames must be non-zero");
+ return -EINVAL;
+ }
+
+ if ((ecoalesce->rx_max_coalesced_frames > 1 &&
+ !ecoalesce->rx_coalesce_usecs) ||
+ (ecoalesce->tx_max_coalesced_frames > 1 &&
+ !ecoalesce->tx_coalesce_usecs)) {
+ NL_SET_ERR_MSG(extack,
+ "usecs must be non-zero when frames is greater than one");
+ return -EINVAL;
+ }
+
+ lp->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames;
+ lp->coalesce_usec_rx = ecoalesce->rx_coalesce_usecs;
+ lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames;
+ lp->coalesce_usec_tx = ecoalesce->tx_coalesce_usecs;
return 0;
}
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next v4 3/6] net: xilinx: axienet: Combine CR calculation
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 1/6] net: xilinx: axienet: Add some symbolic constants for IRQ delay timer Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 2/6] net: xilinx: axienet: Report an error for bad coalesce settings Sean Anderson
@ 2025-01-16 23:29 ` Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 4/6] net: xilinx: axienet: Support adjusting coalesce settings while running Sean Anderson
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson
Combine the common parts of the CR calculations for better code reuse.
While we're at it, simplify the code a bit.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>
---
(no changes since v3)
Changes in v3:
- Fix mismatched parameter name documentation for axienet_calc_cr
- Integrate some cleanups originally included in
https://lore.kernel.org/netdev/20240909230908.1319982-1-sean.anderson@linux.dev/
Changes in v2:
- Split off from runtime coalesce modification support
drivers/net/ethernet/xilinx/xilinx_axienet.h | 3 -
.../net/ethernet/xilinx/xilinx_axienet_main.c | 64 ++++++++++---------
2 files changed, 34 insertions(+), 33 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index a3f4f3e42587..8fd3b45ef6aa 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -112,9 +112,6 @@
#define XAXIDMA_DELAY_MASK 0xFF000000 /* Delay timeout counter */
#define XAXIDMA_COALESCE_MASK 0x00FF0000 /* Coalesce counter */
-#define XAXIDMA_DELAY_SHIFT 24
-#define XAXIDMA_COALESCE_SHIFT 16
-
#define XAXIDMA_IRQ_IOC_MASK 0x00001000 /* Completion intr */
#define XAXIDMA_IRQ_DELAY_MASK 0x00002000 /* Delay interrupt */
#define XAXIDMA_IRQ_ERROR_MASK 0x00004000 /* Error interrupt */
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 9e7fa012e4fa..bd95f2ba3bae 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -224,22 +224,40 @@ static void axienet_dma_bd_release(struct net_device *ndev)
}
/**
- * axienet_usec_to_timer - Calculate IRQ delay timer value
- * @lp: Pointer to the axienet_local structure
- * @coalesce_usec: Microseconds to convert into timer value
+ * axienet_calc_cr() - Calculate control register value
+ * @lp: Device private data
+ * @count: Number of completions before an interrupt
+ * @usec: Microseconds after the last completion before an interrupt
+ *
+ * Calculate a control register value based on the coalescing settings. The
+ * run/stop bit is not set.
*/
-static u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec)
+static u32 axienet_calc_cr(struct axienet_local *lp, u32 count, u32 usec)
{
- u32 result;
- u64 clk_rate = 125000000; /* arbitrary guess if no clock rate set */
+ u32 cr;
- if (lp->axi_clk)
- clk_rate = clk_get_rate(lp->axi_clk);
+ cr = FIELD_PREP(XAXIDMA_COALESCE_MASK, count) | XAXIDMA_IRQ_IOC_MASK |
+ XAXIDMA_IRQ_ERROR_MASK;
+ /* Only set interrupt delay timer if not generating an interrupt on
+ * the first packet. Otherwise leave at 0 to disable delay interrupt.
+ */
+ if (count > 1) {
+ u64 clk_rate = 125000000; /* arbitrary guess if no clock rate set */
+ u32 timer;
- /* 1 Timeout Interval = 125 * (clock period of SG clock) */
- result = DIV64_U64_ROUND_CLOSEST((u64)coalesce_usec * clk_rate,
- XAXIDMA_DELAY_SCALE);
- return min(result, FIELD_MAX(XAXIDMA_DELAY_MASK));
+ if (lp->axi_clk)
+ clk_rate = clk_get_rate(lp->axi_clk);
+
+ /* 1 Timeout Interval = 125 * (clock period of SG clock) */
+ timer = DIV64_U64_ROUND_CLOSEST((u64)usec * clk_rate,
+ XAXIDMA_DELAY_SCALE);
+
+ timer = min(timer, FIELD_MAX(XAXIDMA_DELAY_MASK));
+ cr |= FIELD_PREP(XAXIDMA_DELAY_MASK, timer) |
+ XAXIDMA_IRQ_DELAY_MASK;
+ }
+
+ return cr;
}
/**
@@ -249,27 +267,13 @@ static u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec)
static void axienet_dma_start(struct axienet_local *lp)
{
/* Start updating the Rx channel control register */
- lp->rx_dma_cr = (lp->coalesce_count_rx << XAXIDMA_COALESCE_SHIFT) |
- XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK;
- /* Only set interrupt delay timer if not generating an interrupt on
- * the first RX packet. Otherwise leave at 0 to disable delay interrupt.
- */
- if (lp->coalesce_count_rx > 1)
- lp->rx_dma_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_rx)
- << XAXIDMA_DELAY_SHIFT) |
- XAXIDMA_IRQ_DELAY_MASK;
+ lp->rx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_rx,
+ lp->coalesce_usec_rx);
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr);
/* Start updating the Tx channel control register */
- lp->tx_dma_cr = (lp->coalesce_count_tx << XAXIDMA_COALESCE_SHIFT) |
- XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK;
- /* Only set interrupt delay timer if not generating an interrupt on
- * the first TX packet. Otherwise leave at 0 to disable delay interrupt.
- */
- if (lp->coalesce_count_tx > 1)
- lp->tx_dma_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_tx)
- << XAXIDMA_DELAY_SHIFT) |
- XAXIDMA_IRQ_DELAY_MASK;
+ lp->tx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_tx,
+ lp->coalesce_usec_tx);
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
/* Populate the tail pointer and bring the Rx Axi DMA engine out of
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next v4 4/6] net: xilinx: axienet: Support adjusting coalesce settings while running
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
` (2 preceding siblings ...)
2025-01-16 23:29 ` [PATCH net-next v4 3/6] net: xilinx: axienet: Combine CR calculation Sean Anderson
@ 2025-01-16 23:29 ` Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 5/6] net: xilinx: axienet: Get coalesce parameters from driver state Sean Anderson
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson
In preparation for adaptive IRQ coalescing, we first need to support
adjusting the settings at runtime. The existing code doesn't require any
locking because
- dma_start is the only function that modifies rx/tx_dma_cr. It is
always called with IRQs and NAPI disabled, so nothing else is touching
the hardware.
- The IRQs don't race with poll, since the latter is a softirq.
- The IRQs don't race with dma_stop since they both just clear the
control registers.
- dma_stop doesn't race with poll since the former is called with NAPI
disabled.
However, once we introduce another function that modifies rx/tx_dma_cr,
we need to have some locking to prevent races. Introduce two locks to
protect these variables and their registers.
The control register values are now generated where the coalescing
settings are set. Converting coalescing settings to control register
values may require sleeping because of clk_get_rate. However, the
read/modify/write of the control registers themselves can't sleep
because it needs to happen in IRQ context. By pre-calculating the
control register values, we avoid introducing an additional mutex.
Since axienet_dma_start writes the control settings when it runs, we
don't bother updating the CR registers when rx/tx_dma_started is false.
This prevents any issues from writing to the control registers in the
middle of a reset sequence.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>
---
(no changes since v3)
Changes in v3:
- Move spin (un)locking in IRQs inside the if condition of
napi_schedule_prep. This lets us hold the lock just for the rmw.
- Fix function name in doc comments for axienet_update_coalesce_rx/tx
Changes in v2:
- Don't use spin_lock_irqsave when we know the context
- Split the CR calculation refactor from runtime coalesce settings
adjustment support for easier review.
- Have axienet_update_coalesce_rx/tx take the cr value/mask instead of
calculating it with axienet_calc_cr. This will make it easier to add
partial updates in the next few commits.
- Split off CR calculation merging into another patch
drivers/net/ethernet/xilinx/xilinx_axienet.h | 8 ++
.../net/ethernet/xilinx/xilinx_axienet_main.c | 134 +++++++++++++++---
2 files changed, 119 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 8fd3b45ef6aa..6b8e550c2155 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -484,7 +484,9 @@ struct skbuf_dma_descriptor {
* @regs: Base address for the axienet_local device address space
* @dma_regs: Base address for the axidma device address space
* @napi_rx: NAPI RX control structure
+ * @rx_cr_lock: Lock protecting @rx_dma_cr, its register, and @rx_dma_started
* @rx_dma_cr: Nominal content of RX DMA control register
+ * @rx_dma_started: Set when RX DMA is started
* @rx_bd_v: Virtual address of the RX buffer descriptor ring
* @rx_bd_p: Physical address(start address) of the RX buffer descr. ring
* @rx_bd_num: Size of RX buffer descriptor ring
@@ -494,7 +496,9 @@ struct skbuf_dma_descriptor {
* @rx_bytes: RX byte count for statistics
* @rx_stat_sync: Synchronization object for RX stats
* @napi_tx: NAPI TX control structure
+ * @tx_cr_lock: Lock protecting @tx_dma_cr, its register, and @tx_dma_started
* @tx_dma_cr: Nominal content of TX DMA control register
+ * @tx_dma_started: Set when TX DMA is started
* @tx_bd_v: Virtual address of the TX buffer descriptor ring
* @tx_bd_p: Physical address(start address) of the TX buffer descr. ring
* @tx_bd_num: Size of TX buffer descriptor ring
@@ -566,7 +570,9 @@ struct axienet_local {
void __iomem *dma_regs;
struct napi_struct napi_rx;
+ spinlock_t rx_cr_lock;
u32 rx_dma_cr;
+ bool rx_dma_started;
struct axidma_bd *rx_bd_v;
dma_addr_t rx_bd_p;
u32 rx_bd_num;
@@ -576,7 +582,9 @@ struct axienet_local {
struct u64_stats_sync rx_stat_sync;
struct napi_struct napi_tx;
+ spinlock_t tx_cr_lock;
u32 tx_dma_cr;
+ bool tx_dma_started;
struct axidma_bd *tx_bd_v;
dma_addr_t tx_bd_p;
u32 tx_bd_num;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index bd95f2ba3bae..b70e65757454 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -266,16 +266,12 @@ static u32 axienet_calc_cr(struct axienet_local *lp, u32 count, u32 usec)
*/
static void axienet_dma_start(struct axienet_local *lp)
{
+ spin_lock_irq(&lp->rx_cr_lock);
+
/* Start updating the Rx channel control register */
- lp->rx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_rx,
- lp->coalesce_usec_rx);
+ lp->rx_dma_cr &= ~XAXIDMA_CR_RUNSTOP_MASK;
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr);
- /* Start updating the Tx channel control register */
- lp->tx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_tx,
- lp->coalesce_usec_tx);
- axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
-
/* Populate the tail pointer and bring the Rx Axi DMA engine out of
* halted state. This will make the Rx side ready for reception.
*/
@@ -284,6 +280,14 @@ static void axienet_dma_start(struct axienet_local *lp)
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr);
axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p +
(sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1)));
+ lp->rx_dma_started = true;
+
+ spin_unlock_irq(&lp->rx_cr_lock);
+ spin_lock_irq(&lp->tx_cr_lock);
+
+ /* Start updating the Tx channel control register */
+ lp->tx_dma_cr &= ~XAXIDMA_CR_RUNSTOP_MASK;
+ axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
/* Write to the RS (Run-stop) bit in the Tx channel control register.
* Tx channel is now ready to run. But only after we write to the
@@ -292,6 +296,9 @@ static void axienet_dma_start(struct axienet_local *lp)
axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
lp->tx_dma_cr |= XAXIDMA_CR_RUNSTOP_MASK;
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
+ lp->tx_dma_started = true;
+
+ spin_unlock_irq(&lp->tx_cr_lock);
}
/**
@@ -627,14 +634,22 @@ static void axienet_dma_stop(struct axienet_local *lp)
int count;
u32 cr, sr;
- cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
- cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
+ spin_lock_irq(&lp->rx_cr_lock);
+
+ cr = lp->rx_dma_cr & ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+ lp->rx_dma_started = false;
+
+ spin_unlock_irq(&lp->rx_cr_lock);
synchronize_irq(lp->rx_irq);
- cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
- cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
+ spin_lock_irq(&lp->tx_cr_lock);
+
+ cr = lp->tx_dma_cr & ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK);
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+ lp->tx_dma_started = false;
+
+ spin_unlock_irq(&lp->tx_cr_lock);
synchronize_irq(lp->tx_irq);
/* Give DMAs a chance to halt gracefully */
@@ -983,7 +998,9 @@ static int axienet_tx_poll(struct napi_struct *napi, int budget)
* cause an immediate interrupt if any TX packets are
* already pending.
*/
+ spin_lock_irq(&lp->tx_cr_lock);
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
+ spin_unlock_irq(&lp->tx_cr_lock);
}
return packets;
}
@@ -1249,7 +1266,9 @@ static int axienet_rx_poll(struct napi_struct *napi, int budget)
* cause an immediate interrupt if any RX packets are
* already pending.
*/
+ spin_lock_irq(&lp->rx_cr_lock);
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr);
+ spin_unlock_irq(&lp->rx_cr_lock);
}
return packets;
}
@@ -1287,11 +1306,14 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
/* Disable further TX completion interrupts and schedule
* NAPI to handle the completions.
*/
- u32 cr = lp->tx_dma_cr;
-
- cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
if (napi_schedule_prep(&lp->napi_tx)) {
+ u32 cr;
+
+ spin_lock(&lp->tx_cr_lock);
+ cr = lp->tx_dma_cr;
+ cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+ spin_unlock(&lp->tx_cr_lock);
__napi_schedule(&lp->napi_tx);
}
}
@@ -1332,11 +1354,15 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
/* Disable further RX completion interrupts and schedule
* NAPI receive.
*/
- u32 cr = lp->rx_dma_cr;
-
- cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
if (napi_schedule_prep(&lp->napi_rx)) {
+ u32 cr;
+
+ spin_lock(&lp->rx_cr_lock);
+ cr = lp->rx_dma_cr;
+ cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+ spin_unlock(&lp->rx_cr_lock);
+
__napi_schedule(&lp->napi_rx);
}
}
@@ -2002,6 +2028,62 @@ axienet_ethtools_set_pauseparam(struct net_device *ndev,
return phylink_ethtool_set_pauseparam(lp->phylink, epauseparm);
}
+/**
+ * axienet_update_coalesce_rx() - Set RX CR
+ * @lp: Device private data
+ * @cr: Value to write to the RX CR
+ * @mask: Bits to set from @cr
+ */
+static void axienet_update_coalesce_rx(struct axienet_local *lp, u32 cr,
+ u32 mask)
+{
+ spin_lock_irq(&lp->rx_cr_lock);
+ lp->rx_dma_cr &= ~mask;
+ lp->rx_dma_cr |= cr;
+ /* If DMA isn't started, then the settings will be applied the next
+ * time dma_start() is called.
+ */
+ if (lp->rx_dma_started) {
+ u32 reg = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET);
+
+ /* Don't enable IRQs if they are disabled by NAPI */
+ if (reg & XAXIDMA_IRQ_ALL_MASK)
+ cr = lp->rx_dma_cr;
+ else
+ cr = lp->rx_dma_cr & ~XAXIDMA_IRQ_ALL_MASK;
+ axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
+ }
+ spin_unlock_irq(&lp->rx_cr_lock);
+}
+
+/**
+ * axienet_update_coalesce_tx() - Set TX CR
+ * @lp: Device private data
+ * @cr: Value to write to the TX CR
+ * @mask: Bits to set from @cr
+ */
+static void axienet_update_coalesce_tx(struct axienet_local *lp, u32 cr,
+ u32 mask)
+{
+ spin_lock_irq(&lp->tx_cr_lock);
+ lp->tx_dma_cr &= ~mask;
+ lp->tx_dma_cr |= cr;
+ /* If DMA isn't started, then the settings will be applied the next
+ * time dma_start() is called.
+ */
+ if (lp->tx_dma_started) {
+ u32 reg = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET);
+
+ /* Don't enable IRQs if they are disabled by NAPI */
+ if (reg & XAXIDMA_IRQ_ALL_MASK)
+ cr = lp->tx_dma_cr;
+ else
+ cr = lp->tx_dma_cr & ~XAXIDMA_IRQ_ALL_MASK;
+ axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+ }
+ spin_unlock_irq(&lp->tx_cr_lock);
+}
+
/**
* axienet_ethtools_get_coalesce - Get DMA interrupt coalescing count.
* @ndev: Pointer to net_device structure
@@ -2050,12 +2132,7 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
struct axienet_local *lp = netdev_priv(ndev);
-
- if (netif_running(ndev)) {
- NL_SET_ERR_MSG(extack,
- "Please stop netif before applying configuration");
- return -EBUSY;
- }
+ u32 cr;
if (ecoalesce->rx_max_coalesced_frames > 255 ||
ecoalesce->tx_max_coalesced_frames > 255) {
@@ -2083,6 +2160,11 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames;
lp->coalesce_usec_tx = ecoalesce->tx_coalesce_usecs;
+ cr = axienet_calc_cr(lp, lp->coalesce_count_rx, lp->coalesce_usec_rx);
+ axienet_update_coalesce_rx(lp, cr, ~XAXIDMA_CR_RUNSTOP_MASK);
+
+ cr = axienet_calc_cr(lp, lp->coalesce_count_tx, lp->coalesce_usec_tx);
+ axienet_update_coalesce_tx(lp, cr, ~XAXIDMA_CR_RUNSTOP_MASK);
return 0;
}
@@ -2862,10 +2944,16 @@ static int axienet_probe(struct platform_device *pdev)
axienet_set_mac_address(ndev, NULL);
}
+ spin_lock_init(&lp->rx_cr_lock);
+ spin_lock_init(&lp->tx_cr_lock);
lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD;
lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD;
lp->coalesce_usec_rx = XAXIDMA_DFT_RX_USEC;
lp->coalesce_usec_tx = XAXIDMA_DFT_TX_USEC;
+ lp->rx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_rx,
+ lp->coalesce_usec_rx);
+ lp->tx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_tx,
+ lp->coalesce_usec_tx);
ret = axienet_mdio_setup(lp);
if (ret)
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next v4 5/6] net: xilinx: axienet: Get coalesce parameters from driver state
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
` (3 preceding siblings ...)
2025-01-16 23:29 ` [PATCH net-next v4 4/6] net: xilinx: axienet: Support adjusting coalesce settings while running Sean Anderson
@ 2025-01-16 23:29 ` Sean Anderson
2025-01-19 1:18 ` Jakub Kicinski
2025-01-16 23:29 ` [PATCH net-next v4 6/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
` (2 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson
The cr variables now contain the same values as the control registers
themselves. Extract/calculate the values from the variables instead of
saving the user-specified values. This allows us to remove some
bookeeping, and also lets the user know what the actual coalesce
settings are.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed by: Shannon Nelson <shannon.nelson@amd.com>
---
(no changes since v2)
Changes in v2:
- New
drivers/net/ethernet/xilinx/xilinx_axienet.h | 8 ---
.../net/ethernet/xilinx/xilinx_axienet_main.c | 70 +++++++++++++------
2 files changed, 47 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 6b8e550c2155..45d8d80dbb1a 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -533,10 +533,6 @@ struct skbuf_dma_descriptor {
* supported, the maximum frame size would be 9k. Else it is
* 1522 bytes (assuming support for basic VLAN)
* @rxmem: Stores rx memory size for jumbo frame handling.
- * @coalesce_count_rx: Store the irq coalesce on RX side.
- * @coalesce_usec_rx: IRQ coalesce delay for RX
- * @coalesce_count_tx: Store the irq coalesce on TX side.
- * @coalesce_usec_tx: IRQ coalesce delay for TX
* @use_dmaengine: flag to check dmaengine framework usage.
* @tx_chan: TX DMA channel.
* @rx_chan: RX DMA channel.
@@ -615,10 +611,6 @@ struct axienet_local {
u32 max_frm_size;
u32 rxmem;
- u32 coalesce_count_rx;
- u32 coalesce_usec_rx;
- u32 coalesce_count_tx;
- u32 coalesce_usec_tx;
u8 use_dmaengine;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index b70e65757454..81224b672eb0 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -223,6 +223,13 @@ static void axienet_dma_bd_release(struct net_device *ndev)
lp->rx_bd_p);
}
+static u64 axienet_dma_rate(struct axienet_local *lp)
+{
+ if (lp->axi_clk)
+ return clk_get_rate(lp->axi_clk);
+ return 125000000; /* arbitrary guess if no clock rate set */
+}
+
/**
* axienet_calc_cr() - Calculate control register value
* @lp: Device private data
@@ -242,12 +249,9 @@ static u32 axienet_calc_cr(struct axienet_local *lp, u32 count, u32 usec)
* the first packet. Otherwise leave at 0 to disable delay interrupt.
*/
if (count > 1) {
- u64 clk_rate = 125000000; /* arbitrary guess if no clock rate set */
+ u64 clk_rate = axienet_dma_rate(lp);
u32 timer;
- if (lp->axi_clk)
- clk_rate = clk_get_rate(lp->axi_clk);
-
/* 1 Timeout Interval = 125 * (clock period of SG clock) */
timer = DIV64_U64_ROUND_CLOSEST((u64)usec * clk_rate,
XAXIDMA_DELAY_SCALE);
@@ -260,6 +264,23 @@ static u32 axienet_calc_cr(struct axienet_local *lp, u32 count, u32 usec)
return cr;
}
+/**
+ * axienet_cr_params() - Extract coalesce parameters from the CR
+ * @lp: Device private data
+ * @cr: The control register to parse
+ * @count: Number of packets before an interrupt
+ * @usec: Idle time (in usec) before an interrupt
+ */
+static void axienet_coalesce_params(struct axienet_local *lp, u32 cr,
+ u32 *count, u32 *usec)
+{
+ u64 clk_rate = axienet_dma_rate(lp);
+ u64 timer = FIELD_GET(XAXIDMA_DELAY_MASK, cr);
+
+ *count = FIELD_GET(XAXIDMA_COALESCE_MASK, cr);
+ *usec = DIV64_U64_ROUND_CLOSEST(timer * XAXIDMA_DELAY_SCALE, clk_rate);
+}
+
/**
* axienet_dma_start - Set up DMA registers and start DMA operation
* @lp: Pointer to the axienet_local structure
@@ -2104,11 +2125,21 @@ axienet_ethtools_get_coalesce(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
struct axienet_local *lp = netdev_priv(ndev);
+ u32 cr;
- ecoalesce->rx_max_coalesced_frames = lp->coalesce_count_rx;
- ecoalesce->rx_coalesce_usecs = lp->coalesce_usec_rx;
- ecoalesce->tx_max_coalesced_frames = lp->coalesce_count_tx;
- ecoalesce->tx_coalesce_usecs = lp->coalesce_usec_tx;
+ spin_lock_irq(&lp->rx_cr_lock);
+ cr = lp->rx_dma_cr;
+ spin_unlock_irq(&lp->rx_cr_lock);
+ axienet_coalesce_params(lp, cr,
+ &ecoalesce->rx_max_coalesced_frames,
+ &ecoalesce->rx_coalesce_usecs);
+
+ spin_lock_irq(&lp->tx_cr_lock);
+ cr = lp->tx_dma_cr;
+ spin_unlock_irq(&lp->tx_cr_lock);
+ axienet_coalesce_params(lp, cr,
+ &ecoalesce->tx_max_coalesced_frames,
+ &ecoalesce->tx_coalesce_usecs);
return 0;
}
@@ -2155,15 +2186,12 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
return -EINVAL;
}
- lp->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames;
- lp->coalesce_usec_rx = ecoalesce->rx_coalesce_usecs;
- lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames;
- lp->coalesce_usec_tx = ecoalesce->tx_coalesce_usecs;
-
- cr = axienet_calc_cr(lp, lp->coalesce_count_rx, lp->coalesce_usec_rx);
+ cr = axienet_calc_cr(lp, ecoalesce->rx_max_coalesced_frames,
+ ecoalesce->rx_coalesce_usecs);
axienet_update_coalesce_rx(lp, cr, ~XAXIDMA_CR_RUNSTOP_MASK);
- cr = axienet_calc_cr(lp, lp->coalesce_count_tx, lp->coalesce_usec_tx);
+ cr = axienet_calc_cr(lp, ecoalesce->tx_max_coalesced_frames,
+ ecoalesce->tx_coalesce_usecs);
axienet_update_coalesce_tx(lp, cr, ~XAXIDMA_CR_RUNSTOP_MASK);
return 0;
}
@@ -2946,14 +2974,10 @@ static int axienet_probe(struct platform_device *pdev)
spin_lock_init(&lp->rx_cr_lock);
spin_lock_init(&lp->tx_cr_lock);
- lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD;
- lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD;
- lp->coalesce_usec_rx = XAXIDMA_DFT_RX_USEC;
- lp->coalesce_usec_tx = XAXIDMA_DFT_TX_USEC;
- lp->rx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_rx,
- lp->coalesce_usec_rx);
- lp->tx_dma_cr = axienet_calc_cr(lp, lp->coalesce_count_tx,
- lp->coalesce_usec_tx);
+ lp->rx_dma_cr = axienet_calc_cr(lp, XAXIDMA_DFT_RX_THRESHOLD,
+ XAXIDMA_DFT_RX_USEC);
+ lp->tx_dma_cr = axienet_calc_cr(lp, XAXIDMA_DFT_TX_THRESHOLD,
+ XAXIDMA_DFT_TX_USEC);
ret = axienet_mdio_setup(lp);
if (ret)
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH net-next v4 6/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
` (4 preceding siblings ...)
2025-01-16 23:29 ` [PATCH net-next v4 5/6] net: xilinx: axienet: Get coalesce parameters from driver state Sean Anderson
@ 2025-01-16 23:29 ` Sean Anderson
2025-01-19 1:19 ` [PATCH net-next v4 0/6] " Jakub Kicinski
2025-01-19 1:50 ` patchwork-bot+netdevbpf
7 siblings, 0 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-16 23:29 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Radhey Shyam Pandey, netdev
Cc: Michal Simek, linux-kernel, Shannon Nelson, linux-arm-kernel,
Sean Anderson, Heng Qi
The default RX IRQ coalescing settings of one IRQ per packet can represent
a significant CPU load. However, increasing the coalescing unilaterally
can result in undesirable latency under low load. Adaptive IRQ
coalescing with DIM offers a way to adjust the coalescing settings based
on load.
This device only supports "CQE" mode [1], where each packet resets the
timer. Therefore, an interrupt is fired either when we receive
coalesce_count_rx packets or when the interface is idle for
coalesce_usec_rx. With this in mind, consider the following scenarios:
Link saturated
Here we want to set coalesce_count_rx to a large value, in order to
coalesce more packets and reduce CPU load. coalesce_usec_rx should
be set to at least the time for one packet. Otherwise the link will
be "idle" and we will get an interrupt for each packet anyway.
Bursts of packets
Each burst should be coalesced into a single interrupt, although it
may be prudent to reduce coalesce_count_rx for better latency.
coalesce_usec_rx should be set to at least the time for one packet
so bursts are coalesced. However, additional time beyond the packet
time will just increase latency at the end of a burst.
Sporadic packets
Due to low load, we can set coalesce_count_rx to 1 in order to
reduce latency to the minimum. coalesce_usec_rx does not matter in
this case.
Based on this analysis, I expected the CQE profiles to look something
like
usec = 0, pkts = 1 // Low load
usec = 16, pkts = 4
usec = 16, pkts = 16
usec = 16, pkts = 64
usec = 16, pkts = 256 // High load
Where usec is set to 16 to be a few us greater than the 12.3 us packet
time of a 1500 MTU packet at 1 GBit/s. However, the CQE profile is
instead
usec = 2, pkts = 256 // Low load
usec = 8, pkts = 128
usec = 16, pkts = 64
usec = 32, pkts = 64
usec = 64, pkts = 64 // High load
I found this very surprising. The number of coalesced packets
*decreases* as load increases. But as load increases we have more
opportunities to coalesce packets without affecting latency as much.
Additionally, the profile *increases* the usec as the load increases.
But as load increases, the gaps between packets will tend to become
smaller, making it possible to *decrease* usec for better latency at the
end of a "burst".
I consider the default CQE profile unsuitable for this NIC. Therefore,
we use the first profile outlined in this commit instead.
coalesce_usec_rx is set to 16 by default, but the user can customize it.
This may be necessary if they are using jumbo frames. I think adjusting
the profile times based on the link speed/mtu would be good improvement
for generic DIM.
In addition to the above profile problems, I noticed the following
additional issues with DIM while testing:
- DIM tends to "wander" when at low load, since the performance gradient
is pretty flat. If you only have 10p/ms anyway then adjusting the
coalescing settings will not affect throughput very much.
- DIM takes a long time to adjust back to low indices when load is
decreased following a period of high load. This is because it only
re-evaluates its settings once every 64 interrupts. However, at low
load 64 interrupts can be several seconds.
Finally: performance. This patch increases receive throughput with
iperf3 from 840 Mbits/sec to 938 Mbits/sec, decreases interrupts from
69920/sec to 316/sec, and decreases CPU utilization (4x Cortex-A53) from
43% to 9%.
[1] Who names this stuff?
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Reviewed by: Shannon Nelson <shannon.nelson@amd.com>
---
Heng, maybe you have some comments on DIM regarding the above?
Changes in v4:
- Fix incorrect function name in doc comment for axienet_coalesce_params
Changes in v3:
- Adjust axienet_local doc comment order to match the members
Changes in v2:
- Don't take the RTNL in axienet_rx_dim_work to avoid deadlock. Instead,
calculate a partial cr update that axienet_update_coalesce_rx can
perform under a spin lock.
- Use READ/WRITE_ONCE when accessing/modifying rx_irqs
drivers/net/ethernet/xilinx/Kconfig | 1 +
drivers/net/ethernet/xilinx/xilinx_axienet.h | 10 ++-
.../net/ethernet/xilinx/xilinx_axienet_main.c | 82 +++++++++++++++++--
3 files changed, 83 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig
index 35d96c633a33..7502214cc7d5 100644
--- a/drivers/net/ethernet/xilinx/Kconfig
+++ b/drivers/net/ethernet/xilinx/Kconfig
@@ -28,6 +28,7 @@ config XILINX_AXI_EMAC
depends on HAS_IOMEM
depends on XILINX_DMA
select PHYLINK
+ select DIMLIB
help
This driver supports the 10/100/1000 Ethernet from Xilinx for the
AXI bus interface used in Xilinx Virtex FPGAs and Soc's.
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 45d8d80dbb1a..5ff742103beb 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -9,6 +9,7 @@
#ifndef XILINX_AXIENET_H
#define XILINX_AXIENET_H
+#include <linux/dim.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
@@ -123,8 +124,7 @@
/* Default TX/RX Threshold and delay timer values for SGDMA mode */
#define XAXIDMA_DFT_TX_THRESHOLD 24
#define XAXIDMA_DFT_TX_USEC 50
-#define XAXIDMA_DFT_RX_THRESHOLD 1
-#define XAXIDMA_DFT_RX_USEC 50
+#define XAXIDMA_DFT_RX_USEC 16
#define XAXIDMA_BD_CTRL_TXSOF_MASK 0x08000000 /* First tx packet */
#define XAXIDMA_BD_CTRL_TXEOF_MASK 0x04000000 /* Last tx packet */
@@ -484,6 +484,9 @@ struct skbuf_dma_descriptor {
* @regs: Base address for the axienet_local device address space
* @dma_regs: Base address for the axidma device address space
* @napi_rx: NAPI RX control structure
+ * @rx_dim: DIM state for the receive queue
+ * @rx_dim_enabled: Whether DIM is enabled or not
+ * @rx_irqs: Number of interrupts
* @rx_cr_lock: Lock protecting @rx_dma_cr, its register, and @rx_dma_started
* @rx_dma_cr: Nominal content of RX DMA control register
* @rx_dma_started: Set when RX DMA is started
@@ -566,6 +569,9 @@ struct axienet_local {
void __iomem *dma_regs;
struct napi_struct napi_rx;
+ struct dim rx_dim;
+ bool rx_dim_enabled;
+ u16 rx_irqs;
spinlock_t rx_cr_lock;
u32 rx_dma_cr;
bool rx_dma_started;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 81224b672eb0..0673b2694e4c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -265,7 +265,7 @@ static u32 axienet_calc_cr(struct axienet_local *lp, u32 count, u32 usec)
}
/**
- * axienet_cr_params() - Extract coalesce parameters from the CR
+ * axienet_coalesce_params() - Extract coalesce parameters from the CR
* @lp: Device private data
* @cr: The control register to parse
* @count: Number of packets before an interrupt
@@ -1283,6 +1283,18 @@ static int axienet_rx_poll(struct napi_struct *napi, int budget)
axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p);
if (packets < budget && napi_complete_done(napi, packets)) {
+ if (READ_ONCE(lp->rx_dim_enabled)) {
+ struct dim_sample sample = {
+ .time = ktime_get(),
+ /* Safe because we are the only writer */
+ .pkt_ctr = u64_stats_read(&lp->rx_packets),
+ .byte_ctr = u64_stats_read(&lp->rx_bytes),
+ .event_ctr = READ_ONCE(lp->rx_irqs),
+ };
+
+ net_dim(&lp->rx_dim, &sample);
+ }
+
/* Re-enable RX completion interrupts. This should
* cause an immediate interrupt if any RX packets are
* already pending.
@@ -1375,6 +1387,7 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
/* Disable further RX completion interrupts and schedule
* NAPI receive.
*/
+ WRITE_ONCE(lp->rx_irqs, READ_ONCE(lp->rx_irqs) + 1);
if (napi_schedule_prep(&lp->napi_rx)) {
u32 cr;
@@ -1676,6 +1689,7 @@ static int axienet_open(struct net_device *ndev)
if (lp->eth_irq > 0)
free_irq(lp->eth_irq, ndev);
err_phy:
+ cancel_work_sync(&lp->rx_dim.work);
cancel_delayed_work_sync(&lp->stats_work);
phylink_stop(lp->phylink);
phylink_disconnect_phy(lp->phylink);
@@ -1705,6 +1719,7 @@ static int axienet_stop(struct net_device *ndev)
napi_disable(&lp->napi_rx);
}
+ cancel_work_sync(&lp->rx_dim.work);
cancel_delayed_work_sync(&lp->stats_work);
phylink_stop(lp->phylink);
@@ -2077,6 +2092,31 @@ static void axienet_update_coalesce_rx(struct axienet_local *lp, u32 cr,
spin_unlock_irq(&lp->rx_cr_lock);
}
+/**
+ * axienet_dim_coalesce_count_rx() - RX coalesce count for DIM
+ * @lp: Device private data
+ */
+static u32 axienet_dim_coalesce_count_rx(struct axienet_local *lp)
+{
+ return min(1 << (lp->rx_dim.profile_ix << 1), 255);
+}
+
+/**
+ * axienet_rx_dim_work() - Adjust RX DIM settings
+ * @work: The work struct
+ */
+static void axienet_rx_dim_work(struct work_struct *work)
+{
+ struct axienet_local *lp =
+ container_of(work, struct axienet_local, rx_dim.work);
+ u32 cr = axienet_calc_cr(lp, axienet_dim_coalesce_count_rx(lp), 0);
+ u32 mask = XAXIDMA_COALESCE_MASK | XAXIDMA_IRQ_IOC_MASK |
+ XAXIDMA_IRQ_ERROR_MASK;
+
+ axienet_update_coalesce_rx(lp, cr, mask);
+ lp->rx_dim.state = DIM_START_MEASURE;
+}
+
/**
* axienet_update_coalesce_tx() - Set TX CR
* @lp: Device private data
@@ -2127,6 +2167,8 @@ axienet_ethtools_get_coalesce(struct net_device *ndev,
struct axienet_local *lp = netdev_priv(ndev);
u32 cr;
+ ecoalesce->use_adaptive_rx_coalesce = lp->rx_dim_enabled;
+
spin_lock_irq(&lp->rx_cr_lock);
cr = lp->rx_dma_cr;
spin_unlock_irq(&lp->rx_cr_lock);
@@ -2163,7 +2205,9 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
struct axienet_local *lp = netdev_priv(ndev);
- u32 cr;
+ bool new_dim = ecoalesce->use_adaptive_rx_coalesce;
+ bool old_dim = lp->rx_dim_enabled;
+ u32 cr, mask = ~XAXIDMA_CR_RUNSTOP_MASK;
if (ecoalesce->rx_max_coalesced_frames > 255 ||
ecoalesce->tx_max_coalesced_frames > 255) {
@@ -2177,7 +2221,7 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
return -EINVAL;
}
- if ((ecoalesce->rx_max_coalesced_frames > 1 &&
+ if (((ecoalesce->rx_max_coalesced_frames > 1 || new_dim) &&
!ecoalesce->rx_coalesce_usecs) ||
(ecoalesce->tx_max_coalesced_frames > 1 &&
!ecoalesce->tx_coalesce_usecs)) {
@@ -2186,9 +2230,27 @@ axienet_ethtools_set_coalesce(struct net_device *ndev,
return -EINVAL;
}
- cr = axienet_calc_cr(lp, ecoalesce->rx_max_coalesced_frames,
- ecoalesce->rx_coalesce_usecs);
- axienet_update_coalesce_rx(lp, cr, ~XAXIDMA_CR_RUNSTOP_MASK);
+ if (new_dim && !old_dim) {
+ cr = axienet_calc_cr(lp, axienet_dim_coalesce_count_rx(lp),
+ ecoalesce->rx_coalesce_usecs);
+ } else if (!new_dim) {
+ if (old_dim) {
+ WRITE_ONCE(lp->rx_dim_enabled, false);
+ napi_synchronize(&lp->napi_rx);
+ flush_work(&lp->rx_dim.work);
+ }
+
+ cr = axienet_calc_cr(lp, ecoalesce->rx_max_coalesced_frames,
+ ecoalesce->rx_coalesce_usecs);
+ } else {
+ /* Dummy value for count just to calculate timer */
+ cr = axienet_calc_cr(lp, 2, ecoalesce->rx_coalesce_usecs);
+ mask = XAXIDMA_DELAY_MASK | XAXIDMA_IRQ_DELAY_MASK;
+ }
+
+ axienet_update_coalesce_rx(lp, cr, mask);
+ if (new_dim && !old_dim)
+ WRITE_ONCE(lp->rx_dim_enabled, true);
cr = axienet_calc_cr(lp, ecoalesce->tx_max_coalesced_frames,
ecoalesce->tx_coalesce_usecs);
@@ -2430,7 +2492,8 @@ axienet_ethtool_get_rmon_stats(struct net_device *dev,
static const struct ethtool_ops axienet_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
- ETHTOOL_COALESCE_USECS,
+ ETHTOOL_COALESCE_USECS |
+ ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
.get_drvinfo = axienet_ethtools_get_drvinfo,
.get_regs_len = axienet_ethtools_get_regs_len,
.get_regs = axienet_ethtools_get_regs,
@@ -2974,7 +3037,10 @@ static int axienet_probe(struct platform_device *pdev)
spin_lock_init(&lp->rx_cr_lock);
spin_lock_init(&lp->tx_cr_lock);
- lp->rx_dma_cr = axienet_calc_cr(lp, XAXIDMA_DFT_RX_THRESHOLD,
+ INIT_WORK(&lp->rx_dim.work, axienet_rx_dim_work);
+ lp->rx_dim_enabled = true;
+ lp->rx_dim.profile_ix = 1;
+ lp->rx_dma_cr = axienet_calc_cr(lp, axienet_dim_coalesce_count_rx(lp),
XAXIDMA_DFT_RX_USEC);
lp->tx_dma_cr = axienet_calc_cr(lp, XAXIDMA_DFT_TX_THRESHOLD,
XAXIDMA_DFT_TX_USEC);
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v4 5/6] net: xilinx: axienet: Get coalesce parameters from driver state
2025-01-16 23:29 ` [PATCH net-next v4 5/6] net: xilinx: axienet: Get coalesce parameters from driver state Sean Anderson
@ 2025-01-19 1:18 ` Jakub Kicinski
0 siblings, 0 replies; 11+ messages in thread
From: Jakub Kicinski @ 2025-01-19 1:18 UTC (permalink / raw)
To: Sean Anderson
Cc: David S . Miller, Eric Dumazet, Paolo Abeni, Radhey Shyam Pandey,
netdev, Michal Simek, linux-kernel, Shannon Nelson,
linux-arm-kernel
On Thu, 16 Jan 2025 18:29:53 -0500 Sean Anderson wrote:
> +/**
> + * axienet_cr_params() - Extract coalesce parameters from the CR
> + * @lp: Device private data
> + * @cr: The control register to parse
> + * @count: Number of packets before an interrupt
> + * @usec: Idle time (in usec) before an interrupt
> + */
> +static void axienet_coalesce_params(struct axienet_local *lp, u32 cr,
> + u32 *count, u32 *usec)
W=1 builds hit:
drivers/net/ethernet/xilinx/xilinx_axienet_main.c:277: warning: expecting prototype for axienet_cr_params(). Prototype was for axienet_coalesce_params() instead
I'll apply the first 2 patches, 2nd one is almost a fix..
The rest may need to wait until after the merge window, unless Linus
tags -rc8.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
` (5 preceding siblings ...)
2025-01-16 23:29 ` [PATCH net-next v4 6/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
@ 2025-01-19 1:19 ` Jakub Kicinski
2025-01-21 15:59 ` Sean Anderson
2025-01-19 1:50 ` patchwork-bot+netdevbpf
7 siblings, 1 reply; 11+ messages in thread
From: Jakub Kicinski @ 2025-01-19 1:19 UTC (permalink / raw)
To: Sean Anderson
Cc: David S . Miller, Eric Dumazet, Paolo Abeni, Radhey Shyam Pandey,
netdev, Michal Simek, linux-kernel, Shannon Nelson,
linux-arm-kernel, Heng Qi
On Thu, 16 Jan 2025 18:29:48 -0500 Sean Anderson wrote:
> - Fix incorrect function name in doc comment for axienet_coalesce_params
Huh, I wonder if you sent the wrong version..
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
` (6 preceding siblings ...)
2025-01-19 1:19 ` [PATCH net-next v4 0/6] " Jakub Kicinski
@ 2025-01-19 1:50 ` patchwork-bot+netdevbpf
7 siblings, 0 replies; 11+ messages in thread
From: patchwork-bot+netdevbpf @ 2025-01-19 1:50 UTC (permalink / raw)
To: Sean Anderson
Cc: davem, edumazet, kuba, pabeni, radhey.shyam.pandey, netdev,
michal.simek, linux-kernel, shannon.nelson, linux-arm-kernel,
hengqi
Hello:
This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Thu, 16 Jan 2025 18:29:48 -0500 you wrote:
> To improve performance without sacrificing latency under low load,
> enable DIM. While I appreciate not having to write the library myself, I
> do think there are many unusual aspects to DIM, as detailed in the last
> patch.
>
> Changes in v4:
> - Fix incorrect function name in doc comment for axienet_coalesce_params
> - Rebase onto net-next/master
>
> [...]
Here is the summary with links:
- [net-next,v4,1/6] net: xilinx: axienet: Add some symbolic constants for IRQ delay timer
https://git.kernel.org/netdev/net-next/c/5cff9d1756fb
- [net-next,v4,2/6] net: xilinx: axienet: Report an error for bad coalesce settings
https://git.kernel.org/netdev/net-next/c/9d301a53a532
- [net-next,v4,3/6] net: xilinx: axienet: Combine CR calculation
(no matching commit)
- [net-next,v4,4/6] net: xilinx: axienet: Support adjusting coalesce settings while running
(no matching commit)
- [net-next,v4,5/6] net: xilinx: axienet: Get coalesce parameters from driver state
(no matching commit)
- [net-next,v4,6/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM
2025-01-19 1:19 ` [PATCH net-next v4 0/6] " Jakub Kicinski
@ 2025-01-21 15:59 ` Sean Anderson
0 siblings, 0 replies; 11+ messages in thread
From: Sean Anderson @ 2025-01-21 15:59 UTC (permalink / raw)
To: Jakub Kicinski
Cc: David S . Miller, Eric Dumazet, Paolo Abeni, Radhey Shyam Pandey,
netdev, Michal Simek, linux-kernel, Shannon Nelson,
linux-arm-kernel, Heng Qi
On 1/18/25 20:19, Jakub Kicinski wrote:
> On Thu, 16 Jan 2025 18:29:48 -0500 Sean Anderson wrote:
>> - Fix incorrect function name in doc comment for axienet_coalesce_params
>
> Huh, I wonder if you sent the wrong version..
I think I applied the hunk to the wrong patch.
--Sean
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-01-21 15:59 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-16 23:29 [PATCH net-next v4 0/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 1/6] net: xilinx: axienet: Add some symbolic constants for IRQ delay timer Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 2/6] net: xilinx: axienet: Report an error for bad coalesce settings Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 3/6] net: xilinx: axienet: Combine CR calculation Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 4/6] net: xilinx: axienet: Support adjusting coalesce settings while running Sean Anderson
2025-01-16 23:29 ` [PATCH net-next v4 5/6] net: xilinx: axienet: Get coalesce parameters from driver state Sean Anderson
2025-01-19 1:18 ` Jakub Kicinski
2025-01-16 23:29 ` [PATCH net-next v4 6/6] net: xilinx: axienet: Enable adaptive IRQ coalescing with DIM Sean Anderson
2025-01-19 1:19 ` [PATCH net-next v4 0/6] " Jakub Kicinski
2025-01-21 15:59 ` Sean Anderson
2025-01-19 1:50 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).