* [PATCHv3 6/6] serial: mxs-auart: terminate DMA before releasing channels in exit
From: Rosen Penev @ 2026-06-11 3:38 UTC (permalink / raw)
To: linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:TTY LAYER AND SERIAL DRIVERS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20260611033856.6476-1-rosenp@gmail.com>
mxs_auart_dma_exit_channel() calls dma_release_channel() and then
kfree() on the DMA buffers without first terminating any in-flight
transfers. If an asynchronous DMA transfer completes after the buffers
have been freed, the callback will access freed memory.
Call dmaengine_terminate_sync() on each channel before releasing it
to safely abort pending transfers.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/tty/serial/mxs-auart.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index fe48a372d022..ec2c60dd0f52 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -872,10 +872,12 @@ static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s)
static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s)
{
if (s->tx_dma_chan) {
+ dmaengine_terminate_sync(s->tx_dma_chan);
dma_release_channel(s->tx_dma_chan);
s->tx_dma_chan = NULL;
}
if (s->rx_dma_chan) {
+ dmaengine_terminate_sync(s->rx_dma_chan);
dma_release_channel(s->rx_dma_chan);
s->rx_dma_chan = NULL;
}
--
2.54.0
^ permalink raw reply related
* [PATCHv3 5/6] serial: mxs-auart: clamp RX DMA count to buffer size
From: Rosen Penev @ 2026-06-11 3:38 UTC (permalink / raw)
To: linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:TTY LAYER AND SERIAL DRIVERS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20260611033856.6476-1-rosenp@gmail.com>
In dma_rx_callback(), the RX count from the hardware status register
(AUART_STAT_RXCOUNT_MASK = 0xffff) is passed directly to
tty_insert_flip_string() without any bounds check. Since rx_dma_buf
is allocated with UART_XMIT_SIZE (4096 bytes), a hardware fault or
compromised peripheral reporting a count larger than 4096 would cause
an out-of-bounds read, potentially leaking kernel memory.
Clamp the count to UART_XMIT_SIZE before use.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/tty/serial/mxs-auart.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index e2b656638ab3..fe48a372d022 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -823,7 +823,7 @@ static void dma_rx_callback(void *arg)
stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR |
AUART_STAT_PERR | AUART_STAT_FERR);
- count = stat & AUART_STAT_RXCOUNT_MASK;
+ count = min_t(u32, stat & AUART_STAT_RXCOUNT_MASK, UART_XMIT_SIZE);
tty_insert_flip_string(port, s->rx_dma_buf, count);
mxs_write(stat, s, REG_STAT);
--
2.54.0
^ permalink raw reply related
* [PATCHv3 3/6] serial: mxs-auart: use devm resources for iomem and GPIO IRQs
From: Rosen Penev @ 2026-06-11 3:38 UTC (permalink / raw)
To: linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:TTY LAYER AND SERIAL DRIVERS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20260611033856.6476-1-rosenp@gmail.com>
Replace platform_get_resource + ioremap with
devm_platform_get_and_ioremap_resource and convert GPIO IRQ
request_irq/free_irq to devm_request_irq. This eliminates the
mxs_auart_free_gpio_irq function and its call sites, and the
out_iounmap error label. Simplify the remove function accordingly.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/tty/serial/mxs-auart.c | 53 +++++++---------------------------
1 file changed, 11 insertions(+), 42 deletions(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index aa59a48bfad7..4499e3206e85 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1517,15 +1517,6 @@ static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
return 0;
}
-static void mxs_auart_free_gpio_irq(struct mxs_auart_port *s)
-{
- enum mctrl_gpio_idx i;
-
- for (i = 0; i < UART_GPIO_MAX; i++)
- if (s->gpio_irq[i] >= 0)
- free_irq(s->gpio_irq[i], s);
-}
-
static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s)
{
int *irq = s->gpio_irq;
@@ -1537,21 +1528,13 @@ static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s)
continue;
irq_set_status_flags(irq[i], IRQ_NOAUTOEN);
- err = request_irq(irq[i], mxs_auart_irq_handle,
- IRQ_TYPE_EDGE_BOTH, dev_name(s->dev), s);
+ err = devm_request_irq(s->dev, irq[i], mxs_auart_irq_handle,
+ IRQ_TYPE_EDGE_BOTH, dev_name(s->dev), s);
if (err)
dev_err(s->dev, "%s - Can't get %d irq\n",
__func__, irq[i]);
}
- /*
- * If something went wrong, rollback.
- * Be careful: i may be unsigned.
- */
- while (err && (i-- > 0))
- if (irq[i] >= 0)
- free_irq(irq[i], s);
-
return err;
}
@@ -1596,18 +1579,12 @@ static int mxs_auart_probe(struct platform_device *pdev)
if (ret)
return ret;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- ret = -ENXIO;
+ s->port.membase = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
+ if (IS_ERR(s->port.membase)) {
+ ret = PTR_ERR(s->port.membase);
goto out_disable_clk;
}
-
s->port.mapbase = r->start;
- s->port.membase = ioremap(r->start, resource_size(r));
- if (!s->port.membase) {
- ret = -ENOMEM;
- goto out_disable_clk;
- }
s->port.ops = &mxs_auart_ops;
s->port.iotype = UPIO_MEM;
s->port.fifosize = MXS_AUART_FIFO_SIZE;
@@ -1622,21 +1599,21 @@ static int mxs_auart_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
- goto out_iounmap;
+ goto out_disable_clk;
}
s->port.irq = irq;
ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
dev_name(&pdev->dev), s);
if (ret)
- goto out_iounmap;
+ goto out_disable_clk;
platform_set_drvdata(pdev, s);
ret = mxs_auart_init_gpios(s, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize GPIOs.\n");
- goto out_iounmap;
+ goto out_disable_clk;
}
/*
@@ -1644,7 +1621,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
*/
ret = mxs_auart_request_gpio_irq(s);
if (ret)
- goto out_iounmap;
+ goto out_disable_clk;
auart_port[s->port.line] = s;
@@ -1667,11 +1644,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
return 0;
out_free_qpio_irq:
- mxs_auart_free_gpio_irq(s);
- auart_port[pdev->id] = NULL;
-
-out_iounmap:
- iounmap(s->port.membase);
+ auart_port[s->port.line] = NULL;
out_disable_clk:
clk_disable_unprepare(s->clk);
@@ -1683,11 +1656,7 @@ static void mxs_auart_remove(struct platform_device *pdev)
struct mxs_auart_port *s = platform_get_drvdata(pdev);
uart_remove_one_port(&auart_driver, &s->port);
- auart_port[pdev->id] = NULL;
- mxs_auart_free_gpio_irq(s);
- iounmap(s->port.membase);
- if (is_asm9260_auart(s))
- clk_disable_unprepare(s->clk);
+ auart_port[s->port.line] = NULL;
}
static struct platform_driver mxs_auart_driver = {
--
2.54.0
^ permalink raw reply related
* [PATCHv3 1/6] serial: mxs-auart: fix cast type for of_device_get_match_data
From: Rosen Penev @ 2026-06-11 3:38 UTC (permalink / raw)
To: linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:TTY LAYER AND SERIAL DRIVERS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20260611033856.6476-1-rosenp@gmail.com>
of_device_get_match_data returns const void*. Cast to unsigned long to
avoid implicit integer truncation warnings. All the data parameters are
correct anyway.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/tty/serial/mxs-auart.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 697318dbb146..de97c0f74e7d 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1598,7 +1598,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
return -EINVAL;
}
- s->devtype = (enum mxs_auart_type)of_device_get_match_data(&pdev->dev);
+ s->devtype = (unsigned long)of_device_get_match_data(&pdev->dev);
ret = mxs_get_clks(s, pdev);
if (ret)
--
2.54.0
^ permalink raw reply related
* [PATCHv3 2/6] serial: mxs-auart: rework clock handling in mxs_get_clks and probe
From: Rosen Penev @ 2026-06-11 3:38 UTC (permalink / raw)
To: linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:TTY LAYER AND SERIAL DRIVERS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20260611033856.6476-1-rosenp@gmail.com>
Use devm_clk_get_enabled for the AHB clock so its enable/disable
lifetime is managed by the driver model. Move the mod clock
(clk) prepare_enable out of mxs_get_clks and into probe so that
clk_set_rate is called while the clock is still disabled, avoiding
CLK_SET_RATE_GATE failures. Clean up the error labels accordingly.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/tty/serial/mxs-auart.c | 47 ++++++++++++----------------------
1 file changed, 17 insertions(+), 30 deletions(-)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index de97c0f74e7d..aa59a48bfad7 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1470,34 +1470,22 @@ static int mxs_get_clks(struct mxs_auart_port *s,
return PTR_ERR(s->clk);
}
- s->clk_ahb = devm_clk_get(s->dev, "ahb");
+ s->clk_ahb = devm_clk_get_enabled(s->dev, "ahb");
if (IS_ERR(s->clk_ahb)) {
dev_err(s->dev, "Failed to get \"ahb\" clk\n");
return PTR_ERR(s->clk_ahb);
}
- err = clk_prepare_enable(s->clk_ahb);
- if (err) {
- dev_err(s->dev, "Failed to enable ahb_clk!\n");
- return err;
- }
-
+ /*
+ * Set mod clock rate while it is still disabled so
+ * CLK_SET_RATE_GATE does not cause clk_set_rate to fail.
+ * The mod clock will be enabled in mxs_auart_startup()
+ * and in probe after mxs_get_clks returns.
+ */
err = clk_set_rate(s->clk, clk_get_rate(s->clk_ahb));
- if (err) {
+ if (err)
dev_err(s->dev, "Failed to set rate!\n");
- goto disable_clk_ahb;
- }
- err = clk_prepare_enable(s->clk);
- if (err) {
- dev_err(s->dev, "Failed to enable clk!\n");
- goto disable_clk_ahb;
- }
-
- return 0;
-
-disable_clk_ahb:
- clk_disable_unprepare(s->clk_ahb);
return err;
}
@@ -1604,17 +1592,21 @@ static int mxs_auart_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = clk_prepare_enable(s->clk);
+ if (ret)
+ return ret;
+
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
ret = -ENXIO;
- goto out_disable_clks;
+ goto out_disable_clk;
}
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
if (!s->port.membase) {
ret = -ENOMEM;
- goto out_disable_clks;
+ goto out_disable_clk;
}
s->port.ops = &mxs_auart_ops;
s->port.iotype = UPIO_MEM;
@@ -1681,11 +1673,8 @@ static int mxs_auart_probe(struct platform_device *pdev)
out_iounmap:
iounmap(s->port.membase);
-out_disable_clks:
- if (is_asm9260_auart(s)) {
- clk_disable_unprepare(s->clk);
- clk_disable_unprepare(s->clk_ahb);
- }
+out_disable_clk:
+ clk_disable_unprepare(s->clk);
return ret;
}
@@ -1697,10 +1686,8 @@ static void mxs_auart_remove(struct platform_device *pdev)
auart_port[pdev->id] = NULL;
mxs_auart_free_gpio_irq(s);
iounmap(s->port.membase);
- if (is_asm9260_auart(s)) {
+ if (is_asm9260_auart(s))
clk_disable_unprepare(s->clk);
- clk_disable_unprepare(s->clk_ahb);
- }
}
static struct platform_driver mxs_auart_driver = {
--
2.54.0
^ permalink raw reply related
* [PATCHv3 0/6] serial: mxs-auart: devm conversion, clock rework, and IRQ ordering fixes
From: Rosen Penev @ 2026-06-11 3:38 UTC (permalink / raw)
To: linux-serial
Cc: Greg Kroah-Hartman, Jiri Slaby, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam,
open list:TTY LAYER AND SERIAL DRIVERS,
open list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
This series cleans up the mxs-auart driver by converting to devm-managed
resources, fixing clock prepare/enable ordering, and addressing IRQ
registration races.
Patch 1 fixes compilation on 64-bit build with W=1
Patch 2 reworks the clock handling to use devm_clk_get_enabled and
reorders clk_prepare_enable after clk_set_rate to avoid
CLK_SET_RATE_GATE failures.
Patch 3 converts iomem mapping and GPIO IRQ requests to devm,
removing the manual cleanup paths.
Patch 4 moves the main UART IRQ registration after uart_add_one_port
so the port state is initialized before the handler can run, and
manages the module clock for console vs non-console ports correctly.
v3: two more sashiko fixes
v2: split off of_device_get_match_data change.
Rosen Penev (6):
serial: mxs-auart: fix cast type for of_device_get_match_data
serial: mxs-auart: rework clock handling in mxs_get_clks and probe
serial: mxs-auart: use devm resources for iomem and GPIO IRQs
serial: mxs-auart: fix IRQ registration ordering and manage console
clock
serial: mxs-auart: clamp RX DMA count to buffer size
serial: mxs-auart: terminate DMA before releasing channels in exit
drivers/tty/serial/mxs-auart.c | 145 +++++++++++++++------------------
1 file changed, 66 insertions(+), 79 deletions(-)
--
2.54.0
^ permalink raw reply
* [GIT PULL]arm64: BST C1200 eMMC defconfig for v7.2
From: gordon.ge @ 2026-06-11 3:25 UTC (permalink / raw)
To: soc; +Cc: arnd, yangzh0906, linux-arm-kernel, bst-upstream
Hi Arnd:
Thank you for the review.
I have fixed the two issues you mentioned and updated the pull request.
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
git@github.com:BlackSesame-SoC/linux.git tags/bst-arm64-emmc-defconfig-for-v7.2
for you to fetch changes up to 6e2453ddd971f598290da40a0bcc8cf5c11f50c7:
Enable CONFIG_MMC_SDHCI_BST to support eMMC on Black Sesame Technologies C1200 boards. (2026-06-11 10:42:11 +0800)
----------------------------------------------------------------
arm64: BST C1200 eMMC defconfig for v7.2
Black Sesame Technologies:
Enable eMMC controller on BST C1200 CDCU1.0 board:
- Enable CONFIG_MMC_SDHCI_BST=y in arm64 defconfig
The MMC driver was merged via mmc-next in v7.1-rc1.
This is the remaining defconfig piece.
Signed-off-by: Gordon Ge <gordon.ge@bst.ai>
----------------------------------------------------------------
Gordon Ge (1):
Enable CONFIG_MMC_SDHCI_BST to support eMMC on Black Sesame Technologies C1200 boards.
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+)
^ permalink raw reply
* [GIT PULL]arm64: BST C1200 eMMC DTS for v7.2
From: gordon.ge @ 2026-06-11 3:21 UTC (permalink / raw)
To: soc; +Cc: arnd, yangzh0906, linux-arm-kernel, bst-upstream
Hi Arnd:
Thank you for the review.
I have fixed the two issues you mentioned and updated the pull request.
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
git@github.com:BlackSesame-SoC/linux.git tags/bst-arm64-emmc-dts-for-v7.2
for you to fetch changes up to 6e3b49b69038ac794414ed0731634e2677b834b8:
Add eMMC controller support to the BST C1200 device tree: (2026-06-11 10:37:12 +0800)
----------------------------------------------------------------
arm64: BST C1200 eMMC DTS for v7.2
Black Sesame Technologies:
Enable eMMC controller on BST C1200 CDCU1.0 board:
- Add mmc0 node in bstc1200.dtsi (DWCMSHC SDHCI controller)
- Add fixed clock definition and reserved SRAM bounce buffer
- Enable mmc0 with 8-bit bus on CDCU1.0 ADAS 4C2G board
The MMC driver was merged via mmc-next in v7.1-rc1.
this is the remaining DTS piece.
Signed-off-by: Gordon Ge <gordon.ge@bst.ai>
----------------------------------------------------------------
Gordon Ge (1):
Add eMMC controller support to the BST C1200 device tree:
.../arm64/boot/dts/bst/bstc1200-cdcu1.0-adas_4c2g.dts | 19 +++++++++++++++++++
arch/arm64/boot/dts/bst/bstc1200.dtsi | 18 ++++++++++++++++++
2 files changed, 37 insertions(+)
^ permalink raw reply
* Re: [PATCH v3 00/11] kdump: reduce vmcore size and capture time
From: Wandun @ 2026-06-11 3:09 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, loongarch, linux-riscv,
devicetree, kexec, iommu, zhaomeijing, Rob Herring, saravanak,
bhe, rppt, pjw, palmer, aou, chenhuacai, kernel
Cc: catalin.marinas, will, alex, akpm, pasha.tatashin, pratyush,
ruirui.yang, m.szyprowski, robin.murphy
In-Reply-To: <7ed207fa-4c86-426a-8570-495902ce04c3@gmail.com>
On 6/11/26 10:09, Wandun wrote:
>
>
> On 5/27/26 11:29, Wandun Chen wrote:
>> From: Wandun Chen <chenwandun@lixiang.com>
>>
>> On SoCs that carve out large firmware-owned reserved memory (GPU
>> firmware, DSP, modem, camera ISP, NPU, ...), kdump currently dumps
>> those carveouts as part of system RAM even though their contents are
>> firmware state that is not useful for kernel crash analysis.
>>
>> This series introduces an opt-in 'dumpable' flag [1] on struct
>> reserved_mem and uses it to filter the elfcorehdr PT_LOAD ranges on
>> DT-based architectures (arm64, riscv, loongarch). By default reserved
>> regions are treated as non-dumpable; CMA regions are explicitly opted
>> in because their pages are returned to the buddy allocator and may
>> carry key crash-analysis data.
>>
>> The series is organized as follows:
>> Patches 1-3: Pre-existing fixes and a small prep change.
>> Patches 4-5: Restructure to allow appending /memreserve/ entries.
>> Patches 6-7: Add a dumpable flag and append /memreserve/ entries.
>> Patch 8: Add generic kdump helpers.
>> Patches 9-11: Wire the helpers into arm64, riscv and loongarch kdump
>> elfcorehdr preparation.
> Hi,
>
> Gentle ping on this series.
>
> Status summary:
> -patch 03: respun separately per Rob's suggestion, picked up for 7.2
> -patch 06: Acked-by: Marek Szyprowski -patch 09: Acked-by: Will Deacon
> The remaining patches (01, 02, 04, 05, 07, 08, 10, 11) are still
> awaiting review. your feedback would be greately appreciated. I know we
> are at the end of 7.1 -rc cycle, I don't want to rush this series, just
> collecting more feedback, and will send next version based on 7.2-rc1.
> If spliting the series into smaller logical group would make review
> easier, please let me know. Best regards, Wandun
Apologies for the formatting issue in my previous email.
Here is the properly formatted version.
Gentle ping on this series.
Status summary:
- patch 03: respun separately per Rob's suggestion, picked up for 7.2
- patch 06: Acked-by: Marek Szyprowski
- patch 09: Acked-by: Will Deacon
The remaining patches (01, 02, 04, 05, 07, 08, 10, 11) are still
awaiting review. Your feedback would be greatly appreciated.
I know we are at the end of 7.1-rc cycle, I don't want to rush this
series, just collecting more feedback, and will send next version based
on 7.2-rc1.
If splitting the series into smaller logical groups would make review
easier, please let me know.
Best regards,
Wandun
>>
>> v2 --> v3:
>> 1. Fix out-of-bounds issue if device tree lacks /reserved-memory node.[2]
>> 2. Fix UAF issue when alloc_reserved_mem_array() fails.
>> 3. Add some prepare patches.
>>
>> v1 --> v2:
>> 1. v1 added an opt-out DT property ('linux,no-dump'). Per Rob's
>> feedback [1], v2 drop that property and exclude reserve memory
>> by default.
>> 2. Split some prepared patches from the original patches.
>> 3. Address coding-style comments on patch 5 from Rob.
>>
>> [1] https://lore.kernel.org/lkml/20260506144542.GA2072596-
>> robh@kernel.org/
>> [2] https://sashiko.dev/#/patchset/20260520091844.592753-1-
>> chenwandun%40lixiang.com?part=4
>>
>> Wandun Chen (11):
>> of: reserved_mem: handle NULL name in of_reserved_mem_lookup()
>> kexec/crash: provide crash_exclude_mem_range() stub when
>> CONFIG_CRASH_DUMP=n
>> of: reserved_mem: avoid post-init UAF when alloc_reserved_mem_array()
>> fails
>> of: reserved_mem: zero total_reserved_mem_cnt if no valid
>> /reserved-memory entry
>> of: reserved_mem: split alloc_reserved_mem_array() from
>> fdt_scan_reserved_mem_late()
>> of: reserved_mem: add dumpable flag to opt-in vmcore
>> of: reserved_mem: save /memreserve/ entries into the reserved_mem
>> array
>> of: reserved_mem: add kdump helpers to exclude non-dumpable regions
>> arm64: kdump: exclude non-dumpable reserved memory regions from vmcore
>> riscv: kdump: exclude non-dumpable reserved memory regions from vmcore
>> loongarch: kdump: exclude non-dumpable reserved memory regions from
>> vmcore
>>
>> arch/arm64/kernel/machine_kexec_file.c | 6 ++
>> arch/loongarch/kernel/machine_kexec_file.c | 6 ++
>> arch/riscv/kernel/machine_kexec_file.c | 4 +
>> drivers/of/fdt.c | 11 +-
>> drivers/of/of_private.h | 3 +
>> drivers/of/of_reserved_mem.c | 117 +++++++++++++++++++--
>> include/linux/crash_core.h | 6 ++
>> include/linux/of_reserved_mem.h | 15 +++
>> kernel/dma/contiguous.c | 1 +
>> 9 files changed, 157 insertions(+), 12 deletions(-)
>>
>
^ permalink raw reply
* RE: [PATCH 0/4] Xilinx TRNG fix and simplification
From: Jain, Harsh (AECG-SSW) @ 2026-06-11 2:52 UTC (permalink / raw)
To: Eric Biggers, linux-crypto@vger.kernel.org, Herbert Xu
Cc: linux-kernel@vger.kernel.org, Botcha, Mounika, Olivia Mackall,
Simek, Michal, linux-arm-kernel@lists.infradead.org,
Savitala, Sarat Chand, Dhanawade, Mohan
In-Reply-To: <20260531191738.55843-1-ebiggers@kernel.org>
AMD General
Acked-by: Harsh Jain <h.jain@amd.com>
> -----Original Message-----
> From: Eric Biggers <ebiggers@kernel.org>
> Sent: Monday, June 1, 2026 12:48 AM
> To: linux-crypto@vger.kernel.org; Herbert Xu <herbert@gondor.apana.org.au>
> Cc: linux-kernel@vger.kernel.org; Botcha, Mounika <Mounika.Botcha@amd.com>;
> Jain, Harsh (AECG-SSW) <h.jain@amd.com>; Olivia Mackall
> <olivia@selenic.com>; Simek, Michal <michal.simek@amd.com>; linux-arm-
> kernel@lists.infradead.org; Eric Biggers <ebiggers@kernel.org>
> Subject: [PATCH 0/4] Xilinx TRNG fix and simplification
>
>
>
> This series fixes and greatly simplifies the Xilinx TRNG driver by:
>
> - Removing the gratuitous crypto_rng interface, leaving just hwrng which
> is the one that actually matters.
>
> - Replacing the really complicated AES based entropy extraction
> algorithm with a much simpler one.
>
> Note that this mirrors similar changes in other drivers.
>
> Eric Biggers (4):
> crypto: xilinx-trng - Remove crypto_rng interface
> crypto: xilinx-trng - Fix return value of xtrng_hwrng_trng_read()
> crypto: xilinx-trng - Replace crypto_drbg_ctr_df() with HMAC-SHA512
> hwrng: xilinx - Move xilinx-rng into drivers/char/hw_random/
>
> MAINTAINERS | 2 +-
> arch/arm64/configs/defconfig | 2 +-
> crypto/Kconfig | 5 -
> crypto/Makefile | 2 -
> crypto/df_sp80090a.c | 222 ------------------
> drivers/char/hw_random/Kconfig | 11 +
> drivers/char/hw_random/Makefile | 1 +
> .../xilinx => char/hw_random}/xilinx-trng.c | 134 ++---------
> drivers/crypto/Kconfig | 13 -
> drivers/crypto/xilinx/Makefile | 1 -
> include/crypto/df_sp80090a.h | 53 -----
> 11 files changed, 37 insertions(+), 409 deletions(-)
> delete mode 100644 crypto/df_sp80090a.c
> rename drivers/{crypto/xilinx => char/hw_random}/xilinx-trng.c (75%)
> delete mode 100644 include/crypto/df_sp80090a.h
>
>
> base-commit: 5624ea54f3ba5c83d2e5503411a31a8be0278c1e
> prerequisite-patch-id: 07e982b663ac3f8312ca524f6b91b5b38661df5e
> prerequisite-patch-id: 72064361a8f36e015ab0b7e1fa4d364b40d90506
> prerequisite-patch-id: 8978b8e0db7f47935e5f6f0aff14a97f55d3073c
> prerequisite-patch-id: 6aa0e3e93a008279d71e535a3d0cf48643f55e19
> --
> 2.54.0
^ permalink raw reply
* RE: [PATCH v2 1/2] PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95
From: Hongxing Zhu @ 2026-06-11 2:51 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Frank Li, l.stach@pengutronix.de, lpieralisi@kernel.org,
kwilczynski@kernel.org, robh@kernel.org, bhelgaas@google.com,
s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
imx@lists.linux.dev, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
In-Reply-To: <xwupi3bxiihnmddqkdc6xsixkbirdpnips6qy5n4xchtcysnfq@f6pa5usw2a4f>
> -----Original Message-----
> From: Manivannan Sadhasivam <mani@kernel.org>
> Sent: Tuesday, June 9, 2026 11:48 PM
> To: Hongxing Zhu <hongxing.zhu@nxp.com>
> Cc: Frank Li <frank.li@nxp.com>; l.stach@pengutronix.de; lpieralisi@kernel.org;
> kwilczynski@kernel.org; robh@kernel.org; bhelgaas@google.com;
> s.hauer@pengutronix.de; kernel@pengutronix.de; festevam@gmail.com; linux-
> pci@vger.kernel.org; linux-arm-kernel@lists.infradead.org; imx@lists.linux.dev;
> linux-kernel@vger.kernel.org; stable@vger.kernel.org
> Subject: Re: [PATCH v2 1/2] PCI: imx6: Configure REF_USE_PAD before PHY reset
> for i.MX95
>
> On Mon, May 18, 2026 at 03:27:14PM +0800, Richard Zhu wrote:
> > According to the i.MX95 PCIe PHY Databook, the ref_use_pad signal in
> > the Common Block Signals section selects the reference clock source
> > connected to the PHY pads. Per the specification, any change to this
> > input must be followed by a PHY reset assertion to take effect.
> >
> > Move the REF_USE_PAD configuration before the PHY reset toggle to
> > comply with the required initialization sequence.
> >
> > Fixes: 47f54a902dcd ("PCI: imx6: Toggle the core reset for i.MX95
> > PCIe")
> > Cc: <stable@vger.kernel.org>
> > Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> > Reviewed-by: Frank Li <Frank.Li@nxp.com>
> > ---
> > drivers/pci/controller/dwc/pci-imx6.c | 27
> > ++++++++++++++++++++++++---
> > 1 file changed, 24 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> > b/drivers/pci/controller/dwc/pci-imx6.c
> > index 002e0a0d9382..66e760015c92 100644
> > --- a/drivers/pci/controller/dwc/pci-imx6.c
> > +++ b/drivers/pci/controller/dwc/pci-imx6.c
> > @@ -138,6 +138,7 @@ struct imx_pcie_drvdata {
> > const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
> > const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
> > const struct pci_epc_features *epc_features;
> > + int (*init_pre_reset)(struct imx_pcie *pcie);
>
> I renamed the callback and helper while applying:
>
> s/init_pre_reset/select_ref_clk_src
Thanks for your kindly help.
Best Regards
Richard Zhu
>
> - Mani
>
> > int (*init_phy)(struct imx_pcie *pcie);
> > int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
> > int (*core_reset)(struct imx_pcie *pcie, bool assert); @@ -249,6
> > +250,24 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie
> *imx_pcie)
> > return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 :
> IOMUXC_GPR14;
> > }
> >
> > +static int imx95_pcie_init_pre_reset(struct imx_pcie *imx_pcie) {
> > + bool ext = imx_pcie->enable_ext_refclk;
> > +
> > + /*
> > + * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is
> > + * used to select reference clock connected to a pair of pads.
> > + *
> > + * Any change in this input must be followed by phy_reset assertion.
> > + */
> > +
> > + regmap_update_bits(imx_pcie->iomuxc_gpr,
> IMX95_PCIE_PHY_GEN_CTRL,
> > + IMX95_PCIE_REF_USE_PAD,
> > + ext ? IMX95_PCIE_REF_USE_PAD : 0);
> > +
> > + return 0;
> > +}
> > +
> > static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) {
> > bool ext = imx_pcie->enable_ext_refclk; @@ -271,9 +290,6 @@ static
> > int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
> > IMX95_PCIE_PHY_CR_PARA_SEL,
> > IMX95_PCIE_PHY_CR_PARA_SEL);
> >
> > - regmap_update_bits(imx_pcie->iomuxc_gpr,
> IMX95_PCIE_PHY_GEN_CTRL,
> > - IMX95_PCIE_REF_USE_PAD,
> > - ext ? IMX95_PCIE_REF_USE_PAD : 0);
> > regmap_update_bits(imx_pcie->iomuxc_gpr,
> IMX95_PCIE_SS_RW_REG_0,
> > IMX95_PCIE_REF_CLKEN,
> > ext ? 0 : IMX95_PCIE_REF_CLKEN); @@ -1348,6
> +1364,9 @@ static
> > int imx_pcie_host_init(struct dw_pcie_rp *pp)
> > pp->bridge->disable_device = imx_pcie_disable_device;
> > }
> >
> > + if (imx_pcie->drvdata->init_pre_reset)
> > + imx_pcie->drvdata->init_pre_reset(imx_pcie);
> > +
> > imx_pcie_assert_core_reset(imx_pcie);
> >
> > if (imx_pcie->drvdata->init_phy)
> > @@ -2047,6 +2066,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
> > .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
> > .core_reset = imx95_pcie_core_reset,
> > .init_phy = imx95_pcie_init_phy,
> > + .init_pre_reset = imx95_pcie_init_pre_reset,
> > .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
> > .enable_ref_clk = imx95_pcie_enable_ref_clk,
> > .clr_clkreq_override = imx95_pcie_clr_clkreq_override, @@ -
> 2102,6
> > +2122,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
> > .ltssm_mask = IMX95_PCIE_LTSSM_EN,
> > .mode_off[0] = IMX95_PE0_GEN_CTRL_1,
> > .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
> > + .init_pre_reset = imx95_pcie_init_pre_reset,
> > .init_phy = imx95_pcie_init_phy,
> > .core_reset = imx95_pcie_core_reset,
> > .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
> >
> > base-commit: 40b7f61a1a4d7fd18188f3f87e15ff5a90ce1d31
> > --
> > 2.37.1
> >
>
> --
> மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v5] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
From: Andrew Jeffery @ 2026-06-11 2:50 UTC (permalink / raw)
To: Karthikeyan KS
Cc: joel, andrew, Kees Cook, linux-arm-kernel, linux-aspeed,
linux-kernel, linux-hardening, stable
In-Reply-To: <20260610172310.163321-1-karthiproffesional@gmail.com>
Hi Karthikeyan,
On Wed, 2026-06-10 at 17:23 +0000, Karthikeyan KS wrote:
> put_fifo_with_discard() acts as both producer and consumer on the kfifo:
> it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from
> the IRQ handler without synchronizing with snoop_file_read(), which also
> consumes via kfifo_to_user(). On SMP systems this concurrent access can
> leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp
> to (in - out) is ineffective and kfifo_copy_to_user() can attempt a
> copy_to_user() past the kmalloc-2k backing store:
>
> usercopy: Kernel memory exposure attempt detected from SLUB object
> 'kmalloc-2k' (offset 0, size 2049)!
> kernel BUG at mm/usercopy.c!
> Call trace:
> usercopy_abort
> __check_heap_object
> __check_object_size
> kfifo_copy_to_user
> __kfifo_to_user
> snoop_file_read
> vfs_read
>
> Serialize kfifo access with a per-channel spinlock. The reader drains
> into a bounce buffer under the lock with kfifo_out_spinlocked() and then
> copies to userspace after dropping it, since copy_to_user() may sleep on
> a page fault.
>
> Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev")
> Cc: stable@vger.kernel.org
> Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
> ---
> Andrew,
>
> Thanks for the review.
>
> Changes since v4:
> - Use __free(kfree) for automatic cleanup
> - Allocate clamped count instead of full SNOOP_FIFO_SIZE
> - Use kfifo_out_spinlocked() in snoop_file_read
> - Use scoped_guard(spinlock) in put_fifo_with_discard
>
> drivers/soc/aspeed/aspeed-lpc-snoop.c | 25 +++++++++++++++++++------
> 1 file changed, 19 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> index b03310c0830d..c9c87a794228 100644
> --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
> +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
> @@ -11,6 +11,7 @@
> */
>
> #include <linux/bitops.h>
> +#include <linux/cleanup.h>
> #include <linux/clk.h>
> #include <linux/dev_printk.h>
> #include <linux/interrupt.h>
> @@ -74,6 +75,7 @@ struct aspeed_lpc_snoop_channel_cfg {
> struct aspeed_lpc_snoop_channel {
> const struct aspeed_lpc_snoop_channel_cfg *cfg;
> bool enabled;
> + spinlock_t lock; /* serialises @fifo: irq producer vs reader */
I'd prefer we avoid trailing comments, which it seems you've added this
time around. Since you did that ...
> struct kfifo fifo;
... in this specific case we can improve on the comment, with:
struct kfifo fifo __guarded_by(&lock);
More details here:
https://docs.kernel.org/dev-tools/context-analysis.html
Adding a change along these lines currently produces:
../drivers/soc/aspeed/aspeed-lpc-snoop.c:164:32: warning: reading variable 'fifo' requires holding spinlock '&aspeed_lpc_snoop_channel::lock' [-Wthread-safety-analysis]
164 | if (!kfifo_initialized(&chan->fifo))
| ^
I ended up applying this on top of your patch:
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b35d74592964..9cba7be8c395 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -4,3 +4,5 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+
+CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index 9165a543a250..7fa1a345acac 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -75,8 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
- spinlock_t lock; /* serialises @fifo: irq producer vs reader */
- struct kfifo fifo;
+ spinlock_t lock;
+ struct kfifo fifo __guarded_by(&lock);
wait_queue_head_t wq;
struct miscdevice miscdev;
};
@@ -161,9 +161,9 @@ static const struct file_operations snoop_fops = {
/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
- if (!kfifo_initialized(&chan->fifo))
- return;
scoped_guard(spinlock, &chan->lock) {
+ if (!kfifo_initialized(&chan->fifo))
+ return;
if (kfifo_is_full(&chan->fifo))
kfifo_skip(&chan->fifo);
kfifo_put(&chan->fifo, val);
@@ -240,7 +240,6 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
return -EBUSY;
init_waitqueue_head(&channel->wq);
- spin_lock_init(&channel->lock);
channel->cfg = cfg;
channel->miscdev.minor = MISC_DYNAMIC_MINOR;
@@ -252,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
if (!channel->miscdev.name)
return -ENOMEM;
- rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
- if (rc)
- return rc;
+ scoped_guard(spinlock_init, &channel->lock) {
+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+ }
rc = misc_register(&channel->miscdev);
if (rc)
I prefer that we add the annotation as the compiler analysis provides
some comfort in contrast to the comment.
Otherwise, the rest of the fix seems okay to me.
Andrew
> wait_queue_head_t wq;
> struct miscdevice miscdev;
> @@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> size_t count, loff_t *ppos)
> {
> struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
> + u8 *buf __free(kfree) = NULL;
> unsigned int copied;
> int ret = 0;
>
> @@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
> if (ret == -ERESTARTSYS)
> return -EINTR;
> }
> - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
> - if (ret)
> - return ret;
> +
> + count = min_t(size_t, count, SNOOP_FIFO_SIZE);
> +
> + buf = kmalloc(count, GFP_KERNEL);
> + if (!buf)
> + return -ENOMEM;
> +
> + copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock);
> + if (copied && copy_to_user(buffer, buf, copied))
> + return -EFAULT;
>
> return copied;
> }
> @@ -153,9 +163,11 @@ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
> {
> if (!kfifo_initialized(&chan->fifo))
> return;
> - if (kfifo_is_full(&chan->fifo))
> - kfifo_skip(&chan->fifo);
> - kfifo_put(&chan->fifo, val);
> + scoped_guard(spinlock, &chan->lock) {
> + if (kfifo_is_full(&chan->fifo))
> + kfifo_skip(&chan->fifo);
> + kfifo_put(&chan->fifo, val);
> + }
> wake_up_interruptible(&chan->wq);
> }
>
> @@ -228,6 +240,7 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
> return -EBUSY;
>
> init_waitqueue_head(&channel->wq);
> + spin_lock_init(&channel->lock);
>
> channel->cfg = cfg;
> channel->miscdev.minor = MISC_DYNAMIC_MINOR;
^ permalink raw reply
* [STATUS] arm64/for-kernelci - 596d152bc5e34b870b32ba3c6d6a766d563ebda0
From: KernelCI bot @ 2026-06-11 2:30 UTC (permalink / raw)
To: kernelci-results; +Cc: will, linux-arm-kernel
Hello,
Status summary for arm64/for-kernelci
Dashboard:
https://d.kernelci.org/c/arm64/for-kernelci/596d152bc5e34b870b32ba3c6d6a766d563ebda0/
giturl: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git
branch: for-kernelci
commit hash: 596d152bc5e34b870b32ba3c6d6a766d563ebda0
origin: maestro
test start time: 2026-06-10 12:20:56.217000+00:00
Builds: 8 ✅ 0 ❌ 0 ⚠️
Boots: 35 ✅ 0 ❌ 1 ⚠️
Tests: 8876 ✅ 0 ❌ 90 ⚠️
### POSSIBLE REGRESSIONS
No possible regressions observed.
### FIXED REGRESSIONS
No fixed regressions observed.
### UNSTABLE TESTS
Hardware: bcm2711-rpi-4-b
> Config: defconfig+lab-setup+kselftest
- Architecture/compiler: arm64/gcc-14
- boot
last run: https://d.kernelci.org/test/maestro:6a29618edabb8c619d43f8f0
history: > ⚠️ > ⚠️ > ✅ > ✅ > ✅
Sent every day if there were changes in the past 24 hours.
Legend: ✅ PASS ❌ FAIL ⚠️ INCONCLUSIVE
--
This is an experimental report format. Please send feedback in!
Talk to us at kernelci@lists.linux.dev
Made with love by the KernelCI team - https://kernelci.org
^ permalink raw reply
* [PATCH v5 net-next 9/9] net: dsa: netc: implement dynamic FDB entry ageing
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
The NETC switch does not age out dynamic FDB entries automatically.
Without software management, stale entries persist after topology
changes and cause incorrect forwarding.
Add a delayed work that periodically removes entries that have not been
refreshed within the specified cycles. The effective ageing time is:
ageing_time = fdbt_ageing_delay * 100
Default values are 3s interval and 100 cycles (300s total), matching
the IEEE 802.1Q default ageing time. The work starts when the first
port joins a bridge (tracked via br_cnt) and is cancelled when the
last port leaves. All FDB operations are serialized under fdbt_lock.
Implement .set_ageing_time() to allow the bridge layer to reconfigure
ageing parameters on demand.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/dsa/netc/netc_main.c | 67 ++++++++++++++++++++++++++++++
drivers/net/dsa/netc/netc_switch.h | 7 ++++
2 files changed, 74 insertions(+)
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index b375dba9fff0..77077352c1a5 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -447,6 +447,25 @@ static void netc_free_ntmp_user(struct netc_switch *priv)
netc_free_ntmp_bitmaps(priv);
}
+static void netc_clean_fdbt_ageing_entries(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct netc_switch *priv;
+
+ priv = container_of(dwork, struct netc_switch, fdbt_ageing_work);
+
+ /* Update the activity element in FDB table */
+ mutex_lock(&priv->fdbt_lock);
+ ntmp_fdbt_update_activity_element(&priv->ntmp);
+ /* Delete the ageing entries after the activity element is updated */
+ ntmp_fdbt_delete_ageing_entries(&priv->ntmp, NETC_FDBT_AGEING_THRESH);
+ mutex_unlock(&priv->fdbt_lock);
+
+ if (atomic_read(&priv->br_cnt))
+ schedule_delayed_work(&priv->fdbt_ageing_work,
+ READ_ONCE(priv->fdbt_ageing_delay));
+}
+
static void netc_switch_dos_default_config(struct netc_switch *priv)
{
struct netc_switch_regs *regs = &priv->regs;
@@ -872,6 +891,10 @@ static int netc_setup(struct dsa_switch *ds)
INIT_HLIST_HEAD(&priv->fdb_list);
mutex_init(&priv->fdbt_lock);
+ priv->fdbt_ageing_delay = NETC_FDBT_AGEING_DELAY;
+ atomic_set(&priv->br_cnt, 0);
+ INIT_DELAYED_WORK(&priv->fdbt_ageing_work,
+ netc_clean_fdbt_ageing_entries);
INIT_HLIST_HEAD(&priv->vlan_list);
mutex_init(&priv->vft_lock);
@@ -936,6 +959,7 @@ static void netc_teardown(struct dsa_switch *ds)
{
struct netc_switch *priv = ds->priv;
+ disable_delayed_work_sync(&priv->fdbt_ageing_work);
netc_destroy_all_lists(priv);
netc_free_host_flood_rules(priv);
netc_free_ntmp_user(priv);
@@ -1970,6 +1994,7 @@ static int netc_port_bridge_join(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack)
{
struct netc_port *np = NETC_PORT(ds, port);
+ struct netc_switch *priv = ds->priv;
u16 vlan_unaware_pvid;
int err;
@@ -1997,6 +2022,10 @@ static int netc_port_bridge_join(struct dsa_switch *ds, int port,
out:
netc_port_remove_host_flood(np, np->host_flood);
+ if (atomic_inc_return(&priv->br_cnt) == 1)
+ schedule_delayed_work(&priv->fdbt_ageing_work,
+ READ_ONCE(priv->fdbt_ageing_delay));
+
return 0;
disable_mlo:
@@ -2023,6 +2052,7 @@ static void netc_port_bridge_leave(struct dsa_switch *ds, int port,
{
struct netc_port *np = NETC_PORT(ds, port);
struct net_device *ndev = np->dp->user;
+ struct netc_switch *priv = ds->priv;
u16 vlan_unaware_pvid;
bool mc, uc;
@@ -2030,6 +2060,9 @@ static void netc_port_bridge_leave(struct dsa_switch *ds, int port,
netc_port_set_pvid(np, NETC_STANDALONE_PVID);
np->pvid = NETC_STANDALONE_PVID;
+ if (atomic_dec_and_test(&priv->br_cnt))
+ cancel_delayed_work_sync(&priv->fdbt_ageing_work);
+
netc_port_remove_dynamic_entries(np);
uc = ndev->flags & IFF_PROMISC;
mc = ndev->flags & (IFF_PROMISC | IFF_ALLMULTI);
@@ -2066,6 +2099,37 @@ static void netc_port_bridge_leave(struct dsa_switch *ds, int port,
netc_port_del_vlan_entry(np, vlan_unaware_pvid);
}
+static int netc_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+ struct netc_switch *priv = ds->priv;
+ unsigned long delay_jiffies;
+
+ /* The dynamic FDB entry is deleted when its activity counter reaches
+ * NETC_FDBT_AGEING_THRESH (100). Each delayed_work tick increments
+ * the counter by 1 if the entry is inactive.
+ *
+ * Therefore:
+ * msecs (ms) = NETC_FDBT_AGEING_THRESH * delay_ms (ms)
+ * delay_ms = msecs / NETC_FDBT_AGEING_THRESH
+ * delay_jiffies = (delay_ms / 1000) * HZ
+ * = (msecs * HZ) / (1000 * NETC_FDBT_AGEING_THRESH)
+ *
+ * Use DIV_ROUND_CLOSEST_ULL to perform a single nearest-jiffy
+ * rounding, avoiding the two-step rounding error of the intermediate
+ * delay_ms approach.
+ * Maximum error = +/-0.5 jiffy * 100 = +/-50000/HZ ms.
+ */
+ delay_jiffies = DIV_ROUND_CLOSEST_ULL((u64)msecs * HZ,
+ 1000 * NETC_FDBT_AGEING_THRESH);
+ WRITE_ONCE(priv->fdbt_ageing_delay, delay_jiffies);
+
+ if (atomic_read(&priv->br_cnt))
+ mod_delayed_work(system_percpu_wq, &priv->fdbt_ageing_work,
+ READ_ONCE(priv->fdbt_ageing_delay));
+
+ return 0;
+}
+
static void netc_port_fast_age(struct dsa_switch *ds, int port)
{
struct netc_port *np = NETC_PORT(ds, port);
@@ -2357,6 +2421,7 @@ static const struct dsa_switch_ops netc_switch_ops = {
.port_vlan_del = netc_port_vlan_del,
.port_bridge_join = netc_port_bridge_join,
.port_bridge_leave = netc_port_bridge_leave,
+ .set_ageing_time = netc_set_ageing_time,
.port_fast_age = netc_port_fast_age,
.get_pause_stats = netc_port_get_pause_stats,
.get_rmon_stats = netc_port_get_rmon_stats,
@@ -2406,6 +2471,8 @@ static int netc_switch_probe(struct pci_dev *pdev,
ds->phylink_mac_ops = &netc_phylink_mac_ops;
ds->fdb_isolation = true;
ds->max_num_bridges = priv->info->num_ports - 1;
+ ds->ageing_time_min = 1000;
+ ds->ageing_time_max = U32_MAX;
ds->priv = priv;
priv->ds = ds;
diff --git a/drivers/net/dsa/netc/netc_switch.h b/drivers/net/dsa/netc/netc_switch.h
index 982c8d3a3fbf..305f2a92e2f9 100644
--- a/drivers/net/dsa/netc/netc_switch.h
+++ b/drivers/net/dsa/netc/netc_switch.h
@@ -50,6 +50,9 @@
/* PAUSE refresh threshold: send refresh when timer reaches this value */
#define NETC_PAUSE_THRESH 0x7FFF
+#define NETC_FDBT_AGEING_DELAY (3 * HZ)
+#define NETC_FDBT_AGEING_THRESH 100
+
struct netc_switch;
struct netc_switch_info {
@@ -124,6 +127,10 @@ struct netc_switch {
struct ntmp_user ntmp;
struct hlist_head fdb_list;
struct mutex fdbt_lock; /* FDB table lock */
+ struct delayed_work fdbt_ageing_work;
+ /* (fdbt_ageing_delay * NETC_FDBT_AGEING_THRESH) is ageing time */
+ unsigned long fdbt_ageing_delay;
+ atomic_t br_cnt;
struct hlist_head vlan_list;
struct mutex vft_lock; /* VLAN filter table lock */
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 8/9] net: dsa: netc: add bridge mode support
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
Wire up the port_bridge_join, port_bridge_leave and port_vlan_filtering
DSA callbacks to support both VLAN-unaware and VLAN-aware bridge modes.
For VLAN-unaware bridges, each bridge instance is assigned a dedicated
internal PVID via NETC_VLAN_UNAWARE_PVID(bridge.num), counting down
from VID 4095. A VFT entry is created for this PVID with hardware MAC
learning and flood-on-miss forwarding enabled. The CPU port is included
as a VFT member so frames can reach the host. The reserved VID range is
blocked in port_vlan_add to prevent user-space conflicts.
Only one VLAN-aware bridge is supported at a time; this constraint is
enforced in port_bridge_join and port_vlan_filtering. The per-port PVID
is tracked in software and written to the BPDVR register whenever VLAN
filtering is active.
When a port leaves the bridge, its dynamic FDB entries are flushed right
away in port_bridge_leave(), without waiting for the ageing cycle. When
a link down event occurs on a port, netc_mac_link_down() will also clear
the port's dynamic FDB entries via netc_port_remove_dynamic_entries().
Non-bridge ports have no dynamic FDB entries, so this call is always
safe. Additionally, .port_fast_age() callback is added to flush the
dynamic FDB entries associated to a port.
Host flood rules are removed from the ingress port filter table when a
port joins a bridge to avoid bypassing FDB lookup and MAC learning.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/dsa/netc/netc_main.c | 377 +++++++++++++++++++++++++++--
drivers/net/dsa/netc/netc_switch.h | 2 +
2 files changed, 363 insertions(+), 16 deletions(-)
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index 9382e7b68e65..b375dba9fff0 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -509,6 +509,17 @@ static void netc_port_set_mlo(struct netc_port *np, enum netc_mlo mlo)
netc_port_rmw(np, NETC_BPCR, BPCR_MLO, FIELD_PREP(BPCR_MLO, mlo));
}
+static void netc_port_set_pvid(struct netc_port *np, u16 pvid)
+{
+ netc_port_rmw(np, NETC_BPDVR, BPDVR_VID, pvid);
+}
+
+static void netc_port_set_vlan_aware(struct netc_port *np, bool aware)
+{
+ netc_port_rmw(np, NETC_BPDVR, BPDVR_RXVAM,
+ aware ? 0 : BPDVR_RXVAM);
+}
+
static void netc_port_fixed_config(struct netc_port *np)
{
/* Default IPV and DR setting */
@@ -534,7 +545,7 @@ static void netc_port_default_config(struct netc_port *np)
netc_port_fixed_config(np);
/* Default VLAN unaware */
- netc_port_rmw(np, NETC_BPDVR, BPDVR_RXVAM, BPDVR_RXVAM);
+ netc_port_set_vlan_aware(np, false);
if (dsa_port_is_cpu(np->dp))
/* For CPU port, source port pruning is disabled */
@@ -695,10 +706,16 @@ static int netc_port_del_fdb_entry(struct netc_port *np,
entry = netc_lookup_fdb_entry(priv, addr, vid);
if (unlikely(!entry))
- /* Currently only single port mode is supported, MAC learning
- * is disabled, so there is no dynamically learned FDB entry.
- * We need to support deleting dynamically FDB entry when the
- * bridge mode is supported.
+ /* The hardware-learned dynamic FDB entries cannot be deleted
+ * through .port_fdb_del() interface.
+ * For NTF_MASTER path: Since hardware-learned dynamic FDB
+ * entries are never synchronized back to the bridge software
+ * database. br_fdb_delete() -> br_fdb_find() cannot find the
+ * FDB entry, so .port_fdb_del() will not be called.
+ * For NTF_SELF path: dsa_user_netdev_ops does not implement
+ * ndo_fdb_del(), so rtnl_fdb_del() falls back to
+ * ndo_dflt_fdb_del(), which only supports NUD_PERMANENT static
+ * entries and rejects all others with -EINVAL.
*/
goto unlock_fdbt;
@@ -1277,6 +1294,16 @@ static int netc_port_add_vlan_entry(struct netc_port *np, u16 vid,
entry->ect_gid = NTMP_NULL_ENTRY_ID;
bitmap_stg = BIT(index) | VFT_STG_ID(0);
+ /* If the VID is a VLAN-unaware PVID, the CPU port needs to be
+ * a member of this VLAN.
+ */
+ if (dsa_port_is_user(np->dp) &&
+ vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) {
+ struct dsa_port *cpu_dp = np->dp->cpu_dp;
+
+ bitmap_stg |= BIT(cpu_dp->index);
+ }
+
cfg = FIELD_PREP(VFT_MLO, MLO_HW) |
FIELD_PREP(VFT_MFO, MFO_NO_MATCH_FLOOD);
@@ -1314,11 +1341,16 @@ static int netc_port_add_vlan_entry(struct netc_port *np, u16 vid,
return err;
}
-static bool netc_port_vlan_egress_rule_changed(struct netc_vlan_entry *entry,
+static bool netc_port_vlan_egress_rule_changed(struct netc_switch *priv,
+ struct netc_vlan_entry *entry,
int port, bool untagged)
{
bool old_untagged = !!(entry->untagged_port_bitmap & BIT(port));
+ /* VLAN-unaware VIDs have no egress rules, so return 'false' */
+ if (entry->vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges))
+ return false;
+
return old_untagged != untagged;
}
@@ -1341,7 +1373,8 @@ static int netc_port_set_vlan_entry(struct netc_port *np, u16 vid,
}
/* Check whether the egress VLAN rule is changed */
- changed = netc_port_vlan_egress_rule_changed(entry, port, untagged);
+ changed = netc_port_vlan_egress_rule_changed(priv, entry, port,
+ untagged);
if (changed) {
entry->untagged_port_bitmap ^= BIT(port);
err = netc_port_update_vlan_egress_rule(np, entry);
@@ -1405,6 +1438,17 @@ static int netc_port_del_vlan_entry(struct netc_port *np, u16 vid)
cfge = &entry->cfge;
vlan_port_bitmap = FIELD_GET(VFT_PORT_MEMBERSHIP,
le32_to_cpu(cfge->bitmap_stg));
+ /* If the VID is a VLAN-unaware PVID, we need to clear the CPU
+ * port bit of vlan_port_bitmap, so that the VLAN entry can be
+ * deleted if no user ports use this VLAN.
+ */
+ if (dsa_port_is_user(np->dp) &&
+ vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) {
+ struct dsa_port *cpu_dp = np->dp->cpu_dp;
+
+ vlan_port_bitmap &= ~BIT(cpu_dp->index);
+ }
+
/* If the VLAN only belongs to the current port */
if (vlan_port_bitmap == BIT(port)) {
err = ntmp_vft_delete_entry(&priv->ntmp, vid);
@@ -1510,17 +1554,50 @@ static int netc_port_max_mtu(struct dsa_switch *ds, int port)
return NETC_MAX_FRAME_LEN - VLAN_ETH_HLEN - ETH_FCS_LEN;
}
+static struct net_device *netc_classify_db(struct dsa_db db)
+{
+ switch (db.type) {
+ case DSA_DB_PORT:
+ return NULL;
+ case DSA_DB_BRIDGE:
+ return db.bridge.dev;
+ default:
+ return ERR_PTR(-EOPNOTSUPP);
+ }
+}
+
+static u16 netc_vlan_unaware_pvid(struct dsa_bridge *bridge)
+{
+ u32 br_num;
+
+ if (!bridge)
+ return NETC_STANDALONE_PVID;
+
+ br_num = bridge->num;
+
+ /* The br_num is supposed to be 1 ~ ds->max_num_bridges, see
+ * dsa_bridge_num_get(). Since max_num_bridges is non-zero,
+ * so dsa_port_bridge_create() will return an error if
+ * dsa_bridge_num_get() returns 0.
+ */
+ if (WARN_ON(!br_num))
+ return NETC_STANDALONE_PVID;
+
+ return NETC_VLAN_UNAWARE_PVID(br_num);
+}
+
static int netc_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
+ struct net_device *br_ndev = netc_classify_db(db);
struct netc_port *np = NETC_PORT(ds, port);
- /* Currently, only support standalone port mode, so only
- * NETC_STANDALONE_PVID (= 0) is supported here.
- */
- if (vid != NETC_STANDALONE_PVID)
- return -EOPNOTSUPP;
+ if (IS_ERR(br_ndev))
+ return PTR_ERR(br_ndev);
+
+ if (!vid)
+ vid = netc_vlan_unaware_pvid(br_ndev ? &db.bridge : NULL);
return netc_port_set_fdb_entry(np, addr, vid);
}
@@ -1529,10 +1606,14 @@ static int netc_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
+ struct net_device *br_ndev = netc_classify_db(db);
struct netc_port *np = NETC_PORT(ds, port);
- if (vid != NETC_STANDALONE_PVID)
- return -EOPNOTSUPP;
+ if (IS_ERR(br_ndev))
+ return PTR_ERR(br_ndev);
+
+ if (!vid)
+ vid = netc_vlan_unaware_pvid(br_ndev ? &db.bridge : NULL);
return netc_port_del_fdb_entry(np, addr, vid);
}
@@ -1568,6 +1649,8 @@ static int netc_port_fdb_dump(struct dsa_switch *ds, int port,
cfg = le32_to_cpu(cfge->cfg);
is_static = (cfg & FDBT_DYNAMIC) ? false : true;
vid = le16_to_cpu(keye->fid);
+ if (vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges))
+ vid = 0;
err = cb(keye->mac_addr, vid, is_static, data);
if (err)
@@ -1670,12 +1753,23 @@ static void netc_port_remove_host_flood(struct netc_port *np,
struct ipft_entry_data *host_flood)
{
struct netc_switch *priv = np->switch_priv;
+ bool disable_host_flood = false;
if (!host_flood)
return;
+ if (np->host_flood == host_flood)
+ disable_host_flood = true;
+
ntmp_ipft_delete_entry(&priv->ntmp, host_flood->entry_id);
kfree(host_flood);
+
+ if (disable_host_flood) {
+ np->host_flood = NULL;
+ np->uc = false;
+ np->mc = false;
+ netc_port_wr(np, NETC_PIPFCR, 0);
+ }
}
static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
@@ -1684,6 +1778,17 @@ static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
struct netc_port *np = NETC_PORT(ds, port);
struct ipft_entry_data *old_host_flood;
+ /* Do not add host flood rule to ingress port filter table when
+ * the port has joined a bridge. Otherwise, the ingress frames
+ * will bypass FDB table lookup and MAC learning, so the frames
+ * will be redirected directly to the CPU port.
+ */
+ if (dsa_port_bridge_dev_get(np->dp)) {
+ netc_port_remove_host_flood(np, np->host_flood);
+
+ return;
+ }
+
if (np->uc == uc && np->mc == mc)
return;
@@ -1705,12 +1810,90 @@ static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
netc_port_remove_host_flood(np, old_host_flood);
}
+static int netc_single_vlan_aware_bridge(struct dsa_switch *ds,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *br_ndev = NULL;
+ struct dsa_port *dp;
+
+ dsa_switch_for_each_available_port(dp, ds) {
+ struct net_device *port_br = dsa_port_bridge_dev_get(dp);
+
+ if (!port_br || !br_vlan_enabled(port_br))
+ continue;
+
+ if (!br_ndev) {
+ br_ndev = port_br;
+ continue;
+ }
+
+ if (br_ndev == port_br)
+ continue;
+
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only one VLAN-aware bridge is supported");
+
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int netc_port_vlan_filtering(struct dsa_switch *ds,
+ int port, bool vlan_aware,
+ struct netlink_ext_ack *extack)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ u16 pvid;
+ int err;
+
+ /* Before calling port_vlan_filtering(), br_vlan_filter_toggle() has
+ * already updated the BROPT_VLAN_ENABLED bit of br->options. So the
+ * VLAN filtering status of the switch ports can be checked by the
+ * br_vlan_enabled() function.
+ */
+ err = netc_single_vlan_aware_bridge(ds, extack);
+ if (err)
+ return err;
+
+ pvid = netc_vlan_unaware_pvid(np->dp->bridge);
+ if (pvid == NETC_STANDALONE_PVID) {
+ vlan_aware = false;
+ goto bpdvr_config;
+ }
+
+ if (vlan_aware) {
+ /* The FDB entries associated with unaware_pvid do not need
+ * to be deleted, so that when switching from VLAN-aware to
+ * VLAN-unaware mode, these FDB entries do not need to be
+ * re-added.
+ */
+ err = netc_port_del_vlan_entry(np, pvid);
+ if (err)
+ return err;
+
+ pvid = np->pvid;
+ } else {
+ err = netc_port_set_vlan_entry(np, pvid, false);
+ if (err)
+ return err;
+ }
+
+bpdvr_config:
+ netc_port_set_vlan_aware(np, vlan_aware);
+ netc_port_set_pvid(np, pvid);
+
+ return 0;
+}
+
static int netc_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
{
struct netc_port *np = NETC_PORT(ds, port);
+ struct dsa_port *dp = np->dp;
bool untagged;
+ int err;
/* The 8021q layer may attempt to change NETC_STANDALONE_PVID
* (VID 0), so we need to ignore it.
@@ -1718,20 +1901,176 @@ static int netc_port_vlan_add(struct dsa_switch *ds, int port,
if (vlan->vid == NETC_STANDALONE_PVID)
return 0;
+ if (vlan->vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges)) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "VID %d~4095 reserved for VLAN-unaware bridge",
+ NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges));
+ return -EINVAL;
+ }
+
untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+ err = netc_port_set_vlan_entry(np, vlan->vid, untagged);
+ if (err)
+ return err;
+
+ if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+ np->pvid = vlan->vid;
+ if (dsa_port_is_vlan_filtering(dp))
+ netc_port_set_pvid(np, vlan->vid);
- return netc_port_set_vlan_entry(np, vlan->vid, untagged);
+ return 0;
+ }
+
+ if (np->pvid != vlan->vid)
+ return 0;
+
+ /* Delete PVID */
+ np->pvid = NETC_STANDALONE_PVID;
+ if (dsa_port_is_vlan_filtering(dp))
+ netc_port_set_pvid(np, NETC_STANDALONE_PVID);
+
+ return 0;
}
static int netc_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct netc_port *np = NETC_PORT(ds, port);
+ int err;
if (vlan->vid == NETC_STANDALONE_PVID)
return 0;
- return netc_port_del_vlan_entry(np, vlan->vid);
+ if (vlan->vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges))
+ return -EINVAL;
+
+ err = netc_port_del_vlan_entry(np, vlan->vid);
+ if (err)
+ return err;
+
+ if (np->pvid == vlan->vid) {
+ np->pvid = NETC_STANDALONE_PVID;
+
+ /* Set the port PVID to NETC_STANDALONE_PVID if the VLAN-aware
+ * bridge port has no PVID. The untagged frames will not be
+ * forwarded to other user ports, as NETC_STANDALONE_PVID VLAN
+ * entry has disabled MAC learning and flooding, and other user
+ * ports do not have FDB entries with NETC_STANDALONE_PVID.
+ */
+ if (dsa_port_is_vlan_filtering(np->dp))
+ netc_port_set_pvid(np, NETC_STANDALONE_PVID);
+ }
+
+ return 0;
+}
+
+static int netc_port_bridge_join(struct dsa_switch *ds, int port,
+ struct dsa_bridge bridge,
+ bool *tx_fwd_offload,
+ struct netlink_ext_ack *extack)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ u16 vlan_unaware_pvid;
+ int err;
+
+ if (!bridge.num) {
+ NL_SET_ERR_MSG_MOD(extack, "Bridge number 0 is unsupported");
+ return -EINVAL;
+ }
+
+ err = netc_single_vlan_aware_bridge(ds, extack);
+ if (err)
+ return err;
+
+ netc_port_set_mlo(np, MLO_NOT_OVERRIDE);
+
+ if (br_vlan_enabled(bridge.dev))
+ goto out;
+
+ vlan_unaware_pvid = NETC_VLAN_UNAWARE_PVID(bridge.num);
+ err = netc_port_set_vlan_entry(np, vlan_unaware_pvid, false);
+ if (err)
+ goto disable_mlo;
+
+ netc_port_set_pvid(np, vlan_unaware_pvid);
+
+out:
+ netc_port_remove_host_flood(np, np->host_flood);
+
+ return 0;
+
+disable_mlo:
+ netc_port_set_mlo(np, MLO_DISABLE);
+
+ return err;
+}
+
+static void netc_port_remove_dynamic_entries(struct netc_port *np)
+{
+ struct netc_switch *priv = np->switch_priv;
+
+ /* Return if the port is not available */
+ if (!np->dp)
+ return;
+
+ mutex_lock(&priv->fdbt_lock);
+ ntmp_fdbt_delete_port_dynamic_entries(&priv->ntmp, np->dp->index);
+ mutex_unlock(&priv->fdbt_lock);
+}
+
+static void netc_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct dsa_bridge bridge)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ struct net_device *ndev = np->dp->user;
+ u16 vlan_unaware_pvid;
+ bool mc, uc;
+
+ netc_port_set_mlo(np, MLO_DISABLE);
+ netc_port_set_pvid(np, NETC_STANDALONE_PVID);
+ np->pvid = NETC_STANDALONE_PVID;
+
+ netc_port_remove_dynamic_entries(np);
+ uc = ndev->flags & IFF_PROMISC;
+ mc = ndev->flags & (IFF_PROMISC | IFF_ALLMULTI);
+
+ if (netc_port_add_host_flood_rule(np, uc, mc))
+ dev_warn(ds->dev,
+ "Failed to restore host flood rule on port %d\n",
+ port);
+
+ /* When a port leaves a VLAN-aware bridge, dsa_port_bridge_leave()
+ * follows the sequence below:
+ *
+ * 1. dsa_port_bridge_destroy() is called to set dp->bridge to NULL.
+ * 2. dsa_broadcast() is called, which eventually invokes
+ * ds->ops->port_bridge_leave()
+ * 3. dsa_port_switchdev_unsync_attrs() is called, which triggers
+ * dsa_port_reset_vlan_filtering() and ultimately calls
+ * ds->ops->port_vlan_filtering() to transition the port from
+ * VLAN-aware mode to VLAN-unaware mode.
+ *
+ * At step 3, since dp->bridge has already been set to NULL in step 1,
+ * netc_port_vlan_filtering() will detect this and skip the creation
+ * of an unaware PVID entry in the VLAN filter table. Therefore, it is
+ * safe to return directly here.
+ */
+ if (br_vlan_enabled(bridge.dev))
+ return;
+
+ vlan_unaware_pvid = NETC_VLAN_UNAWARE_PVID(bridge.num);
+ /* There is no need to check the return value even if it fails.
+ * Because the PVID has been set to NETC_STANDALONE_PVID, the
+ * frames will not match this VLAN entry.
+ */
+ netc_port_del_vlan_entry(np, vlan_unaware_pvid);
+}
+
+static void netc_port_fast_age(struct dsa_switch *ds, int port)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+
+ netc_port_remove_dynamic_entries(np);
}
static void netc_phylink_get_caps(struct dsa_switch *ds, int port,
@@ -1988,6 +2327,7 @@ static void netc_mac_link_down(struct phylink_config *config,
np = NETC_PORT(dp->ds, dp->index);
netc_port_mac_rx_graceful_stop(np);
netc_port_mac_tx_graceful_stop(np);
+ netc_port_remove_dynamic_entries(np);
}
static const struct phylink_mac_ops netc_phylink_mac_ops = {
@@ -2012,8 +2352,12 @@ static const struct dsa_switch_ops netc_switch_ops = {
.port_mdb_add = netc_port_mdb_add,
.port_mdb_del = netc_port_mdb_del,
.port_set_host_flood = netc_port_set_host_flood,
+ .port_vlan_filtering = netc_port_vlan_filtering,
.port_vlan_add = netc_port_vlan_add,
.port_vlan_del = netc_port_vlan_del,
+ .port_bridge_join = netc_port_bridge_join,
+ .port_bridge_leave = netc_port_bridge_leave,
+ .port_fast_age = netc_port_fast_age,
.get_pause_stats = netc_port_get_pause_stats,
.get_rmon_stats = netc_port_get_rmon_stats,
.get_eth_ctrl_stats = netc_port_get_eth_ctrl_stats,
@@ -2061,6 +2405,7 @@ static int netc_switch_probe(struct pci_dev *pdev,
ds->ops = &netc_switch_ops;
ds->phylink_mac_ops = &netc_phylink_mac_ops;
ds->fdb_isolation = true;
+ ds->max_num_bridges = priv->info->num_ports - 1;
ds->priv = priv;
priv->ds = ds;
diff --git a/drivers/net/dsa/netc/netc_switch.h b/drivers/net/dsa/netc/netc_switch.h
index 9ff334301fbc..982c8d3a3fbf 100644
--- a/drivers/net/dsa/netc/netc_switch.h
+++ b/drivers/net/dsa/netc/netc_switch.h
@@ -33,6 +33,7 @@
#define NETC_MAX_FRAME_LEN 9600
#define NETC_STANDALONE_PVID 0
+#define NETC_VLAN_UNAWARE_PVID(br_id) (4096 - (br_id))
/* Threshold format: MANT (bits 11:4) * 2^EXP (bits 3:0)
* Unit: Memory words (average of 20 bytes each)
@@ -79,6 +80,7 @@ struct netc_port {
u16 enable:1;
u16 uc:1;
u16 mc:1;
+ u16 pvid;
struct ipft_entry_data *host_flood;
};
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 7/9] net: dsa: netc: add VLAN filter table and egress treatment management
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
Implement the DSA .port_vlan_add and .port_vlan_del operations to enable
VLAN-aware bridge offloading on the NETC switch.
VLAN membership is maintained in the VLAN Filter Table (VFT). Adding the
first port to a VLAN creates a new VFT entry with hardware MAC learning
and flood-on-miss forwarding; subsequent ports update the existing
entry's membership bitmap. Removing the last port deletes the entry.
Egress tagging is handled through the Egress Treatment Table (ETT). Each
VLAN is allocated a group of ETT entries, one per available port. Ports
are assigned a sequential ett_offset during initialisation, used to
address each port's entry within the group. Untagged ports configure the
ETT to strip the outer VLAN tag; tagged ports pass frames through
unmodified. Each ETT group is optionally paired with an Egress Counter
Table (ECT) group for per-port frame counting, allocated on a best-effort
basis. When the egress rule of an ETT entry changes, the counter of the
corresponding ECT entry will be recounted to track the number of frames
that match the new egress rule.
A software shadow list serialised by vft_lock tracks active VLAN state
across both port membership and egress tagging. VID 0 is used for single
port mode and is ignored by both callbacks.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/dsa/netc/netc_main.c | 439 +++++++++++++++++++++++++++++
drivers/net/dsa/netc/netc_switch.h | 24 ++
include/linux/fsl/ntmp.h | 15 +
3 files changed, 478 insertions(+)
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index d4475ad7ed6c..9382e7b68e65 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -37,6 +37,27 @@ static void netc_destroy_fdb_list(struct netc_switch *priv)
netc_del_fdb_entry(entry);
}
+static struct netc_vlan_entry *
+netc_lookup_vlan_entry(struct netc_switch *priv, u16 vid)
+{
+ struct netc_vlan_entry *entry;
+
+ hlist_for_each_entry(entry, &priv->vlan_list, node)
+ if (entry->vid == vid)
+ return entry;
+
+ return NULL;
+}
+
+static void netc_destroy_vlan_list(struct netc_switch *priv)
+{
+ struct netc_vlan_entry *entry;
+ struct hlist_node *tmp;
+
+ hlist_for_each_entry_safe(entry, tmp, &priv->vlan_list, node)
+ netc_del_vlan_entry(entry);
+}
+
static enum dsa_tag_protocol
netc_get_tag_protocol(struct dsa_switch *ds, int port,
enum dsa_tag_protocol mprot)
@@ -222,6 +243,7 @@ static int netc_init_all_ports(struct netc_switch *priv)
struct device *dev = priv->dev;
struct netc_port *np;
struct dsa_port *dp;
+ int ett_offset = 0;
int err;
priv->ports = devm_kcalloc(dev, priv->info->num_ports,
@@ -251,6 +273,8 @@ static int netc_init_all_ports(struct netc_switch *priv)
dsa_switch_for_each_available_port(dp, priv->ds) {
np = priv->ports[dp->index];
np->dp = dp;
+ np->ett_offset = ett_offset++;
+ priv->port_bitmap |= BIT(dp->index);
err = netc_port_get_info_from_dt(np, dp->dn, dev);
if (err)
@@ -831,6 +855,8 @@ static int netc_setup(struct dsa_switch *ds)
INIT_HLIST_HEAD(&priv->fdb_list);
mutex_init(&priv->fdbt_lock);
+ INIT_HLIST_HEAD(&priv->vlan_list);
+ mutex_init(&priv->vft_lock);
netc_switch_fixed_config(priv);
@@ -858,6 +884,7 @@ static int netc_setup(struct dsa_switch *ds)
* hardware state.
*/
mutex_destroy(&priv->fdbt_lock);
+ mutex_destroy(&priv->vft_lock);
netc_free_ntmp_user(priv);
return err;
@@ -867,6 +894,8 @@ static void netc_destroy_all_lists(struct netc_switch *priv)
{
netc_destroy_fdb_list(priv);
mutex_destroy(&priv->fdbt_lock);
+ netc_destroy_vlan_list(priv);
+ mutex_destroy(&priv->vft_lock);
}
static void netc_free_host_flood_rules(struct netc_switch *priv)
@@ -1025,6 +1054,385 @@ static void netc_switch_get_ip_revision(struct netc_switch *priv)
priv->revision = FIELD_GET(IPBRR0_IP_REV, val);
}
+static void netc_init_ett_cfge(struct ett_cfge_data *cfge,
+ bool untagged, u32 ect_eid)
+{
+ u32 vuda_sqta = FMTEID_VUDA_SQTA;
+ u16 efm_cfg = 0;
+
+ if (ect_eid != NTMP_NULL_ENTRY_ID) {
+ /* Increase egress frame counter */
+ efm_cfg |= FIELD_PREP(ETT_ECA, ETT_ECA_INC);
+ cfge->ec_eid = cpu_to_le32(ect_eid);
+ }
+
+ /* If egress rule is VLAN untagged */
+ if (untagged) {
+ /* delete outer VLAN tag */
+ vuda_sqta |= FIELD_PREP(FMTEID_VUDA, FMTEID_VUDA_DEL_OTAG);
+ /* length change: twos-complement notation */
+ efm_cfg |= FIELD_PREP(ETT_EFM_LEN_CHANGE,
+ ETT_FRM_LEN_DEL_VLAN);
+ }
+
+ cfge->efm_eid = cpu_to_le32(vuda_sqta);
+ cfge->efm_cfg = cpu_to_le16(efm_cfg);
+}
+
+static int netc_add_ett_entry(struct netc_switch *priv, bool untagged,
+ u32 ett_eid, u32 ect_eid)
+{
+ struct ntmp_user *ntmp = &priv->ntmp;
+ struct ett_cfge_data cfge = {};
+
+ netc_init_ett_cfge(&cfge, untagged, ect_eid);
+
+ return ntmp_ett_add_entry(ntmp, ett_eid, &cfge);
+}
+
+static int netc_update_ett_entry(struct netc_switch *priv, bool untagged,
+ u32 ett_eid, u32 ect_eid)
+{
+ struct ntmp_user *ntmp = &priv->ntmp;
+ struct ett_cfge_data cfge = {};
+
+ netc_init_ett_cfge(&cfge, untagged, ect_eid);
+
+ return ntmp_ett_update_entry(ntmp, ett_eid, &cfge);
+}
+
+static int netc_add_ett_group_entries(struct netc_switch *priv,
+ u32 untagged_port_bitmap,
+ u32 ett_base_eid,
+ u32 ect_base_eid)
+{
+ struct netc_port **ports = priv->ports;
+ u32 ett_eid, ect_eid;
+ bool untagged;
+ int i, err;
+
+ for (i = 0; i < priv->info->num_ports; i++) {
+ if (!ports[i]->dp)
+ continue;
+
+ untagged = !!(untagged_port_bitmap & BIT(i));
+ ett_eid = ett_base_eid + ports[i]->ett_offset;
+ ect_eid = NTMP_NULL_ENTRY_ID;
+ if (ect_base_eid != NTMP_NULL_ENTRY_ID)
+ ect_eid = ect_base_eid + ports[i]->ett_offset;
+
+ err = netc_add_ett_entry(priv, untagged, ett_eid, ect_eid);
+ if (err)
+ goto clear_ett_entries;
+ }
+
+ return 0;
+
+clear_ett_entries:
+ while (--i >= 0) {
+ if (!ports[i]->dp)
+ continue;
+
+ ett_eid = ett_base_eid + ports[i]->ett_offset;
+ ntmp_ett_delete_entry(&priv->ntmp, ett_eid);
+ }
+
+ return err;
+}
+
+static int netc_add_vlan_egress_rule(struct netc_switch *priv,
+ struct netc_vlan_entry *entry)
+{
+ u32 num_ports = netc_num_available_ports(priv);
+ struct ntmp_user *ntmp = &priv->ntmp;
+ u32 ect_eid = NTMP_NULL_ENTRY_ID;
+ u32 ett_eid, ett_gid, ect_gid;
+ int err;
+
+ /* Step 1: Find available egress counter table entries and update
+ * these entries.
+ */
+ ect_gid = ntmp_lookup_free_eid(ntmp->ect_gid_bitmap,
+ ntmp->ect_bitmap_size);
+ if (ect_gid == NTMP_NULL_ENTRY_ID) {
+ dev_info(priv->dev,
+ "No egress counter table entries available\n");
+ } else {
+ ect_eid = ect_gid * num_ports;
+ for (int i = 0; i < num_ports; i++)
+ /* There is no need to check the return value, the only
+ * issue is that the entry's counter might be inaccurate,
+ * but it will not affect the functionality, it is only
+ * for future debugging.
+ */
+ ntmp_ect_update_entry(ntmp, ect_eid + i);
+ }
+
+ /* Step 2: Find available egress treatment table entries and add
+ * these entries.
+ */
+ ett_gid = ntmp_lookup_free_eid(ntmp->ett_gid_bitmap,
+ ntmp->ett_bitmap_size);
+ if (ett_gid == NTMP_NULL_ENTRY_ID) {
+ dev_err(priv->dev,
+ "No egress treatment table entries available\n");
+ err = -ENOSPC;
+ goto clear_ect_gid;
+ }
+
+ ett_eid = ett_gid * num_ports;
+ err = netc_add_ett_group_entries(priv, entry->untagged_port_bitmap,
+ ett_eid, ect_eid);
+ if (err)
+ goto clear_ett_gid;
+
+ entry->cfge.et_eid = cpu_to_le32(ett_eid);
+ entry->ect_gid = ect_gid;
+
+ return 0;
+
+clear_ett_gid:
+ ntmp_clear_eid_bitmap(ntmp->ett_gid_bitmap, ett_gid);
+
+clear_ect_gid:
+ if (ect_gid != NTMP_NULL_ENTRY_ID)
+ ntmp_clear_eid_bitmap(ntmp->ect_gid_bitmap, ect_gid);
+
+ return err;
+}
+
+static void netc_delete_vlan_egress_rule(struct netc_switch *priv,
+ struct netc_vlan_entry *entry)
+{
+ u32 num_ports = netc_num_available_ports(priv);
+ struct ntmp_user *ntmp = &priv->ntmp;
+ u32 ett_eid, ett_gid;
+
+ ett_eid = le32_to_cpu(entry->cfge.et_eid);
+ if (ett_eid == NTMP_NULL_ENTRY_ID)
+ return;
+
+ ett_gid = ett_eid / num_ports;
+ ntmp_clear_eid_bitmap(ntmp->ett_gid_bitmap, ett_gid);
+ for (int i = 0; i < num_ports; i++)
+ ntmp_ett_delete_entry(ntmp, ett_eid + i);
+
+ if (entry->ect_gid == NTMP_NULL_ENTRY_ID)
+ return;
+
+ ntmp_clear_eid_bitmap(ntmp->ect_gid_bitmap, entry->ect_gid);
+}
+
+static int netc_port_update_vlan_egress_rule(struct netc_port *np,
+ struct netc_vlan_entry *entry)
+{
+ bool untagged = !!(entry->untagged_port_bitmap & BIT(np->dp->index));
+ u32 num_ports = netc_num_available_ports(np->switch_priv);
+ u32 ett_eid = le32_to_cpu(entry->cfge.et_eid);
+ struct netc_switch *priv = np->switch_priv;
+ u32 ect_eid = NTMP_NULL_ENTRY_ID;
+ int err;
+
+ if (ett_eid == NTMP_NULL_ENTRY_ID)
+ return 0;
+
+ if (entry->ect_gid != NTMP_NULL_ENTRY_ID)
+ /* Each ETT entry maps to an ECT entry if ect_gid is not NULL
+ * entry ID. The offset of the ECT entry corresponding to the
+ * port in the group is equal to ett_offset.
+ */
+ ect_eid = entry->ect_gid * num_ports + np->ett_offset;
+
+ ett_eid += np->ett_offset;
+ err = netc_update_ett_entry(priv, untagged, ett_eid, ect_eid);
+ if (err) {
+ dev_err(priv->dev,
+ "Failed to update VLAN %u egress rule on port %d\n",
+ entry->vid, np->dp->index);
+ return err;
+ }
+
+ if (ect_eid != NTMP_NULL_ENTRY_ID)
+ ntmp_ect_update_entry(&priv->ntmp, ect_eid);
+
+ return 0;
+}
+
+static int netc_port_add_vlan_entry(struct netc_port *np, u16 vid,
+ bool untagged)
+{
+ struct netc_switch *priv = np->switch_priv;
+ struct netc_vlan_entry *entry;
+ struct vft_cfge_data *cfge;
+ u32 index = np->dp->index;
+ u32 bitmap_stg;
+ int err;
+ u16 cfg;
+
+ entry = kzalloc_obj(*entry);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->vid = vid;
+ entry->ect_gid = NTMP_NULL_ENTRY_ID;
+
+ bitmap_stg = BIT(index) | VFT_STG_ID(0);
+ cfg = FIELD_PREP(VFT_MLO, MLO_HW) |
+ FIELD_PREP(VFT_MFO, MFO_NO_MATCH_FLOOD);
+
+ cfge = &entry->cfge;
+ cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
+ cfge->bitmap_stg = cpu_to_le32(bitmap_stg);
+ cfge->fid = cpu_to_le16(vid);
+ cfge->cfg = cpu_to_le16(cfg);
+ cfge->eta_port_bitmap = cpu_to_le32(priv->port_bitmap);
+
+ if (untagged)
+ entry->untagged_port_bitmap = BIT(index);
+
+ err = netc_add_vlan_egress_rule(priv, entry);
+ if (err)
+ goto free_vlan_entry;
+
+ err = ntmp_vft_add_entry(&priv->ntmp, vid, cfge);
+ if (err) {
+ dev_err(priv->dev,
+ "Failed to add VLAN %u entry on port %d\n",
+ vid, index);
+ goto delete_vlan_egress_rule;
+ }
+
+ netc_add_vlan_entry(priv, entry);
+
+ return 0;
+
+delete_vlan_egress_rule:
+ netc_delete_vlan_egress_rule(priv, entry);
+free_vlan_entry:
+ kfree(entry);
+
+ return err;
+}
+
+static bool netc_port_vlan_egress_rule_changed(struct netc_vlan_entry *entry,
+ int port, bool untagged)
+{
+ bool old_untagged = !!(entry->untagged_port_bitmap & BIT(port));
+
+ return old_untagged != untagged;
+}
+
+static int netc_port_set_vlan_entry(struct netc_port *np, u16 vid,
+ bool untagged)
+{
+ struct netc_switch *priv = np->switch_priv;
+ struct netc_vlan_entry *entry;
+ struct vft_cfge_data *cfge;
+ int port = np->dp->index;
+ bool changed;
+ int err = 0;
+
+ mutex_lock(&priv->vft_lock);
+
+ entry = netc_lookup_vlan_entry(priv, vid);
+ if (!entry) {
+ err = netc_port_add_vlan_entry(np, vid, untagged);
+ goto unlock_vft;
+ }
+
+ /* Check whether the egress VLAN rule is changed */
+ changed = netc_port_vlan_egress_rule_changed(entry, port, untagged);
+ if (changed) {
+ entry->untagged_port_bitmap ^= BIT(port);
+ err = netc_port_update_vlan_egress_rule(np, entry);
+ if (err) {
+ entry->untagged_port_bitmap ^= BIT(port);
+ goto unlock_vft;
+ }
+ }
+
+ cfge = &entry->cfge;
+ if (cfge->bitmap_stg & cpu_to_le32(BIT(port)))
+ goto unlock_vft;
+
+ cfge->bitmap_stg |= cpu_to_le32(BIT(port));
+ err = ntmp_vft_update_entry(&priv->ntmp, vid, cfge);
+ if (err) {
+ dev_err(priv->dev,
+ "Failed to update VLAN %u entry on port %d\n",
+ vid, port);
+
+ goto restore_bitmap_stg;
+ }
+
+ mutex_unlock(&priv->vft_lock);
+
+ return 0;
+
+restore_bitmap_stg:
+ cfge->bitmap_stg &= cpu_to_le32(~BIT(port));
+ if (changed) {
+ entry->untagged_port_bitmap ^= BIT(port);
+ /* Recover the corresponding ETT entry. It doesn't matter
+ * if it fails because the bit corresponding to the port
+ * in the port bitmap of the VFT entry is not set. so the
+ * frame will not match that ETT entry.
+ */
+ if (netc_port_update_vlan_egress_rule(np, entry))
+ entry->untagged_port_bitmap ^= BIT(port);
+ }
+unlock_vft:
+ mutex_unlock(&priv->vft_lock);
+
+ return err;
+}
+
+static int netc_port_del_vlan_entry(struct netc_port *np, u16 vid)
+{
+ struct netc_switch *priv = np->switch_priv;
+ struct netc_vlan_entry *entry;
+ struct vft_cfge_data *cfge;
+ int port = np->dp->index;
+ u32 vlan_port_bitmap;
+ int err = 0;
+
+ mutex_lock(&priv->vft_lock);
+
+ entry = netc_lookup_vlan_entry(priv, vid);
+ if (!entry)
+ goto unlock_vft;
+
+ cfge = &entry->cfge;
+ vlan_port_bitmap = FIELD_GET(VFT_PORT_MEMBERSHIP,
+ le32_to_cpu(cfge->bitmap_stg));
+ /* If the VLAN only belongs to the current port */
+ if (vlan_port_bitmap == BIT(port)) {
+ err = ntmp_vft_delete_entry(&priv->ntmp, vid);
+ if (err)
+ goto unlock_vft;
+
+ netc_delete_vlan_egress_rule(priv, entry);
+ netc_del_vlan_entry(entry);
+
+ goto unlock_vft;
+ }
+
+ if (!(vlan_port_bitmap & BIT(port)))
+ goto unlock_vft;
+
+ cfge->bitmap_stg &= cpu_to_le32(~BIT(port));
+ err = ntmp_vft_update_entry(&priv->ntmp, vid, cfge);
+ if (err) {
+ cfge->bitmap_stg |= cpu_to_le32(BIT(port));
+ goto unlock_vft;
+ }
+
+unlock_vft:
+ mutex_unlock(&priv->vft_lock);
+
+ return err;
+}
+
static int netc_port_enable(struct dsa_switch *ds, int port,
struct phy_device *phy)
{
@@ -1297,6 +1705,35 @@ static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
netc_port_remove_host_flood(np, old_host_flood);
}
+static int netc_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+ bool untagged;
+
+ /* The 8021q layer may attempt to change NETC_STANDALONE_PVID
+ * (VID 0), so we need to ignore it.
+ */
+ if (vlan->vid == NETC_STANDALONE_PVID)
+ return 0;
+
+ untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+
+ return netc_port_set_vlan_entry(np, vlan->vid, untagged);
+}
+
+static int netc_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct netc_port *np = NETC_PORT(ds, port);
+
+ if (vlan->vid == NETC_STANDALONE_PVID)
+ return 0;
+
+ return netc_port_del_vlan_entry(np, vlan->vid);
+}
+
static void netc_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
@@ -1575,6 +2012,8 @@ static const struct dsa_switch_ops netc_switch_ops = {
.port_mdb_add = netc_port_mdb_add,
.port_mdb_del = netc_port_mdb_del,
.port_set_host_flood = netc_port_set_host_flood,
+ .port_vlan_add = netc_port_vlan_add,
+ .port_vlan_del = netc_port_vlan_del,
.get_pause_stats = netc_port_get_pause_stats,
.get_rmon_stats = netc_port_get_rmon_stats,
.get_eth_ctrl_stats = netc_port_get_eth_ctrl_stats,
diff --git a/drivers/net/dsa/netc/netc_switch.h b/drivers/net/dsa/netc/netc_switch.h
index 4fbd12825b67..9ff334301fbc 100644
--- a/drivers/net/dsa/netc/netc_switch.h
+++ b/drivers/net/dsa/netc/netc_switch.h
@@ -74,6 +74,7 @@ struct netc_port {
struct dsa_port *dp;
struct clk *ref_clk; /* RGMII/RMII reference clock */
struct mii_bus *emdio;
+ int ett_offset;
u16 enable:1;
u16 uc:1;
@@ -94,6 +95,14 @@ struct netc_fdb_entry {
struct hlist_node node;
};
+struct netc_vlan_entry {
+ u16 vid;
+ u32 ect_gid;
+ u32 untagged_port_bitmap;
+ struct vft_cfge_data cfge;
+ struct hlist_node node;
+};
+
struct netc_port_stat {
int reg;
char name[ETH_GSTRING_LEN] __nonstring;
@@ -108,10 +117,13 @@ struct netc_switch {
const struct netc_switch_info *info;
struct netc_switch_regs regs;
struct netc_port **ports;
+ u32 port_bitmap; /* bitmap of available ports */
struct ntmp_user ntmp;
struct hlist_head fdb_list;
struct mutex fdbt_lock; /* FDB table lock */
+ struct hlist_head vlan_list;
+ struct mutex vft_lock; /* VLAN filter table lock */
/* Switch hardware capabilities */
u32 htmcapr_num_words;
@@ -153,6 +165,18 @@ static inline void netc_del_fdb_entry(struct netc_fdb_entry *entry)
kfree(entry);
}
+static inline void netc_add_vlan_entry(struct netc_switch *priv,
+ struct netc_vlan_entry *entry)
+{
+ hlist_add_head(&entry->node, &priv->vlan_list);
+}
+
+static inline void netc_del_vlan_entry(struct netc_vlan_entry *entry)
+{
+ hlist_del(&entry->node);
+ kfree(entry);
+}
+
int netc_switch_platform_probe(struct netc_switch *priv);
/* ethtool APIs */
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 4d329488763d..d3b6c476b91a 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -262,6 +262,21 @@ struct bpt_cfge_data {
__le32 fc_ports;
};
+union ntmp_fmt_eid {
+ __le32 index;
+#define FMTEID_INDEX GENMASK(12, 0)
+ __le32 vuda_sqta;
+#define FMTEID_VUDA GENMASK(1, 0)
+#define FMTEID_VUDA_DEL_OTAG 2
+#define FMTEID_SQTA GENMASK(4, 2)
+#define FMTEID_SQTA_DEL 2
+#define FMTEID_VUDA_SQTA BIT(13)
+ __le32 vara_vid;
+#define FMTEID_VID GENMASK(11, 0)
+#define FMTEID_VARA GENMASK(13, 12)
+#define FMTEID_VARA_VID BIT(14)
+};
+
#if IS_ENABLED(CONFIG_NXP_NETC_LIB)
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs);
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 6/9] net: enetc: add helpers to set/clear table bitmap
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
NTMP index tables require software to allocate and manage entry IDs.
Add two bitmap helper functions to facilitate this management:
ntmp_lookup_free_eid(): finds the first zero bit in the given bitmap,
sets it to mark the entry as in-use, and returns the corresponding entry
ID. Returns NTMP_NULL_ENTRY_ID if no free entry is available.
ntmp_clear_eid_bitmap(): clears the bit associated with the given entry
ID in the bitmap to mark the entry as free. It is a no-op if the entry
ID is NTMP_NULL_ENTRY_ID.
Both functions are exported for use by other modules, such as the NETC
switch driver which needs to manage group index bitmaps for the Egress
Treatment Table (ETT) and Egress Count Table (ECT).
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 24 +++++++++++++++++++++
include/linux/fsl/ntmp.h | 2 ++
2 files changed, 26 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 601435966ed1..0d83f0c9dc6e 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -47,6 +47,30 @@
#define RSST_STSE_DATA_SIZE(n) ((n) * 8)
#define RSST_CFGE_DATA_SIZE(n) (n)
+u32 ntmp_lookup_free_eid(unsigned long *bitmap, u32 size)
+{
+ u32 entry_id;
+
+ entry_id = find_first_zero_bit(bitmap, size);
+ if (entry_id == size)
+ return NTMP_NULL_ENTRY_ID;
+
+ /* Set the bit once we found it */
+ __set_bit(entry_id, bitmap);
+
+ return entry_id;
+}
+EXPORT_SYMBOL_GPL(ntmp_lookup_free_eid);
+
+void ntmp_clear_eid_bitmap(unsigned long *bitmap, u32 entry_id)
+{
+ if (entry_id == NTMP_NULL_ENTRY_ID)
+ return;
+
+ __clear_bit(entry_id, bitmap);
+}
+EXPORT_SYMBOL_GPL(ntmp_clear_eid_bitmap);
+
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs)
{
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index e8b1bd802f19..4d329488763d 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -266,6 +266,8 @@ struct bpt_cfge_data {
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs);
void ntmp_free_cbdr(struct netc_cbdr *cbdr);
+u32 ntmp_lookup_free_eid(unsigned long *bitmap, u32 size);
+void ntmp_clear_eid_bitmap(unsigned long *bitmap, u32 entry_id);
/* NTMP APIs */
int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id,
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 5/9] net: dsa: netc: initialize the group bitmap of ETT and ECT
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
The Egress Treatment Table (ETT) and Egress Count Table (ECT) are both
index tables whose entry IDs are allocated by software. Every num_ports
entries form a group, where each entry in the group corresponds to one
port. To facilitate group allocation and management, initialize the group
index bitmaps for both tables based on hardware capabilities reported by
ETTCAPR and ECTCAPR registers.
The bitmap size per table is calculated as the total number of hardware
entries divided by the number of available ports, which gives the number
of groups available for software allocation. A set bit in the bitmap
represents a group index that has been allocated.
These bitmaps will be used by subsequent patches that add VLAN support.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/dsa/netc/netc_main.c | 90 ++++++++++++++++++++++++++-
drivers/net/dsa/netc/netc_switch_hw.h | 6 ++
include/linux/fsl/ntmp.h | 7 +++
3 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index fa7dd307ce13..d4475ad7ed6c 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -323,16 +323,104 @@ static void netc_remove_all_cbdrs(struct netc_switch *priv)
ntmp_free_cbdr(&ntmp->ring[i]);
}
+static u32 netc_num_available_ports(struct netc_switch *priv)
+{
+ struct dsa_port *dp;
+ u32 num_ports = 0;
+
+ dsa_switch_for_each_available_port(dp, priv->ds)
+ num_ports++;
+
+ return num_ports;
+}
+
+static int netc_init_ntmp_bitmap_sizes(struct netc_switch *priv)
+{
+ u32 num_ports = netc_num_available_ports(priv);
+ struct netc_switch_regs *regs = &priv->regs;
+ struct ntmp_user *ntmp = &priv->ntmp;
+ u32 val;
+
+ if (!num_ports)
+ return -EINVAL;
+
+ val = netc_base_rd(regs, NETC_ETTCAPR);
+ ntmp->ett_bitmap_size = NETC_GET_NUM_ENTRIES(val) / num_ports;
+ if (!ntmp->ett_bitmap_size)
+ return -EINVAL;
+
+ val = netc_base_rd(regs, NETC_ECTCAPR);
+ ntmp->ect_bitmap_size = NETC_GET_NUM_ENTRIES(val) / num_ports;
+ if (!ntmp->ect_bitmap_size)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int netc_init_ntmp_bitmaps(struct netc_switch *priv)
+{
+ struct ntmp_user *ntmp = &priv->ntmp;
+
+ ntmp->ett_gid_bitmap = bitmap_zalloc(ntmp->ett_bitmap_size,
+ GFP_KERNEL);
+ if (!ntmp->ett_gid_bitmap)
+ return -ENOMEM;
+
+ ntmp->ect_gid_bitmap = bitmap_zalloc(ntmp->ect_bitmap_size,
+ GFP_KERNEL);
+ if (!ntmp->ect_gid_bitmap)
+ goto free_ett_gid_bitmap;
+
+ return 0;
+
+free_ett_gid_bitmap:
+ bitmap_free(ntmp->ett_gid_bitmap);
+ ntmp->ett_gid_bitmap = NULL;
+
+ return -ENOMEM;
+}
+
+static void netc_free_ntmp_bitmaps(struct netc_switch *priv)
+{
+ struct ntmp_user *ntmp = &priv->ntmp;
+
+ bitmap_free(ntmp->ect_gid_bitmap);
+ ntmp->ect_gid_bitmap = NULL;
+
+ bitmap_free(ntmp->ett_gid_bitmap);
+ ntmp->ett_gid_bitmap = NULL;
+}
+
static int netc_init_ntmp_user(struct netc_switch *priv)
{
+ int err;
+
netc_init_ntmp_tbl_versions(priv);
- return netc_init_all_cbdrs(priv);
+ err = netc_init_ntmp_bitmap_sizes(priv);
+ if (err)
+ return err;
+
+ err = netc_init_ntmp_bitmaps(priv);
+ if (err)
+ return err;
+
+ err = netc_init_all_cbdrs(priv);
+ if (err)
+ goto free_ntmp_bitmaps;
+
+ return 0;
+
+free_ntmp_bitmaps:
+ netc_free_ntmp_bitmaps(priv);
+
+ return err;
}
static void netc_free_ntmp_user(struct netc_switch *priv)
{
netc_remove_all_cbdrs(priv);
+ netc_free_ntmp_bitmaps(priv);
}
static void netc_switch_dos_default_config(struct netc_switch *priv)
diff --git a/drivers/net/dsa/netc/netc_switch_hw.h b/drivers/net/dsa/netc/netc_switch_hw.h
index 1d976882a6cc..1404ae41c7bc 100644
--- a/drivers/net/dsa/netc/netc_switch_hw.h
+++ b/drivers/net/dsa/netc/netc_switch_hw.h
@@ -36,6 +36,12 @@
#define DOSL3CR_SAMEADDR BIT(0)
#define DOSL3CR_IPSAMCC BIT(1)
+#define NETC_ETTCAPR 0x18c4
+#define NETC_ECTCAPR 0x18ec
+/* Index table NUM_ENTRIES mask */
+#define NETC_NUM_ENTRIES GENMASK(15, 0)
+#define NETC_GET_NUM_ENTRIES(v) FIELD_GET(NETC_NUM_ENTRIES, (v))
+
/* Hash table memory capability register, the memory is shared by
* the following tables:
*
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 1222901f48a7..e8b1bd802f19 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -3,6 +3,7 @@
#ifndef __NETC_NTMP_H
#define __NETC_NTMP_H
+#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/if_ether.h>
@@ -70,6 +71,12 @@ struct ntmp_user {
struct device *dev;
struct netc_cbdr *ring;
struct netc_tbl_vers tbl;
+
+ /* NTMP table bitmaps for resource management */
+ u32 ett_bitmap_size;
+ u32 ect_bitmap_size;
+ unsigned long *ett_gid_bitmap; /* only valid for switch */
+ unsigned long *ect_gid_bitmap; /* only valid for switch */
};
struct maft_entry_data {
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 4/9] net: enetc: add "Update" operation to the egress count table
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
The egress count table is a static bounded index table, egress related
statistics are maintained in this table. The table is implemented as a
linear array of entries accessed using an index (0, 1, 2, ..., n) that
uniquely identifies an entry within the array. Egress Counter Entry ID
(EC_EID) is used as an index to an entry in this table. The EC_EID is
specified in the egress treatment table.
Egress count table entries are always present and enabled. The table
only supports access via entry ID, which is assigned by the software.
And it supports Update, Query and Query followed by Update operations.
Currently, only Update operation is supported.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 45 +++++++++++++++++++++
include/linux/fsl/ntmp.h | 2 +
2 files changed, 47 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 9249f78219ed..601435966ed1 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -25,6 +25,7 @@
#define NTMP_FDBT_ID 15
#define NTMP_VFT_ID 18
#define NTMP_ETT_ID 33
+#define NTMP_ECT_ID 39
#define NTMP_BPT_ID 41
/* Generic Update Actions for most tables */
@@ -33,6 +34,7 @@
/* Specific Update Actions for some tables */
#define FDBT_UA_ACTEU BIT(1)
+#define ECT_UA_STSEU BIT(0)
#define BPT_UA_BPSEU BIT(1)
/* Query Action: 0: Full query. 1: Query entry ID, the fields after entry
@@ -287,6 +289,8 @@ static const char *ntmp_table_name(int tbl_id)
return "VLAN Filter Table";
case NTMP_ETT_ID:
return "Egress Treatment Table";
+ case NTMP_ECT_ID:
+ return "Egress Count Table";
case NTMP_BPT_ID:
return "Buffer Pool Table";
default:
@@ -1197,6 +1201,47 @@ int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id)
}
EXPORT_SYMBOL_GPL(ntmp_ett_delete_entry);
+/**
+ * ntmp_ect_update_entry - reset the statistics element data of the
+ * specified egress counter table entry
+ * @user: target ntmp_user struct
+ * @entry_id: entry ID
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_ect_update_entry(struct ntmp_user *user, u32 entry_id)
+{
+ struct ntmp_req_by_eid *req;
+ struct netc_swcbd swcbd;
+ struct netc_cbdr *cbdr;
+ union netc_cbd cbd;
+ int err;
+
+ swcbd.size = sizeof(*req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd_eid(req, user->tbl.ect_ver, 0, ECT_UA_STSEU, entry_id);
+
+ /* Request header */
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
+ NTMP_ECT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to update %s entry 0x%x, err: %pe\n",
+ ntmp_table_name(NTMP_ECT_ID), entry_id, ERR_PTR(err));
+
+ ntmp_unlock_cbdr(cbdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_ect_update_entry);
+
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
const struct bpt_cfge_data *cfge)
{
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 0c951e1c763d..1222901f48a7 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -37,6 +37,7 @@ struct netc_tbl_vers {
u8 bpt_ver;
u8 ipft_ver;
u8 ett_ver;
+ u8 ect_ver;
};
struct netc_swcbd {
@@ -294,6 +295,7 @@ int ntmp_ett_add_entry(struct ntmp_user *user, u32 entry_id,
int ntmp_ett_update_entry(struct ntmp_user *user, u32 entry_id,
const struct ett_cfge_data *cfge);
int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id);
+int ntmp_ect_update_entry(struct ntmp_user *user, u32 entry_id);
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
const struct bpt_cfge_data *cfge);
#else
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 3/9] net: enetc: add interfaces to manage egress treatment table
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
Each entry in the egress treatment table contains the egress packet
processing actions to be applied to a grouping or scope of packets
exiting on a particular egress port of the switch. A scope of packets,
for example, could be the packets exiting a particular VLAN, matching
a particular 802.1Q bridge forwarding entry or belonging to a stream
identified at ingress. The egress treatment table is implemented as a
linear array of entries accessed using an index (0,1, 2, ..., n) that
uniquely identifies an entry within the array.
The egress treatment table only supports access vid entry ID, which is
assigned by the software. It supports Add, Update, Delete and Query
operations. Note that only Query operation is not supported yet.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 106 ++++++++++++++++++
.../ethernet/freescale/enetc/ntmp_private.h | 8 ++
include/linux/fsl/ntmp.h | 23 ++++
3 files changed, 137 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 4e60bbc38cfa..9249f78219ed 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -24,6 +24,7 @@
#define NTMP_IPFT_ID 13
#define NTMP_FDBT_ID 15
#define NTMP_VFT_ID 18
+#define NTMP_ETT_ID 33
#define NTMP_BPT_ID 41
/* Generic Update Actions for most tables */
@@ -284,6 +285,8 @@ static const char *ntmp_table_name(int tbl_id)
return "FDB Table";
case NTMP_VFT_ID:
return "VLAN Filter Table";
+ case NTMP_ETT_ID:
+ return "Egress Treatment Table";
case NTMP_BPT_ID:
return "Buffer Pool Table";
default:
@@ -1091,6 +1094,109 @@ int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid)
}
EXPORT_SYMBOL_GPL(ntmp_vft_delete_entry);
+/**
+ * ntmp_ett_set_entry - add a new entry to the egress treatment table or
+ * update the configuration element data of the specified entry
+ * @user: target ntmp_user struct
+ * @entry_id: entry ID
+ * @cmd: command type, NTMP_CMD_ADD or NTMP_CMD_UPDATE
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+static int ntmp_ett_set_entry(struct ntmp_user *user, u32 entry_id,
+ int cmd, const struct ett_cfge_data *cfge)
+{
+ struct netc_swcbd swcbd;
+ struct ett_req_ua *req;
+ struct netc_cbdr *cbdr;
+ union netc_cbd cbd;
+ int err;
+
+ if (cmd != NTMP_CMD_ADD && cmd != NTMP_CMD_UPDATE)
+ return -EINVAL;
+
+ swcbd.size = sizeof(*req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd_eid(&req->rbe, user->tbl.ett_ver, 0,
+ NTMP_GEN_UA_CFGEU, entry_id);
+ req->cfge = *cfge;
+
+ /* Request header */
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0),
+ NTMP_ETT_ID, cmd, NTMP_AM_ENTRY_ID);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ ntmp_unlock_cbdr(cbdr);
+
+ return err;
+}
+
+/**
+ * ntmp_ett_add_entry - add a new entry to the egress treatment table
+ * @user: target ntmp_user struct
+ * @entry_id: entry ID
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_ett_add_entry(struct ntmp_user *user, u32 entry_id,
+ const struct ett_cfge_data *cfge)
+{
+ int err;
+
+ err = ntmp_ett_set_entry(user, entry_id, NTMP_CMD_ADD, cfge);
+ if (err)
+ dev_err(user->dev, "Failed to add %s entry 0x%x, err: %pe\n",
+ ntmp_table_name(NTMP_ETT_ID), entry_id, ERR_PTR(err));
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_ett_add_entry);
+
+/**
+ * ntmp_ett_update_entry - update the configuration element data of the
+ * specified entry
+ * @user: target ntmp_user struct
+ * @entry_id: entry ID
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_ett_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct ett_cfge_data *cfge)
+{
+ int err;
+
+ err = ntmp_ett_set_entry(user, entry_id, NTMP_CMD_UPDATE, cfge);
+ if (err)
+ dev_err(user->dev,
+ "Failed to update %s entry 0x%x, err: %pe\n",
+ ntmp_table_name(NTMP_ETT_ID), entry_id, ERR_PTR(err));
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_ett_update_entry);
+
+/**
+ * ntmp_ett_delete_entry - delete the specified egress treatment table entry
+ * @user: target ntmp_user struct
+ * @entry_id: entry ID
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id)
+{
+ return ntmp_delete_entry_by_id(user, NTMP_ETT_ID, user->tbl.ett_ver,
+ entry_id, NTMP_EID_REQ_LEN, 0);
+}
+EXPORT_SYMBOL_GPL(ntmp_ett_delete_entry);
+
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
const struct bpt_cfge_data *cfge)
{
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 9d30f128849a..531ea7ddd145 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -217,6 +217,14 @@ struct vft_req_qd {
union vft_access_key ak;
};
+/* Egress Treatment Table Request Data Buffer Format of Update and Add
+ * actions
+ */
+struct ett_req_ua {
+ struct ntmp_req_by_eid rbe;
+ struct ett_cfge_data cfge;
+};
+
/* Buffer Pool Table Request Data Buffer Format of Update action */
struct bpt_req_update {
struct ntmp_req_by_eid rbe;
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 36a9089526ad..0c951e1c763d 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -36,6 +36,7 @@ struct netc_tbl_vers {
u8 vft_ver;
u8 bpt_ver;
u8 ipft_ver;
+ u8 ett_ver;
};
struct netc_swcbd {
@@ -214,6 +215,23 @@ struct vft_cfge_data {
__le32 et_eid;
};
+struct ett_cfge_data {
+ __le16 efm_cfg;
+#define ETT_EFM_MODE GENMASK(1, 0)
+#define ETT_ESQA GENMASK(5, 4)
+#define ETT_ECA GENMASK(8, 6)
+#define ETT_ECA_INC 1
+#define ETT_EFM_LEN_CHANGE GENMASK(15, 9)
+#define ETT_FRM_LEN_DEL_VLAN 0x7c
+#define ETT_FRM_LEN_DEL_RTAG 0x7a
+#define ETT_FRM_LEN_DEL_VLAN_RTAG 0x76
+ __le16 efm_data_len;
+#define ETT_EFM_DATA_LEN GENMASK(10, 0)
+ __le32 efm_eid;
+ __le32 ec_eid;
+ __le32 esqa_tgt_eid;
+};
+
struct bpt_bpse_data {
__le32 amount_used;
__le32 amount_used_hwm;
@@ -271,6 +289,11 @@ int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
int ntmp_vft_update_entry(struct ntmp_user *user, u16 vid,
const struct vft_cfge_data *cfge);
int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid);
+int ntmp_ett_add_entry(struct ntmp_user *user, u32 entry_id,
+ const struct ett_cfge_data *cfge);
+int ntmp_ett_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct ett_cfge_data *cfge);
+int ntmp_ett_delete_entry(struct ntmp_user *user, u32 entry_id);
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
const struct bpt_cfge_data *cfge);
#else
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 2/9] net: enetc: add "Update" and "Delete" operations to VLAN filter table
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
Add two interfaces to manage entries in the VLAN filter table:
ntmp_vft_update_entry(): Update the configuration element data of the
specified VLAN filter entry based on the given VLAN ID. It uses the
exact key access method to locate the entry.
ntmp_vft_delete_entry(): Delete the VLAN filter entry corresponding to
the specified VLAN ID. It also uses the exact key access method to
identify the target entry.
In addition, introduce struct vft_req_qd to describe the request data
buffer format for Query and Delete actions of the VLAN filter table,
which contains a common request data header and a VLAN access key.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 103 ++++++++++++++++--
.../ethernet/freescale/enetc/ntmp_private.h | 6 +
include/linux/fsl/ntmp.h | 3 +
3 files changed, 105 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index bda26fe93b8d..4e60bbc38cfa 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -956,15 +956,17 @@ int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port)
EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_port_dynamic_entries);
/**
- * ntmp_vft_add_entry - add an entry into the VLAN filter table
+ * ntmp_vft_set_entry - add an entry into the VLAN filter table or update
+ * the configuration element data of the specified VLAN filter entry
* @user: target ntmp_user struct
* @vid: VLAN ID
+ * @cmd: command type, NTMP_CMD_ADD or NTMP_CMD_UPDATE
* @cfge: configuration element data
*
* Return: 0 on success, otherwise a negative error code
*/
-int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
- const struct vft_cfge_data *cfge)
+static int ntmp_vft_set_entry(struct ntmp_user *user, u16 vid, int cmd,
+ const struct vft_cfge_data *cfge)
{
struct netc_swcbd swcbd;
struct vft_req_ua *req;
@@ -973,34 +975,121 @@ int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
u32 len;
int err;
+ if (cmd != NTMP_CMD_ADD && cmd != NTMP_CMD_UPDATE)
+ return -EINVAL;
+
swcbd.size = sizeof(*req);
err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
if (err)
return err;
/* Request data */
- ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0,
- NTMP_GEN_UA_CFGEU);
+ ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, NTMP_GEN_UA_CFGEU);
req->ak.exact.vid = cpu_to_le16(vid);
req->cfge = *cfge;
/* Request header */
len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID,
- NTMP_CMD_ADD, NTMP_AM_EXACT_KEY);
+ cmd, NTMP_AM_EXACT_KEY);
ntmp_select_and_lock_cbdr(user, &cbdr);
err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ ntmp_unlock_cbdr(cbdr);
+
+ return err;
+}
+
+/**
+ * ntmp_vft_add_entry - add an entry into the VLAN filter table
+ * @user: target ntmp_user struct
+ * @vid: VLAN ID
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
+ const struct vft_cfge_data *cfge)
+{
+ int err;
+
+ err = ntmp_vft_set_entry(user, vid, NTMP_CMD_ADD, cfge);
if (err)
dev_err(user->dev,
"Failed to add %s entry, vid: %u, err: %pe\n",
ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_vft_add_entry);
+
+/**
+ * ntmp_vft_update_entry - update the configuration element data of the
+ * specified VLAN filter entry
+ * @user: target ntmp_user struct
+ * @vid: VLAN ID
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_vft_update_entry(struct ntmp_user *user, u16 vid,
+ const struct vft_cfge_data *cfge)
+{
+ int err;
+
+ err = ntmp_vft_set_entry(user, vid, NTMP_CMD_UPDATE, cfge);
+ if (err)
+ dev_err(user->dev,
+ "Failed to update %s entry, vid: %u, err: %pe\n",
+ ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_vft_update_entry);
+
+/**
+ * ntmp_vft_delete_entry - delete the VLAN filter entry based on the
+ * specified VLAN ID
+ * @user: target ntmp_user struct
+ * @vid: VLAN ID
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid)
+{
+ struct netc_swcbd swcbd;
+ struct vft_req_qd *req;
+ struct netc_cbdr *cbdr;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ swcbd.size = sizeof(*req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0, 0);
+ req->ak.exact.vid = cpu_to_le16(vid);
+
+ /* Request header */
+ len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_VFT_ID,
+ NTMP_CMD_DELETE, NTMP_AM_EXACT_KEY);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to delete %s entry, vid: %u, err: %pe\n",
+ ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
+
ntmp_unlock_cbdr(cbdr);
return err;
}
-EXPORT_SYMBOL_GPL(ntmp_vft_add_entry);
+EXPORT_SYMBOL_GPL(ntmp_vft_delete_entry);
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
const struct bpt_cfge_data *cfge)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index ad532b059ba8..9d30f128849a 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -211,6 +211,12 @@ struct vft_req_ua {
struct vft_cfge_data cfge;
};
+/* VLAN Filter Table Request Data Buffer Format of Query and Delete actions */
+struct vft_req_qd {
+ struct ntmp_cmn_req_data crd;
+ union vft_access_key ak;
+};
+
/* Buffer Pool Table Request Data Buffer Format of Update action */
struct bpt_req_update {
struct ntmp_req_by_eid rbe;
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 5db078e1caa0..36a9089526ad 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -268,6 +268,9 @@ int ntmp_fdbt_delete_ageing_entries(struct ntmp_user *user, u8 act_cnt);
int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port);
int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
const struct vft_cfge_data *cfge);
+int ntmp_vft_update_entry(struct ntmp_user *user, u16 vid,
+ const struct vft_cfge_data *cfge);
+int ntmp_vft_delete_entry(struct ntmp_user *user, u16 vid);
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
const struct bpt_cfge_data *cfge);
#else
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 1/9] net: enetc: add interfaces to manage dynamic FDB entries
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
In-Reply-To: <20260611021458.2629145-1-wei.fang@oss.nxp.com>
From: Wei Fang <wei.fang@nxp.com>
Add three interfaces to manage dynamic entries in the FDB table:
ntmp_fdbt_update_activity_element(): Update the activity element of all
dynamic FDB entries. For each entry, if its activity flag is not set,
which means no packet has matched this entry since the last update, the
activity counter is incremented. Otherwise, both the activity flag and
activity counter are reset. The activity counter is used to track how
long an FDB entry has been inactive, which is useful for implementing
an ageing mechanism.
ntmp_fdbt_delete_ageing_entries(): Delete all dynamic FDB entries whose
activity flag is not set and whose activity counter is greater than or
equal to the specified threshold. This is used to remove stale entries
that have been inactive for too long.
ntmp_fdbt_delete_port_dynamic_entries(): Delete all dynamic FDB entries
associated with the specified switch port. This is typically called when
a port goes down or is removed from a bridge.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 162 ++++++++++++++++++
.../ethernet/freescale/enetc/ntmp_private.h | 4 +-
include/linux/fsl/ntmp.h | 3 +
3 files changed, 167 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index f71cad943424..bda26fe93b8d 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -31,6 +31,7 @@
#define NTMP_GEN_UA_STSEU BIT(1)
/* Specific Update Actions for some tables */
+#define FDBT_UA_ACTEU BIT(1)
#define BPT_UA_BPSEU BIT(1)
/* Query Action: 0: Full query. 1: Query entry ID, the fields after entry
@@ -793,6 +794,167 @@ int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
}
EXPORT_SYMBOL_GPL(ntmp_fdbt_search_port_entry);
+/**
+ * ntmp_fdbt_update_activity_element - update the activity element of all
+ * the dynamic entries in the FDB table.
+ * @user: target ntmp_user struct
+ *
+ * A single activity update management could be used to process all the
+ * dynamic entries in the FDB table. When hardware process an activity
+ * update management command for an entry in the FDB table and the entry
+ * does not have its activity flag set, the activity counter is incremented.
+ * However, if the activity flag is set, then both the activity flag and
+ * activity counter are reset. Software can issue the activity update
+ * management commands at predefined times and the value of the activity
+ * counter can then be used to estimate the period of how long an FDB
+ * entry has been inactive.
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_update_activity_element(struct ntmp_user *user)
+{
+ struct fdbt_req_ua *req;
+ struct netc_swcbd swcbd;
+ struct netc_cbdr *cbdr;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ swcbd.size = sizeof(*req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, FDBT_UA_ACTEU);
+ req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
+ req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC);
+ req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC;
+
+ /* Request header */
+ len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
+ /* For activity update, the access method must be search */
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
+ NTMP_CMD_UPDATE, NTMP_AM_SEARCH);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to update activity of %s, err: %pe\n",
+ ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
+
+ ntmp_unlock_cbdr(cbdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_update_activity_element);
+
+/**
+ * ntmp_fdbt_delete_ageing_entries - delete all the ageing dynamic entries
+ * in the FDB table
+ * @user: target ntmp_user struct
+ * @act_cnt: the target value of the activity counter
+ *
+ * The matching rule is that the activity flag is not set and the activity
+ * counter is greater than or equal to act_cnt
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_delete_ageing_entries(struct ntmp_user *user, u8 act_cnt)
+{
+ struct fdbt_req_qd *req;
+ struct netc_swcbd swcbd;
+ struct netc_cbdr *cbdr;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ if (act_cnt > FDBT_ACT_CNT)
+ return -EINVAL;
+
+ swcbd.size = sizeof(*req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
+ req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
+ req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC);
+ req->ak.search.acte = act_cnt;
+ /* Exact match with ACTE_DATA[ACT_FLAG] AND
+ * match >= ACTE_DATA[ACT_CNT]
+ */
+ req->ak.search.acte_mc = FDBT_ACTE_MC;
+ req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC;
+
+ /* Request header */
+ len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
+ NTMP_CMD_DELETE, NTMP_AM_SEARCH);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to delete ageing entries of %s, err: %pe\n",
+ ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
+
+ ntmp_unlock_cbdr(cbdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_ageing_entries);
+
+/**
+ * ntmp_fdbt_delete_port_dynamic_entries - delete all dynamic FDB entries
+ * associated with the specified switch port
+ * @user: target ntmp_user struct
+ * @port: the specified switch port ID
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port)
+{
+ struct fdbt_req_qd *req;
+ struct netc_swcbd swcbd;
+ struct netc_cbdr *cbdr;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ swcbd.size = sizeof(*req);
+ err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
+ req->ak.search.resume_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
+ req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port));
+ req->ak.search.cfge.cfg = cpu_to_le32(FDBT_DYNAMIC);
+ /* Match CFGE_DATA[DYNAMIC & PORT_BITMAP] field */
+ req->ak.search.cfge_mc = FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP;
+
+ /* Request header */
+ len = NTMP_LEN(swcbd.size, NTMP_STATUS_RESP_LEN);
+ ntmp_fill_request_hdr(&cbd, swcbd.dma, len, NTMP_FDBT_ID,
+ NTMP_CMD_DELETE, NTMP_AM_SEARCH);
+
+ ntmp_select_and_lock_cbdr(user, &cbdr);
+ err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to delete dynamic %s entries on port %d, err: %pe\n",
+ ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err));
+
+ ntmp_unlock_cbdr(cbdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_port_dynamic_entries);
+
/**
* ntmp_vft_add_entry - add an entry into the VLAN filter table
* @user: target ntmp_user struct
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 0a9b87286105..ad532b059ba8 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -155,8 +155,8 @@ struct fdbt_ak_search {
#define FDBT_KEYE_MAC GENMASK(1, 0)
u8 cfge_mc;
#define FDBT_CFGE_MC GENMASK(2, 0)
-#define FDBT_CFGE_MC_ANY 0
-#define FDBT_CFGE_MC_DYNAMIC 1
+#define FDBT_CFGE_MC_ANY 0
+#define FDBT_CFGE_MC_DYNAMIC 1
#define FDBT_CFGE_MC_PORT_BITMAP 2
#define FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP 3
u8 acte_mc;
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 88166f9ad3a2..5db078e1caa0 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -263,6 +263,9 @@ int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id);
int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
u32 *resume_entry_id,
struct fdbt_entry_data *entry);
+int ntmp_fdbt_update_activity_element(struct ntmp_user *user);
+int ntmp_fdbt_delete_ageing_entries(struct ntmp_user *user, u8 act_cnt);
+int ntmp_fdbt_delete_port_dynamic_entries(struct ntmp_user *user, int port);
int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
const struct vft_cfge_data *cfge);
int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
--
2.34.1
^ permalink raw reply related
* [PATCH v5 net-next 0/9] net: dsa: netc: add bridge mode support
From: wei.fang @ 2026-06-11 2:14 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, chleroy, andrew, olteanv, linux
Cc: wei.fang, imx, netdev, linux-kernel, linuxppc-dev,
linux-arm-kernel
From: Wei Fang <wei.fang@nxp.com>
This series adds bridge mode support to the NETC DSA switch driver,
covering both VLAN-aware and VLAN-unaware operation.
The NETC switch manages forwarding through a set of hardware tables
accessed via NTMP: the FDB table (FDBT), VLAN filter table (VFT), egress
treatment table (ETT), and egress count table (ECT). The series extends
the NTMP layer with the operations required for bridging, then builds the
DSA bridge callbacks on top.
Since all switch ports share the VFT, so only one VLAN-aware bridge is
supported.
FDB aging is managed in software. A periodic delayed work sweeps the
table using the hardware activity element mechanism, with a default aging
time of 300 seconds matching the IEEE 802.1Q standard. Per-port entries
are also flushed immediately on bridge leave and link-down events.
---
v5:
1. Use __set_bit()/__clear_bit() instead of set_bit()/clear_bit() in
ntmp_lookup_free_eid()/ntmp_clear_eid_bitmap()
2. Correct the ect_eid check in netc_delete_vlan_egress_rule()
v4 link: https://lore.kernel.org/imx/20260609032955.2066089-1-wei.fang@oss.nxp.com/
v3 link: https://lore.kernel.org/imx/20260605014808.686024-1-wei.fang@oss.nxp.com/
v2 link: https://lore.kernel.org/imx/20260602072313.3162120-1-wei.fang@oss.nxp.com/
v1 link: https://lore.kernel.org/imx/20260527100217.794987-1-wei.fang@oss.nxp.com/
---
Wei Fang (9):
net: enetc: add interfaces to manage dynamic FDB entries
net: enetc: add "Update" and "Delete" operations to VLAN filter table
net: enetc: add interfaces to manage egress treatment table
net: enetc: add "Update" operation to the egress count table
net: dsa: netc: initialize the group bitmap of ETT and ECT
net: enetc: add helpers to set/clear table bitmap
net: dsa: netc: add VLAN filter table and egress treatment management
net: dsa: netc: add bridge mode support
net: dsa: netc: implement dynamic FDB entry ageing
drivers/net/dsa/netc/netc_main.c | 965 +++++++++++++++++-
drivers/net/dsa/netc/netc_switch.h | 33 +
drivers/net/dsa/netc/netc_switch_hw.h | 6 +
drivers/net/ethernet/freescale/enetc/ntmp.c | 440 +++++++-
.../ethernet/freescale/enetc/ntmp_private.h | 18 +-
include/linux/fsl/ntmp.h | 55 +
6 files changed, 1495 insertions(+), 22 deletions(-)
--
2.34.1
^ permalink raw reply
* Re: [PATCH v3 00/11] kdump: reduce vmcore size and capture time
From: Wandun @ 2026-06-11 2:09 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, loongarch, linux-riscv,
devicetree, kexec, iommu, zhaomeijing, Rob Herring, saravanak,
bhe, rppt, pjw, palmer, aou, chenhuacai, kernel
Cc: catalin.marinas, will, alex, akpm, pasha.tatashin, pratyush,
ruirui.yang, m.szyprowski, robin.murphy
In-Reply-To: <20260527032917.3385849-1-chenwandun1@gmail.com>
On 5/27/26 11:29, Wandun Chen wrote:
> From: Wandun Chen <chenwandun@lixiang.com>
>
> On SoCs that carve out large firmware-owned reserved memory (GPU
> firmware, DSP, modem, camera ISP, NPU, ...), kdump currently dumps
> those carveouts as part of system RAM even though their contents are
> firmware state that is not useful for kernel crash analysis.
>
> This series introduces an opt-in 'dumpable' flag [1] on struct
> reserved_mem and uses it to filter the elfcorehdr PT_LOAD ranges on
> DT-based architectures (arm64, riscv, loongarch). By default reserved
> regions are treated as non-dumpable; CMA regions are explicitly opted
> in because their pages are returned to the buddy allocator and may
> carry key crash-analysis data.
>
> The series is organized as follows:
> Patches 1-3: Pre-existing fixes and a small prep change.
> Patches 4-5: Restructure to allow appending /memreserve/ entries.
> Patches 6-7: Add a dumpable flag and append /memreserve/ entries.
> Patch 8: Add generic kdump helpers.
> Patches 9-11: Wire the helpers into arm64, riscv and loongarch kdump
> elfcorehdr preparation.
Hi,
Gentle ping on this series.
Status summary:
-patch 03: respun separately per Rob's suggestion, picked up for 7.2
-patch 06: Acked-by: Marek Szyprowski -patch 09: Acked-by: Will Deacon
The remaining patches (01, 02, 04, 05, 07, 08, 10, 11) are still
awaiting review. your feedback would be greately appreciated. I know we
are at the end of 7.1 -rc cycle, I don't want to rush this series, just
collecting more feedback, and will send next version based on 7.2-rc1.
If spliting the series into smaller logical group would make review
easier, please let me know. Best regards, Wandun
>
> v2 --> v3:
> 1. Fix out-of-bounds issue if device tree lacks /reserved-memory node.[2]
> 2. Fix UAF issue when alloc_reserved_mem_array() fails.
> 3. Add some prepare patches.
>
> v1 --> v2:
> 1. v1 added an opt-out DT property ('linux,no-dump'). Per Rob's
> feedback [1], v2 drop that property and exclude reserve memory
> by default.
> 2. Split some prepared patches from the original patches.
> 3. Address coding-style comments on patch 5 from Rob.
>
> [1] https://lore.kernel.org/lkml/20260506144542.GA2072596-robh@kernel.org/
> [2] https://sashiko.dev/#/patchset/20260520091844.592753-1-chenwandun%40lixiang.com?part=4
>
> Wandun Chen (11):
> of: reserved_mem: handle NULL name in of_reserved_mem_lookup()
> kexec/crash: provide crash_exclude_mem_range() stub when
> CONFIG_CRASH_DUMP=n
> of: reserved_mem: avoid post-init UAF when alloc_reserved_mem_array()
> fails
> of: reserved_mem: zero total_reserved_mem_cnt if no valid
> /reserved-memory entry
> of: reserved_mem: split alloc_reserved_mem_array() from
> fdt_scan_reserved_mem_late()
> of: reserved_mem: add dumpable flag to opt-in vmcore
> of: reserved_mem: save /memreserve/ entries into the reserved_mem
> array
> of: reserved_mem: add kdump helpers to exclude non-dumpable regions
> arm64: kdump: exclude non-dumpable reserved memory regions from vmcore
> riscv: kdump: exclude non-dumpable reserved memory regions from vmcore
> loongarch: kdump: exclude non-dumpable reserved memory regions from
> vmcore
>
> arch/arm64/kernel/machine_kexec_file.c | 6 ++
> arch/loongarch/kernel/machine_kexec_file.c | 6 ++
> arch/riscv/kernel/machine_kexec_file.c | 4 +
> drivers/of/fdt.c | 11 +-
> drivers/of/of_private.h | 3 +
> drivers/of/of_reserved_mem.c | 117 +++++++++++++++++++--
> include/linux/crash_core.h | 6 ++
> include/linux/of_reserved_mem.h | 15 +++
> kernel/dma/contiguous.c | 1 +
> 9 files changed, 157 insertions(+), 12 deletions(-)
>
^ permalink raw reply
* [soc:sophgo/dt] BUILD SUCCESS 903a9364e40563faf4730dc63ad7446246f494ff
From: kernel test robot @ 2026-06-11 1:53 UTC (permalink / raw)
To: Inochi Amaoto; +Cc: linux-arm-kernel, arm
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git sophgo/dt
branch HEAD: 903a9364e40563faf4730dc63ad7446246f494ff riscv: dts: sophgo: reduce SG2042 MSI count to 16
elapsed time: 2231m
configs tested: 295
configs skipped: 3
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-16.1.0
alpha allyesconfig gcc-16.1.0
alpha defconfig gcc-16.1.0
arc allmodconfig clang-23
arc allmodconfig gcc-16.1.0
arc allnoconfig gcc-16.1.0
arc allyesconfig clang-23
arc allyesconfig gcc-16.1.0
arc defconfig gcc-16.1.0
arc randconfig-001-20260610 gcc-8.5.0
arc randconfig-001-20260611 gcc-14.3.0
arc randconfig-002-20260610 gcc-9.5.0
arc randconfig-002-20260611 gcc-14.3.0
arm allnoconfig clang-23
arm allnoconfig gcc-16.1.0
arm allyesconfig clang-23
arm allyesconfig gcc-16.1.0
arm defconfig clang-23
arm defconfig gcc-16.1.0
arm mvebu_v7_defconfig clang-23
arm randconfig-001-20260610 clang-23
arm randconfig-001-20260611 gcc-14.3.0
arm randconfig-002-20260610 clang-20
arm randconfig-002-20260611 gcc-14.3.0
arm randconfig-003-20260610 clang-23
arm randconfig-003-20260611 gcc-14.3.0
arm randconfig-004-20260610 clang-23
arm randconfig-004-20260611 gcc-14.3.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-16.1.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001-20260610 gcc-11.5.0
arm64 randconfig-001-20260611 gcc-14.3.0
arm64 randconfig-002-20260610 gcc-9.5.0
arm64 randconfig-002-20260611 gcc-14.3.0
arm64 randconfig-003-20260610 gcc-8.5.0
arm64 randconfig-003-20260611 gcc-14.3.0
arm64 randconfig-004-20260610 clang-23
arm64 randconfig-004-20260611 gcc-14.3.0
csky allmodconfig gcc-16.1.0
csky allnoconfig gcc-16.1.0
csky defconfig gcc-16.1.0
csky randconfig-001-20260610 gcc-9.5.0
csky randconfig-001-20260611 gcc-14.3.0
csky randconfig-002-20260610 gcc-11.5.0
csky randconfig-002-20260611 gcc-14.3.0
hexagon allmodconfig clang-23
hexagon allmodconfig gcc-16.1.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-16.1.0
hexagon defconfig clang-23
hexagon defconfig gcc-16.1.0
hexagon randconfig-001-20260610 clang-23
hexagon randconfig-001-20260611 clang-17
hexagon randconfig-002-20260610 clang-22
hexagon randconfig-002-20260611 clang-17
i386 allmodconfig clang-22
i386 allnoconfig gcc-14
i386 allnoconfig gcc-16.1.0
i386 allyesconfig clang-22
i386 buildonly-randconfig-001 clang-22
i386 buildonly-randconfig-001-20260610 gcc-14
i386 buildonly-randconfig-001-20260611 clang-22
i386 buildonly-randconfig-002 clang-22
i386 buildonly-randconfig-002-20260610 gcc-14
i386 buildonly-randconfig-002-20260611 clang-22
i386 buildonly-randconfig-003 clang-22
i386 buildonly-randconfig-003-20260610 gcc-14
i386 buildonly-randconfig-003-20260611 clang-22
i386 buildonly-randconfig-004 clang-22
i386 buildonly-randconfig-004-20260610 gcc-14
i386 buildonly-randconfig-004-20260611 clang-22
i386 buildonly-randconfig-005 clang-22
i386 buildonly-randconfig-005-20260610 gcc-14
i386 buildonly-randconfig-005-20260611 clang-22
i386 buildonly-randconfig-006 clang-22
i386 buildonly-randconfig-006-20260610 clang-22
i386 buildonly-randconfig-006-20260611 clang-22
i386 defconfig clang-22
i386 defconfig gcc-16.1.0
i386 randconfig-001-20260610 gcc-14
i386 randconfig-001-20260611 gcc-14
i386 randconfig-002-20260610 gcc-14
i386 randconfig-002-20260611 gcc-14
i386 randconfig-003-20260610 clang-22
i386 randconfig-003-20260611 gcc-14
i386 randconfig-004-20260610 gcc-14
i386 randconfig-004-20260611 gcc-14
i386 randconfig-005-20260610 clang-22
i386 randconfig-005-20260611 gcc-14
i386 randconfig-006-20260610 gcc-14
i386 randconfig-006-20260611 gcc-14
i386 randconfig-007-20260610 gcc-14
i386 randconfig-007-20260611 gcc-14
i386 randconfig-011-20260610 clang-22
i386 randconfig-011-20260611 gcc-14
i386 randconfig-012-20260610 clang-22
i386 randconfig-012-20260611 gcc-14
i386 randconfig-013-20260610 clang-22
i386 randconfig-013-20260611 gcc-14
i386 randconfig-014-20260610 clang-22
i386 randconfig-014-20260611 gcc-14
i386 randconfig-015-20260610 clang-22
i386 randconfig-015-20260611 gcc-14
i386 randconfig-016-20260610 clang-22
i386 randconfig-016-20260611 gcc-14
i386 randconfig-017-20260610 gcc-14
i386 randconfig-017-20260611 gcc-14
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-20
loongarch allnoconfig gcc-16.1.0
loongarch defconfig clang-23
loongarch randconfig-001-20260610 gcc-16.1.0
loongarch randconfig-001-20260611 clang-17
loongarch randconfig-002-20260610 gcc-16.1.0
loongarch randconfig-002-20260611 clang-17
m68k allmodconfig gcc-16.1.0
m68k allnoconfig gcc-16.1.0
m68k allyesconfig clang-23
m68k allyesconfig gcc-16.1.0
m68k defconfig clang-23
m68k defconfig gcc-16.1.0
m68k mvme16x_defconfig gcc-16.1.0
microblaze allnoconfig gcc-16.1.0
microblaze allyesconfig gcc-16.1.0
microblaze defconfig clang-23
microblaze defconfig gcc-16.1.0
mips allmodconfig gcc-16.1.0
mips allnoconfig gcc-16.1.0
mips allyesconfig gcc-16.1.0
nios2 allmodconfig clang-20
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-23
nios2 defconfig gcc-11.5.0
nios2 randconfig-001-20260610 gcc-11.5.0
nios2 randconfig-001-20260611 clang-17
nios2 randconfig-002-20260610 gcc-11.5.0
nios2 randconfig-002-20260611 clang-17
openrisc allmodconfig clang-20
openrisc allmodconfig gcc-16.1.0
openrisc allnoconfig clang-23
openrisc allnoconfig gcc-16.1.0
openrisc defconfig gcc-16.1.0
openrisc simple_smp_defconfig gcc-16.1.0
parisc allmodconfig gcc-16.1.0
parisc allnoconfig clang-23
parisc allnoconfig gcc-16.1.0
parisc allyesconfig clang-17
parisc allyesconfig gcc-16.1.0
parisc defconfig gcc-16.1.0
parisc randconfig-001-20260610 gcc-8.5.0
parisc randconfig-001-20260611 gcc-13.4.0
parisc randconfig-002-20260610 gcc-8.5.0
parisc randconfig-002-20260611 gcc-13.4.0
parisc64 defconfig clang-23
parisc64 defconfig gcc-16.1.0
powerpc allmodconfig gcc-16.1.0
powerpc allnoconfig clang-23
powerpc allnoconfig gcc-16.1.0
powerpc randconfig-001-20260610 gcc-9.5.0
powerpc randconfig-001-20260611 gcc-13.4.0
powerpc randconfig-002-20260610 clang-23
powerpc randconfig-002-20260611 gcc-13.4.0
powerpc64 randconfig-001-20260610 clang-23
powerpc64 randconfig-001-20260611 gcc-13.4.0
powerpc64 randconfig-002-20260610 gcc-10.5.0
powerpc64 randconfig-002-20260611 gcc-13.4.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allnoconfig gcc-16.1.0
riscv allyesconfig clang-23
riscv defconfig clang-23
riscv defconfig gcc-16.1.0
riscv randconfig-001-20260610 gcc-15.2.0
riscv randconfig-001-20260611 gcc-12.5.0
riscv randconfig-002-20260610 gcc-8.5.0
riscv randconfig-002-20260611 gcc-12.5.0
s390 allmodconfig clang-17
s390 allmodconfig clang-23
s390 allnoconfig clang-23
s390 allyesconfig gcc-16.1.0
s390 defconfig clang-18
s390 defconfig gcc-16.1.0
s390 randconfig-001-20260610 clang-23
s390 randconfig-001-20260611 gcc-12.5.0
s390 randconfig-002-20260610 clang-23
s390 randconfig-002-20260611 gcc-12.5.0
sh allmodconfig gcc-16.1.0
sh allnoconfig clang-23
sh allnoconfig gcc-16.1.0
sh allyesconfig clang-17
sh allyesconfig gcc-16.1.0
sh defconfig gcc-14
sh defconfig gcc-16.1.0
sh randconfig-001-20260610 gcc-16.1.0
sh randconfig-001-20260611 gcc-12.5.0
sh randconfig-002-20260610 gcc-16.1.0
sh randconfig-002-20260611 gcc-12.5.0
sh sh7770_generic_defconfig gcc-16.1.0
sparc allnoconfig clang-23
sparc allnoconfig gcc-16.1.0
sparc defconfig gcc-16.1.0
sparc randconfig-001-20260610 gcc-8.5.0
sparc randconfig-001-20260611 gcc-15.2.0
sparc randconfig-002-20260610 gcc-14.3.0
sparc randconfig-002-20260611 gcc-15.2.0
sparc64 allmodconfig clang-20
sparc64 defconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260610 gcc-12.5.0
sparc64 randconfig-001-20260611 gcc-15.2.0
sparc64 randconfig-002-20260610 clang-23
sparc64 randconfig-002-20260611 gcc-15.2.0
um allmodconfig clang-17
um allmodconfig clang-23
um allnoconfig clang-16
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-16.1.0
um defconfig clang-23
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260610 clang-23
um randconfig-001-20260611 gcc-15.2.0
um randconfig-002-20260610 clang-18
um randconfig-002-20260611 gcc-15.2.0
um x86_64_defconfig clang-23
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-22
x86_64 allnoconfig clang-22
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-22
x86_64 buildonly-randconfig-001-20260610 clang-22
x86_64 buildonly-randconfig-001-20260611 gcc-14
x86_64 buildonly-randconfig-002-20260610 gcc-14
x86_64 buildonly-randconfig-002-20260611 gcc-14
x86_64 buildonly-randconfig-003-20260610 clang-22
x86_64 buildonly-randconfig-003-20260611 gcc-14
x86_64 buildonly-randconfig-004-20260610 clang-22
x86_64 buildonly-randconfig-004-20260611 gcc-14
x86_64 buildonly-randconfig-005-20260610 gcc-14
x86_64 buildonly-randconfig-005-20260611 gcc-14
x86_64 buildonly-randconfig-006-20260610 gcc-14
x86_64 buildonly-randconfig-006-20260611 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001-20260610 clang-22
x86_64 randconfig-001-20260611 gcc-14
x86_64 randconfig-002-20260610 clang-22
x86_64 randconfig-002-20260611 gcc-14
x86_64 randconfig-003-20260610 gcc-13
x86_64 randconfig-003-20260611 gcc-14
x86_64 randconfig-004-20260610 clang-22
x86_64 randconfig-004-20260611 gcc-14
x86_64 randconfig-005-20260610 clang-22
x86_64 randconfig-005-20260611 gcc-14
x86_64 randconfig-006-20260610 gcc-13
x86_64 randconfig-006-20260611 gcc-14
x86_64 randconfig-011-20260611 clang-22
x86_64 randconfig-011-20260611 gcc-14
x86_64 randconfig-012-20260611 gcc-14
x86_64 randconfig-013-20260611 gcc-14
x86_64 randconfig-014-20260611 gcc-14
x86_64 randconfig-015-20260611 gcc-14
x86_64 randconfig-016-20260611 clang-22
x86_64 randconfig-016-20260611 gcc-14
x86_64 randconfig-071-20260610 gcc-14
x86_64 randconfig-071-20260611 clang-22
x86_64 randconfig-072-20260610 clang-22
x86_64 randconfig-072-20260611 clang-22
x86_64 randconfig-073-20260610 clang-22
x86_64 randconfig-073-20260611 clang-22
x86_64 randconfig-074-20260610 gcc-14
x86_64 randconfig-074-20260611 clang-22
x86_64 randconfig-075-20260610 gcc-14
x86_64 randconfig-075-20260611 clang-22
x86_64 randconfig-076-20260610 clang-22
x86_64 randconfig-076-20260611 clang-22
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-kselftests clang-22
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-22
xtensa allnoconfig clang-23
xtensa allnoconfig gcc-16.1.0
xtensa allyesconfig clang-20
xtensa randconfig-001-20260610 gcc-8.5.0
xtensa randconfig-001-20260611 gcc-15.2.0
xtensa randconfig-002-20260610 gcc-8.5.0
xtensa randconfig-002-20260611 gcc-15.2.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox