* [RFC PATCH v3 5/9] accel: rocket: Keep the IOMMU domain attached across jobs
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
rocket attached the job's IOMMU domain in rocket_job_run() and
detached it again on every completion and reset. Each attach/detach
toggles the rk_iommu stall/force-reset/paging handshake, and on
RK3568 the NPU MMU is idle between jobs, so that handshake times out
and logs a burst of "stall/paging request timed out" errors for
every job.
Attach the per-context domain once and keep it: track the attached
domain in the core, swap it only when a job from a different context
runs, and detach it at core teardown. A reference on the attached
domain is held so it outlives the job that first attached it and is
released on swap/teardown.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_core.c | 6 ++++++
drivers/accel/rocket/rocket_core.h | 3 +++
drivers/accel/rocket/rocket_job.c | 27 +++++++++++++++++++++------
3 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index a8de876365873..634f78dfe2887 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -13,6 +13,7 @@
#include <linux/reset.h>
#include "rocket_core.h"
+#include "rocket_drv.h"
#include "rocket_job.h"
int rocket_core_init(struct rocket_core *core)
@@ -112,6 +113,11 @@ void rocket_core_fini(struct rocket_core *core)
{
pm_runtime_dont_use_autosuspend(core->dev);
pm_runtime_disable(core->dev);
+ if (core->attached_domain) {
+ iommu_detach_group(NULL, core->iommu_group);
+ rocket_iommu_domain_put(core->attached_domain);
+ core->attached_domain = NULL;
+ }
iommu_group_put(core->iommu_group);
core->iommu_group = NULL;
rocket_job_fini(core);
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index 66d138a8ed773..05a197a9c0113 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -42,6 +42,8 @@ struct rocket_soc_data {
#define rocket_core_writel(core, reg, value) \
writel(value, (core)->core_iomem + (REG_CORE_##reg) - REG_CORE_S_STATUS)
+struct rocket_iommu_domain;
+
struct rocket_core {
struct device *dev;
struct rocket_device *rdev;
@@ -56,6 +58,7 @@ struct rocket_core {
struct reset_control_bulk_data resets[2];
struct iommu_group *iommu_group;
+ struct rocket_iommu_domain *attached_domain;
struct mutex job_lock;
struct rocket_job *in_flight_job;
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index e25234261536b..b248371be8a1e 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -9,6 +9,7 @@
#include <drm/rocket_accel.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
+#include <linux/kref.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -314,9 +315,26 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
if (ret < 0)
return fence;
- ret = iommu_attach_group(job->domain->domain, core->iommu_group);
- if (ret < 0)
- return fence;
+ /*
+ * Attach the job's IOMMU domain only when it differs from the one
+ * already attached. Re-attaching per job toggles the rk_iommu
+ * stall/reset handshake on an idle NPU MMU, which is slow and
+ * noisy; keep the domain attached across jobs instead.
+ */
+ if (core->attached_domain != job->domain) {
+ if (core->attached_domain) {
+ iommu_detach_group(NULL, core->iommu_group);
+ rocket_iommu_domain_put(core->attached_domain);
+ core->attached_domain = NULL;
+ }
+
+ ret = iommu_attach_group(job->domain->domain, core->iommu_group);
+ if (ret < 0)
+ return fence;
+
+ kref_get(&job->domain->kref);
+ core->attached_domain = job->domain;
+ }
scoped_guard(mutex, &core->job_lock) {
core->in_flight_job = job;
@@ -340,7 +358,6 @@ static void rocket_job_handle_irq(struct rocket_core *core)
return;
}
- iommu_detach_group(NULL, iommu_group_get(core->dev));
dma_fence_signal(core->in_flight_job->done_fence);
pm_runtime_put_autosuspend(core->dev);
core->in_flight_job = NULL;
@@ -376,8 +393,6 @@ rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
*/
rocket_core_reset(core);
- iommu_detach_group(NULL, core->iommu_group);
-
/* NPU has been reset, we can clear the reset pending bit. */
atomic_set(&core->reset.pending, 0);
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 4/9] accel: rocket: Reset the NPU before detaching the IOMMU on timeout
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
On a job timeout the NPU AXI master can be left wedged with
outstanding transactions. rocket_reset() detached the IOMMU group
before resetting the hardware, so iommu_detach_group() ->
__iommu_group_set_core_domain() asked the rk_iommu to stall and wait
for the in-flight transactions to drain. They never did, the stall
request timed out (-ETIMEDOUT) and the IOMMU core WARNed:
WARNING: drivers/iommu/iommu.c:157 __iommu_group_set_core_domain
iommu_detach_group
rocket_reset
rocket_job_timedout
Assert the core reset first: it quiesces the AXI master so the
following IOMMU detach completes cleanly. Move the detach after
rocket_core_reset() and out of the job_lock (it does not touch
in_flight_job).
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_job.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index ac51bff39833f..e25234261536b 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -364,14 +364,20 @@ rocket_reset(struct rocket_core *core, struct drm_sched_job *bad)
if (core->in_flight_job)
pm_runtime_put_noidle(core->dev);
- iommu_detach_group(NULL, core->iommu_group);
-
core->in_flight_job = NULL;
}
- /* Proceed with reset now. */
+ /*
+ * Reset the NPU hardware before detaching the IOMMU. A timed-out job
+ * leaves the NPU AXI master wedged; detaching the IOMMU then issues a
+ * stall request that never drains and times out (warning in the IOMMU
+ * core). Asserting the core reset first quiesces the master so the
+ * detach completes cleanly.
+ */
rocket_core_reset(core);
+ iommu_detach_group(NULL, core->iommu_group);
+
/* NPU has been reset, we can clear the reset pending bit. */
atomic_set(&core->reset.pending, 0);
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 3/9] accel: rocket: Add RK3568 SoC support
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The RK3568 has a single core of the same NVDLA-derived NPU IP as the
RK3588, with a 32-bit AXI master. Unlike the RK3588 it must be powered
on and de-idled through the PMU, and its PVTPLL clock started via SCMI,
before the NPU is reachable. Add rk3568_soc_data with an noc_init
callback performing this bring-up.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/accel/rocket/rocket_core.c | 9 +++++
drivers/accel/rocket/rocket_core.h | 3 ++
drivers/accel/rocket/rocket_drv.c | 53 ++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
diff --git a/drivers/accel/rocket/rocket_core.c b/drivers/accel/rocket/rocket_core.c
index 09c445af7de73..a8de876365873 100644
--- a/drivers/accel/rocket/rocket_core.c
+++ b/drivers/accel/rocket/rocket_core.c
@@ -88,6 +88,15 @@ int rocket_core_init(struct rocket_core *core)
return err;
}
+ if (core->soc_data->noc_init) {
+ err = core->soc_data->noc_init(core);
+ if (err) {
+ pm_runtime_put_sync(dev);
+ rocket_job_fini(core);
+ return err;
+ }
+ }
+
version = rocket_pc_readl(core, VERSION);
version += rocket_pc_readl(core, VERSION_NUM) & 0xffff;
diff --git a/drivers/accel/rocket/rocket_core.h b/drivers/accel/rocket/rocket_core.h
index d6421251670dc..66d138a8ed773 100644
--- a/drivers/accel/rocket/rocket_core.h
+++ b/drivers/accel/rocket/rocket_core.h
@@ -18,10 +18,13 @@ struct rocket_core;
* struct rocket_soc_data - per-SoC configuration data
* @num_cores: Number of NPU cores in this SoC.
* @dma_bits: Physical address width reachable by the NPU's AXI master.
+ * @noc_init: Optional callback to power on and de-idle the NPU NOC bus.
+ * Required on RK3568, where this is done through the PMU.
*/
struct rocket_soc_data {
unsigned int num_cores;
unsigned int dma_bits;
+ int (*noc_init)(struct rocket_core *core);
};
#define rocket_pc_readl(core, reg) \
diff --git a/drivers/accel/rocket/rocket_drv.c b/drivers/accel/rocket/rocket_drv.c
index c18840e5aff76..5a72d0b5f4dff 100644
--- a/drivers/accel/rocket/rocket_drv.c
+++ b/drivers/accel/rocket/rocket_drv.c
@@ -9,9 +9,11 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/iommu.h>
+#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include "rocket_device.h"
#include "rocket_drv.h"
@@ -217,12 +219,63 @@ static void rocket_remove(struct platform_device *pdev)
}
}
+/*
+ * On RK3568 the NPU NOC bus is gated and idle out of reset and must be
+ * powered on and de-idled through the PMU before the NPU is reachable. PMU
+ * registers use a write-mask protocol: the upper 16 bits enable writes to the
+ * matching lower 16 bits.
+ *
+ * The NPU's high-speed clock is a PVTPLL managed by TF-A via SCMI and must be
+ * running before the NOC acknowledges the de-idle request. Force a real SCMI
+ * rate change (an intermediate rate defeats the clock framework's
+ * unchanged-rate shortcut) now that the power domain is on and clocks enabled.
+ */
+#define ROCKET_RK3568_SCMI_CLK 2
+
+static int rk3568_noc_init(struct rocket_core *core)
+{
+ struct regmap *pmu;
+ unsigned int val;
+ int ret;
+
+ clk_set_rate(core->clks[ROCKET_RK3568_SCMI_CLK].clk, 600000000UL);
+ clk_set_rate(core->clks[ROCKET_RK3568_SCMI_CLK].clk, 1000000000UL);
+
+ pmu = syscon_regmap_lookup_by_phandle(core->dev->of_node, "rockchip,pmu");
+ if (IS_ERR(pmu))
+ return dev_err_probe(core->dev, PTR_ERR(pmu),
+ "failed to get PMU regmap\n");
+
+ /* Power on the NPU power domain (PWR_GATE_SFTCON bit 1 = 0). */
+ regmap_write(pmu, 0xa0, BIT(1 + 16));
+
+ /* Disable NPU NOC auto-idle (NOC_AUTO_CON0 bit 2). */
+ regmap_write(pmu, 0x70, BIT(2 + 16));
+
+ /* Request NPU bus de-idle (BUS_IDLE_SFTCON0 bit 2 = 0). */
+ regmap_write(pmu, 0x50, BIT(2 + 16));
+
+ /* Wait for the bus to report active (BUS_IDLE_ST bit 2 = 0). */
+ ret = regmap_read_poll_timeout(pmu, 0x68, val, !(val & BIT(2)), 10, 1000);
+ if (ret)
+ dev_err(core->dev, "timed out waiting for NPU bus de-idle\n");
+
+ return ret;
+}
+
+static const struct rocket_soc_data rk3568_soc_data = {
+ .num_cores = 1,
+ .dma_bits = 32,
+ .noc_init = rk3568_noc_init,
+};
+
static const struct rocket_soc_data rk3588_soc_data = {
.num_cores = 3,
.dma_bits = 40,
};
static const struct of_device_id dt_match[] = {
+ { .compatible = "rockchip,rk3568-rknn-core", .data = &rk3568_soc_data },
{ .compatible = "rockchip,rk3588-rknn-core", .data = &rk3588_soc_data },
{}
};
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 6/9] iommu/rockchip: Clear AUTO_GATING bit 1 on the RK356x v1 IOMMU
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
On the RK356x v1 IOMMU, RK_MMU_AUTO_GATING resets to 0x3. Bit 1 enables
auto clock-gating of the page-table walker, so the walker's AXI master
loses its clock between transactions; a TLB-miss page walk then never
completes and the IOMMU is left stuck (PAGING_ENABLED, never IDLE).
Clear bit 1 (keeping bit 0, the slave-port gate) once paging is enabled
so the walker keeps its clock. This is required for the RK3568 NPU MMU.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
drivers/iommu/rockchip-iommu.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 4da80136933c4..e3d8b6e9ca12b 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -953,6 +953,18 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
ret = rk_iommu_enable_paging(iommu);
+ if (!ret) {
+ /*
+ * RK356x v1 IOMMU: RK_MMU_AUTO_GATING bit 1 enables page-walker
+ * auto clock-gating; the walker's AXI master then loses its clock
+ * between transactions and a TLB-miss page walk never completes,
+ * leaving the IOMMU stuck (PAGING_ENABLED, never IDLE). Clear
+ * bit 1 (keep bit 0, the slave-port gate) once paging is enabled.
+ */
+ for (i = 0; i < iommu->num_mmu; i++)
+ rk_iommu_write(iommu->bases[i], RK_MMU_AUTO_GATING, 0x2);
+ }
+
out_disable_stall:
rk_iommu_disable_stall(iommu);
out_disable_clocks:
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 9/9] arm64: dts: rockchip: rk3568-rock-3b: Enable the NPU
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
Enable the NPU and its IOMMU on ROCK 3B.
vdd_npu is marked always-on so the rail is up before genpd de-idles the
NPU NoC at power-on: the PMU de-idle handshake needs the rail powered.
The PVTPLL compute clock is brought up later by the driver.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts b/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts
index 69001e453732e..7ac780ed313d5 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-rock-3b.dts
@@ -330,8 +330,10 @@ regulator-state-mem {
vdd_npu: DCDC_REG4 {
regulator-name = "vdd_npu";
+ regulator-always-on;
+ regulator-boot-on;
regulator-initial-mode = <0x2>;
- regulator-min-microvolt = <500000>;
+ regulator-min-microvolt = <825000>;
regulator-max-microvolt = <1350000>;
regulator-ramp-delay = <6001>;
@@ -787,3 +789,13 @@ vp0_out_hdmi: endpoint@ROCKCHIP_VOP2_EP_HDMI0 {
remote-endpoint = <&hdmi_in_vp0>;
};
};
+
+&rknn_core_0 {
+ npu-supply = <&vdd_npu>;
+ status = "okay";
+};
+
+&rknn_mmu_0 {
+ status = "okay";
+};
+
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 8/9] arm64: dts: rockchip: rk356x: Add the NPU and its IOMMU
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The RK3568 has an NVDLA-derived NPU at fde40000 with its own IOMMU at
fde4b000. Add both nodes (disabled by default) and the NPU power-domain
child under the PMU power-controller, and point rockchip,pmu at the PMU
syscon that controls the NPU NoC bus-idle.
The power-domain deliberately carries no pm_qos: qos_npu sits behind the
NPU NoC, which is gated until the NPU is brought up, so a genpd power-off
QoS save would fault reading it.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
arch/arm64/boot/dts/rockchip/rk356x-base.dtsi | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
index 64bdd8b7754b5..50ce5a5e4fc24 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x-base.dtsi
@@ -512,6 +512,13 @@ power-domain@RK3568_PD_GPU {
#power-domain-cells = <0>;
};
+ pd_npu: power-domain@RK3568_PD_NPU {
+ reg = <RK3568_PD_NPU>;
+ clocks = <&cru ACLK_NPU_PRE>,
+ <&cru HCLK_NPU_PRE>;
+ #power-domain-cells = <0>;
+ };
+
/* These power domains are grouped by VD_LOGIC */
power-domain@RK3568_PD_VI {
reg = <RK3568_PD_VI>;
@@ -948,6 +955,37 @@ qos_rga_wr: qos@fe158300 {
reg = <0x0 0xfe158300 0x0 0x20>;
};
+ rknn_core_0: npu@fde40000 {
+ compatible = "rockchip,rk3568-rknn-core";
+ reg = <0x0 0xfde40000 0x0 0x1000>,
+ <0x0 0xfde41000 0x0 0x1000>,
+ <0x0 0xfde43000 0x0 0x1000>;
+ reg-names = "pc", "cna", "core";
+ interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_NPU>, <&cru HCLK_NPU>,
+ <&scmi_clk SCMI_CLK_NPU>, <&cru PCLK_NPU_PRE>;
+ clock-names = "aclk", "hclk", "npu", "pclk";
+ assigned-clocks = <&scmi_clk SCMI_CLK_NPU>;
+ assigned-clock-rates = <200000000>;
+ resets = <&cru SRST_A_NPU>, <&cru SRST_H_NPU>;
+ reset-names = "srst_a", "srst_h";
+ power-domains = <&power RK3568_PD_NPU>;
+ rockchip,pmu = <&pmu>;
+ iommus = <&rknn_mmu_0>;
+ status = "disabled";
+ };
+
+ rknn_mmu_0: iommu@fde4b000 {
+ compatible = "rockchip,iommu";
+ reg = <0x0 0xfde4b000 0x0 0x40>;
+ interrupts = <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "aclk", "iface";
+ clocks = <&cru ACLK_NPU>, <&cru HCLK_NPU>;
+ power-domains = <&power RK3568_PD_NPU>;
+ #iommu-cells = <0>;
+ status = "disabled";
+ };
+
qos_npu: qos@fe180000 {
compatible = "rockchip,rk3568-qos", "syscon";
reg = <0x0 0xfe180000 0x0 0x20>;
--
2.39.5
^ permalink raw reply related
* [RFC PATCH v3 7/9] dt-bindings: npu: rockchip,rk3588-rknn-core: Add RK3568
From: Midgy BALON @ 2026-06-04 13:52 UTC (permalink / raw)
To: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will
Cc: robin.murphy, dri-devel, linux-rockchip, devicetree,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260604135255.62682-1-midgy971@gmail.com>
The RK3568 carries a single core of the same NVDLA-derived NPU IP as the
RK3588. Add its compatible.
On RK3568 the NPU NOC bus-idle and power gating are controlled through the
system PMU rather than a dedicated register block, so add a rockchip,pmu
phandle to that syscon. The RK3568 NPU has no dedicated SRAM rail, so
sram-supply is required only on RK3588.
Signed-off-by: Midgy BALON <midgy971@gmail.com>
---
.../npu/rockchip,rk3588-rknn-core.yaml | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
index caca2a4903cd1..af9936b32e9fe 100644
--- a/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
+++ b/Documentation/devicetree/bindings/npu/rockchip,rk3588-rknn-core.yaml
@@ -21,6 +21,7 @@ properties:
compatible:
enum:
+ - rockchip,rk3568-rknn-core
- rockchip,rk3588-rknn-core
reg:
@@ -50,6 +51,13 @@ properties:
npu-supply: true
+ rockchip,pmu:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the PMU syscon. On RK3568 the NPU's NOC bus-idle and
+ power gating are controlled through the PMU; this points to that
+ syscon so those registers can be reached.
+
power-domains:
maxItems: 1
@@ -75,7 +83,15 @@ required:
- resets
- reset-names
- npu-supply
- - sram-supply
+
+if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3588-rknn-core
+then:
+ required:
+ - sram-supply
additionalProperties: false
--
2.39.5
^ permalink raw reply related
* RE: [PATCH v5 05/20] dma-pool: track decrypted atomic pools and select them via attrs
From: Michael Kelley @ 2026-06-04 14:05 UTC (permalink / raw)
To: Jason Gunthorpe, Michael Kelley
Cc: Aneesh Kumar K.V, iommu@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev@lists.ozlabs.org,
linux-s390@vger.kernel.org, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86@kernel.org, Jiri Pirko
In-Reply-To: <20260603005454.GM2487554@ziepe.ca>
From: Jason Gunthorpe <jgg@ziepe.ca> Sent: Tuesday, June 2, 2026 5:55 PM
>
> On Tue, Jun 02, 2026 at 02:24:40PM +0000, Michael Kelley wrote:
>
> > Except that in a normal VM, the "unencrypted" pool attribute does *not*
> > describe the state of the memory itself. In a normal VM, the memory is
> > unencrypted, but the "unencrypted" pool attribute is false. That
> > contradiction is the essence of my concern.
>
> I would argue no..
>
> When CC is enabled the default state of memory in a Linux environment
> is "encrypted". You have to take a special action to "decrypt" it.
>
> Thus the default state of memory in a non-CC environment is also
> paradoxically "encrypted" too.
The need to have such an unnatural premise is usually an indication
of a conceptual problem with the overall model, or perhaps just a
terminology problem.
Here's a proposal. The new DMA attribute is DMA_ATTR_CC_SHARED.
Name the pool attribute "cc_shared" instead of "unencrypted". Having
"cc_shared" set to false in a normal VM doesn't lead to the non-sensical
situation of claiming that a normal VM is encrypted. The boolean
"unencrypted" parameter that has been added to various calls also
becomes "cc_shared". If "CC_SHARED" is a suitable name for the DMA
attribute, it ought to be suitable as the pool attribute. And everything
matches as well.
Michael
> "decryption" is impossible.
>
> Therefore the "unencrypted" state is a special state that only memory
> inside a CC VM can have. A normal VM can never have "unencrypted"
> memory at all, so having it be false in the pool is accurate as far as
> the APIs go.
>
> un-encrypted = true means "the memory in this pool was transformed with
> set_memory_decrypted()" - which is impossible on a normal VM.
>
> Jason
^ permalink raw reply
* [PATCH] docs: arm64: Document that text_offset is always 0
From: Rasmus Villemoes @ 2026-06-04 14:08 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Will Deacon, Jonathan Corbet, linux-doc,
linux-kernel, Rasmus Villemoes
When trying to figure out where to place and call an arm64 Image in
memory, reading booting.rst should provide the answer. However, it
requires quite some digging to figure out that text_offset is set via
".quad 0" in head.S and is thus actually always 0 since v5.10.
Update the documentation and make that explicit. Reword the 2MB
requirement accordingly, and remove the paragraphs that only apply to
the ancient versions where text_offset could be non-zero, as they only
confuse a current reader.
Fixes: 120dc60d0bdb ("arm64: get rid of TEXT_OFFSET")
Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
---
I've included a Fixes tag since I spent way too much time tracking
down where that text_offset might be defined. The mentioned commit did
get rid of all references to TEXT_OFFSET-the-macro, but not
text_offset-the-concept.
Documentation/arch/arm64/booting.rst | 20 +++++---------------
1 file changed, 5 insertions(+), 15 deletions(-)
diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst
index 13ef311dace8..f4cc25b1fd56 100644
--- a/Documentation/arch/arm64/booting.rst
+++ b/Documentation/arch/arm64/booting.rst
@@ -55,9 +55,6 @@ not exceed 2 megabytes in size. Since the dtb will be mapped cacheable
using blocks of up to 2 megabytes in size, it must not be placed within
any 2M region which must be mapped with any specific attributes.
-NOTE: versions prior to v4.2 also require that the DTB be placed within
-the 512 MB region starting at text_offset bytes below the kernel Image.
-
3. Decompress the kernel image
------------------------------
@@ -93,6 +90,8 @@ Header notes:
- As of v3.17, all fields are little endian unless stated otherwise.
+- As of v5.10, text_offset is always 0.
+
- code0/code1 are responsible for branching to stext.
- when booting through EFI, code0/code1 are initially skipped.
@@ -100,12 +99,6 @@ Header notes:
entry point (efi_stub_entry). When the stub has done its work, it
jumps to code0 to resume the normal boot process.
-- Prior to v3.17, the endianness of text_offset was not specified. In
- these cases image_size is zero and text_offset is 0x80000 in the
- endianness of the kernel. Where image_size is non-zero image_size is
- little-endian and must be respected. Where image_size is zero,
- text_offset can be assumed to be 0x80000.
-
- The flags field (introduced in v3.17) is a little-endian 64-bit field
composed as follows:
@@ -135,12 +128,9 @@ Header notes:
end of the kernel image. The amount of space required will vary
depending on selected features, and is effectively unbound.
-The Image must be placed text_offset bytes from a 2MB aligned base
-address anywhere in usable system RAM and called there. The region
-between the 2 MB aligned base address and the start of the image has no
-special significance to the kernel, and may be used for other purposes.
-At least image_size bytes from the start of the image must be free for
-use by the kernel.
+The Image must be placed at a 2MB aligned base address anywhere in
+usable system RAM and called there. At least image_size bytes from
+the start of the image must be free for use by the kernel.
NOTE: versions prior to v4.6 cannot make use of memory below the
physical offset of the Image so it is recommended that the Image be
placed as close as possible to the start of system RAM.
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v6 3/8] perf cs-etm: Use thread-stack for last branch entries
From: James Clark @ 2026-06-04 14:09 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-3-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> CS ETM maintains its own circular array for last branch entries, with
> local helpers to update, copy and reset the branch stack. This duplicates
> logic already provided by the common code.
>
> Record branch with thread_stack__event() and synthesize branch stack
> with thread_stack__br_sample(). This removes the local last_branch_rb
> buffer and position tracking. Keep the buffer number updated via
> thread_stack__set_trace_nr(), which is used when exporting samples to
> Python scripts.
>
> The output should remain same, except that be->flags.predicted is no
> longer set. Since CoreSight trace does not provide branch prediction
> information, clearing the flag avoids confusion.
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 152 +++++++++++++----------------------------------
> 1 file changed, 41 insertions(+), 111 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 5bff8811d61e423463b7bd4e20d599d5b5307a1a..398ab3b7a429d402cc8e5f6cccb35c0b7c253732 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -83,14 +83,13 @@ struct cs_etm_auxtrace {
> struct cs_etm_traceid_queue {
> u8 trace_chan_id;
> u64 period_instructions;
> - size_t last_branch_pos;
> union perf_event *event_buf;
> struct thread *thread;
> struct thread *prev_packet_thread;
> ocsd_ex_level prev_packet_el;
> ocsd_ex_level el;
> + unsigned int br_stack_sz;
> struct branch_stack *last_branch;
> - struct branch_stack *last_branch_rb;
> struct cs_etm_packet *prev_packet;
> struct cs_etm_packet *packet;
> struct cs_etm_packet_queue packet_queue;
> @@ -635,9 +634,8 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> tidq->last_branch = zalloc(sz);
> if (!tidq->last_branch)
> goto out_free;
> - tidq->last_branch_rb = zalloc(sz);
> - if (!tidq->last_branch_rb)
> - goto out_free;
> +
> + tidq->br_stack_sz = etm->synth_opts.last_branch_sz;
> }
>
> tidq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
> @@ -647,7 +645,6 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> return 0;
>
> out_free:
> - zfree(&tidq->last_branch_rb);
> zfree(&tidq->last_branch);
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> @@ -941,7 +938,6 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
> thread__zput(tidq->prev_packet_thread);
> zfree(&tidq->event_buf);
> zfree(&tidq->last_branch);
> - zfree(&tidq->last_branch_rb);
> zfree(&tidq->prev_packet);
> zfree(&tidq->packet);
> zfree(&tidq);
> @@ -1281,57 +1277,6 @@ static int cs_etm__queue_first_cs_timestamp(struct cs_etm_auxtrace *etm,
> return ret;
> }
>
> -static inline
> -void cs_etm__copy_last_branch_rb(struct cs_etm_queue *etmq,
> - struct cs_etm_traceid_queue *tidq)
> -{
> - struct branch_stack *bs_src = tidq->last_branch_rb;
> - struct branch_stack *bs_dst = tidq->last_branch;
> - size_t nr = 0;
> -
> - /*
> - * Set the number of records before early exit: ->nr is used to
> - * determine how many branches to copy from ->entries.
> - */
> - bs_dst->nr = bs_src->nr;
> -
> - /*
> - * Early exit when there is nothing to copy.
> - */
> - if (!bs_src->nr)
> - return;
> -
> - /*
> - * As bs_src->entries is a circular buffer, we need to copy from it in
> - * two steps. First, copy the branches from the most recently inserted
> - * branch ->last_branch_pos until the end of bs_src->entries buffer.
> - */
> - nr = etmq->etm->synth_opts.last_branch_sz - tidq->last_branch_pos;
> - memcpy(&bs_dst->entries[0],
> - &bs_src->entries[tidq->last_branch_pos],
> - sizeof(struct branch_entry) * nr);
> -
> - /*
> - * If we wrapped around at least once, the branches from the beginning
> - * of the bs_src->entries buffer and until the ->last_branch_pos element
> - * are older valid branches: copy them over. The total number of
> - * branches copied over will be equal to the number of branches asked by
> - * the user in last_branch_sz.
> - */
> - if (bs_src->nr >= etmq->etm->synth_opts.last_branch_sz) {
> - memcpy(&bs_dst->entries[nr],
> - &bs_src->entries[0],
> - sizeof(struct branch_entry) * tidq->last_branch_pos);
> - }
> -}
> -
> -static inline
> -void cs_etm__reset_last_branch_rb(struct cs_etm_traceid_queue *tidq)
> -{
> - tidq->last_branch_pos = 0;
> - tidq->last_branch_rb->nr = 0;
> -}
> -
> static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
> u8 trace_chan_id, u64 addr)
> {
> @@ -1400,38 +1345,6 @@ static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
> return addr;
> }
>
> -static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq,
> - struct cs_etm_traceid_queue *tidq)
> -{
> - struct branch_stack *bs = tidq->last_branch_rb;
> - struct branch_entry *be;
> -
> - /*
> - * The branches are recorded in a circular buffer in reverse
> - * chronological order: we start recording from the last element of the
> - * buffer down. After writing the first element of the stack, move the
> - * insert position back to the end of the buffer.
> - */
> - if (!tidq->last_branch_pos)
> - tidq->last_branch_pos = etmq->etm->synth_opts.last_branch_sz;
> -
> - tidq->last_branch_pos -= 1;
> -
> - be = &bs->entries[tidq->last_branch_pos];
> - be->from = cs_etm__last_executed_instr(tidq->prev_packet);
> - be->to = cs_etm__first_executed_instr(tidq->packet);
> - /* No support for mispredict */
> - be->flags.mispred = 0;
> - be->flags.predicted = 1;
> -
> - /*
> - * Increment bs->nr until reaching the number of last branches asked by
> - * the user on the command line.
> - */
> - if (bs->nr < etmq->etm->synth_opts.last_branch_sz)
> - bs->nr += 1;
> -}
> -
> static int cs_etm__inject_event(struct cs_etm_auxtrace *etm, union perf_event *event,
> struct perf_sample *sample, u64 type)
> {
> @@ -1579,6 +1492,37 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
> return etm->latest_kernel_timestamp;
> }
>
> +static void cs_etm__add_stack_event(struct cs_etm_queue *etmq,
> + struct cs_etm_traceid_queue *tidq)
> +{
> + u64 from, to;
> + int size;
> +
> + if (!tidq->prev_packet->last_instr_taken_branch)
> + return;
> +
> + if (tidq->prev_packet->sample_type != CS_ETM_RANGE ||
> + tidq->packet->sample_type != CS_ETM_RANGE)
> + return;
> +
> + if (etmq->etm->synth_opts.last_branch) {
> + from = cs_etm__last_executed_instr(tidq->prev_packet);
> + to = cs_etm__first_executed_instr(tidq->packet);
> +
> + size = cs_etm__instr_size(etmq, tidq->trace_chan_id,
> + tidq->prev_packet->isa, from);
> +
> + /* Enable callchain so thread stack entry can be allocated */
> + thread_stack__event(tidq->thread, tidq->prev_packet->cpu,
> + tidq->prev_packet->flags, from, to, size,
> + etmq->buffer->buffer_nr + 1, true,
> + tidq->br_stack_sz, 0);
> + } else {
> + thread_stack__set_trace_nr(tidq->thread, tidq->prev_packet->cpu,
> + etmq->buffer->buffer_nr + 1);
> + }
> +}
> +
> static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
> struct cs_etm_traceid_queue *tidq,
> u64 addr, u64 period)
> @@ -1608,8 +1552,12 @@ static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq,
>
> cs_etm__copy_insn(etmq, tidq->trace_chan_id, tidq->packet, &sample);
>
> - if (etm->synth_opts.last_branch)
> + if (etm->synth_opts.last_branch) {
> + thread_stack__br_sample(tidq->thread, tidq->packet->cpu,
> + tidq->last_branch,
> + tidq->br_stack_sz);
> sample.branch_stack = tidq->last_branch;
> + }
>
> if (etm->synth_opts.inject) {
> ret = cs_etm__inject_event(etm, event, &sample,
> @@ -1798,14 +1746,7 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
>
> tidq->period_instructions += tidq->packet->instr_count;
>
> - /*
> - * Record a branch when the last instruction in
> - * PREV_PACKET is a branch.
> - */
> - if (etm->synth_opts.last_branch &&
> - tidq->prev_packet->sample_type == CS_ETM_RANGE &&
> - tidq->prev_packet->last_instr_taken_branch)
> - cs_etm__update_last_branch_rb(etmq, tidq);
> + cs_etm__add_stack_event(etmq, tidq);
Would it be cleaner to call this whenever a branch sample is generated?
Seems like the conditions for calling thread_stack__event() and
cs_etm__synth_branch_sample() are slightly different (ignoring the fact
that branches are only generated when the user asks for them).
Maybe the conditions should be different, but maybe a comment why or if
they're the same, a shared function for the conditions would help.
For example, we don't push a branch to the stack for
CS_ETM_DISCONTINUITY, but we do generate a branch sample from 0.
>
> if (etm->synth_opts.instructions &&
> tidq->period_instructions >= etm->instructions_sample_period) {
> @@ -1864,10 +1805,6 @@ static int cs_etm__sample(struct cs_etm_queue *etmq,
> u64 offset = etm->instructions_sample_period - instrs_prev;
> u64 addr;
>
> - /* Prepare last branches for instruction sample */
> - if (etm->synth_opts.last_branch)
> - cs_etm__copy_last_branch_rb(etmq, tidq);
> -
> while (tidq->period_instructions >=
> etm->instructions_sample_period) {
> /*
> @@ -1947,10 +1884,6 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
> etmq->etm->synth_opts.instructions &&
> tidq->prev_packet->sample_type == CS_ETM_RANGE) {
> u64 addr;
> -
> - /* Prepare last branches for instruction sample */
> - cs_etm__copy_last_branch_rb(etmq, tidq);
> -
> /*
> * Generate a last branch event for the branches left in the
> * circular buffer at the end of the trace.
> @@ -1982,7 +1915,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
>
> /* Reset last branches after flush the trace */
> if (etm->synth_opts.last_branch)
> - cs_etm__reset_last_branch_rb(tidq);
> + thread_stack__flush(tidq->thread);
>
> return err;
> }
> @@ -2006,9 +1939,6 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
> tidq->prev_packet->sample_type == CS_ETM_RANGE) {
> u64 addr;
>
> - /* Prepare last branches for instruction sample */
> - cs_etm__copy_last_branch_rb(etmq, tidq);
> -
> /*
> * Use the address of the end of the last reported execution
> * range.
>
^ permalink raw reply
* Re: [PATCH v6 1/8] perf cs-etm: Decode ETE exception packets
From: James Clark @ 2026-06-04 14:10 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-1-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> ETE shares the same packet format as ETMv4, but exception decoding
> handled ETMv4 packets only. As a result, ETE exception packets were
> not classified.
>
> Recognize the ETE magic for exception number decoding.
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 6ec48de29441012f3d827d50616349c6c0d1f037..ab79d08f5a6095448470e2c3ec85ff3db2fb5634 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -2138,7 +2138,7 @@ static bool cs_etm__is_syscall(struct cs_etm_queue *etmq,
> * HVC cases; need to check if it's SVC instruction based on
> * packet address.
> */
> - if (magic == __perf_cs_etmv4_magic) {
> + if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) {
> if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> cs_etm__is_svc_instr(etmq, trace_chan_id, prev_packet,
> prev_packet->end_addr))
> @@ -2161,7 +2161,7 @@ static bool cs_etm__is_async_exception(struct cs_etm_traceid_queue *tidq,
> packet->exception_number == CS_ETMV3_EXC_FIQ)
> return true;
>
> - if (magic == __perf_cs_etmv4_magic)
> + if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic)
> if (packet->exception_number == CS_ETMV4_EXC_RESET ||
> packet->exception_number == CS_ETMV4_EXC_DEBUG_HALT ||
> packet->exception_number == CS_ETMV4_EXC_SYSTEM_ERROR ||
> @@ -2192,7 +2192,7 @@ static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq,
> packet->exception_number == CS_ETMV3_EXC_GENERIC)
> return true;
>
> - if (magic == __perf_cs_etmv4_magic) {
> + if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) {
> if (packet->exception_number == CS_ETMV4_EXC_TRAP ||
> packet->exception_number == CS_ETMV4_EXC_ALIGNMENT ||
> packet->exception_number == CS_ETMV4_EXC_INST_FAULT ||
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v6 2/8] perf cs-etm: Refactor instruction size handling
From: James Clark @ 2026-06-04 14:11 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-2-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> This patch introduces a new function cs_etm__instr_size() to calculate
> the instruction size based on ISA type and instruction address.
>
> Given the trace data can be MB and most likely that will be A64/A32 on
> a lot of platforms, cs_etm__instr_addr() keeps a single ISA type check
> for A64/A32 and executes an optimized calculation (addr + offset * 4).
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 44 +++++++++++++++++++++++---------------------
> 1 file changed, 23 insertions(+), 21 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index ab79d08f5a6095448470e2c3ec85ff3db2fb5634..5bff8811d61e423463b7bd4e20d599d5b5307a1a 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -1347,6 +1347,17 @@ static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq,
> return ((instrBytes[1] & 0xF8) >= 0xE8) ? 4 : 2;
> }
>
> +static inline int cs_etm__instr_size(struct cs_etm_queue *etmq,
> + u8 trace_chan_id,
> + enum cs_etm_isa isa, u64 addr)
> +{
> + if (isa == CS_ETM_ISA_T32)
> + return cs_etm__t32_instr_size(etmq, trace_chan_id, addr);
> +
> + /* Otherwise, 4-byte instruction size for A32/A64 */
> + return 4;
> +}
> +
> static inline u64 cs_etm__first_executed_instr(struct cs_etm_packet *packet)
> {
> /*
> @@ -1375,19 +1386,18 @@ static inline u64 cs_etm__instr_addr(struct cs_etm_queue *etmq,
> const struct cs_etm_packet *packet,
> u64 offset)
> {
> - if (packet->isa == CS_ETM_ISA_T32) {
> - u64 addr = packet->start_addr;
> + u64 addr = packet->start_addr;
>
> - while (offset) {
> - addr += cs_etm__t32_instr_size(etmq,
> - trace_chan_id, addr);
> - offset--;
> - }
> - return addr;
> - }
> + /* 4-byte instruction size for A32/A64 */
> + if (packet->isa == CS_ETM_ISA_A64 || packet->isa == CS_ETM_ISA_A32)
> + return addr + offset * 4;
>
> - /* Assume a 4 byte instruction size (A32/A64) */
> - return packet->start_addr + offset * 4;
> + while (offset) {
> + addr += cs_etm__instr_size(etmq, trace_chan_id,
> + packet->isa, addr);
> + offset--;
> + }
> + return addr;
> }
>
> static void cs_etm__update_last_branch_rb(struct cs_etm_queue *etmq,
> @@ -1540,16 +1550,8 @@ static void cs_etm__copy_insn(struct cs_etm_queue *etmq,
> return;
> }
>
> - /*
> - * T32 instruction size might be 32-bit or 16-bit, decide by calling
> - * cs_etm__t32_instr_size().
> - */
> - if (packet->isa == CS_ETM_ISA_T32)
> - sample->insn_len = cs_etm__t32_instr_size(etmq, trace_chan_id,
> - sample->ip);
> - /* Otherwise, A64 and A32 instruction size are always 32-bit. */
> - else
> - sample->insn_len = 4;
> + sample->insn_len = cs_etm__instr_size(etmq, trace_chan_id,
> + packet->isa, sample->ip);
>
> cs_etm__mem_access(etmq, trace_chan_id, sample->ip, sample->insn_len,
> (void *)sample->insn, 0);
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v6 4/8] perf cs-etm: Flush thread stacks after decoder reset
From: James Clark @ 2026-06-04 14:12 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-4-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> Perf resets the CoreSight decoder when moving to a new AUX trace buffer,
> this causes trace discontinunity globally.
>
> For callchain synthesis, keeping thread-stack state after decoder reset
> can leave stale call/return history attached to threads that are decoded
> later, producing incorrect synthesized callchains.
>
> Flush all host thread stacks after a decoder reset. When virtualization
> is present, flush the guest thread stacks as well.
>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 37 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index 398ab3b7a429d402cc8e5f6cccb35c0b7c253732..ea2424175558ddc0a6f20a9de6c30f377facdc52 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -1956,6 +1956,37 @@ static int cs_etm__end_block(struct cs_etm_queue *etmq,
>
> return 0;
> }
> +
> +static int cs_etm__flush_stack_cb(struct thread *thread,
> + void *data __maybe_unused)
> +{
> + thread_stack__flush(thread);
> + return 0;
> +}
> +
> +static void cs_etm__flush_machine_stack(struct cs_etm_queue *etmq, pid_t pid)
> +{
> + struct machine *machine;
> +
> + machine = machines__find(&etmq->etm->session->machines, pid);
> + if (machine)
> + machine__for_each_thread(machine, cs_etm__flush_stack_cb, NULL);
> +}
> +
> +static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq)
> +{
> + enum cs_etm_pid_fmt pid_fmt = cs_etm__get_pid_fmt(etmq);
> +
> + if (!etmq->etm->synth_opts.last_branch)
> + return;
> +
> + cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID);
> +
> + /* Clear the guest stack if virtualization is supported */
> + if (pid_fmt == CS_ETM_PIDFMT_CTXTID2)
> + cs_etm__flush_machine_stack(etmq, DEFAULT_GUEST_KERNEL_ID);
> +}
> +
> /*
> * cs_etm__get_data_block: Fetch a block from the auxtrace_buffer queue
> * if need be.
> @@ -1978,6 +2009,12 @@ static int cs_etm__get_data_block(struct cs_etm_queue *etmq)
> ret = cs_etm_decoder__reset(etmq->decoder);
> if (ret)
> return ret;
> +
> + /*
> + * Since the decoder is reset, this causes a global trace
> + * discontinuity. Flush all thread stacks.
> + */
> + cs_etm__flush_all_stack(etmq);
> }
>
> return etmq->buf_len;
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v3 05/32] iommu/generic_pt: implement iova_to_phys_length
From: Jason Gunthorpe @ 2026-06-04 14:12 UTC (permalink / raw)
To: Baolu Lu
Cc: Guanghui Feng, adrian.larumbe, airlied, alex, alikernel-developer,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <c179b999-8c7f-4338-8aad-b61fd09f1329@linux.intel.com>
On Thu, Jun 04, 2026 at 11:30:37AM +0800, Baolu Lu wrote:
> > -static __always_inline int __do_iova_to_phys(struct pt_range *range, void *arg,
> > - unsigned int level,
> > - struct pt_table_p *table,
> > - pt_level_fn_t descend_fn)
> > +struct iova_to_phys_length_data {
> > + pt_oaddr_t phys;
> > + size_t length;
> > +};
> > +
> > +static __always_inline int __do_iova_to_phys_length(struct pt_range *range,
> > + void *arg, unsigned int level,
> > + struct pt_table_p *table,
> > + pt_level_fn_t descend_fn)
> > {
> > struct pt_state pts = pt_init(range, level, table);
> > - pt_oaddr_t *res = arg;
> > + struct iova_to_phys_length_data *data = arg;
> > + unsigned int entry_lg2sz;
> > + size_t entry_sz;
> > + pt_oaddr_t expected_oa;
> > switch (pt_load_single_entry(&pts)) {
> > case PT_ENTRY_EMPTY:
> > @@ -159,45 +167,77 @@ static __always_inline int __do_iova_to_phys(struct pt_range *range, void *arg,
> > case PT_ENTRY_TABLE:
> > return pt_descend(&pts, arg, descend_fn);
> > case PT_ENTRY_OA:
> > - *res = pt_entry_oa_exact(&pts);
> > - return 0;
> > + break;
> > }
> > - return -ENOENT;
> > +
> > + data->phys = pt_entry_oa_exact(&pts);
> > + entry_lg2sz = pt_entry_oa_lg2sz(&pts);
> > + entry_sz = log2_to_int(entry_lg2sz);
> > +
> > + /* Start with the full mapping size of the first entry */
> > + data->length = entry_sz;
>
> data->length doesn't account for iova offset. Is this by design? We
> should document this clearly somewhere.
That's defintaely a mistake, the phys has to be offset by the iova in all cases,
it is part of the API.
Also add kunits tests to the iommupt selftest to cover various
scenarios please.
Also this doesn't look quite right, the walk should look more like
unmap where we just walk and stop walking when we hit a physical
address discontiguity. The stop point defines the result length.
Jason
^ permalink raw reply
* Re: [PATCH v3 01/32] iommu: introduce iova_to_phys_length in iommu_domain_ops
From: Jason Gunthorpe @ 2026-06-04 14:16 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-2-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:33PM +0800, Guanghui Feng wrote:
> +phys_addr_t iommu_iova_to_phys_length(struct iommu_domain *domain,
> + dma_addr_t iova,
> + size_t *mapped_length)
> {
This should take in an ending point so the accumulation knows when to
stop, otherwise it is too hard to use.
> - if (domain->type == IOMMU_DOMAIN_IDENTITY)
> + phys_addr_t phys;
> +
> + if (domain->type == IOMMU_DOMAIN_IDENTITY) {
> + if (mapped_length)
> + *mapped_length = PAGE_SIZE;
> return iova;
> + }
>
> - if (domain->type == IOMMU_DOMAIN_BLOCKED)
> - return 0;
> + if (mapped_length)
> + *mapped_length = 0;
> +
> + if (domain->ops->iova_to_phys_length)
> + return domain->ops->iova_to_phys_length(domain, iova, mapped_length);
> +
> + /* Fallback to legacy iova_to_phys without length info */
> + if (!domain->ops->iova_to_phys)
> + return PHYS_ADDR_MAX;
> +
> + phys = domain->ops->iova_to_phys(domain, iova);
> + if (!phys)
> + return PHYS_ADDR_MAX;
And to properly clean up the callers all the non-iommupt paths should
manually do accumulation here as well.
Basically if you call this function you get a maximal contiguous
physical range as efficiently as possible.
Jason
^ permalink raw reply
* Re: [RFC PATCH v3 6/9] iommu/rockchip: Clear AUTO_GATING bit 1 on the RK356x v1 IOMMU
From: Tomeu Vizoso @ 2026-06-04 14:20 UTC (permalink / raw)
To: Midgy BALON
Cc: ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will, robin.murphy,
dri-devel, linux-rockchip, devicetree, linux-arm-kernel, iommu,
linux-kernel
In-Reply-To: <20260604135255.62682-7-midgy971@gmail.com>
On Thu, Jun 4, 2026 at 3:53 PM Midgy BALON <midgy971@gmail.com> wrote:
>
> On the RK356x v1 IOMMU, RK_MMU_AUTO_GATING resets to 0x3. Bit 1 enables
> auto clock-gating of the page-table walker, so the walker's AXI master
> loses its clock between transactions; a TLB-miss page walk then never
> completes and the IOMMU is left stuck (PAGING_ENABLED, never IDLE).
>
> Clear bit 1 (keeping bit 0, the slave-port gate) once paging is enabled
> so the walker keeps its clock. This is required for the RK3568 NPU MMU.
Hi,
I'm not able to review this patch myself, but maybe it can be
submitted separately while we work on the NPU bits?
Regards,
Tomeu
> Signed-off-by: Midgy BALON <midgy971@gmail.com>
> ---
> drivers/iommu/rockchip-iommu.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 4da80136933c4..e3d8b6e9ca12b 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -953,6 +953,18 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
>
> ret = rk_iommu_enable_paging(iommu);
>
> + if (!ret) {
> + /*
> + * RK356x v1 IOMMU: RK_MMU_AUTO_GATING bit 1 enables page-walker
> + * auto clock-gating; the walker's AXI master then loses its clock
> + * between transactions and a TLB-miss page walk never completes,
> + * leaving the IOMMU stuck (PAGING_ENABLED, never IDLE). Clear
> + * bit 1 (keep bit 0, the slave-port gate) once paging is enabled.
> + */
> + for (i = 0; i < iommu->num_mmu; i++)
> + rk_iommu_write(iommu->bases[i], RK_MMU_AUTO_GATING, 0x2);
> + }
> +
> out_disable_stall:
> rk_iommu_disable_stall(iommu);
> out_disable_clocks:
> --
> 2.39.5
>
^ permalink raw reply
* Re: [PATCH v6 5/8] perf cs-etm: Support call indentation
From: James Clark @ 2026-06-04 14:24 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-5-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> This commit supports the field "callindent" to reflect the call stack
> depth.
>
> The branch stack is used by both call indentation and the last branch
> record, which are separate features. Use a new flag "use_br_stack" to
> track whether the branch stack needs to be recorded.
>
> Before:
>
> perf script -F +callindent
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: aaaae3ed077c do_svc+0x14 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: ffff800080010c00 vectors+0x400 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080010c24 vectors+0x424 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114dc el0t_64_sync+0xd4 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114f8 el0t_64_sync+0xf0 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011528 el0t_64_sync+0x120 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011538 el0t_64_sync+0x130 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800080011568 el0t_64_sync+0x160 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800081829110 el0t_64_sync_handler+0x18 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff800081829140 el0t_64_sync_handler+0x48 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> After:
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: aaaae3ed077c do_svc+0x14 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: ffff800080010c00 vectors+0x400 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080010c24 vectors+0x424 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114dc el0t_64_sync+0xd4 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114f8 el0t_64_sync+0xf0 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011528 el0t_64_sync+0x120 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011538 el0t_64_sync+0x130 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800080011568 el0t_64_sync+0x160 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800081829110 el0t_64_sync_handler+0x18 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff800081829140 el0t_64_sync_handler+0x48 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index ea2424175558ddc0a6f20a9de6c30f377facdc52..b31d0dd46a45dc365edd7c2f9e9b2eb077ca23db 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -66,6 +66,7 @@ struct cs_etm_auxtrace {
> bool snapshot_mode;
> bool data_queued;
> bool has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */
> + bool use_thread_stack;
>
> int num_cpu;
> u64 latest_kernel_timestamp;
> @@ -626,7 +627,7 @@ static int cs_etm__init_traceid_queue(struct cs_etm_queue *etmq,
> if (!tidq->prev_packet)
> goto out_free;
>
> - if (etm->synth_opts.last_branch) {
> + if (etm->use_thread_stack) {
> size_t sz = sizeof(struct branch_stack);
>
> sz += etm->synth_opts.last_branch_sz *
> @@ -1505,7 +1506,7 @@ static void cs_etm__add_stack_event(struct cs_etm_queue *etmq,
> tidq->packet->sample_type != CS_ETM_RANGE)
> return;
>
> - if (etmq->etm->synth_opts.last_branch) {
> + if (etmq->etm->use_thread_stack) {
> from = cs_etm__last_executed_instr(tidq->prev_packet);
> to = cs_etm__first_executed_instr(tidq->packet);
>
> @@ -1914,7 +1915,7 @@ static int cs_etm__flush(struct cs_etm_queue *etmq,
> cs_etm__packet_swap(etm, tidq);
>
> /* Reset last branches after flush the trace */
> - if (etm->synth_opts.last_branch)
> + if (etm->use_thread_stack)
> thread_stack__flush(tidq->thread);
>
> return err;
> @@ -1977,7 +1978,7 @@ static void cs_etm__flush_all_stack(struct cs_etm_queue *etmq)
> {
> enum cs_etm_pid_fmt pid_fmt = cs_etm__get_pid_fmt(etmq);
>
> - if (!etmq->etm->synth_opts.last_branch)
> + if (!etmq->etm->use_thread_stack)
> return;
>
> cs_etm__flush_machine_stack(etmq, HOST_KERNEL_ID);
> @@ -3438,6 +3439,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> itrace_synth_opts__set_default(&etm->synth_opts,
> session->itrace_synth_opts->default_no_sample);
> etm->synth_opts.callchain = false;
> + etm->synth_opts.thread_stack = session->itrace_synth_opts->thread_stack;
> }
>
> etm->session = session;
> @@ -3489,6 +3491,10 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> etm->tc.cap_user_time_zero = tc->cap_user_time_zero;
> etm->tc.cap_user_time_short = tc->cap_user_time_short;
> }
> +
> + etm->use_thread_stack = etm->synth_opts.thread_stack ||
> + etm->synth_opts.last_branch;
> +
> err = cs_etm__synth_events(etm, session);
> if (err)
> goto err_free_queues;
>
Reviewed-by: James Clark <james.clark@linaro.org>
^ permalink raw reply
* Re: [PATCH v3 24/32] iommufd: use iova_to_phys_length for efficient unmap
From: Jason Gunthorpe @ 2026-06-04 14:26 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-25-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:56PM +0800, Guanghui Feng wrote:
> Use iommu_iova_to_phys_length() to get PTE page size in
> + for (i = 0; i < npages; i++) {
> + if (!batch_add_pfn(batch, PHYS_PFN(phys) + i))
> + return;
batch_add_pfn_num()
Be mindful that the num is purposfully a u32 so that will need some
attention.
Jason
^ permalink raw reply
* Re: [PATCH v3 23/32] vfio: use iova_to_phys_length for efficient unmap
From: Jason Gunthorpe @ 2026-06-04 14:27 UTC (permalink / raw)
To: Guanghui Feng
Cc: adrian.larumbe, airlied, alex, alikernel-developer, baolu.lu,
boris.brezillon, dri-devel, dwmw2, iommu, joro, kevin.tian, kvm,
linux-arm-kernel, linux-kernel, liviu.dudau, maarten.lankhorst,
mripard, oliver.yang, robh, robin.murphy, shiyu.zsq, steven.price,
suravee.suthikulpanit, tzimmermann, wei.guo.simon, will, xlpang
In-Reply-To: <20260603151804.1963871-24-guanghuifeng@linux.alibaba.com>
On Wed, Jun 03, 2026 at 11:17:55PM +0800, Guanghui Feng wrote:
> Use iommu_iova_to_phys_length() to get PTE page size, allowing
> traversal by actual mapping granularity instead of PAGE_SIZE steps.
>
> Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
> Acked-by: Shiqiang Zhang <shiyu.zsq@linux.alibaba.com>
> Acked-by: Simon Guo <wei.guo.simon@linux.alibaba.com>
> ---
> drivers/vfio/vfio_iommu_type1.c | 27 ++++++++++++++++++++++-----
> 1 file changed, 22 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
> index c8151ba54de3..115d88d7003e 100644
> --- a/drivers/vfio/vfio_iommu_type1.c
> +++ b/drivers/vfio/vfio_iommu_type1.c
> @@ -1177,25 +1177,42 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
>
> iommu_iotlb_gather_init(&iotlb_gather);
> while (pos < dma->size) {
> - size_t unmapped, len;
> + size_t unmapped, len, pgsize;
> phys_addr_t phys, next;
> dma_addr_t iova = dma->iova + pos;
>
> - phys = iommu_iova_to_phys(domain->domain, iova);
> - if (WARN_ON(!phys)) {
> + /* Single page table walk returns both phys and PTE size */
> + phys = iommu_iova_to_phys_length(domain->domain, iova,
> + &pgsize);
> + if (WARN_ON(phys == PHYS_ADDR_MAX)) {
> pos += PAGE_SIZE;
> continue;
> }
> + if (WARN_ON(!pgsize || pgsize < PAGE_SIZE))
> + pgsize = PAGE_SIZE;
>
> /*
> * To optimize for fewer iommu_unmap() calls, each of which
> * may require hardware cache flushing, try to find the
> * largest contiguous physical memory chunk to unmap.
> + *
> + * mapped_length already accounts for contiguous entries
> + * from iova, then try to join following physically
> + * contiguous PTEs.
> */
> - for (len = PAGE_SIZE; pos + len < dma->size; len += PAGE_SIZE) {
> - next = iommu_iova_to_phys(domain->domain, iova + len);
> + len = min_t(size_t, pgsize, dma->size - pos);
> + for (; pos + len < dma->size; ) {
> + size_t next_pgsize;
> +
> + next = iommu_iova_to_phys_length(domain->domain,
> + iova + len,
> + &next_pgsize);
vfio should not be calling it twice, the core code needs to give the
best length as efficiently as it can. not open coding this in callers.
I think I've said this three times now
Jason
^ permalink raw reply
* Re: [PATCH v5 05/20] dma-pool: track decrypted atomic pools and select them via attrs
From: Jason Gunthorpe @ 2026-06-04 14:30 UTC (permalink / raw)
To: Michael Kelley
Cc: Aneesh Kumar K.V, iommu@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
Robin Murphy, Marek Szyprowski, Will Deacon, Marc Zyngier,
Steven Price, Suzuki K Poulose, Catalin Marinas, Jiri Pirko,
Mostafa Saleh, Petr Tesarik, Alexey Kardashevskiy, Dan Williams,
Xu Yilun, linuxppc-dev@lists.ozlabs.org,
linux-s390@vger.kernel.org, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Sven Schnelle, x86@kernel.org, Jiri Pirko
In-Reply-To: <SN6PR02MB4157F94C902B78E55E99372DD4102@SN6PR02MB4157.namprd02.prod.outlook.com>
On Thu, Jun 04, 2026 at 02:05:35PM +0000, Michael Kelley wrote:
> From: Jason Gunthorpe <jgg@ziepe.ca> Sent: Tuesday, June 2, 2026 5:55 PM
> >
> > On Tue, Jun 02, 2026 at 02:24:40PM +0000, Michael Kelley wrote:
> >
> > > Except that in a normal VM, the "unencrypted" pool attribute does *not*
> > > describe the state of the memory itself. In a normal VM, the memory is
> > > unencrypted, but the "unencrypted" pool attribute is false. That
> > > contradiction is the essence of my concern.
> >
> > I would argue no..
> >
> > When CC is enabled the default state of memory in a Linux environment
> > is "encrypted". You have to take a special action to "decrypt" it.
> >
> > Thus the default state of memory in a non-CC environment is also
> > paradoxically "encrypted" too.
>
> The need to have such an unnatural premise is usually an indication
> of a conceptual problem with the overall model, or perhaps just a
> terminology problem.
Oh yes I do think the AMD derived terminogy is aweful :(
> Here's a proposal. The new DMA attribute is DMA_ATTR_CC_SHARED.
> Name the pool attribute "cc_shared" instead of "unencrypted".
Yeah maybe. I sometimes imagine replacing the encrypted/decrypted
names with cc_shared too just to make it sane.
> "cc_shared" set to false in a normal VM doesn't lead to the non-sensical
> situation of claiming that a normal VM is encrypted.
It seems like a good idea to me
Jason
^ permalink raw reply
* [PATCH] phy: freescale: imx8qm-lvds-phy: Fix missing pm_runtime_disable() on probe error path
From: Felix Gu @ 2026-06-04 14:39 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Liu Ying
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel, Felix Gu
If mixel_lvds_phy_reset() fails in probe after pm_runtime_enable(),
the function returns directly without calling pm_runtime_disable(),
leaving runtime PM permanently enabled for the device.
Fix this by using devm_pm_runtime_enable() so that cleanup is
automatic on any probe failure or driver unbind. This also allows
removing the manual err label and the .remove callback.
Fixes: 06ff622d61d2 ("phy: freescale: Add i.MX8qm Mixel LVDS PHY support")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 24 +++++++-----------------
1 file changed, 7 insertions(+), 17 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
index ece357443521..c662f91e598c 100644
--- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
+++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
@@ -345,7 +345,9 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, priv);
- pm_runtime_enable(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
ret = mixel_lvds_phy_reset(dev);
if (ret) {
@@ -355,17 +357,15 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev)
for (i = 0; i < PHY_NUM; i++) {
lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL);
- if (!lvds_phy) {
- ret = -ENOMEM;
- goto err;
- }
+ if (!lvds_phy)
+ return -ENOMEM;
phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops);
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
dev_err(dev, "failed to create PHY for channel%d: %d\n",
i, ret);
- goto err;
+ return ret;
}
lvds_phy->phy = phy;
@@ -379,19 +379,10 @@ static int mixel_lvds_phy_probe(struct platform_device *pdev)
if (IS_ERR(phy_provider)) {
ret = PTR_ERR(phy_provider);
dev_err(dev, "failed to register PHY provider: %d\n", ret);
- goto err;
+ return ret;
}
return 0;
-err:
- pm_runtime_disable(dev);
-
- return ret;
-}
-
-static void mixel_lvds_phy_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
}
static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev)
@@ -432,7 +423,6 @@ MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match);
static struct platform_driver mixel_lvds_phy_driver = {
.probe = mixel_lvds_phy_probe,
- .remove = mixel_lvds_phy_remove,
.driver = {
.pm = &mixel_lvds_phy_pm_ops,
.name = "mixel-lvds-phy",
---
base-commit: a225caacc36546a09586e3ece36c0313146e7da9
change-id: 20260604-lvds-d67cb619df17
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
^ permalink raw reply related
* Re: [PATCH v3 2/2] kconfig: Remove the architecture specific config for Propeller
From: Will Deacon @ 2026-06-04 14:39 UTC (permalink / raw)
To: Rong Xu
Cc: Nathan Chancellor, Yabin Cui, Han Shen, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, H. Peter Anvin,
Kees Cook, Nicolas Schier, Linus Walleij, Arnd Bergmann,
Mathieu Desnoyers, Miguel Ojeda, Peter Zijlstra, Jinjie Ruan,
Lukas Bulwahn, linux-kernel, Juergen Gross, Helge Deller,
Ryan Roberts, Marc Zyngier, Ard Biesheuvel, Vincent Donnefort,
Alice Ryhl, x86, linux-arm-kernel
In-Reply-To: <CAF1bQ=S-p+Mi80a8C8pZzJ3E3bW_5tr6WBUKVV1LaXC9iBor=g@mail.gmail.com>
On Wed, Jun 03, 2026 at 03:15:02PM -0700, Rong Xu wrote:
> On Tue, Jun 2, 2026 at 6:54 PM Nathan Chancellor <nathan@kernel.org> wrote:
> >
> > On Tue, Jun 02, 2026 at 10:52:48AM -0700, Rong Xu wrote:
> > > On Tue, Jun 2, 2026 at 2:43 AM Will Deacon <will@kernel.org> wrote:
> > > > I still don't think it has anything to do with the arch. If the compiler
> > > > supports the option, then we can use it. The arch code in the kernel
> > > > doesn't need to do anything, right? So can you just check if the
> > > > compiler accepts the option using a 'depends on $(cc-option, ...)' line?
> > >
> > > Yes, arch code in the kernel does not need to do anything—it is just a marker.
> > >
> > > I understand your concern. I can use (cc-options,...) in PROPELLER_CLANG config.
> > > But I will not use -fbasic-block-address-map for backward compatiliby reason.
> > > I would use "-fbasic-block-sections=list=/dev/null".
> > >
> > > I'll send the updated patch shortly.
> >
> > Technically, an architecture needs to add the section generated by this
> > compiler option to their linker script to avoid an orphan section
> > warning (or error from CONFIG_WERROR) if enabled, as has been done in
> > this series.
> >
> > I worry that moving to a dynamic check will cause build breakage if an
> > LLVM target gains support for Propeller without having their kernel
> > image linker script adjusted. Maybe that will not happen very often and
> > even if it does, I do not mind taking on the maintenance burden of
> > fixing it but there is a cost of moving to a dynamic check like this.
> >
>
> This is true.
>
> That said, these orphan sections usually won't impact correctness. The
> linker will group these together even withgout the link script change,
> even though it generates a lot of warnings.
>
> With moving to a dynamic check, correct usage of this feature remains
> the user's responsibility -- I think this is Will's point from the
> very beginning.
> As noted in your previous response, the dynamic check is primarily
> intended to prevent "allmodconfig" build failures.
>
> I am comfortable with either the arch_kconfig or dynamic_check
> approach. Once a preferred solution is decided, please let me know so
> I can submit v4 for review.
I continue to object to the pointless ARCH_ selection and much prefer
having the core Kconfig check the cc-option.
Will
^ permalink raw reply
* Re: [PATCH v6 6/8] perf cs-etm: Filter synthesized branch samples
From: James Clark @ 2026-06-04 14:42 UTC (permalink / raw)
To: Leo Yan
Cc: linux-arm-kernel, coresight, linux-perf-users, Leo Yan,
Arnaldo Carvalho de Melo, John Garry, Will Deacon, Mike Leach,
Suzuki K Poulose, Namhyung Kim, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, Al Grant, Paschalis Mpeis,
Amir Ayupov
In-Reply-To: <20260526-b4-arm_cs_callchain_support_v1-v6-6-f9f49f53c9dd@arm.com>
On 26/05/2026 5:59 pm, Leo Yan wrote:
> From: Leo Yan <leo.yan@linaro.org>
>
> CS ETM currently emits branch samples for every decoded branch when
> branch synthesis is enabled. This delivers redundant info when users
> request only call or return branches.
>
> Add a branch filter derived from the itrace "calls" and "returns" options.
> When no filter is set, keep the existing behavior and emit all branch
> samples. When call or return filtering is requested, only synthesize branch
> samples whose flags match the selected branch types, including trace
> begin and end markers.
>
> Before:
>
> perf script -F +callindent
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: aaaae3ed077c do_svc+0x14 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: ffff800080010c00 vectors+0x400 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080010c24 vectors+0x424 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114dc el0t_64_sync+0xd4 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff8000800114f8 el0t_64_sync+0xf0 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011528 el0t_64_sync+0x120 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826600: 1 branches: ffff800080011538 el0t_64_sync+0x130 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800080011568 el0t_64_sync+0x160 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: ffff800081829110 el0t_64_sync_handler+0x18 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff800081829140 el0t_64_sync_handler+0x48 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> After:
>
> callchain_test 9187 [002] 599611.826599: 1 branches: main ffff83312258 __libc_start_call_main+0x78 (/usr/lib/aarch64-linux-gnu/libc.so.6)
> callchain_test 9187 [002] 599611.826599: 1 branches: foo aaaae3ed07c4 main+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: print aaaae3ed07ac foo+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: do_svc aaaae3ed0794 print+0x8 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826599: 1 branches: vectors aaaae3ed0780 do_svc+0x18 (/home/kernel/leoy/test_cs_callchain/callchain_test)
> callchain_test 9187 [002] 599611.826601: 1 branches: el0t_64_sync_handler ffff80008001159c el0t_64_sync+0x194 ([kernel.kallsyms])
> callchain_test 9187 [002] 599611.826601: 1 branches: el0_svc ffff800081829194 el0t_64_sync_handler+0x9c ([kernel.kallsyms])
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Signed-off-by: Leo Yan <leo.yan@arm.com>
> ---
> tools/perf/util/cs-etm.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index b31d0dd46a45dc365edd7c2f9e9b2eb077ca23db..8d98e772ecb307381b5ed1b4bbc4056e8779b261 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -71,6 +71,7 @@ struct cs_etm_auxtrace {
> int num_cpu;
> u64 latest_kernel_timestamp;
> u32 auxtrace_type;
> + u32 branches_filter;
> u64 branches_sample_type;
> u64 branches_id;
> u64 instructions_sample_type;
> @@ -1596,6 +1597,10 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq,
> } dummy_bs;
> u64 ip;
>
> + if (etm->branches_filter &&
> + !(etm->branches_filter & tidq->prev_packet->flags))
> + return 0;
> +
> ip = cs_etm__last_executed_instr(tidq->prev_packet);
>
> event->sample.header.type = PERF_RECORD_SAMPLE;
> @@ -3442,6 +3447,16 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
> etm->synth_opts.thread_stack = session->itrace_synth_opts->thread_stack;
> }
>
> + if (etm->synth_opts.calls)
> + etm->branches_filter |= PERF_IP_FLAG_CALL |
> + PERF_IP_FLAG_TRACE_BEGIN |
> + PERF_IP_FLAG_TRACE_END;
> +
> + if (etm->synth_opts.returns)
> + etm->branches_filter |= PERF_IP_FLAG_RETURN |
> + PERF_IP_FLAG_TRACE_BEGIN |
> + PERF_IP_FLAG_TRACE_END;
> +
This changes the default "perf script" output quite significantly and
will possibly break people's workflows. synth_opts.calls is true by
default but synth_opts.returns is false so we lose all the returns that
we used to have. Not sure if the new behavior is more consistent with
other tools so we can justify changing it? Personally I think including
returns by default made more sense, and it's a more literal
representation of the flow.
itrace.txt says the default is "all events i.e. the same as
--itrace=iybxwpe", but I thought the default was branches? At least for
Coresight it is, so I'm a bit confused.
> etm->session = session;
>
> etm->num_cpu = num_cpu;
>
^ permalink raw reply
* Re: [PATCH v14 09/44] arm64: RMI: Provide functions to delegate/undelegate ranges of memory
From: Steven Price @ 2026-06-04 14:43 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Suzuki K Poulose, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <867bowx3qx.wl-maz@kernel.org>
On 21/05/2026 14:59, Marc Zyngier wrote:
> On Wed, 13 May 2026 14:17:17 +0100,
> Steven Price <steven.price@arm.com> wrote:
>>
>> The RMM requires memory is 'delegated' to it so that it can be used
>> either for a realm guest or for various tracking purposes within the RMM
>> (e.g. for metadata or page tables). Memory that has been delegated
>> cannot be accessed by the host (it will result in a Granule Protection
>> Fault).
>>
>> Undelegation may fail if the memory is still in use by the RMM. This
>> shouldn't happen (Linux should ensure it has destroyed the RMM objects
>> before attempting to undelegate). In the event that it does happen this
>> points to a programming bug and the only reasonable approach is for the
>> physical pages to be leaked - it is up to the caller of
>> rmi_undelegate_range() to handle this.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> v14:
>> * Split into separate patch and moved out of KVM
>> ---
>> arch/arm64/include/asm/rmi_cmds.h | 13 +++++++++++
>> arch/arm64/kernel/rmi.c | 36 +++++++++++++++++++++++++++++++
>> 2 files changed, 49 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
>> index 9078a2920a7c..eb213c8e6f26 100644
>> --- a/arch/arm64/include/asm/rmi_cmds.h
>> +++ b/arch/arm64/include/asm/rmi_cmds.h
>> @@ -33,6 +33,19 @@ struct rmi_sro_state {
>> } while (RMI_RETURN_STATUS(res.a0) == RMI_BUSY || \
>> RMI_RETURN_STATUS(res.a0) == RMI_BLOCKED)
>>
>> +int rmi_delegate_range(phys_addr_t phys, unsigned long size);
>> +int rmi_undelegate_range(phys_addr_t phys, unsigned long size);
>> +
>> +static inline int rmi_delegate_page(phys_addr_t phys)
>> +{
>> + return rmi_delegate_range(phys, PAGE_SIZE);
>> +}
>> +
>> +static inline int rmi_undelegate_page(phys_addr_t phys)
>> +{
>> + return rmi_undelegate_range(phys, PAGE_SIZE);
>> +}
>> +
>> bool rmi_is_available(void);
>>
>> unsigned long rmi_sro_execute(struct rmi_sro_state *sro, gfp_t gfp);
>> diff --git a/arch/arm64/kernel/rmi.c b/arch/arm64/kernel/rmi.c
>> index 52a415e99500..08cef54acadb 100644
>> --- a/arch/arm64/kernel/rmi.c
>> +++ b/arch/arm64/kernel/rmi.c
>> @@ -12,6 +12,42 @@ static bool arm64_rmi_is_available;
>> unsigned long rmm_feat_reg0;
>> unsigned long rmm_feat_reg1;
>>
>> +int rmi_delegate_range(phys_addr_t phys, unsigned long size)
>> +{
>> + unsigned long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_delegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>> + else if (ret != RMI_BUSY && ret != RMI_BLOCKED)
>> + return ret;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +int rmi_undelegate_range(phys_addr_t phys, unsigned long size)
>> +{
>> + unsigned long ret = 0;
>> + unsigned long top = phys + size;
>> + unsigned long out_top;
>> +
>> + WARN_ON(size == 0);
>
> I find it odd to warn on size = 0. After all, free(NULL) is not an
> error. But even then, you continue feeding this to the RMM.
Ok, I'll admit that this is left over debugging - although this is a
condition that shouldn't happen.
Note that the while() condition prevents this from actually getting to
the RMM.
I'll drop the WARN_ON() since it's confusing.
Thanks,
Steve
> You also don't seem to be bothered with that on the delegation side...
>
>> +
>> + while (phys < top) {
>> + ret = rmi_granule_range_undelegate(phys, top, &out_top);
>> + if (ret == RMI_SUCCESS)
>> + phys = out_top;
>
> and size==0 doesn't violate any of the failure conditions listed in
> B4.5.18.2 (beta2). Will you end-up looping around forever?
>
> Same questions for the delegation, obviously.
>
> M.
>
^ permalink raw reply
* [PATCH v3] net: stmmac: fix fatal bus error on resume by reinitializing RX buffers
From: Ding Hui @ 2026-06-04 14:45 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Russell King (Oracle), Maxime Chevallier, Ding Hui,
open list:STMMAC ETHERNET DRIVER,
moderated list:ARM/STM32 ARCHITECTURE,
moderated list:ARM/STM32 ARCHITECTURE, open list
Cc: j.raczynski, xiasanbo, yangchen11, liuxuanjun
From: Ding Hui <dinghui@lixiang.com>
On suspend, stmmac_suspend() calls stmmac_disable_all_queues() which
stops the RX NAPI, but the RX DMA engine may still be running for a
short window before stmmac_stop_all_dma() takes effect. During that
window the hardware can write incoming frames into the buffers pointed
to by the RX descriptors and write back the descriptors (clearing the
OWN bit and overwriting RDES0/1/2 with status/timestamp data). Because
NAPI is already disabled, the driver never refills these descriptors,
so the RX ring is left in a "consumed but not refilled" state with
stale content in the descriptor buffer-address fields.
On resume, stmmac_clear_descriptors() only re-arms the OWN bit and
does not repopulate the RX buffer address fields. When the DMA is
restarted it dereferences these stale addresses and triggers a fatal
bus error (not kernel panic, just a Fatal Bus Error interrupt and
RX DMA engine halts).
Fix this by introducing stmmac_reinit_rx_descriptors(), called from
stmmac_resume() immediately after stmmac_clear_descriptors(). The
helper iterates every RX descriptor slot and re-programs its buffer
address fields:
- For normal (page_pool) queues: restore RDES0/1 from buf->addr and
RDES2 from buf->sec_addr. The DMA mapping has remained valid across
suspend/resume because no pages were freed. Slots left NULL by a
prior GFP_ATOMIC failure in stmmac_rx_refill() before suspend
are re-allocated here with GFP_KERNEL;
-ENOMEM is returned and resume is aborted if allocation fails.
The slots with null buffer are unacceptable, because they will
cause a DMA suspend dead lock problem by the condition of
Current Descriptor Pointer == Descriptor Tail Pointer.
- For AF_XDP zero-copy queues: restore the DMA address from
xsk_buff_xdp_get_dma(buf->xdp). Slots with no xdp buffer
(e.g. TX-only socket, empty fill ring) attempt xsk_buff_alloc()
first; on failure the descriptor is zeroed so the DMA engine skips
the slot safely via an RBU event.
- For chain mode: call stmmac_mode_init() to rebuild the des3 next-
descriptor pointer chain, which hardware may have overwritten with
a PTP timestamp value (as noted in chain_mode.c:refill_desc3()).
After reprogramming all address fields, a final pass restores OWN=1
on every valid slot. This is necessary because set_sec_addr and
chain-mode init unconditionally overwrite des3 (clearing the OWN bit
set by stmmac_clear_descriptors()), and must run after all address
writes are complete.
Also fix stmmac_init_rx_buffers() to actually use its gfp_t flags
parameter instead of the hardcoded GFP_ATOMIC | __GFP_NOWARN.
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
Changes in v3:
- Re-allocate page_pool NULL slots (from prior GFP_ATOMIC failures)
with GFP_KERNEL in stmmac_reinit_rx_descriptors(); return -ENOMEM and
abort resume.
- For XSK NULL slots, attempt xsk_buff_alloc() first; fall back to
stmmac_clear_desc() only when allocation fails.
- Add a re-arm loop at the end of stmmac_reinit_rx_descriptors() to
restore OWN=1 on all valid slots, since set_sec_addr and
chain-mode init both write des3 unconditionally.
- stmmac_reinit_rx_descriptors() now returns int; stmmac_resume()
checks the return value and propagates -ENOMEM with mutex/rtnl cleanup.
- Fix stmmac_init_rx_buffers() to use its flags parameter instead of
hardcoded GFP_ATOMIC | __GFP_NOWARN.
(884d2b845477 ("net: stmmac: Add GFP_DMA32 for rx buffers if no 64
capability"))
- Run stmmac_reinit_rx_descriptors() after stmmac_clear_descriptors()
so that stmmac_clear_desc() on XSK NULL slots overrides the OWN
bit set by stmmac_clear_descriptors().
- Update commit message.
- Link to v2:
https://lore.kernel.org/netdev/20260526022620.501229-1-dinghui1111@163.com/
Changes in v2:
- Introducing stmmac_reinit_rx_descriptors() to reinitializing rx
buffers without any allocation.
- Modify commit log.
- Link to v1:
https://lore.kernel.org/netdev/20260515053856.2310369-1-dinghui1111@163.com/
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 161 +++++++++++++++++-
1 file changed, 160 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3591755ea30b..36428e4ba8fd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1660,7 +1660,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
{
struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
- gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+ gfp_t gfp = flags;
if (priv->dma_cap.host_dma_width <= 32)
gfp |= GFP_DMA32;
@@ -1693,6 +1693,145 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
return 0;
}
+/**
+ * stmmac_reinit_rx_descriptors - re-program RX descriptor buffer addresses
+ * after stmmac_clear_descriptors()
+ * @priv: driver private structure
+ * @dma_conf: structure holding the dma data
+ * @queue: RX queue index
+ *
+ * Description: Called in the resume path after stmmac_clear_descriptors()
+ * has re-armed the OWN bit on every descriptor. Walk buf_pool[] and
+ * re-program the buffer-address fields of every RX descriptor from the
+ * buffers that are already attached to the queue. Slots whose page was
+ * never allocated (GFP_ATOMIC failure before suspend) are re-allocated
+ * here with GFP_KERNEL; the resume path is in process context.
+ *
+ * Between suspend and resume the hardware may have written back status/
+ * length information into the descriptor address fields (RDESx are reused
+ * for status on completion for GMAC4/XGMAC), so the address fields must be
+ * repopulated before the DMA is restarted.
+ *
+ * For XSK slots that have no xdp buffer at suspend time (TX-only socket,
+ * empty fill ring for Rx), xsk_buff_alloc() is attempted but does not
+ * return an error on failure because we can't identify a real TX-only
+ * socket from an alloc error (same as stmmac_alloc_rx_buffers_zc() in
+ * __init_dma_rx_desc_rings); on failure the descriptor is zeroed so the DMA
+ * engine skips the slot safely.
+ *
+ * To avoid the DMA stall after resume in non-XSK mode, this function
+ * re-allocates pages for NULL slots using GFP_KERNEL (the resume path runs
+ * in process context). If allocation fails, -%ENOMEM is returned immediately
+ * and the resume is aborted; the caller should report the error.
+ *
+ * This helper must be called after stmmac_clear_descriptors() and before
+ * stmmac_hw_setup() in stmmac_resume() because we need to wipe the OWN bit
+ * set in stmmac_clear_descriptors() for NULL slots in XSK mode.
+ */
+static int stmmac_reinit_rx_descriptors(struct stmmac_priv *priv,
+ struct stmmac_dma_conf *dma_conf,
+ u32 queue)
+{
+ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+ struct stmmac_rx_buffer *buf;
+ struct dma_desc *p;
+ int i;
+
+ if (rx_q->xsk_pool) {
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ /* The XSK pool may not be fully populated (e.g.
+ * xdpsock TX-only, empty fill ring). Try to refill
+ * from the pool; on failure zero the descriptor so the
+ * DMA engine skips this slot safely.
+ */
+ if (!buf->xdp) {
+ buf->xdp = xsk_buff_alloc(rx_q->xsk_pool);
+ if (!buf->xdp) {
+ stmmac_clear_desc(priv, p);
+ continue;
+ }
+ }
+
+ stmmac_set_desc_addr(priv, p,
+ xsk_buff_xdp_get_dma(buf->xdp));
+ stmmac_set_desc_sec_addr(priv, p, 0, false);
+ }
+ } else {
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ /* buf->page can be NULL when stmmac_rx_refill() hit a
+ * GFP_ATOMIC failure before suspend and left the slot
+ * without a buffer. The resume path runs in process
+ * context, so re-allocate with GFP_KERNEL. Allocation
+ * failure aborts the resume.
+ */
+ if (!buf->page) {
+ int err;
+
+ err = stmmac_init_rx_buffers(priv, dma_conf, p,
+ i, GFP_KERNEL,
+ queue);
+ if (err)
+ return err;
+ /* stmmac_init_rx_buffers() already programmed
+ * the descriptor; skip the reprogramming below.
+ */
+ continue;
+ }
+
+ stmmac_set_desc_addr(priv, p, buf->addr);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr,
+ priv->sph_active &&
+ buf->sec_page);
+
+ if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
+ stmmac_init_desc3(priv, p);
+ }
+ }
+
+ /* Chain mode: re-link descriptor 'next' pointers. This is
+ * allocation-free; it just rewrites the per-descriptor next
+ * field which may have been clobbered by HW writeback.
+ */
+ if (priv->descriptor_mode == STMMAC_CHAIN_MODE) {
+ void *des = priv->extend_desc ? (void *)rx_q->dma_erx
+ : (void *)rx_q->dma_rx;
+
+ stmmac_mode_init(priv, des, rx_q->dma_rx_phy,
+ dma_conf->dma_rx_size, priv->extend_desc);
+ }
+
+ /* Re-arm OWN=1 on every valid slot.
+ *
+ * Two address-programming helpers write des3 unconditionally and
+ * therefore clear the OWN bit that stmmac_clear_descriptors() set:
+ *
+ * - stmmac_desc_ops.set_sec_addr (called by stmmac_set_desc_sec_addr()):
+ * writes des3 with upper_32_bits(addr).
+ *
+ * - stmmac_mode_ops.init() (called by stmmac_mode_init() above): writes
+ * des3 with the next-descriptor physical address.
+ *
+ * A single pass over valid slots restores OWN=1 after all descriptor
+ * fields have been written. NULL slots are left with OWN=0 for XSK mode
+ * so the Rx DMA engine stalls safely.
+ */
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ buf = &rx_q->buf_pool[i];
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+
+ if (rx_q->xsk_pool ? !!buf->xdp : !!buf->page)
+ stmmac_set_rx_owner(priv, p, false);
+ }
+
+ return 0;
+}
+
/**
* stmmac_free_rx_buffer - free RX dma buffers
* @priv: private structure
@@ -8272,6 +8411,7 @@ int stmmac_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
+ u32 queue;
int ret;
if (priv->plat->resume) {
@@ -8321,6 +8461,25 @@ int stmmac_resume(struct device *dev)
stmmac_free_tx_skbufs(priv);
stmmac_clear_descriptors(priv, &priv->dma_conf);
+ /* Re-program the RX descriptor buffer-address fields. Slots that
+ * had no page at suspend time (GFP_ATOMIC failure) are re-allocated
+ * here with GFP_KERNEL; XSK slots without an xdp buffer are refilled
+ * from the pool if possible. Any unrecoverable allocation failure
+ * is reported so the resume can be aborted cleanly.
+ */
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ ret = stmmac_reinit_rx_descriptors(priv, &priv->dma_conf,
+ queue);
+ if (ret) {
+ netdev_err(priv->dev,
+ "%s: rx desc reinit failed on queue %u\n",
+ __func__, queue);
+ mutex_unlock(&priv->lock);
+ rtnl_unlock();
+ return ret;
+ }
+ }
+
ret = stmmac_hw_setup(ndev);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
--
2.34.1
^ permalink raw reply related
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