* RE: [PATCH 1/2] Documentation: ABI: add sysfs interface for ZynqMP CSU registers
From: Thangaraj, Senthil Nathan @ 2026-04-24 4:23 UTC (permalink / raw)
To: Jain, Ronak, Simek, Michal
Cc: linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, Jain, Ronak
In-Reply-To: <20260408114244.2852015-2-ronak.jain@amd.com>
[AMD Official Use Only - AMD Internal Distribution Only]
> -----Original Message-----
> From: Ronak Jain <ronak.jain@amd.com>
> Sent: Wednesday, April 8, 2026 4:43 AM
> To: Simek, Michal <michal.simek@amd.com>; Thangaraj, Senthil Nathan
> <SenthilNathan.Thangaraj@amd.com>
> Cc: linux-kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; Jain,
> Ronak <ronak.jain@amd.com>
> Subject: [PATCH 1/2] Documentation: ABI: add sysfs interface for ZynqMP CSU
> registers
>
> Document the new sysfs interface that exposes Configuration Security Unit
> (CSU) registers through the zynqmp-firmware driver.
>
> The interface is available under:
>
> /sys/devices/platform/firmware:zynqmp-firmware/csu_registers/
>
> The CSU registers are discovered at boot time using the PM_QUERY_DATA
> firmware API. The following registers are currently supported:
>
> - multiboot (CSU_MULTI_BOOT)
> - idcode (CSU_IDCODE, read-only)
> - pcap-status (CSU_PCAP_STATUS, read-only)
>
> Read operations use the existing IOCTL_READ_REG firmware interface, while
> write operations use IOCTL_MASK_WRITE_REG.
>
> Access control is enforced by the firmware. Write attempts to read-only registers
> are rejected by firmware even though the sysfs file permissions allow writes.
>
> Document the ABI entry accordingly.
>
> Signed-off-by: Ronak Jain <ronak.jain@amd.com>
> ---
> .../ABI/stable/sysfs-driver-firmware-zynqmp | 33 +++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
> b/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
> index c3fec3c835af..f537f7d9bb55 100644
> --- a/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
> +++ b/Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
> @@ -254,3 +254,36 @@ Description:
> The expected result is 500.
>
> Users: Xilinx
> +
> +What: /sys/devices/platform/firmware\:zynqmp-
> firmware/csu_registers/*
> +Date: March 2026
Should we need to change the month to April ?
> +KernelVersion: 7.1
> +Contact: "Ronak Jain" <ronak.jain@amd.com>
> +Description:
> + Read/Write CSU (Configuration Security Unit) registers.
> +
> + This interface provides dynamic access to CSU registers that are
> + discovered from the firmware at boot time using PM_QUERY_DATA
> API.
> +
> + The supported registers are:
> +
> + - multiboot: CSU_MULTI_BOOT register
> + - idcode: CSU_IDCODE register (read-only)
> + - pcap-status: CSU_PCAP_STATUS register (read-only)
> +
> + Read operations use the existing IOCTL_READ_REG API.
> + Write operations use the existing IOCTL_MASK_WRITE_REG API.
> +
> + The firmware enforces access control - read-only registers will reject
> + write attempts even though the sysfs permissions show write access.
> +
> + Usage for reading::
> +
> + # cat /sys/devices/platform/firmware\:zynqmp-
> firmware/csu_registers/multiboot
> + # cat
> +/sys/devices/platform/firmware\:zynqmp-firmware/csu_registers/idcode
> +
> + Usage for writing (mask and value are in hexadecimal)::
> +
> + # echo 0xFFFFFFF 0x0 >
> +/sys/devices/platform/firmware\:zynqmp-firmware/csu_registers/multiboot
> +
> +Users: Xilinx/AMD
> --
> 2.34.1
Thanks,
Senthil
^ permalink raw reply
* [PATCH 1/1] arm: traps: remove fp underflow checking
From: Maninder Singh @ 2026-04-24 4:30 UTC (permalink / raw)
To: linux, linus.walleij, ardb, ndesaulniers
Cc: linux-arm-kernel, linux-kernel, Maninder Singh
In-Reply-To: <CGME20260424043122epcas5p17e2f88653e398c5a53c0ffa4c1e01bc1@epcas5p1.samsung.com>
with IRQSTACKS fp can point outside of thread's stack.
And when there is crash reported from IRQ side, it always report
"frame pointer underflow", which is not real underflow.
Unhandled fault: page domain fault (0x81b) at 0x00000000
..
PC is at handle_irq_desc+0x68/0xa4
LR is at generic_handle_domain_irq+0x18/0x1c
..
Call trace: frame pointer underflow
handle_irq_desc from generic_handle_domain_irq+0x18/0x1c
generic_handle_domain_irq from gic_handle_irq+0x78/0x8c
gic_handle_irq from generic_handle_arch_irq+0x3c/0x4c
..
It is just warning print and there is no decision based on this
to stop backtrack or etc. So it can be removed.
Signed-off-by: Maninder Singh <maninder1.s@samsung.com>
---
arch/arm/kernel/traps.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index afbd2ebe5c39..47f8c0738399 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -243,8 +243,8 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk,
} else if (verify_stack(fp)) {
pr_cont("invalid frame pointer 0x%08x", fp);
ok = 0;
- } else if (fp < (unsigned long)end_of_stack(tsk))
- pr_cont("frame pointer underflow");
+ }
+
pr_cont("\n");
if (ok)
--
2.34.1
^ permalink raw reply related
* [PATCH v1] arm64: dts: cix: Add CPU idle states for Sky1
From: Devin Li @ 2026-04-24 4:34 UTC (permalink / raw)
To: peter.chen, fugang.duan, robh, krzk+dt, conor+dt,
cix-kernel-upstream
Cc: linux-arm-kernel, devicetree, linux-kernel, guoyin.chen,
robin.wang, hong.guo, Devin.Li
From: "devin.li" <Devin.Li@cixtech.com>
Add PSCI-based CPU idle state definitions for the Sky1 SoC,
enabling core and cluster level power management through
ARM PSCI firmware.
Three idle states are defined:
- CPU_SLEEP_0: Core idle state for A520 cores
(psci-suspend-param 0x0010000), entry-latency 34us,
exit-latency 100us
- CPU_SLEEP_1: Core idle state for A720 cores
(psci-suspend-param 0x10000), entry-latency 31us,
exit-latency 79us
- CLUSTER_SLEEP_0: Cluster idle state shared by all cores
(psci-suspend-param 0x1010000), entry-latency 41us,
exit-latency 104us
A520 cores (cpu0-3) reference CPU_SLEEP_0 and CLUSTER_SLEEP_0,
while A720 cores (cpu4-11) reference CPU_SLEEP_1 and
CLUSTER_SLEEP_0.
Signed-off-by: devin.li <Devin.Li@cixtech.com>
---
arch/arm64/boot/dts/cix/sky1.dtsi | 41 +++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index bb5cfb1f2113..0611098b5f05 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -23,6 +23,7 @@ cpu0: cpu@0 {
reg = <0x0 0x0>;
device_type = "cpu";
capacity-dmips-mhz = <403>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
cpu1: cpu@100 {
@@ -31,6 +32,7 @@ cpu1: cpu@100 {
reg = <0x0 0x100>;
device_type = "cpu";
capacity-dmips-mhz = <403>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
cpu2: cpu@200 {
@@ -39,6 +41,7 @@ cpu2: cpu@200 {
reg = <0x0 0x200>;
device_type = "cpu";
capacity-dmips-mhz = <403>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
cpu3: cpu@300 {
@@ -47,6 +50,7 @@ cpu3: cpu@300 {
reg = <0x0 0x300>;
device_type = "cpu";
capacity-dmips-mhz = <403>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
cpu4: cpu@400 {
@@ -55,6 +59,7 @@ cpu4: cpu@400 {
reg = <0x0 0x400>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu5: cpu@500 {
@@ -63,6 +68,7 @@ cpu5: cpu@500 {
reg = <0x0 0x500>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu6: cpu@600 {
@@ -71,6 +77,7 @@ cpu6: cpu@600 {
reg = <0x0 0x600>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu7: cpu@700 {
@@ -79,6 +86,7 @@ cpu7: cpu@700 {
reg = <0x0 0x700>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu8: cpu@800 {
@@ -87,6 +95,7 @@ cpu8: cpu@800 {
reg = <0x0 0x800>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu9: cpu@900 {
@@ -95,6 +104,7 @@ cpu9: cpu@900 {
reg = <0x0 0x900>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu10: cpu@a00 {
@@ -103,6 +113,7 @@ cpu10: cpu@a00 {
reg = <0x0 0xa00>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu11: cpu@b00 {
@@ -111,6 +122,7 @@ cpu11: cpu@b00 {
reg = <0x0 0xb00>;
device_type = "cpu";
capacity-dmips-mhz = <1024>;
+ cpu-idle-states = <&CPU_SLEEP_1 &CLUSTER_SLEEP_0>;
};
cpu-map {
@@ -153,6 +165,35 @@ core11 {
};
};
};
+
+ idle-states {
+ CPU_SLEEP_0: cpu-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ local-timer-stop;
+ entry-latency-us = <34>;
+ exit-latency-us = <100>;
+ min-residency-us = <3000>;
+ };
+
+ CPU_SLEEP_1: cpu-sleep-1 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ local-timer-stop;
+ entry-latency-us = <31>;
+ exit-latency-us = <79>;
+ min-residency-us = <3000>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x1010000>;
+ local-timer-stop;
+ entry-latency-us = <41>;
+ exit-latency-us = <104>;
+ min-residency-us = <4000>;
+ };
+ };
};
firmware {
--
2.49.0
^ permalink raw reply related
* RE: [PATCH 2/2] firmware: zynqmp: Add dynamic CSU register discovery and sysfs interface
From: Thangaraj, Senthil Nathan @ 2026-04-24 4:39 UTC (permalink / raw)
To: Jain, Ronak, Simek, Michal
Cc: linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, Jain, Ronak
In-Reply-To: <20260408114244.2852015-3-ronak.jain@amd.com>
[AMD Official Use Only - AMD Internal Distribution Only]
> -----Original Message-----
> From: Ronak Jain <ronak.jain@amd.com>
> Sent: Wednesday, April 8, 2026 4:43 AM
> To: Simek, Michal <michal.simek@amd.com>; Thangaraj, Senthil Nathan
> <SenthilNathan.Thangaraj@amd.com>
> Cc: linux-kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org; Jain,
> Ronak <ronak.jain@amd.com>
> Subject: [PATCH 2/2] firmware: zynqmp: Add dynamic CSU register discovery
> and sysfs interface
>
> Add support for dynamically discovering and exposing Configuration Security
> Unit (CSU) registers through sysfs. Leverage the existing PM_QUERY_DATA
> API to discover available registers at runtime, making the interface flexible and
> maintainable.
>
> Key features:
> - Dynamic register discovery using PM_QUERY_DATA API
> * PM_QID_GET_NODE_COUNT: Query number of available registers
> * PM_QID_GET_NODE_NAME: Query register names by index
> - Automatic sysfs attribute creation under csu_registers/ group
> - Read operations via existing IOCTL_READ_REG API
> - Write operations via existing IOCTL_MASK_WRITE_REG API
> - Firmware-enforced access control (read-only registers reject writes)
>
> The sysfs interface is created at:
> /sys/devices/platform/firmware:zynqmp-firmware/csu_registers/
>
> Currently supported registers include:
> - multiboot (CSU_MULTI_BOOT)
> - idcode (CSU_IDCODE, read-only)
> - pcap-status (CSU_PCAP_STATUS, read-only)
>
> The dynamic discovery approach allows firmware to control which registers are
> exposed without requiring kernel changes, improving maintainability and
> security.
>
> Signed-off-by: Ronak Jain <ronak.jain@amd.com>
> ---
> MAINTAINERS | 10 +
> drivers/firmware/xilinx/Makefile | 2 +-
> drivers/firmware/xilinx/zynqmp-csu-reg.c | 249 +++++++++++++++++++++++
> drivers/firmware/xilinx/zynqmp-csu-reg.h | 18 ++
> drivers/firmware/xilinx/zynqmp.c | 6 +
> include/linux/firmware/xlnx-zynqmp.h | 4 +-
> 6 files changed, 287 insertions(+), 2 deletions(-) create mode 100644
> drivers/firmware/xilinx/zynqmp-csu-reg.c
> create mode 100644 drivers/firmware/xilinx/zynqmp-csu-reg.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 10d12b51b1f6..37fe2b7e0ccf 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -29212,6 +29212,16 @@ F: drivers/dma/xilinx/xdma.c
> F: include/linux/dma/amd_xdma.h
> F: include/linux/platform_data/amd_xdma.h
>
> +XILINX ZYNQMP CSU REGISTER DRIVER
> +M: Senthil Nathan Thangaraj <senthilnathan.thangaraj@amd.com>
> +R: Michal Simek <michal.simek@amd.com>
> +R: Ronak Jain <ronak.jain@amd.com>
> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
> +S: Maintained
> +F: Documentation/ABI/stable/sysfs-driver-firmware-zynqmp
> +F: drivers/firmware/xilinx/zynqmp-csu-reg.c
> +F: drivers/firmware/xilinx/zynqmp-csu-reg.h
> +
> XILINX ZYNQMP DPDMA DRIVER
> M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> L: dmaengine@vger.kernel.org
> diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
> index 8db0e66b6b7e..6203f41daaa6 100644
> --- a/drivers/firmware/xilinx/Makefile
> +++ b/drivers/firmware/xilinx/Makefile
> @@ -1,5 +1,5 @@
> # SPDX-License-Identifier: GPL-2.0
> # Makefile for Xilinx firmwares
>
> -obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o zynqmp-ufs.o zynqmp-
> crypto.o
> +obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o zynqmp-ufs.o zynqmp-
> crypto.o
> +zynqmp-csu-reg.o
> obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o diff --git
> a/drivers/firmware/xilinx/zynqmp-csu-reg.c b/drivers/firmware/xilinx/zynqmp-csu-
> reg.c
> new file mode 100644
> index 000000000000..1f304ce858b1
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp-csu-reg.c
> @@ -0,0 +1,249 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Xilinx Zynq MPSoC CSU Register Access
> + *
> + * Copyright (C) 2026 Advanced Micro Devices, Inc.
> + *
> + * Michal Simek <michal.simek@amd.com>
> + * Ronak Jain <ronak.jain@amd.com>
> + */
> +
> +#include <linux/firmware/xlnx-zynqmp.h> #include
> +<linux/platform_device.h> #include <linux/slab.h> #include
> +<linux/string.h>
> +
> +#include "zynqmp-csu-reg.h"
> +
> +/* Node ID for CSU module in firmware */ #define CSU_NODE_ID 0
> +
> +/* Maximum number of CSU registers supported */ #define MAX_CSU_REGS
> 50
> +
> +/* Size of register name returned by firmware (3 u32 words = 12 bytes)
> +*/ #define CSU_REG_NAME_LEN 12
> +
> +/**
> + * struct zynqmp_csu_reg - CSU register information
> + * @id: Register index from firmware
> + * @name: Register name
> + * @attr: Device attribute for sysfs
> + */
> +struct zynqmp_csu_reg {
> + u32 id;
> + char name[CSU_REG_NAME_LEN];
> + struct device_attribute attr;
> +};
> +
> +/**
> + * struct zynqmp_csu_data - Per-device CSU data
> + * @csu_regs: Array of CSU registers
> + * @csu_reg_count: Number of CSU registers
> + * @csu_attr_group: Attribute group for sysfs */ struct
> +zynqmp_csu_data {
> + struct zynqmp_csu_reg *csu_regs;
> + int csu_reg_count;
> + struct attribute_group csu_attr_group; };
> +
> +/**
> + * zynqmp_pm_get_node_count() - Get number of supported nodes via
> +QUERY_DATA
> + *
> + * Return: Number of nodes on success, or negative error code */
> +static int zynqmp_pm_get_node_count(void) {
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> +
> + qdata.qid = PM_QID_GET_NODE_COUNT;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + return ret_payload[1];
> +}
> +
> +/**
> + * zynqmp_pm_get_node_name() - Get node name via QUERY_DATA
> + * @index: Register index
> + * @name: Buffer to store register name
> + *
> + * Return: 0 on success, error code otherwise */ static int
> +zynqmp_pm_get_node_name(u32 index, char *name) {
> + struct zynqmp_pm_query_data qdata = {0};
> + u32 ret_payload[PAYLOAD_ARG_CNT];
> + int ret;
> +
> + qdata.qid = PM_QID_GET_NODE_NAME;
> + qdata.arg1 = index;
> +
> + ret = zynqmp_pm_query_data(qdata, ret_payload);
> + if (ret)
> + return ret;
> +
> + memcpy(name, &ret_payload[1], CSU_REG_NAME_LEN);
> + name[CSU_REG_NAME_LEN - 1] = '\0';
> +
> + return 0;
> +}
> +
> +/**
> + * zynqmp_csu_reg_show() - Generic show function for all registers
> + * @dev: Device pointer
> + * @attr: Device attribute
> + * @buf: Output buffer
> + *
> + * Return: Number of bytes written to buffer, or error code */ static
> +ssize_t zynqmp_csu_reg_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct zynqmp_csu_reg *reg;
> + u32 value;
> + int ret;
> +
> + /* Use container_of to get register directly */
> + reg = container_of(attr, struct zynqmp_csu_reg, attr);
> +
> + ret = zynqmp_pm_sec_read_reg(CSU_NODE_ID, reg->id, &value);
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "0x%08x\n", value); }
> +
> +/**
> + * zynqmp_csu_reg_store() - Generic store function for writable
> +registers
> + * @dev: Device pointer
> + * @attr: Device attribute
> + * @buf: Input buffer
> + * @count: Buffer size
> + *
> + * Format: "mask value" - both mask and value required
> + * Example: echo "0xFFFFFFFF 0x12345678" > register
> + *
> + * Return: count on success, error code otherwise */ static ssize_t
> +zynqmp_csu_reg_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct zynqmp_csu_reg *reg;
> + u32 mask, value;
> + int ret;
> +
> + reg = container_of(attr, struct zynqmp_csu_reg, attr);
> +
> + if (sscanf(buf, "%x %x", &mask, &value) != 2)
> + return -EINVAL;
> +
> + ret = zynqmp_pm_sec_mask_write_reg(CSU_NODE_ID, reg->id, mask,
> value);
> + if (ret)
> + return ret;
> +
> + return count;
> +}
> +
> +/**
> + * zynqmp_csu_discover_registers() - Discover CSU registers from
> +firmware
> + * @pdev: Platform device pointer
> + *
> + * This function uses PM_QUERY_DATA to discover all available CSU
> +registers
> + * and creates sysfs group under
> +/sys/devices/platform/firmware:zynqmp-firmware/
> + *
> + * Return: 0 on success, error code otherwise */ int
> +zynqmp_csu_discover_registers(struct platform_device *pdev) {
> + struct zynqmp_csu_data *csu_data;
> + struct attribute **attrs;
> + int count, ret, i;
> +
> + ret = zynqmp_pm_is_function_supported(PM_QUERY_DATA,
> PM_QID_GET_NODE_COUNT);
> + if (ret) {
> + dev_dbg(&pdev->dev, "CSU register discovery not supported by
> current firmware\n");
> + return 0;
> + }
Should we also call zynqmp_pm_is_function_supported(PM_QUERY_DATA, PM_QID_GET_NODE_NAME) to verify firmware support before proceeding?
> +
> + count = zynqmp_pm_get_node_count();
> + if (count < 0)
> + return count;
> + if (count == 0) {
> + dev_dbg(&pdev->dev, "No nodes available from firmware\n");
> + return 0;
> + }
> +
> + /* Validate count to prevent excessive memory allocation */
> + if (count > MAX_CSU_REGS) {
> + dev_err(&pdev->dev, "Register count %d exceeds maximum %d\n",
> + count, MAX_CSU_REGS);
> + return -EINVAL;
> + }
> +
> + dev_dbg(&pdev->dev, "Discovered %d nodes from firmware\n", count);
> +
> + csu_data = devm_kzalloc(&pdev->dev, sizeof(*csu_data), GFP_KERNEL);
> + if (!csu_data)
> + return -ENOMEM;
> +
> + csu_data->csu_reg_count = count;
> +
> + csu_data->csu_regs = devm_kcalloc(&pdev->dev, count,
> sizeof(*csu_data->csu_regs),
> + GFP_KERNEL);
> + if (!csu_data->csu_regs) {
> + devm_kfree(&pdev->dev, csu_data);
> + return -ENOMEM;
> + }
> +
> + attrs = devm_kcalloc(&pdev->dev, count + 1, sizeof(*attrs), GFP_KERNEL);
> + if (!attrs) {
> + devm_kfree(&pdev->dev, csu_data->csu_regs);
> + devm_kfree(&pdev->dev, csu_data);
> + return -ENOMEM;
> + }
> +
> + for (i = 0; i < count; i++) {
> + struct zynqmp_csu_reg *reg = &csu_data->csu_regs[i];
> + struct device_attribute *dev_attr = ®->attr;
> +
> + reg->id = i;
> +
> + ret = zynqmp_pm_get_node_name(i, reg->name);
> + if (ret) {
> + dev_warn(&pdev->dev, "Failed to get name for register %d\n",
> i);
> + snprintf(reg->name, sizeof(reg->name), "csu_reg_%d", i);
> + }
> +
> + /* Create sysfs attribute - firmware enforces actual access control */
> + sysfs_attr_init(&dev_attr->attr);
> + dev_attr->attr.name = reg->name;
> + dev_attr->attr.mode = 0644;
> + dev_attr->show = zynqmp_csu_reg_show;
> + dev_attr->store = zynqmp_csu_reg_store;
> +
> + attrs[i] = &dev_attr->attr;
> +
> + dev_dbg(&pdev->dev, "Register %d: id=%d name=%s\n", i, reg->id,
> reg->name);
> + }
> +
> + csu_data->csu_attr_group.name = "csu_registers";
> + csu_data->csu_attr_group.attrs = attrs;
> +
> + ret = devm_device_add_group(&pdev->dev, &csu_data->csu_attr_group);
> + if (ret) {
> + devm_kfree(&pdev->dev, attrs);
> + devm_kfree(&pdev->dev, csu_data->csu_regs);
> + devm_kfree(&pdev->dev, csu_data);
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(zynqmp_csu_discover_registers);
> diff --git a/drivers/firmware/xilinx/zynqmp-csu-reg.h
> b/drivers/firmware/xilinx/zynqmp-csu-reg.h
> new file mode 100644
> index 000000000000..b12415db3496
> --- /dev/null
> +++ b/drivers/firmware/xilinx/zynqmp-csu-reg.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx Zynq MPSoC CSU Register Access
> + *
> + * Copyright (C) 2026 Advanced Micro Devices, Inc.
> + *
> + * Michal Simek <michal.simek@amd.com>
> + * Ronak Jain <ronak.jain@amd.com>
> + */
> +
> +#ifndef __ZYNQMP_CSU_REG_H__
> +#define __ZYNQMP_CSU_REG_H__
> +
> +#include <linux/platform_device.h>
> +
> +int zynqmp_csu_discover_registers(struct platform_device *pdev);
> +
> +#endif /* __ZYNQMP_CSU_REG_H__ */
> diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c
> index fbe8510f4927..b549d07f7497 100644
> --- a/drivers/firmware/xilinx/zynqmp.c
> +++ b/drivers/firmware/xilinx/zynqmp.c
> @@ -27,6 +27,7 @@
>
> #include <linux/firmware/xlnx-zynqmp.h> #include <linux/firmware/xlnx-event-
> manager.h>
> +#include "zynqmp-csu-reg.h"
> #include "zynqmp-debug.h"
>
> /* Max HashMap Order for PM API feature check (1<<7 = 128) */ @@ -2120,6
> +2121,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev)
> dev_err_probe(&pdev->dev, PTR_ERR(em_dev), "EM register
> fail with error\n");
> }
>
> + /* Discover CSU registers dynamically */
> + ret = zynqmp_csu_discover_registers(pdev);
> + if (ret)
> + dev_warn(&pdev->dev, "CSU register discovery failed: %d\n", ret);
> +
> return of_platform_populate(dev->of_node, NULL, NULL, dev); }
>
> diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-
> zynqmp.h
> index d70dcd462b44..a4b293eb96ce 100644
> --- a/include/linux/firmware/xlnx-zynqmp.h
> +++ b/include/linux/firmware/xlnx-zynqmp.h
> @@ -3,7 +3,7 @@
> * Xilinx Zynq MPSoC Firmware layer
> *
> * Copyright (C) 2014-2021 Xilinx
> - * Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc.
> + * Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc.
> *
> * Michal Simek <michal.simek@amd.com>
> * Davorin Mista <davorin.mista@aggios.com> @@ -262,6 +262,8 @@ enum
> pm_query_id {
> PM_QID_CLOCK_GET_NUM_CLOCKS = 12,
> PM_QID_CLOCK_GET_MAX_DIVISOR = 13,
> PM_QID_PINCTRL_GET_ATTRIBUTES = 15,
> + PM_QID_GET_NODE_NAME = 16,
> + PM_QID_GET_NODE_COUNT = 17,
> };
>
> enum rpu_oper_mode {
> --
> 2.34.1
Thanks,
Senthil
^ permalink raw reply
* [PATCH v14 0/3] of: parsing of multi #{iommu,msi}-cells in maps
From: Vijayanand Jitta @ 2026-04-24 5:56 UTC (permalink / raw)
To: Nipun Gupta, Nikhil Agarwal, Joerg Roedel, Will Deacon,
Robin Murphy, Marc Zyngier, Lorenzo Pieralisi, Thomas Gleixner,
Saravana Kannan, Richard Zhu, Lucas Stach,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Helgaas,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
Dmitry Baryshkov, Konrad Dybcio, Bjorn Andersson, Rob Herring,
Conor Dooley, Krzysztof Kozlowski, Prakash Gupta, Vikash Garodia
Cc: linux-kernel, iommu, linux-arm-kernel, devicetree, linux-pci, imx,
xen-devel, linux-arm-msm, Vijayanand Jitta, Charan Teja Kalla
So far our parsing of {iommu,msi}-map properties has always blindly
assumed that the output specifiers will always have exactly 1 cell.
This typically does happen to be the case, but is not actually enforced
(and the PCI msi-map binding even explicitly states support for 0 or 1
cells) - as a result we've now ended up with dodgy DTs out in the field
which depend on this behaviour to map a 1-cell specifier for a 2-cell
provider, despite that being bogus per the bindings themselves.
Since there is some potential use[1] in being able to map at least
single input IDs to multi-cell output specifiers (and properly support
0-cell outputs as well), add support for properly parsing and using the
target nodes' #cells values, albeit with the unfortunate complication of
still having to work around expectations of the old behaviour too.
-- Robin.
Unlike single #{}-cell, it is complex to establish a linear relation
between input 'id' and output specifier for multi-cell properties, thus
it is always expected that len never going to be > 1.
These changes have been tested on QEMU for the arm64 architecture.
Since, this would also need update in dt-schema, raised PR[2] for the
same.
[1] https://lore.kernel.org/all/20250627-video_cb-v3-0-51e18c0ffbce@quicinc.com/
[2] PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184
V14:
- Updated Patch 2 ("of: Factor arguments passed to of_map_id() into a struct") to
fix below two issues in of_msi_xlate() that were introduced by the API refactoring:
1) The refactoring changed of_map_id()'s dual-purpose **target parameter to
an explicit filter_np parameter. In of_msi_xlate(), this caused
of_map_msi_id() to return 0 (pass-through) instead of -ENODEV when a node
has no msi-map, terminating the device hierarchy walk prematurely before
reaching the root complex node that has the msi-map. This broke MSI
allocation for PCIe endpoint devices (e.g., wcn7850 Wi-Fi on ARM64).
2) Additionally, fsl_mc_get_msi_id() passes msi_np == NULL to of_msi_xlate(),
which would dereference NULL with the new API.
Link to v13:
https://patch.msgid.link/20260408-parse_iommu_cells-v13-0-fa921e92661b@oss.qualcomm.com
V13:
- Fix bad_map handling in of_map_id(): 'cells' is re-initialized to 0
on each loop iteration, so the !bad_map guard was insufficient, cells
stayed 0 for all entries after the first. Fix by explicitly setting
cells=1 when bad_map is true on every iteration.
- Collected Acked-by from Frank Li.
Link to v12:
https://patch.msgid.link/20260331-parse_iommu_cells-v12-0-decfd305eea9@oss.qualcomm.com
V12:
- Call of_node_put() unconditionally in imx_pcie_add_lut_by_rid()
thereby addressing comments from Bjorn Helgaas.
Link to v11:
https://lore.kernel.org/r/20260325-parse_iommu_cells-v11-0-1fefa5c0e82c@oss.qualcomm.com
V11:
- Added explicit filter_np parameter to of_map_id() and of_map_msi_id()
per Dmitry Baryshkov's review feedback, making the filter explicit
instead of overloading arg->np as both input filter and output parameter.
- Removed of_node_put() from inside of_map_id(), making the caller responsible
for reference management. Updated of_msi_xlate() to properly handle reference counting.
- Collected ACKed by tags, and fixed minor typos.
Link to v10:
https://lore.kernel.org/r/20260309-parse_iommu_cells-v10-0-c62fcaa5a1d8@oss.qualcomm.com
V10:
- Move of_map_iommu_id()/of_map_msi_id() from include/linux/of.h to
drivers/of/base.c as out-of-line helpers per feedback from Marc Zyngier
and Rob Herring.
- Add kernel-doc to document both helpers for discoverability and
usage clarity.
- Fix of_map_msi_id() wrapper and all its callers (cdx_msi.c,
irq-gic-its-msi-parent.c, drivers/of/irq.c) to correctly use the new
struct of_phandle_args-based API with proper of_node_put() handling
as per feeback from Dmitry.
Link to v9:
https://lore.kernel.org/r/20260301-parse_iommu_cells-v9-0-4d1bceecc5e1@oss.qualcomm.com
V9:
- Updated TO/CC list based on feedback to include all relevant
maintainers.
- No functional changes to the patches themselves.
Link to V8:
https://lore.kernel.org/all/20260226074245.3098486-1-vijayanand.jitta@oss.qualcomm.com/
V8:
- Removed mentions of of_map_args from commit message to match code.
Link to V7:
https://lore.kernel.org/all/20260210101157.2145113-1-vijayanand.jitta@oss.qualcomm.com/
V7:
- Removed of_map_id_args structure and replaced it with
of_phandle_args as suggested by Dmitry.
Link to V6:
https://lore.kernel.org/all/20260121055400.937856-1-vijayanand.jitta@oss.qualcomm.com/
V6:
- Fixed build error reported by kernel test bot.
Link to V5:
https://lore.kernel.org/all/20260118181125.1436036-1-vijayanand.jitta@oss.qualcomm.com/
V5:
- Fixed Build Warnings.
- Raised PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184
Link to V4:
https://lore.kernel.org/all/20251231114257.2382820-1-vijayanand.jitta@oss.qualcomm.com/
V4:
- Added Reviewed-by tag.
- Resolved warnings reported by kernel test bot, minor code
reorganization.
Link to V3:
https://lore.kernel.org/all/20251221213602.2413124-1-vijayanand.jitta@oss.qualcomm.com/
V3:
- Added Reviewed-by tag.
- Updated of_map_id_args struct as a wrapper to of_phandle_args and
added comment description as suggested by Rob Herring.
Link to V2:
https://lore.kernel.org/all/20251204095530.8627-1-vijayanand.jitta@oss.qualcomm.com/
V2:
- Incorporated the patches from Robin that does the clean implementation.
- Dropped the patches the were adding multi-map support from this series
as suggested.
V1:
https://lore.kernel.org/all/cover.1762235099.git.charan.kalla@oss.qualcomm.com/
RFC:
https://lore.kernel.org/all/20250928171718.436440-1-charan.kalla@oss.qualcomm.com/#r
Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
---
To: Nipun Gupta <nipun.gupta@amd.com>
To: Nikhil Agarwal <nikhil.agarwal@amd.com>
To: Joerg Roedel <joro@8bytes.org>
To: Will Deacon <will@kernel.org>
To: Robin Murphy <robin.murphy@arm.com>
To: Lorenzo Pieralisi <lpieralisi@kernel.org>
To: Marc Zyngier <maz@kernel.org>
To: Thomas Gleixner <tglx@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Saravana Kannan <saravanak@kernel.org>
To: Richard Zhu <hongxing.zhu@nxp.com>
To: Lucas Stach <l.stach@pengutronix.de>
To: Krzysztof Wilczyński <kwilczynski@kernel.org>
To: Manivannan Sadhasivam <mani@kernel.org>
To: Bjorn Helgaas <bhelgaas@google.com>
To: Frank Li <Frank.Li@nxp.com>
To: Sascha Hauer <s.hauer@pengutronix.de>
To: Pengutronix Kernel Team <kernel@pengutronix.de>
To: Fabio Estevam <festevam@gmail.com>
To: Juergen Gross <jgross@suse.com>
To: Stefano Stabellini <sstabellini@kernel.org>
To: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: iommu@lists.linux.dev
Cc: linux-arm-kernel@lists.infradead.org
Cc: devicetree@vger.kernel.org
Cc: linux-pci@vger.kernel.org
Cc: imx@lists.linux.dev
Cc: xen-devel@lists.xenproject.org
---
Charan Teja Kalla (1):
of: Factor arguments passed to of_map_id() into a struct
Robin Murphy (2):
of: Add convenience wrappers for of_map_id()
of: Respect #{iommu,msi}-cells in maps
drivers/cdx/cdx_msi.c | 8 +-
drivers/iommu/of_iommu.c | 6 +-
drivers/irqchip/irq-gic-its-msi-parent.c | 11 +-
drivers/of/base.c | 215 ++++++++++++++++++++++++-------
drivers/of/irq.c | 31 ++++-
drivers/pci/controller/dwc/pci-imx6.c | 34 +++--
drivers/pci/controller/pcie-apple.c | 6 +-
drivers/xen/grant-dma-ops.c | 5 +-
include/linux/of.h | 30 ++++-
9 files changed, 257 insertions(+), 89 deletions(-)
---
base-commit: 3fa5e5702a82d259897bd7e209469bc06368bf31
change-id: 20260301-parse_iommu_cells-1c33768aebba
Best regards,
--
Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v14 1/3] of: Add convenience wrappers for of_map_id()
From: Vijayanand Jitta @ 2026-04-24 5:56 UTC (permalink / raw)
To: Nipun Gupta, Nikhil Agarwal, Joerg Roedel, Will Deacon,
Robin Murphy, Marc Zyngier, Lorenzo Pieralisi, Thomas Gleixner,
Saravana Kannan, Richard Zhu, Lucas Stach,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Helgaas,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
Dmitry Baryshkov, Konrad Dybcio, Bjorn Andersson, Rob Herring,
Conor Dooley, Krzysztof Kozlowski, Prakash Gupta, Vikash Garodia
Cc: linux-kernel, iommu, linux-arm-kernel, devicetree, linux-pci, imx,
xen-devel, linux-arm-msm, Vijayanand Jitta
In-Reply-To: <20260424-parse_iommu_cells-v14-0-fd02f11b6c38@oss.qualcomm.com>
From: Robin Murphy <robin.murphy@arm.com>
Since we now have quite a few users parsing "iommu-map" and "msi-map"
properties, give them some wrappers to conveniently encapsulate the
appropriate sets of property names. This will also make it easier to
then change of_map_id() to correctly account for specifier cells.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Marc Zyngier <maz@kernel.org>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
---
drivers/cdx/cdx_msi.c | 3 +--
drivers/iommu/of_iommu.c | 4 +---
drivers/irqchip/irq-gic-its-msi-parent.c | 2 +-
drivers/of/base.c | 38 ++++++++++++++++++++++++++++++++
drivers/of/irq.c | 3 +--
drivers/pci/controller/dwc/pci-imx6.c | 6 ++---
drivers/pci/controller/pcie-apple.c | 3 +--
drivers/xen/grant-dma-ops.c | 3 +--
include/linux/of.h | 18 +++++++++++++++
9 files changed, 64 insertions(+), 16 deletions(-)
diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c
index 91b95422b263..63b3544ec997 100644
--- a/drivers/cdx/cdx_msi.c
+++ b/drivers/cdx/cdx_msi.c
@@ -128,8 +128,7 @@ static int cdx_msi_prepare(struct irq_domain *msi_domain,
int ret;
/* Retrieve device ID from requestor ID using parent device */
- ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask",
- NULL, &dev_id);
+ ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id);
if (ret) {
dev_err(dev, "of_map_id failed for MSI: %d\n", ret);
return ret;
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 6b989a62def2..a511ecf21fcd 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -48,9 +48,7 @@ static int of_iommu_configure_dev_id(struct device_node *master_np,
struct of_phandle_args iommu_spec = { .args_count = 1 };
int err;
- err = of_map_id(master_np, *id, "iommu-map",
- "iommu-map-mask", &iommu_spec.np,
- iommu_spec.args);
+ err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args);
if (err)
return err;
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index d36b278ae66c..b63343a227a9 100644
--- a/drivers/irqchip/irq-gic-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -180,7 +180,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
struct device_node *msi_ctrl __free(device_node) = NULL;
- return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
+ return of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, dev_id);
}
static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 57420806c1a2..ae04487bd614 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2201,3 +2201,41 @@ int of_map_id(const struct device_node *np, u32 id,
return 0;
}
EXPORT_SYMBOL_GPL(of_map_id);
+
+/**
+ * of_map_iommu_id - Translate an ID using "iommu-map" bindings.
+ * @np: root complex device node.
+ * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform
+ * stream/device ID) used as the lookup key in the iommu-map table.
+ * @target: optional pointer to a target device node.
+ * @id_out: optional pointer to receive the translated ID.
+ *
+ * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask".
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_map_iommu_id(const struct device_node *np, u32 id,
+ struct device_node **target, u32 *id_out)
+{
+ return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out);
+}
+EXPORT_SYMBOL_GPL(of_map_iommu_id);
+
+/**
+ * of_map_msi_id - Translate an ID using "msi-map" bindings.
+ * @np: root complex device node.
+ * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform
+ * stream/device ID) used as the lookup key in the msi-map table.
+ * @target: optional pointer to a target device node.
+ * @id_out: optional pointer to receive the translated ID.
+ *
+ * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask".
+ *
+ * Return: 0 on success or a standard error code on failure.
+ */
+int of_map_msi_id(const struct device_node *np, u32 id,
+ struct device_node **target, u32 *id_out)
+{
+ return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out);
+}
+EXPORT_SYMBOL_GPL(of_map_msi_id);
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 6367c67732d2..e37c1b3f8736 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -817,8 +817,7 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in)
* "msi-map" or an "msi-parent" property.
*/
for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
- if (!of_map_id(parent_dev->of_node, id_in, "msi-map",
- "msi-map-mask", msi_np, &id_out))
+ if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out))
break;
if (!of_check_msi_parent(parent_dev->of_node, msi_np))
break;
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index a5b8d0b71677..bff8289f804a 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1144,8 +1144,7 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid)
u32 sid = 0;
target = NULL;
- err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask",
- &target, &sid_i);
+ err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i);
if (target) {
of_node_put(target);
} else {
@@ -1158,8 +1157,7 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid)
}
target = NULL;
- err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask",
- &target, &sid_m);
+ err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m);
/*
* err_m target
diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
index 2d92fc79f6dd..a0937b7b3c4d 100644
--- a/drivers/pci/controller/pcie-apple.c
+++ b/drivers/pci/controller/pcie-apple.c
@@ -764,8 +764,7 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d
dev_dbg(&pdev->dev, "added to bus %s, index %d\n",
pci_name(pdev->bus->self), port->idx);
- err = of_map_id(port->pcie->dev->of_node, rid, "iommu-map",
- "iommu-map-mask", NULL, &sid);
+ err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid);
if (err)
return err;
diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c
index c2603e700178..1b7696b2d762 100644
--- a/drivers/xen/grant-dma-ops.c
+++ b/drivers/xen/grant-dma-ops.c
@@ -325,8 +325,7 @@ static int xen_dt_grant_init_backend_domid(struct device *dev,
struct pci_dev *pdev = to_pci_dev(dev);
u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn);
- if (of_map_id(np, rid, "iommu-map", "iommu-map-mask", &iommu_spec.np,
- iommu_spec.args)) {
+ if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) {
dev_dbg(dev, "Cannot translate ID\n");
return -ESRCH;
}
diff --git a/include/linux/of.h b/include/linux/of.h
index be6ec4916adf..fe841f3cc747 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -465,6 +465,12 @@ int of_map_id(const struct device_node *np, u32 id,
const char *map_name, const char *map_mask_name,
struct device_node **target, u32 *id_out);
+int of_map_iommu_id(const struct device_node *np, u32 id,
+ struct device_node **target, u32 *id_out);
+
+int of_map_msi_id(const struct device_node *np, u32 id,
+ struct device_node **target, u32 *id_out);
+
phys_addr_t of_dma_get_max_cpu_address(struct device_node *np);
struct kimage;
@@ -934,6 +940,18 @@ static inline int of_map_id(const struct device_node *np, u32 id,
return -EINVAL;
}
+static inline int of_map_iommu_id(const struct device_node *np, u32 id,
+ struct device_node **target, u32 *id_out)
+{
+ return -EINVAL;
+}
+
+static inline int of_map_msi_id(const struct device_node *np, u32 id,
+ struct device_node **target, u32 *id_out)
+{
+ return -EINVAL;
+}
+
static inline phys_addr_t of_dma_get_max_cpu_address(struct device_node *np)
{
return PHYS_ADDR_MAX;
--
2.34.1
^ permalink raw reply related
* Re: [RFC PATCH v2 1/4] security: ima: call ima_init() again at late_initcall_sync for defered TPM
From: Yeoreum Yun @ 2026-04-24 5:57 UTC (permalink / raw)
To: Paul Moore
Cc: Mimi Zohar, roberto.sassu, Jonathan McDowell,
linux-security-module, linux-kernel, linux-integrity,
linux-arm-kernel, kvmarm, jmorris, serge, dmitry.kasatkin,
eric.snowberg, jarkko, jgg, sudeep.holla, maz, oupton, joey.gouly,
suzuki.poulose, yuzenghui, catalin.marinas, will, noodles,
sebastianene
In-Reply-To: <CAHC9VhRQWHEWQ5NzOPiu8jtYv6UsRm8WVS4fd74AbkOcAd4y_g@mail.gmail.com>
Hi Paul,
> On Thu, Apr 23, 2026 at 2:13 PM Yeoreum Yun <yeoreum.yun@arm.com> wrote:
> >
> > Sounds good. Once the patch is posted, I’ll review it as well.
> > Sorry again for the noise, and thanks for your patience ;)
>
> My apologies for not getting a chance to look at this patchset sooner.
>
> This seems like an obvious, perhaps even stupid, question, but I have
> to ask: if IMA can be properly initialized via late_initcall_sync(),
> why not simply do the initialization in late_initcall_sync() and drop
> the late_initcall() initialization?
>
> Does any IMA functionality suffer if initialization waits until
> late_initcall_sync()? If so, it seems non-critical if waiting until
> _sync() is acceptable, as it appears in these patches/comments.
This is the way first patch did, and here is some discussion for this
(Might you have seen, but in case of you missed):
- https://lore.kernel.org/all/a6a0e15286c983d720de227c6827adbe976c5b9b.camel@linux.ibm.com/
Thanks.
--
Sincerely,
Yeoreum Yun
^ permalink raw reply
* [PATCH v14 2/3] of: Factor arguments passed to of_map_id() into a struct
From: Vijayanand Jitta @ 2026-04-24 5:56 UTC (permalink / raw)
To: Nipun Gupta, Nikhil Agarwal, Joerg Roedel, Will Deacon,
Robin Murphy, Marc Zyngier, Lorenzo Pieralisi, Thomas Gleixner,
Saravana Kannan, Richard Zhu, Lucas Stach,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Helgaas,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
Dmitry Baryshkov, Konrad Dybcio, Bjorn Andersson, Rob Herring,
Conor Dooley, Krzysztof Kozlowski, Prakash Gupta, Vikash Garodia
Cc: linux-kernel, iommu, linux-arm-kernel, devicetree, linux-pci, imx,
xen-devel, linux-arm-msm, Vijayanand Jitta, Charan Teja Kalla
In-Reply-To: <20260424-parse_iommu_cells-v14-0-fd02f11b6c38@oss.qualcomm.com>
From: Charan Teja Kalla <charan.kalla@oss.qualcomm.com>
Change of_map_id() to take a pointer to struct of_phandle_args
instead of passing target device node and translated IDs separately.
Update all callers accordingly.
Add an explicit filter_np parameter to of_map_id() and of_map_msi_id()
to separate the filter input from the output. Previously, the target
parameter served dual purpose: as an input filter (if non-NULL, only
match entries targeting that node) and as an output (receiving the
matched node with a reference held). Now filter_np is the explicit
input filter and arg->np is the pure output.
Previously, of_map_id() would call of_node_put() on the matched node
when a filter was provided, making reference ownership inconsistent.
Remove this internal of_node_put() call so that of_map_id() now always
transfers ownership of the matched node reference to the caller via
arg->np. Callers are now consistently responsible for releasing this
reference with of_node_put(arg->np) when done.
Acked-by: Frank Li <Frank.Li@nxp.com>
Suggested-by: Rob Herring (Arm) <robh@kernel.org>
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com>
Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
---
drivers/cdx/cdx_msi.c | 7 ++--
drivers/iommu/of_iommu.c | 4 +-
drivers/irqchip/irq-gic-its-msi-parent.c | 11 ++++--
drivers/of/base.c | 68 +++++++++++++++++---------------
drivers/of/irq.c | 30 +++++++++++---
drivers/pci/controller/dwc/pci-imx6.c | 32 +++++++--------
drivers/pci/controller/pcie-apple.c | 5 ++-
drivers/xen/grant-dma-ops.c | 4 +-
include/linux/of.h | 14 ++++---
9 files changed, 104 insertions(+), 71 deletions(-)
diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c
index 63b3544ec997..6924e07c7528 100644
--- a/drivers/cdx/cdx_msi.c
+++ b/drivers/cdx/cdx_msi.c
@@ -121,22 +121,23 @@ static int cdx_msi_prepare(struct irq_domain *msi_domain,
struct device *dev,
int nvec, msi_alloc_info_t *info)
{
+ struct of_phandle_args msi_spec = {};
struct cdx_device *cdx_dev = to_cdx_device(dev);
struct device *parent = cdx_dev->cdx->dev;
struct msi_domain_info *msi_info;
- u32 dev_id;
int ret;
/* Retrieve device ID from requestor ID using parent device */
- ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id);
+ ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &msi_spec);
if (ret) {
dev_err(dev, "of_map_id failed for MSI: %d\n", ret);
return ret;
}
+ of_node_put(msi_spec.np);
#ifdef GENERIC_MSI_DOMAIN_OPS
/* Set the device Id to be passed to the GIC-ITS */
- info->scratchpad[0].ul = dev_id;
+ info->scratchpad[0].ul = msi_spec.args[0];
#endif
msi_info = msi_get_domain_info(msi_domain->parent);
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index a511ecf21fcd..a18bb60f6f3d 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -45,10 +45,10 @@ static int of_iommu_configure_dev_id(struct device_node *master_np,
struct device *dev,
const u32 *id)
{
- struct of_phandle_args iommu_spec = { .args_count = 1 };
+ struct of_phandle_args iommu_spec = {};
int err;
- err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args);
+ err = of_map_iommu_id(master_np, *id, &iommu_spec);
if (err)
return err;
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index b63343a227a9..dd5f84b6470a 100644
--- a/drivers/irqchip/irq-gic-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -152,6 +152,8 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u32 *dev_id,
phys_addr_t *pa)
{
+ struct device_node *msi_ctrl __free(device_node) = NULL;
+ struct of_phandle_args msi_spec = {};
struct of_phandle_iterator it;
int ret;
@@ -178,9 +180,12 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
}
}
- struct device_node *msi_ctrl __free(device_node) = NULL;
-
- return of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, dev_id);
+ ret = of_map_msi_id(dev->of_node, dev->id, NULL, &msi_spec);
+ if (!ret) {
+ msi_ctrl = msi_spec.np;
+ *dev_id = msi_spec.args[0];
+ }
+ return ret;
}
static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
diff --git a/drivers/of/base.c b/drivers/of/base.c
index ae04487bd614..b3d002015192 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2102,36 +2102,37 @@ int of_find_last_cache_level(unsigned int cpu)
* @id: device ID to map.
* @map_name: property name of the map to use.
* @map_mask_name: optional property name of the mask to use.
- * @target: optional pointer to a target device node.
- * @id_out: optional pointer to receive the translated ID.
+ * @filter_np: optional device node to filter matches by, or NULL to match any.
+ * If non-NULL, only map entries targeting this node will be matched.
+ * @arg: pointer to a &struct of_phandle_args for the result. On success,
+ * @arg->args[0] will contain the translated ID. If a map entry was
+ * matched, @arg->np will be set to the target node with a reference
+ * held that the caller must release with of_node_put().
*
* Given a device ID, look up the appropriate implementation-defined
* platform ID and/or the target device which receives transactions on that
- * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or
- * @id_out may be NULL if only the other is required. If @target points to
- * a non-NULL device node pointer, only entries targeting that node will be
- * matched; if it points to a NULL value, it will receive the device node of
- * the first matching target phandle, with a reference held.
+ * ID, as per the "iommu-map" and "msi-map" bindings.
*
* Return: 0 on success or a standard error code on failure.
*/
int of_map_id(const struct device_node *np, u32 id,
const char *map_name, const char *map_mask_name,
- struct device_node **target, u32 *id_out)
+ const struct device_node *filter_np, struct of_phandle_args *arg)
{
u32 map_mask, masked_id;
int map_len;
const __be32 *map = NULL;
- if (!np || !map_name || (!target && !id_out))
+ if (!np || !map_name || !arg)
return -EINVAL;
map = of_get_property(np, map_name, &map_len);
if (!map) {
- if (target)
+ if (filter_np)
return -ENODEV;
/* Otherwise, no map implies no translation */
- *id_out = id;
+ arg->args[0] = id;
+ arg->args_count = 1;
return 0;
}
@@ -2173,18 +2174,14 @@ int of_map_id(const struct device_node *np, u32 id,
if (!phandle_node)
return -ENODEV;
- if (target) {
- if (*target)
- of_node_put(phandle_node);
- else
- *target = phandle_node;
-
- if (*target != phandle_node)
- continue;
+ if (filter_np && filter_np != phandle_node) {
+ of_node_put(phandle_node);
+ continue;
}
- if (id_out)
- *id_out = masked_id - id_base + out_base;
+ arg->np = phandle_node;
+ arg->args[0] = masked_id - id_base + out_base;
+ arg->args_count = 1;
pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n",
np, map_name, map_mask, id_base, out_base,
@@ -2193,11 +2190,11 @@ int of_map_id(const struct device_node *np, u32 id,
}
pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name,
- id, target && *target ? *target : NULL);
+ id, filter_np);
/* Bypasses translation */
- if (id_out)
- *id_out = id;
+ arg->args[0] = id;
+ arg->args_count = 1;
return 0;
}
EXPORT_SYMBOL_GPL(of_map_id);
@@ -2207,17 +2204,19 @@ EXPORT_SYMBOL_GPL(of_map_id);
* @np: root complex device node.
* @id: Requester ID of the device (e.g. PCI RID/BDF or a platform
* stream/device ID) used as the lookup key in the iommu-map table.
- * @target: optional pointer to a target device node.
- * @id_out: optional pointer to receive the translated ID.
+ * @arg: pointer to a &struct of_phandle_args for the result. On success,
+ * @arg->args[0] contains the translated ID. If a map entry was matched,
+ * @arg->np holds a reference to the target node that the caller must
+ * release with of_node_put().
*
* Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask".
*
* Return: 0 on success or a standard error code on failure.
*/
int of_map_iommu_id(const struct device_node *np, u32 id,
- struct device_node **target, u32 *id_out)
+ struct of_phandle_args *arg)
{
- return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out);
+ return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg);
}
EXPORT_SYMBOL_GPL(of_map_iommu_id);
@@ -2226,16 +2225,21 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id);
* @np: root complex device node.
* @id: Requester ID of the device (e.g. PCI RID/BDF or a platform
* stream/device ID) used as the lookup key in the msi-map table.
- * @target: optional pointer to a target device node.
- * @id_out: optional pointer to receive the translated ID.
+ * @filter_np: optional MSI controller node to filter matches by, or NULL
+ * to match any. If non-NULL, only map entries targeting this node will
+ * be matched.
+ * @arg: pointer to a &struct of_phandle_args for the result. On success,
+ * @arg->args[0] contains the translated ID. If a map entry was matched,
+ * @arg->np holds a reference to the target node that the caller must
+ * release with of_node_put().
*
* Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask".
*
* Return: 0 on success or a standard error code on failure.
*/
int of_map_msi_id(const struct device_node *np, u32 id,
- struct device_node **target, u32 *id_out)
+ const struct device_node *filter_np, struct of_phandle_args *arg)
{
- return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out);
+ return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg);
}
EXPORT_SYMBOL_GPL(of_map_msi_id);
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index e37c1b3f8736..4040467742c4 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -796,19 +796,22 @@ static int of_check_msi_parent(struct device_node *dev_node, struct device_node
/**
* of_msi_xlate - map a MSI ID and find relevant MSI controller node
* @dev: device for which the mapping is to be done.
- * @msi_np: Pointer to target MSI controller node
+ * @msi_np: Pointer to target MSI controller node, or NULL if the caller
+ * only needs the translated ID without receiving the controller node.
+ * If non-NULL and pointing to a non-NULL node, only entries targeting
+ * that node will be matched. If non-NULL and pointing to NULL, it will
+ * receive the first matching target node with a reference held.
* @id_in: Device ID.
*
* Walk up the device hierarchy looking for devices with a "msi-map"
* or "msi-parent" property. If found, apply the mapping to @id_in.
- * If @msi_np points to a non-NULL device node pointer, only entries targeting
- * that node will be matched; if it points to a NULL value, it will receive the
- * device node of the first matching target phandle, with a reference held.
*
* Returns: The mapped MSI id.
*/
u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in)
{
+ struct device_node *local_np = NULL;
+ struct device_node **np = msi_np ?: &local_np;
struct device *parent_dev;
u32 id_out = id_in;
@@ -817,11 +820,26 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in)
* "msi-map" or an "msi-parent" property.
*/
for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
- if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out))
+ struct of_phandle_args msi_spec = {};
+
+ if (!of_map_msi_id(parent_dev->of_node, id_in, *np, &msi_spec)) {
+ /*
+ * Pass-through result: no msi-map on this node (or no
+ * matching entry). Keep walking up the hierarchy.
+ */
+ if (!msi_spec.np)
+ continue;
+ id_out = msi_spec.args[0];
+ if (!*np)
+ *np = msi_spec.np;
+ else
+ of_node_put(msi_spec.np);
break;
- if (!of_check_msi_parent(parent_dev->of_node, msi_np))
+ }
+ if (!of_check_msi_parent(parent_dev->of_node, np))
break;
}
+ of_node_put(local_np);
return id_out;
}
EXPORT_SYMBOL_GPL(of_msi_xlate);
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index bff8289f804a..c0544d9c0921 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -1137,30 +1137,32 @@ static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 rid)
static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid)
{
+ struct of_phandle_args iommu_spec = {};
+ struct of_phandle_args msi_spec = {};
struct device *dev = imx_pcie->pci->dev;
- struct device_node *target;
u32 sid_i, sid_m;
int err_i, err_m;
u32 sid = 0;
- target = NULL;
- err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i);
- if (target) {
- of_node_put(target);
- } else {
+ err_i = of_map_iommu_id(dev->of_node, rid, &iommu_spec);
+ if (!err_i)
+ sid_i = iommu_spec.args[0];
+ of_node_put(iommu_spec.np);
+ if (!err_i && !iommu_spec.np) {
/*
- * "target == NULL && err_i == 0" means RID out of map range.
- * Use 1:1 map RID to streamID. Hardware can't support this
- * because the streamID is only 6 bits
+ * "iommu_spec.np == NULL && err_i == 0" means RID out of map
+ * range. Use 1:1 map RID to streamID. Hardware can't support
+ * this because the streamID is only 6 bits.
*/
err_i = -EINVAL;
}
- target = NULL;
- err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m);
-
+ err_m = of_map_msi_id(dev->of_node, rid, NULL, &msi_spec);
+ if (!err_m)
+ sid_m = msi_spec.args[0];
+ of_node_put(msi_spec.np);
/*
- * err_m target
+ * err_m msi_spec.np
* 0 NULL RID out of range. Use 1:1 map RID to
* streamID, Current hardware can't
* support it, so return -EINVAL.
@@ -1168,10 +1170,8 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid)
* 0 != NULL Get correct streamID from RID
* != 0 != NULL Invalid combination
*/
- if (!err_m && !target)
+ if (!err_m && !msi_spec.np)
return -EINVAL;
- else if (target)
- of_node_put(target); /* Find streamID map entry for RID in msi-map */
/*
* msi-map iommu-map
diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
index a0937b7b3c4d..c2cffc0659f4 100644
--- a/drivers/pci/controller/pcie-apple.c
+++ b/drivers/pci/controller/pcie-apple.c
@@ -755,6 +755,7 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d
{
u32 sid, rid = pci_dev_id(pdev);
struct apple_pcie_port *port;
+ struct of_phandle_args iommu_spec = {};
int idx, err;
port = apple_pcie_get_port(pdev);
@@ -764,10 +765,12 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d
dev_dbg(&pdev->dev, "added to bus %s, index %d\n",
pci_name(pdev->bus->self), port->idx);
- err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid);
+ err = of_map_iommu_id(port->pcie->dev->of_node, rid, &iommu_spec);
if (err)
return err;
+ of_node_put(iommu_spec.np);
+ sid = iommu_spec.args[0];
mutex_lock(&port->pcie->lock);
idx = bitmap_find_free_region(port->sid_map, port->sid_map_sz, 0);
diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c
index 1b7696b2d762..2aa1a772a0ff 100644
--- a/drivers/xen/grant-dma-ops.c
+++ b/drivers/xen/grant-dma-ops.c
@@ -319,13 +319,13 @@ static int xen_dt_grant_init_backend_domid(struct device *dev,
struct device_node *np,
domid_t *backend_domid)
{
- struct of_phandle_args iommu_spec = { .args_count = 1 };
+ struct of_phandle_args iommu_spec = {};
if (dev_is_pci(dev)) {
struct pci_dev *pdev = to_pci_dev(dev);
u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn);
- if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) {
+ if (of_map_iommu_id(np, rid, &iommu_spec)) {
dev_dbg(dev, "Cannot translate ID\n");
return -ESRCH;
}
diff --git a/include/linux/of.h b/include/linux/of.h
index fe841f3cc747..8548cd9eb4f1 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -463,13 +463,13 @@ bool of_console_check(const struct device_node *dn, char *name, int index);
int of_map_id(const struct device_node *np, u32 id,
const char *map_name, const char *map_mask_name,
- struct device_node **target, u32 *id_out);
+ const struct device_node *filter_np, struct of_phandle_args *arg);
int of_map_iommu_id(const struct device_node *np, u32 id,
- struct device_node **target, u32 *id_out);
+ struct of_phandle_args *arg);
int of_map_msi_id(const struct device_node *np, u32 id,
- struct device_node **target, u32 *id_out);
+ const struct device_node *filter_np, struct of_phandle_args *arg);
phys_addr_t of_dma_get_max_cpu_address(struct device_node *np);
@@ -935,19 +935,21 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
static inline int of_map_id(const struct device_node *np, u32 id,
const char *map_name, const char *map_mask_name,
- struct device_node **target, u32 *id_out)
+ const struct device_node *filter_np,
+ struct of_phandle_args *arg)
{
return -EINVAL;
}
static inline int of_map_iommu_id(const struct device_node *np, u32 id,
- struct device_node **target, u32 *id_out)
+ struct of_phandle_args *arg)
{
return -EINVAL;
}
static inline int of_map_msi_id(const struct device_node *np, u32 id,
- struct device_node **target, u32 *id_out)
+ const struct device_node *filter_np,
+ struct of_phandle_args *arg)
{
return -EINVAL;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v14 3/3] of: Respect #{iommu,msi}-cells in maps
From: Vijayanand Jitta @ 2026-04-24 5:56 UTC (permalink / raw)
To: Nipun Gupta, Nikhil Agarwal, Joerg Roedel, Will Deacon,
Robin Murphy, Marc Zyngier, Lorenzo Pieralisi, Thomas Gleixner,
Saravana Kannan, Richard Zhu, Lucas Stach,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Helgaas,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
Dmitry Baryshkov, Konrad Dybcio, Bjorn Andersson, Rob Herring,
Conor Dooley, Krzysztof Kozlowski, Prakash Gupta, Vikash Garodia
Cc: linux-kernel, iommu, linux-arm-kernel, devicetree, linux-pci, imx,
xen-devel, linux-arm-msm, Vijayanand Jitta, Charan Teja Kalla
In-Reply-To: <20260424-parse_iommu_cells-v14-0-fd02f11b6c38@oss.qualcomm.com>
From: Robin Murphy <robin.murphy@arm.com>
So far our parsing of {iommu,msi}-map properties has always blindly
assumed that the output specifiers will always have exactly 1 cell.
This typically does happen to be the case, but is not actually enforced
(and the PCI msi-map binding even explicitly states support for 0 or 1
cells) - as a result we've now ended up with dodgy DTs out in the field
which depend on this behaviour to map a 1-cell specifier for a 2-cell
provider, despite that being bogus per the bindings themselves.
Since there is some potential use in being able to map at least single
input IDs to multi-cell output specifiers (and properly support 0-cell
outputs as well), add support for properly parsing and using the target
nodes' #cells values, albeit with the unfortunate complication of still
having to work around expectations of the old behaviour too.
Since there are multi-cell output specifiers, the callers of of_map_id()
may need to get the exact cell output value for further processing.
Update of_map_id() to set args_count in the output to reflect the actual
number of output specifier cells.
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com>
Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
---
drivers/of/base.c | 157 +++++++++++++++++++++++++++++++++++++++++------------
include/linux/of.h | 6 +-
2 files changed, 125 insertions(+), 38 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index b3d002015192..2554e4f1a181 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2096,18 +2096,48 @@ int of_find_last_cache_level(unsigned int cpu)
return cache_level;
}
+/*
+ * Some DTs have an iommu-map targeting a 2-cell IOMMU node while
+ * specifying only 1 cell. Fortunately they all consist of value '1'
+ * as the 2nd cell entry with the same target, so check for that pattern.
+ *
+ * Example:
+ * IOMMU node:
+ * #iommu-cells = <2>;
+ *
+ * Device node:
+ * iommu-map = <0x0000 &smmu 0x0000 0x1>,
+ * <0x0100 &smmu 0x0100 0x1>;
+ */
+static bool of_check_bad_map(const __be32 *map, int len)
+{
+ __be32 phandle = map[1];
+
+ if (len % 4)
+ return false;
+ for (int i = 0; i < len; i += 4) {
+ if (map[i + 1] != phandle || map[i + 3] != cpu_to_be32(1))
+ return false;
+ }
+ return true;
+}
+
/**
* of_map_id - Translate an ID through a downstream mapping.
* @np: root complex device node.
* @id: device ID to map.
* @map_name: property name of the map to use.
+ * @cells_name: property name of target specifier cells.
* @map_mask_name: optional property name of the mask to use.
* @filter_np: optional device node to filter matches by, or NULL to match any.
* If non-NULL, only map entries targeting this node will be matched.
* @arg: pointer to a &struct of_phandle_args for the result. On success,
- * @arg->args[0] will contain the translated ID. If a map entry was
- * matched, @arg->np will be set to the target node with a reference
- * held that the caller must release with of_node_put().
+ * @arg->args_count will be set to the number of output specifier cells
+ * as defined by @cells_name in the target node, and
+ * @arg->args[0..args_count-1] will contain the translated output
+ * specifier values. If a map entry was matched, @arg->np will be set
+ * to the target node with a reference held that the caller must release
+ * with of_node_put().
*
* Given a device ID, look up the appropriate implementation-defined
* platform ID and/or the target device which receives transactions on that
@@ -2116,17 +2146,19 @@ int of_find_last_cache_level(unsigned int cpu)
* Return: 0 on success or a standard error code on failure.
*/
int of_map_id(const struct device_node *np, u32 id,
- const char *map_name, const char *map_mask_name,
+ const char *map_name, const char *cells_name,
+ const char *map_mask_name,
const struct device_node *filter_np, struct of_phandle_args *arg)
{
u32 map_mask, masked_id;
- int map_len;
+ int map_bytes, map_len, offset = 0;
+ bool bad_map = false;
const __be32 *map = NULL;
if (!np || !map_name || !arg)
return -EINVAL;
- map = of_get_property(np, map_name, &map_len);
+ map = of_get_property(np, map_name, &map_bytes);
if (!map) {
if (filter_np)
return -ENODEV;
@@ -2136,11 +2168,9 @@ int of_map_id(const struct device_node *np, u32 id,
return 0;
}
- if (!map_len || map_len % (4 * sizeof(*map))) {
- pr_err("%pOF: Error: Bad %s length: %d\n", np,
- map_name, map_len);
- return -EINVAL;
- }
+ if (map_bytes % sizeof(*map))
+ goto err_map_len;
+ map_len = map_bytes / sizeof(*map);
/* The default is to select all bits. */
map_mask = 0xffffffff;
@@ -2153,39 +2183,84 @@ int of_map_id(const struct device_node *np, u32 id,
of_property_read_u32(np, map_mask_name, &map_mask);
masked_id = map_mask & id;
- for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {
+
+ while (offset < map_len) {
struct device_node *phandle_node;
- u32 id_base = be32_to_cpup(map + 0);
- u32 phandle = be32_to_cpup(map + 1);
- u32 out_base = be32_to_cpup(map + 2);
- u32 id_len = be32_to_cpup(map + 3);
+ u32 id_base, phandle, id_len, id_off, cells = 0;
+ const __be32 *out_base;
+
+ if (map_len - offset < 2)
+ goto err_map_len;
+
+ id_base = be32_to_cpup(map + offset);
if (id_base & ~map_mask) {
- pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n",
- np, map_name, map_name,
- map_mask, id_base);
+ pr_err("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n",
+ np, map_name, map_mask_name, map_mask, id_base);
return -EFAULT;
}
- if (masked_id < id_base || masked_id >= id_base + id_len)
- continue;
-
+ phandle = be32_to_cpup(map + offset + 1);
phandle_node = of_find_node_by_phandle(phandle);
if (!phandle_node)
return -ENODEV;
+ if (bad_map) {
+ cells = 1;
+ } else if (of_property_read_u32(phandle_node, cells_name, &cells)) {
+ pr_err("%pOF: missing %s property\n", phandle_node, cells_name);
+ of_node_put(phandle_node);
+ return -EINVAL;
+ }
+
+ if (map_len - offset < 3 + cells) {
+ of_node_put(phandle_node);
+ goto err_map_len;
+ }
+
+ if (offset == 0 && cells == 2) {
+ bad_map = of_check_bad_map(map, map_len);
+ if (bad_map) {
+ pr_warn_once("%pOF: %s mismatches target %s, assuming extra cell of 0\n",
+ np, map_name, cells_name);
+ cells = 1;
+ }
+ }
+
+ out_base = map + offset + 2;
+ offset += 3 + cells;
+
+ id_len = be32_to_cpup(map + offset - 1);
+ if (id_len > 1 && cells > 1) {
+ /*
+ * With 1 output cell we reasonably assume its value
+ * has a linear relationship to the input; with more,
+ * we'd need help from the provider to know what to do.
+ */
+ pr_err("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n",
+ np, map_name, id_len, cells);
+ of_node_put(phandle_node);
+ return -EINVAL;
+ }
+ id_off = masked_id - id_base;
+ if (masked_id < id_base || id_off >= id_len) {
+ of_node_put(phandle_node);
+ continue;
+ }
+
if (filter_np && filter_np != phandle_node) {
of_node_put(phandle_node);
continue;
}
arg->np = phandle_node;
- arg->args[0] = masked_id - id_base + out_base;
- arg->args_count = 1;
+ for (int i = 0; i < cells; i++)
+ arg->args[i] = id_off + be32_to_cpu(out_base[i]);
+ arg->args_count = cells;
pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n",
- np, map_name, map_mask, id_base, out_base,
- id_len, id, masked_id - id_base + out_base);
+ np, map_name, map_mask, id_base, be32_to_cpup(out_base),
+ id_len, id, id_off + be32_to_cpup(out_base));
return 0;
}
@@ -2196,6 +2271,10 @@ int of_map_id(const struct device_node *np, u32 id,
arg->args[0] = id;
arg->args_count = 1;
return 0;
+
+err_map_len:
+ pr_err("%pOF: Error: Bad %s length: %d\n", np, map_name, map_bytes);
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(of_map_id);
@@ -2205,18 +2284,21 @@ EXPORT_SYMBOL_GPL(of_map_id);
* @id: Requester ID of the device (e.g. PCI RID/BDF or a platform
* stream/device ID) used as the lookup key in the iommu-map table.
* @arg: pointer to a &struct of_phandle_args for the result. On success,
- * @arg->args[0] contains the translated ID. If a map entry was matched,
- * @arg->np holds a reference to the target node that the caller must
- * release with of_node_put().
+ * @arg->args_count will be set to the number of output specifier cells
+ * and @arg->args[0..args_count-1] will contain the translated output
+ * specifier values. If a map entry was matched, @arg->np holds a
+ * reference to the target node that the caller must release with
+ * of_node_put().
*
- * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask".
+ * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells",
+ * and "iommu-map-mask".
*
* Return: 0 on success or a standard error code on failure.
*/
int of_map_iommu_id(const struct device_node *np, u32 id,
struct of_phandle_args *arg)
{
- return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg);
+ return of_map_id(np, id, "iommu-map", "#iommu-cells", "iommu-map-mask", NULL, arg);
}
EXPORT_SYMBOL_GPL(of_map_iommu_id);
@@ -2229,17 +2311,20 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id);
* to match any. If non-NULL, only map entries targeting this node will
* be matched.
* @arg: pointer to a &struct of_phandle_args for the result. On success,
- * @arg->args[0] contains the translated ID. If a map entry was matched,
- * @arg->np holds a reference to the target node that the caller must
- * release with of_node_put().
+ * @arg->args_count will be set to the number of output specifier cells
+ * and @arg->args[0..args_count-1] will contain the translated output
+ * specifier values. If a map entry was matched, @arg->np holds a
+ * reference to the target node that the caller must release with
+ * of_node_put().
*
- * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask".
+ * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells",
+ * and "msi-map-mask".
*
* Return: 0 on success or a standard error code on failure.
*/
int of_map_msi_id(const struct device_node *np, u32 id,
const struct device_node *filter_np, struct of_phandle_args *arg)
{
- return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg);
+ return of_map_id(np, id, "msi-map", "#msi-cells", "msi-map-mask", filter_np, arg);
}
EXPORT_SYMBOL_GPL(of_map_msi_id);
diff --git a/include/linux/of.h b/include/linux/of.h
index 8548cd9eb4f1..51ac8539f2c3 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -462,7 +462,8 @@ const char *of_prop_next_string(const struct property *prop, const char *cur);
bool of_console_check(const struct device_node *dn, char *name, int index);
int of_map_id(const struct device_node *np, u32 id,
- const char *map_name, const char *map_mask_name,
+ const char *map_name, const char *cells_name,
+ const char *map_mask_name,
const struct device_node *filter_np, struct of_phandle_args *arg);
int of_map_iommu_id(const struct device_node *np, u32 id,
@@ -934,7 +935,8 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
}
static inline int of_map_id(const struct device_node *np, u32 id,
- const char *map_name, const char *map_mask_name,
+ const char *map_name, const char *cells_name,
+ const char *map_mask_name,
const struct device_node *filter_np,
struct of_phandle_args *arg)
{
--
2.34.1
^ permalink raw reply related
* RE: [PATCH v3 0/3] Add i.MX94 remoteproc support and reset vector handling improvements
From: Peng Fan @ 2026-04-24 6:17 UTC (permalink / raw)
To: Mathieu Poirier, Peng Fan (OSS)
Cc: Bjorn Andersson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Daniel Baluta, linux-remoteproc@vger.kernel.org,
devicetree@vger.kernel.org, imx@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
In-Reply-To: <aepUOjXBOWK-0wt9@p14s>
Hi Mathieu,
> Subject: Re: [PATCH v3 0/3] Add i.MX94 remoteproc support and reset
> vector handling improvements
[....]
> >
> > ---
> > Peng Fan (3):
> > dt-bindings: remoteproc: imx-rproc: Support i.MX94
> > remoteproc: imx_rproc: Program non-zero SM CPU/LMM reset
> vector
> > remoteproc: imx_rproc: Add support for i.MX94
> >
> > .../bindings/remoteproc/fsl,imx-rproc.yaml | 3 +
> > drivers/remoteproc/imx_rproc.c | 98
> +++++++++++++++++++++-
> > drivers/remoteproc/imx_rproc.h | 2 +
> > 3 files changed, 101 insertions(+), 2 deletions(-)
>
> Much better - I'll pick this up when 7.1-rc1 comes out.
Thanks.
Daniel sent me a note in Wednesday during a private chat
that in patch 3, the M33S OCRAM secure region was not
included, only non secure region was included.
I will post patch v4 to address Daniel's conern.
Thanks,
Peng.
>
> Thanks,
> Mathieu
>
> > ---
> > base-commit: 724699d8d0523909da51fda8d1e10c1ff867b280
> > change-id: 20260311-imx943-rproc-2050e00b65f7
> >
> > Best regards,
> > --
> > Peng Fan <peng.fan@nxp.com>
> >
^ permalink raw reply
* Re: [PATCH] KVM: arm64: Wake-up from WFI when iqrchip is in userspace
From: Yao Yuan @ 2026-04-24 6:33 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvmarm, kvm, linux-arm-kernel, Joey Gouly, Suzuki K Poulose,
Oliver Upton, Zenghui Yu
In-Reply-To: <20260423163607.486345-1-maz@kernel.org>
On Thu, Apr 23, 2026 at 05:36:07PM +0800, Marc Zyngier wrote:
> It appears that there is nothing in the wake-up path that
> evaluates whether the in-kernel interrupts are pending unless
> we have a vgic.
>
> This means that the userspace irqchip support has been broken for
> about four years, and nobody noticed. It was also broken before
> as we wouldn't wake-up on a PMU interrupt, but hey, who cares...
>
> It is probably time to remove the feature altogether, because it
> was a terrible idea 10 years ago, and it still is.
>
> Fixes: b57de4ffd7c6d ("KVM: arm64: Simplify kvm_cpu_has_pending_timer()")
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
> arch/arm64/kvm/arm.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 176cbe8baad30..8bb2c7422cc8b 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -824,6 +824,10 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
> {
> bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF | HCR_VSE);
>
Hi Marc,
> + irq_lines |= (!irqchip_in_kernel(v->kvm) &&
> + (kvm_timer_should_notify_user(v) ||
> + kvm_pmu_should_notify_user(v)));
How about a new helper like 'kvm_should_notify_us_irqchip()' ?
We can replace the same part at beginning of kvm_vcpu_exit_request() and
here w/ unlikely().
> +
> return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
> && !kvm_arm_vcpu_stopped(v) && !v->arch.pause);
> }
> --
> 2.47.3
>
^ permalink raw reply
* [PATCH 1/1] Revert "scsi: ufs: Use pre-calculated offsets in ufshcd_init_lrb()"
From: ed.tsai @ 2026-04-24 6:35 UTC (permalink / raw)
To: ed.tsai, Alim Akhtar, Avri Altman, Bart Van Assche,
James E.J. Bottomley, Martin K. Petersen, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, wsd_upstream,
peter.wang, alice.chao, naomi.chu, chun-hung.wu, linux-scsi
From: Ed Tsai <ed.tsai@mediatek.com>
This reverts commit d5130c5a093257aa4542aaded8034ef116a7624a.
The offsets stored in utp_transfer_req_desc are in double words on
hosts without UFSHCD_QUIRK_PRDT_BYTE_GRAN, using them directly to
compute ucd_rsp_dma_addr and ucd_prdt_dma_addr results in incorrect
DMA addresses. The manual offsetof(struct utp_transfer_cmd_desc, ...)
calculations always yield correct byte offsets regardless of the quirk
and should be used instead.
Note that these DMA addresses are only used in ufshcd_print_tr() for
error logging, so the impact is limited to misleading error logs.
Fixes: d5130c5a0932 ("scsi: ufs: Use pre-calculated offsets in ufshcd_init_lrb()")
Signed-off-by: Ed Tsai <ed.tsai@mediatek.com>
---
drivers/ufs/core/ufshcd.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 4805e40ed4d7..3df2b44111cc 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -2971,8 +2971,9 @@ static void ufshcd_init_lrb(struct ufs_hba *hba, struct scsi_cmnd *cmd)
struct utp_transfer_req_desc *utrdlp = hba->utrdl_base_addr;
dma_addr_t cmd_desc_element_addr =
hba->ucdl_dma_addr + i * ufshcd_get_ucd_size(hba);
- u16 response_offset = le16_to_cpu(utrdlp[i].response_upiu_offset);
- u16 prdt_offset = le16_to_cpu(utrdlp[i].prd_table_offset);
+ u16 response_offset = offsetof(struct utp_transfer_cmd_desc,
+ response_upiu);
+ u16 prdt_offset = offsetof(struct utp_transfer_cmd_desc, prd_table);
struct ufshcd_lrb *lrb = scsi_cmd_priv(cmd);
lrb->utr_descriptor_ptr = utrdlp + i;
--
2.45.2
^ permalink raw reply related
* [PATCH v12 0/2] Add i.MX8ULP ISI and CSI-2 support
From: Guoniu Zhou @ 2026-04-24 6:49 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Laurent Pinchart,
Fabio Estevam, Philipp Zabel, Mauro Carvalho Chehab, Hans Verkuil
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media,
Guoniu Zhou, stable
Add support for the Image Sensing Interface (ISI) and MIPI CSI-2 receiver
on i.MX8ULP.
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
This was previously sent as patch 5/5 in the v10 series based on media
tree [1]. Patches 1-4 have already been applied to linux-next tree.
Changes in v12:
- Add one new patch
- Swap ISI axi and apb clocks to align with dt-binding requirements
- Update commit message
- See each patch's changelog for details.
Changes in v11:
- Rebased on latest media/next
- Removed #include <dt-bindings/reset/imx8ulp-pcc-reset.h> which was
deleted by Rob's dt-bindings cleanup series [2]
- Replaced reset macros with numeric values and added comments to
document the reset indices
- Link to v10: https://lore.kernel.org/r/20251205-csi2_imx8ulp-v10-5-190cdadb20a3@nxp.com
Changes in v6:
- Update compatible string in dts for csi node.
- Link to v5: https://lore.kernel.org/r/20250901-csi2_imx8ulp-v5-4-67964d1471f3@nxp.com
Changes in v4:
- Change csr clock name to pclk which is more readability.
- Link to v3: https://lore.kernel.org/all/20250825-csi2_imx8ulp-v3-4-35885aba62bc@nxp.com
Changes in v3:
- Change pclk clock name to csr to match IP port name.
- Link to v2: https://lore.kernel.org/all/20250822-csi2_imx8ulp-v2-4-26a444394965@nxp.com
Changes in v2:
- Move dts patch as the last one.
- Add "fsl,imx8qxp-mipi-csi2" to compatible string list of csi node.
- Link to v1: https://lore.kernel.org/all/20250812081923.1019345-3-guoniu.zhou@oss.nxp.com
[1] https://lore.kernel.org/all/20251205-csi2_imx8ulp-v10-0-190cdadb20a3@nxp.com/
[2] https://lore.kernel.org/all/20251212231203.727227-1-robh@kernel.org/
---
Guoniu Zhou (2):
media: dt-bindings: nxp,imx8-isi: Drop fsl,blk-ctrl requirement for i.MX8ULP
arm64: dts: imx8ulp: Add MIPI CSI-2 and ISI nodes
.../devicetree/bindings/media/nxp,imx8-isi.yaml | 4 +-
arch/arm64/boot/dts/freescale/imx8ulp.dtsi | 66 ++++++++++++++++++++++
2 files changed, 69 insertions(+), 1 deletion(-)
---
base-commit: 4fbeef21f5387234111b5d52924e77757626faa5
change-id: 20250819-csi2_imx8ulp-9db386dd6bdf
Best regards,
--
Guoniu Zhou <guoniu.zhou@oss.nxp.com>
^ permalink raw reply
* [PATCH v12 1/2] media: dt-bindings: nxp,imx8-isi: Drop fsl,blk-ctrl requirement for i.MX8ULP
From: Guoniu Zhou @ 2026-04-24 6:49 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Laurent Pinchart,
Fabio Estevam, Philipp Zabel, Mauro Carvalho Chehab, Hans Verkuil
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media,
Guoniu Zhou, stable
In-Reply-To: <20260424-csi2_imx8ulp-v12-0-da148eabc035@oss.nxp.com>
The i.MX8ULP variant does not require the fsl,blk-ctrl property. Add
fsl,imx8ulp-isi to the exception list alongside fsl,imx91-isi.
Fixes: 288517a3c6c9 ("dt-bindings: media: nxp,imx8-isi: Add i.MX8ULP ISI compatible string")
Cc: stable@vger.kernel.org
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
Changes in v12:
- New added in v12
---
Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
index b59c4ce30b8b..279c9cd3034a 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
@@ -117,7 +117,9 @@ allOf:
compatible:
not:
contains:
- const: fsl,imx91-isi
+ enum:
+ - fsl,imx8ulp-isi
+ - fsl,imx91-isi
then:
required:
- fsl,blk-ctrl
--
2.34.1
^ permalink raw reply related
* [PATCH v12 2/2] arm64: dts: imx8ulp: Add MIPI CSI-2 and ISI nodes
From: Guoniu Zhou @ 2026-04-24 6:49 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Laurent Pinchart,
Fabio Estevam, Philipp Zabel, Mauro Carvalho Chehab, Hans Verkuil
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, linux-media,
Guoniu Zhou
In-Reply-To: <20260424-csi2_imx8ulp-v12-0-da148eabc035@oss.nxp.com>
The MIPI CSI-2 in the i.MX8ULP is almost identical to the version present
in the i.MX8QXP/QM and is routed to the ISI. Add both the MIPI CSI-2 and
ISI nodes, disabled by default, as they require an attached camera sensor
to function.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
Changes in v12:
- Swap ISI axi and apb clocks to align with dt-binding requirements
- Update commit message
Changes in v11:
- Removed #include <dt-bindings/reset/imx8ulp-pcc-reset.h> which was
deleted by Rob's dt-bindings cleanup series [2]
- Replaced reset macros with numeric values and added comments to
document the reset indices
Changes in v1-v10:
- See cover letter
---
arch/arm64/boot/dts/freescale/imx8ulp.dtsi | 66 ++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
index 9b5d98766512..c5cae7675ce0 100644
--- a/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8ulp.dtsi
@@ -859,6 +859,72 @@ spdif: spdif@2dab0000 {
dma-names = "rx", "tx";
status = "disabled";
};
+
+ isi: isi@2dac0000 {
+ compatible = "fsl,imx8ulp-isi";
+ reg = <0x2dac0000 0x10000>;
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cgc2 IMX8ULP_CLK_LPAV_AXI_DIV>,
+ <&pcc5 IMX8ULP_CLK_ISI>;
+ clock-names = "axi", "apb";
+ power-domains = <&scmi_devpd IMX8ULP_PD_ISI>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ isi_in: endpoint {
+ remote-endpoint = <&mipi_csi_out>;
+ };
+ };
+ };
+ };
+
+ mipi_csi: csi@2daf0000 {
+ compatible = "fsl,imx8ulp-mipi-csi2";
+ reg = <0x2daf0000 0x10000>,
+ <0x2dad0000 0x10000>;
+ clocks = <&pcc5 IMX8ULP_CLK_CSI>,
+ <&pcc5 IMX8ULP_CLK_CSI_CLK_ESC>,
+ <&pcc5 IMX8ULP_CLK_CSI_CLK_UI>,
+ <&pcc5 IMX8ULP_CLK_CSI_REGS>;
+ clock-names = "core", "esc", "ui", "pclk";
+ assigned-clocks = <&pcc5 IMX8ULP_CLK_CSI>,
+ <&pcc5 IMX8ULP_CLK_CSI_CLK_ESC>,
+ <&pcc5 IMX8ULP_CLK_CSI_CLK_UI>,
+ <&pcc5 IMX8ULP_CLK_CSI_REGS>;
+ assigned-clock-parents = <&cgc2 IMX8ULP_CLK_PLL4_PFD1_DIV1>,
+ <&cgc2 IMX8ULP_CLK_PLL4_PFD1_DIV2>,
+ <&cgc2 IMX8ULP_CLK_PLL4_PFD0_DIV1>;
+ assigned-clock-rates = <200000000>,
+ <80000000>,
+ <100000000>,
+ <79200000>;
+ power-domains = <&scmi_devpd IMX8ULP_PD_MIPI_CSI>;
+ resets = <&pcc5 5>, /* PCC5_CSI_REGS_SWRST */
+ <&pcc5 6>; /* PCC5_CSI_SWRST> */
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ };
+
+ port@1 {
+ reg = <1>;
+
+ mipi_csi_out: endpoint {
+ remote-endpoint = <&isi_in>;
+ };
+ };
+ };
+ };
};
gpiod: gpio@2e200000 {
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v3 4/5] KVM: arm64: Enable HDBSS support and handle HDBSSF events
From: Tian Zheng @ 2026-04-24 6:48 UTC (permalink / raw)
To: Leonardo Bras
Cc: maz, oupton, catalin.marinas, corbet, pbonzini, will, yuzenghui,
wangzhou1, liuyonglong, Jonathan.Cameron, yezhenyu2, linuxarm,
joey.gouly, kvmarm, kvm, linux-arm-kernel, linux-doc,
linux-kernel, skhan, suzuki.poulose
In-Reply-To: <aeeHNMqDx5Yipt_K@devkitleo>
On 4/21/2026 10:18 PM, Leonardo Bras wrote:
> On Mon, Mar 30, 2026 at 12:31:28PM +0100, Leonardo Bras wrote:
>> On Sat, Mar 28, 2026 at 02:05:25PM +0800, Tian Zheng wrote:
>>> On 3/27/2026 11:00 PM, Leonardo Bras wrote:
>>>> On Fri, Mar 27, 2026 at 03:35:29PM +0800, Tian Zheng wrote:
>>>>> On 3/26/2026 2:05 AM, Leonardo Bras wrote:
>>>>>> Hello Tian,
>>>>>>
>>>>>> I am currently working on HACDBS enablement(which will be rebased on top of
>>>>>> this patchset) and due to the fact HACDBS and HDBSS are kind of
>>>>>> complementary I will sometimes come with some questions for issues I have
>>>>>> faced myself on that part. :)
>>>>>>
>>>>>> (see below)
>>>>> Of course! Happy to exchange ideas and learn together.
>>>> :)
> Hello Tian,
>
> On the above, HACDBS depends on HACDBSIRQ which can be announced by either
> device-tree or ACPI.
>
> Do you think it's ok for it to be ACPI only, for now?
>
> Thanks!
> Leo
Hi Leo,
No problem with ACPI-only for HACDBSIRQ for now.
Feel free to let me know if you have any questions while working
on HACDBS on top of HDBSS.
Tian
^ permalink raw reply
* [PATCH v18 0/8] ring-buffer: Making persistent ring buffers robust
From: Masami Hiramatsu (Google) @ 2026-04-24 6:51 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
Hi,
Here is the 18th version of improvement patches for making persistent
ring buffers robust to failures.
The previous version is here:
https://lore.kernel.org/all/177687458572.932171.10907864814735342737.stgit@mhiramat.tok.corp.google.com/
This version fixes a newly found bug and some review comments from
Sashiko[1], also, add 2 cleanups, which includes:
[1/8] Do not double count the reader_page when verifying persistent
ring buffer.
[2/8] Add Geert's Ack (Thanks!)
[3/8] Fix to substract BUF_PAGE_HDR_SIZE from meta->subbuf_size
to make the limit of commit size.
[4/8] Reset timestamp of reader_page when the entire cpu_buffer is
invalid.
[5/8] In rb_test_inject_invalid_pages(), changed entry_bytes and
idx to unsigned long.
[7/8] Cleanup persistent ring buffer validation code.
[8/8] Cleanup buffer_data_page related code.
[1] https://sashiko.dev/#/patchset/177687458572.932171.10907864814735342737.stgit%40mhiramat.tok.corp.google.com
Thank you,
Masami Hiramatsu (Google) (8):
ring-buffer: Do not double count the reader_page
ring-buffer: Flush and stop persistent ring buffer on panic
ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
ring-buffer: Add persistent ring buffer invalid-page inject test
ring-buffer: Show commit numbers in buffer_meta file
ring-buffer: Cleanup persistent ring buffer validation
ring-buffer: Cleanup buffer_data_page related code
arch/alpha/include/asm/Kbuild | 1
arch/arc/include/asm/Kbuild | 1
arch/arm/include/asm/Kbuild | 1
arch/arm64/include/asm/ring_buffer.h | 10 +
arch/csky/include/asm/Kbuild | 1
arch/hexagon/include/asm/Kbuild | 1
arch/loongarch/include/asm/Kbuild | 1
arch/m68k/include/asm/Kbuild | 1
arch/microblaze/include/asm/Kbuild | 1
arch/mips/include/asm/Kbuild | 1
arch/nios2/include/asm/Kbuild | 1
arch/openrisc/include/asm/Kbuild | 1
arch/parisc/include/asm/Kbuild | 1
arch/powerpc/include/asm/Kbuild | 1
arch/riscv/include/asm/Kbuild | 1
arch/s390/include/asm/Kbuild | 1
arch/sh/include/asm/Kbuild | 1
arch/sparc/include/asm/Kbuild | 1
arch/um/include/asm/Kbuild | 1
arch/x86/include/asm/Kbuild | 1
arch/xtensa/include/asm/Kbuild | 1
include/asm-generic/ring_buffer.h | 13 +
include/linux/ring_buffer.h | 1
kernel/trace/Kconfig | 34 ++
kernel/trace/ring_buffer.c | 472 +++++++++++++++++++++++-----------
kernel/trace/trace.c | 4
26 files changed, 395 insertions(+), 159 deletions(-)
create mode 100644 arch/arm64/include/asm/ring_buffer.h
create mode 100644 include/asm-generic/ring_buffer.h
base-commit: 6170922f137231b98fc568571befef63e1edff3f
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* [PATCH v18 1/8] ring-buffer: Do not double count the reader_page
From: Masami Hiramatsu (Google) @ 2026-04-24 6:52 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Since the cpu_buffer->reader_page is updated if there are unwound
pages. After that update, we should skip the page if it is the
original reader_page, because the original reader_page is already
checked.
Fixes: ca296d32ece3 ("tracing: ring_buffer: Rewind persistent ring buffer on reboot")
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v18:
- Newly added.
---
kernel/trace/ring_buffer.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index cef49f8871d2..5326924615a4 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -1884,7 +1884,7 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu)
static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
{
struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta;
- struct buffer_page *head_page, *orig_head;
+ struct buffer_page *head_page, *orig_head, *orig_reader;
unsigned long entry_bytes = 0;
unsigned long entries = 0;
int ret;
@@ -1895,16 +1895,17 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
return;
orig_head = head_page = cpu_buffer->head_page;
+ orig_reader = cpu_buffer->reader_page;
/* Do the reader page first */
- ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu);
if (ret < 0) {
pr_info("Ring buffer reader page is invalid\n");
goto invalid;
}
entries += ret;
- entry_bytes += local_read(&cpu_buffer->reader_page->page->commit);
- local_set(&cpu_buffer->reader_page->entries, ret);
+ entry_bytes += local_read(&orig_reader->page->commit);
+ local_set(&orig_reader->entries, ret);
ts = head_page->page->time_stamp;
@@ -2007,8 +2008,8 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Iterate until finding the commit page */
for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) {
- /* Reader page has already been done */
- if (head_page == cpu_buffer->reader_page)
+ /* The original reader page has already been checked/counted. */
+ if (head_page == orig_reader)
continue;
ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
^ permalink raw reply related
* [PATCH v18 2/8] ring-buffer: Flush and stop persistent ring buffer on panic
From: Masami Hiramatsu (Google) @ 2026-04-24 6:52 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
On real hardware, panic and machine reboot may not flush hardware cache
to memory. This means the persistent ring buffer, which relies on a
coherent state of memory, may not have its events written to the buffer
and they may be lost. Moreover, there may be inconsistency with the
counters which are used for validation of the integrity of the
persistent ring buffer which may cause all data to be discarded.
To avoid this issue, stop recording of the ring buffer on panic and
flush the cache of the ring buffer's memory.
Fixes: e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance")
Cc: stable@vger.kernel.org
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
Changes in v13:
- Fix a rebase conflict.
Changes in v11:
- Do nothing by default since flush_cache_vmap() does nothing on x86
but it can cause deadlock on some architectures via on_each_cpu()
because other CPUs will be stoppped when panic notifier is called.
Changes in v9:
- Fix typo of & to &&.
- Fix typo of "Generic"
Changes in v6:
- Introduce asm/ring_buffer.h for arch_ring_buffer_flush_range().
- Use flush_cache_vmap() instead of flush_cache_all().
Changes in v5:
- Use ring_buffer_record_off() instead of ring_buffer_record_disable().
- Use flush_cache_all() to ensure flush all cache.
Changes in v3:
- update patch description.
---
arch/alpha/include/asm/Kbuild | 1 +
arch/arc/include/asm/Kbuild | 1 +
arch/arm/include/asm/Kbuild | 1 +
arch/arm64/include/asm/ring_buffer.h | 10 ++++++++++
arch/csky/include/asm/Kbuild | 1 +
arch/hexagon/include/asm/Kbuild | 1 +
arch/loongarch/include/asm/Kbuild | 1 +
arch/m68k/include/asm/Kbuild | 1 +
arch/microblaze/include/asm/Kbuild | 1 +
arch/mips/include/asm/Kbuild | 1 +
arch/nios2/include/asm/Kbuild | 1 +
arch/openrisc/include/asm/Kbuild | 1 +
arch/parisc/include/asm/Kbuild | 1 +
arch/powerpc/include/asm/Kbuild | 1 +
arch/riscv/include/asm/Kbuild | 1 +
arch/s390/include/asm/Kbuild | 1 +
arch/sh/include/asm/Kbuild | 1 +
arch/sparc/include/asm/Kbuild | 1 +
arch/um/include/asm/Kbuild | 1 +
arch/x86/include/asm/Kbuild | 1 +
arch/xtensa/include/asm/Kbuild | 1 +
include/asm-generic/ring_buffer.h | 13 +++++++++++++
kernel/trace/ring_buffer.c | 22 ++++++++++++++++++++++
23 files changed, 65 insertions(+)
create mode 100644 arch/arm64/include/asm/ring_buffer.h
create mode 100644 include/asm-generic/ring_buffer.h
diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild
index 483965c5a4de..b154b4e3dfa8 100644
--- a/arch/alpha/include/asm/Kbuild
+++ b/arch/alpha/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += agp.h
generic-y += asm-offsets.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 4c69522e0328..483caacc6988 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -5,5 +5,6 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 03657ff8fbe3..decad5f2c826 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -3,6 +3,7 @@ generic-y += early_ioremap.h
generic-y += extable.h
generic-y += flat.h
generic-y += parport.h
+generic-y += ring_buffer.h
generated-y += mach-types.h
generated-y += unistd-nr.h
diff --git a/arch/arm64/include/asm/ring_buffer.h b/arch/arm64/include/asm/ring_buffer.h
new file mode 100644
index 000000000000..62316c406888
--- /dev/null
+++ b/arch/arm64/include/asm/ring_buffer.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_ARM64_RING_BUFFER_H
+#define _ASM_ARM64_RING_BUFFER_H
+
+#include <asm/cacheflush.h>
+
+/* Flush D-cache on persistent ring buffer */
+#define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end)
+
+#endif /* _ASM_ARM64_RING_BUFFER_H */
diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild
index 3a5c7f6e5aac..7dca0c6cdc84 100644
--- a/arch/csky/include/asm/Kbuild
+++ b/arch/csky/include/asm/Kbuild
@@ -9,6 +9,7 @@ generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
generic-y += text-patching.h
diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild
index 1efa1e993d4b..0f887d4238ed 100644
--- a/arch/hexagon/include/asm/Kbuild
+++ b/arch/hexagon/include/asm/Kbuild
@@ -5,4 +5,5 @@ generic-y += extable.h
generic-y += iomap.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild
index 9034b583a88a..7e92957baf6a 100644
--- a/arch/loongarch/include/asm/Kbuild
+++ b/arch/loongarch/include/asm/Kbuild
@@ -10,5 +10,6 @@ generic-y += qrwlock.h
generic-y += user.h
generic-y += ioctl.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
generic-y += statfs.h
generic-y += text-patching.h
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index b282e0dd8dc1..62543bf305ff 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -3,5 +3,6 @@ generated-y += syscall_table.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += text-patching.h
diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild
index 7178f990e8b3..0030309b47ad 100644
--- a/arch/microblaze/include/asm/Kbuild
+++ b/arch/microblaze/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += syscalls.h
generic-y += tlb.h
generic-y += user.h
diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild
index 684569b2ecd6..9771c3d85074 100644
--- a/arch/mips/include/asm/Kbuild
+++ b/arch/mips/include/asm/Kbuild
@@ -12,5 +12,6 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild
index 28004301c236..0a2530964413 100644
--- a/arch/nios2/include/asm/Kbuild
+++ b/arch/nios2/include/asm/Kbuild
@@ -5,6 +5,7 @@ generic-y += cmpxchg.h
generic-y += extable.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += spinlock.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild
index cef49d60d74c..8aa34621702d 100644
--- a/arch/openrisc/include/asm/Kbuild
+++ b/arch/openrisc/include/asm/Kbuild
@@ -8,4 +8,5 @@ generic-y += spinlock_types.h
generic-y += spinlock.h
generic-y += qrwlock_types.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild
index 4fb596d94c89..d48d158f7241 100644
--- a/arch/parisc/include/asm/Kbuild
+++ b/arch/parisc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild
index 2e23533b67e3..805b5aeebb6f 100644
--- a/arch/powerpc/include/asm/Kbuild
+++ b/arch/powerpc/include/asm/Kbuild
@@ -5,4 +5,5 @@ generated-y += syscall_table_spu.h
generic-y += agp.h
generic-y += mcs_spinlock.h
generic-y += qrwlock.h
+generic-y += ring_buffer.h
generic-y += early_ioremap.h
diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
index bd5fc9403295..7721b63642f4 100644
--- a/arch/riscv/include/asm/Kbuild
+++ b/arch/riscv/include/asm/Kbuild
@@ -14,5 +14,6 @@ generic-y += ticket_spinlock.h
generic-y += qrwlock.h
generic-y += qrwlock_types.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += vmlinux.lds.h
diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild
index 80bad7de7a04..0c1fc47c3ba0 100644
--- a/arch/s390/include/asm/Kbuild
+++ b/arch/s390/include/asm/Kbuild
@@ -7,3 +7,4 @@ generated-y += unistd_nr.h
generic-y += asm-offsets.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild
index 4d3f10ed8275..f0403d3ee8ab 100644
--- a/arch/sh/include/asm/Kbuild
+++ b/arch/sh/include/asm/Kbuild
@@ -3,4 +3,5 @@ generated-y += syscall_table.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += parport.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 17ee8a273aa6..49c6bb326b75 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -4,4 +4,5 @@ generated-y += syscall_table_64.h
generic-y += agp.h
generic-y += kvm_para.h
generic-y += mcs_spinlock.h
+generic-y += ring_buffer.h
generic-y += text-patching.h
diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild
index 1b9b82bbe322..2a1629ba8140 100644
--- a/arch/um/include/asm/Kbuild
+++ b/arch/um/include/asm/Kbuild
@@ -17,6 +17,7 @@ generic-y += module.lds.h
generic-y += parport.h
generic-y += percpu.h
generic-y += preempt.h
+generic-y += ring_buffer.h
generic-y += runtime-const.h
generic-y += softirq_stack.h
generic-y += switch_to.h
diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild
index 4566000e15c4..078fd2c0d69d 100644
--- a/arch/x86/include/asm/Kbuild
+++ b/arch/x86/include/asm/Kbuild
@@ -14,3 +14,4 @@ generic-y += early_ioremap.h
generic-y += fprobe.h
generic-y += mcs_spinlock.h
generic-y += mmzone.h
+generic-y += ring_buffer.h
diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild
index 13fe45dea296..e57af619263a 100644
--- a/arch/xtensa/include/asm/Kbuild
+++ b/arch/xtensa/include/asm/Kbuild
@@ -6,5 +6,6 @@ generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += qrwlock.h
generic-y += qspinlock.h
+generic-y += ring_buffer.h
generic-y += user.h
generic-y += text-patching.h
diff --git a/include/asm-generic/ring_buffer.h b/include/asm-generic/ring_buffer.h
new file mode 100644
index 000000000000..201d2aee1005
--- /dev/null
+++ b/include/asm-generic/ring_buffer.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Generic arch dependent ring_buffer macros.
+ */
+#ifndef __ASM_GENERIC_RING_BUFFER_H__
+#define __ASM_GENERIC_RING_BUFFER_H__
+
+#include <linux/cacheflush.h>
+
+/* Flush cache on ring buffer range if needed. Do nothing by default. */
+#define arch_ring_buffer_flush_range(start, end) do { } while (0)
+
+#endif /* __ASM_GENERIC_RING_BUFFER_H__ */
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 5326924615a4..7288383b1f27 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -7,6 +7,7 @@
#include <linux/ring_buffer_types.h>
#include <linux/sched/isolation.h>
#include <linux/trace_recursion.h>
+#include <linux/panic_notifier.h>
#include <linux/trace_events.h>
#include <linux/ring_buffer.h>
#include <linux/trace_clock.h>
@@ -31,6 +32,7 @@
#include <linux/oom.h>
#include <linux/mm.h>
+#include <asm/ring_buffer.h>
#include <asm/local64.h>
#include <asm/local.h>
#include <asm/setup.h>
@@ -559,6 +561,7 @@ struct trace_buffer {
unsigned long range_addr_start;
unsigned long range_addr_end;
+ struct notifier_block flush_nb;
struct ring_buffer_meta *meta;
@@ -2521,6 +2524,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}
+/* Stop recording on a persistent buffer and flush cache if needed. */
+static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
+{
+ struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
+
+ ring_buffer_record_off(buffer);
+ arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
+ return NOTIFY_DONE;
+}
+
static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
int order, unsigned long start,
unsigned long end,
@@ -2651,6 +2664,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
mutex_init(&buffer->mutex);
+ /* Persistent ring buffer needs to flush cache before reboot. */
+ if (start && end) {
+ buffer->flush_nb.notifier_call = rb_flush_buffer_cb;
+ atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb);
+ }
+
return_ptr(buffer);
fail_free_buffers:
@@ -2749,6 +2768,9 @@ ring_buffer_free(struct trace_buffer *buffer)
{
int cpu;
+ if (buffer->range_addr_start && buffer->range_addr_end)
+ atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb);
+
cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node);
irq_work_sync(&buffer->irq_work.work);
^ permalink raw reply related
* Re: [PATCH 1/6] arm64: defconfig: Move entries to match savedefconfig
From: Linus Walleij @ 2026-04-24 6:52 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Catalin Marinas, Will Deacon,
Arnd Bergmann, Alexandre Belloni, Drew Fustini, soc
In-Reply-To: <20260412-defconfig-v1-1-a46918286451@oss.qualcomm.com>
On Sun, Apr 12, 2026 at 6:21 PM Krzysztof Kozlowski
<krzysztof.kozlowski@oss.qualcomm.com> wrote:
> Only re-shuffle entries to match savedefconfig, without removing any
> symbols. This helps in reviewing defconfig when comparing with
> savedefconfig and looking for obsolete symbols. No impact on
> include/generated/autoconf.h.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v18 3/8] ring-buffer: Skip invalid sub-buffers when validating persistent ring buffer
From: Masami Hiramatsu (Google) @ 2026-04-24 6:52 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Skip invalid sub-buffers when validating the persistent ring buffer
instead of discarding the entire ring buffer. Only skipped buffers
are invalidated (cleared).
If the cache data in memory fails to be synchronized during a reboot,
the persistent ring buffer may become partially corrupted, but other
sub-buffers may still contain readable event data. Only discard the
subbuffers that are found to be corrupted.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v18:
- Minor update by the new fix.
- Fix to substract BUF_PAGE_HDR_SIZE from meta->subbuf_size
to make the limit of commit size.
Changes in v17:
- Fix to use rb_page_size() of rewound pages for entry_bytes.
Changes in v15:
- Skip reader_page loop check on persistent ring buffer because
there can be contiguous empty(invalidated) pages.
- Do not show discarded page number information if it is 0.
Changes in v11:
- Fix a typo.
Changes in v9:
- Add meta->subbuf_size check.
- Fix a typo.
- Handle invalid reader_page case.
Changes in v8:
- Add comment in rb_valudate_buffer()
- Clear the RB_MISSED_* flags in rb_valudate_buffer() instead of
skipping subbuf.
- Remove unused subbuf local variable from rb_cpu_meta_valid().
Changes in v7:
- Combined with Handling RB_MISSED_* flags patch, focus on validation at boot.
- Remove checking subbuffer data when validating metadata, because it should be done
later.
- Do not mark the discarded sub buffer page but just reset it.
Changes in v6:
- Show invalid page detection message once per CPU.
Changes in v5:
- Instead of showing errors for each page, just show the number
of discarded pages at last.
Changes in v3:
- Record missed data event on commit.
---
kernel/trace/ring_buffer.c | 111 ++++++++++++++++++++++++++------------------
1 file changed, 66 insertions(+), 45 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 7288383b1f27..404c1fcac0ae 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -370,6 +370,12 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
return local_read(&bpage->page->commit);
}
+/* Size is determined by what has been committed */
+static __always_inline unsigned int rb_page_size(struct buffer_page *bpage)
+{
+ return rb_page_commit(bpage) & ~RB_MISSED_MASK;
+}
+
static void free_buffer_page(struct buffer_page *bpage)
{
/* Range pages are not to be freed */
@@ -1762,7 +1768,6 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
unsigned long *subbuf_mask)
{
int subbuf_size = PAGE_SIZE;
- struct buffer_data_page *subbuf;
unsigned long buffers_start;
unsigned long buffers_end;
int i;
@@ -1770,6 +1775,11 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
if (!subbuf_mask)
return false;
+ if (meta->subbuf_size != PAGE_SIZE) {
+ pr_info("Ring buffer boot meta [%d] invalid subbuf_size\n", cpu);
+ return false;
+ }
+
buffers_start = meta->first_buffer;
buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs);
@@ -1786,11 +1796,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
return false;
}
- subbuf = rb_subbufs_from_meta(meta);
-
bitmap_clear(subbuf_mask, 0, meta->nr_subbufs);
- /* Is the meta buffers and the subbufs themselves have correct data? */
+ /*
+ * Ensure the meta::buffers array has correct data. The data in each subbufs
+ * are checked later in rb_meta_validate_events().
+ */
for (i = 0; i < meta->nr_subbufs; i++) {
if (meta->buffers[i] < 0 ||
meta->buffers[i] >= meta->nr_subbufs) {
@@ -1798,18 +1809,12 @@ static bool rb_cpu_meta_valid(struct ring_buffer_cpu_meta *meta, int cpu,
return false;
}
- if ((unsigned)local_read(&subbuf->commit) > subbuf_size) {
- pr_info("Ring buffer boot meta [%d] buffer invalid commit\n", cpu);
- return false;
- }
-
if (test_bit(meta->buffers[i], subbuf_mask)) {
pr_info("Ring buffer boot meta [%d] array has duplicates\n", cpu);
return false;
}
set_bit(meta->buffers[i], subbuf_mask);
- subbuf = (void *)subbuf + subbuf_size;
}
return true;
@@ -1873,13 +1878,22 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
return events;
}
-static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu)
+static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
+ struct ring_buffer_cpu_meta *meta)
{
unsigned long long ts;
+ unsigned long tail;
u64 delta;
- int tail;
- tail = local_read(&dpage->commit);
+ /*
+ * When a sub-buffer is recovered from a read, the commit value may
+ * have RB_MISSED_* bits set, as these bits are reset on reuse.
+ * Even after clearing these bits, a commit value greater than the
+ * subbuf_size is considered invalid.
+ */
+ tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
+ if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE)
+ return -1;
return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
}
@@ -1890,6 +1904,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
struct buffer_page *head_page, *orig_head, *orig_reader;
unsigned long entry_bytes = 0;
unsigned long entries = 0;
+ int discarded = 0;
int ret;
u64 ts;
int i;
@@ -1901,14 +1916,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_reader = cpu_buffer->reader_page;
/* Do the reader page first */
- ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu, meta);
if (ret < 0) {
- pr_info("Ring buffer reader page is invalid\n");
- goto invalid;
+ pr_info("Ring buffer meta [%d] invalid reader page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ /* Instead of discard whole ring buffer, discard only this sub-buffer. */
+ local_set(&orig_reader->entries, 0);
+ local_set(&orig_reader->page->commit, 0);
+ } else {
+ entries += ret;
+ entry_bytes += rb_page_size(orig_reader);
+ local_set(&orig_reader->entries, ret);
}
- entries += ret;
- entry_bytes += local_read(&orig_reader->page->commit);
- local_set(&orig_reader->entries, ret);
ts = head_page->page->time_stamp;
@@ -1936,7 +1956,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
break;
/* Stop rewind if the page is invalid. */
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
if (ret < 0)
break;
@@ -1945,7 +1965,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (ret)
local_inc(&cpu_buffer->pages_touched);
entries += ret;
- entry_bytes += rb_page_commit(head_page);
+ entry_bytes += rb_page_size(head_page);
}
if (i)
pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i);
@@ -2015,21 +2035,24 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == orig_reader)
continue;
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu);
+ ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
if (ret < 0) {
- pr_info("Ring buffer meta [%d] invalid buffer page\n",
- cpu_buffer->cpu);
- goto invalid;
- }
-
- /* If the buffer has content, update pages_touched */
- if (ret)
- local_inc(&cpu_buffer->pages_touched);
-
- entries += ret;
- entry_bytes += local_read(&head_page->page->commit);
- local_set(&head_page->entries, ret);
+ if (!discarded)
+ pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ /* Instead of discard whole ring buffer, discard only this sub-buffer. */
+ local_set(&head_page->entries, 0);
+ local_set(&head_page->page->commit, 0);
+ } else {
+ /* If the buffer has content, update pages_touched */
+ if (ret)
+ local_inc(&cpu_buffer->pages_touched);
+ entries += ret;
+ entry_bytes += rb_page_size(head_page);
+ local_set(&head_page->entries, ret);
+ }
if (head_page == cpu_buffer->commit_page)
break;
}
@@ -2043,7 +2066,10 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
local_set(&cpu_buffer->entries, entries);
local_set(&cpu_buffer->entries_bytes, entry_bytes);
- pr_info("Ring buffer meta [%d] is from previous boot!\n", cpu_buffer->cpu);
+ pr_info("Ring buffer meta [%d] is from previous boot!", cpu_buffer->cpu);
+ if (discarded)
+ pr_cont(" (%d pages discarded)", discarded);
+ pr_cont("\n");
return;
invalid:
@@ -3330,12 +3356,6 @@ rb_iter_head_event(struct ring_buffer_iter *iter)
return NULL;
}
-/* Size is determined by what has been committed */
-static __always_inline unsigned rb_page_size(struct buffer_page *bpage)
-{
- return rb_page_commit(bpage) & ~RB_MISSED_MASK;
-}
-
static __always_inline unsigned
rb_commit_index(struct ring_buffer_per_cpu *cpu_buffer)
{
@@ -5648,11 +5668,12 @@ __rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
again:
/*
* This should normally only loop twice. But because the
- * start of the reader inserts an empty page, it causes
- * a case where we will loop three times. There should be no
- * reason to loop four times (that I know of).
+ * start of the reader inserts an empty page, it causes a
+ * case where we will loop three times. There should be no
+ * reason to loop four times unless the ring buffer is a
+ * recovered persistent ring buffer.
*/
- if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3)) {
+ if (RB_WARN_ON(cpu_buffer, ++nr_loops > 3 && !cpu_buffer->ring_meta)) {
reader = NULL;
goto out;
}
^ permalink raw reply related
* [PATCH v18 4/8] ring-buffer: Skip invalid sub-buffers when rewinding persistent ring buffer
From: Masami Hiramatsu (Google) @ 2026-04-24 6:52 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Skip invalid sub-buffers when rewinding the persistent ring buffer
instead of stopping the rewinding the ring buffer. The skipped
buffers are cleared.
To ensure the rewinding stops at the unused page, this also clears
buffer_data_page::time_stamp when tracing resets the buffer. This
allows us to identify unused pages and empty pages.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v18:
- Reset timestamp of reader_page when the entire cpu_buffer is
invalid.
- Minor update by new fix.
Changes in v17:
- Fix to verify head_page at first before using its timestamp.
- Reset timestamp if the page is invalid.
Changes in v12:
- Fix build error.
Changes in v11:
- Reset timestamp when the buffer is invalid.
- When rewinding, skip subbuf page if timestamp is wrong and
check timestamp after validating buffer data page.
Changes in v10:
- Newly added.
---
kernel/trace/ring_buffer.c | 94 ++++++++++++++++++++++++++------------------
1 file changed, 55 insertions(+), 39 deletions(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 404c1fcac0ae..899a78b307e6 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -363,6 +363,7 @@ struct buffer_page {
static void rb_init_page(struct buffer_data_page *bpage)
{
local_set(&bpage->commit, 0);
+ bpage->time_stamp = 0;
}
static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
@@ -1878,12 +1879,14 @@ static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu
return events;
}
-static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
- struct ring_buffer_cpu_meta *meta)
+static int rb_validate_buffer(struct buffer_page *bpage, int cpu,
+ struct ring_buffer_cpu_meta *meta, u64 prev_ts, u64 next_ts)
{
+ struct buffer_data_page *dpage = bpage->page;
unsigned long long ts;
unsigned long tail;
u64 delta;
+ int ret = -1;
/*
* When a sub-buffer is recovered from a read, the commit value may
@@ -1892,9 +1895,19 @@ static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu,
* subbuf_size is considered invalid.
*/
tail = local_read(&dpage->commit) & ~RB_MISSED_MASK;
- if (tail > meta->subbuf_size - BUF_PAGE_HDR_SIZE)
- return -1;
- return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
+ if (tail <= meta->subbuf_size - BUF_PAGE_HDR_SIZE)
+ ret = rb_read_data_buffer(dpage, tail, cpu, &ts, &delta);
+
+ if (ret < 0 || (prev_ts && prev_ts > ts) || (next_ts && ts > next_ts)) {
+ local_set(&bpage->entries, 0);
+ local_set(&bpage->page->commit, 0);
+ bpage->page->time_stamp = prev_ts ? prev_ts : next_ts;
+ ret = -1;
+ } else {
+ local_set(&bpage->entries, ret);
+ }
+
+ return ret;
}
/* If the meta data has been validated, now validate the events */
@@ -1915,25 +1928,29 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
orig_head = head_page = cpu_buffer->head_page;
orig_reader = cpu_buffer->reader_page;
- /* Do the reader page first */
- ret = rb_validate_buffer(orig_reader->page, cpu_buffer->cpu, meta);
+ /* Do the head page first */
+ ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, 0);
+ if (ret < 0) {
+ pr_info("Ring buffer meta [%d] invalid head page detected\n",
+ cpu_buffer->cpu);
+ goto skip_rewind;
+ }
+ ts = head_page->page->time_stamp;
+
+ /* Do the reader page - reader must be previous to head. */
+ ret = rb_validate_buffer(orig_reader, cpu_buffer->cpu, meta, 0, ts);
if (ret < 0) {
pr_info("Ring buffer meta [%d] invalid reader page detected\n",
cpu_buffer->cpu);
discarded++;
- /* Instead of discard whole ring buffer, discard only this sub-buffer. */
- local_set(&orig_reader->entries, 0);
- local_set(&orig_reader->page->commit, 0);
} else {
entries += ret;
entry_bytes += rb_page_size(orig_reader);
- local_set(&orig_reader->entries, ret);
+ ts = orig_reader->page->time_stamp;
}
- ts = head_page->page->time_stamp;
-
/*
- * Try to rewind the head so that we can read the pages which already
+ * Try to rewind the head so that we can read the pages which are already
* read in the previous boot.
*/
if (head_page == cpu_buffer->tail_page)
@@ -1946,26 +1963,27 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == cpu_buffer->tail_page)
break;
- /* Ensure the page has older data than head. */
- if (ts < head_page->page->time_stamp)
- break;
-
- ts = head_page->page->time_stamp;
- /* Ensure the page has correct timestamp and some data. */
- if (!ts || rb_page_commit(head_page) == 0)
+ /* Rewind until unused page (no timestamp, no commit). */
+ if (!head_page->page->time_stamp && rb_page_commit(head_page) == 0)
break;
- /* Stop rewind if the page is invalid. */
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
- if (ret < 0)
- break;
-
- /* Recover the number of entries and update stats. */
- local_set(&head_page->entries, ret);
- if (ret)
- local_inc(&cpu_buffer->pages_touched);
- entries += ret;
- entry_bytes += rb_page_size(head_page);
+ /*
+ * Skip if the page is invalid, or its timestamp is newer than the
+ * previous valid page.
+ */
+ ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, 0, ts);
+ if (ret < 0) {
+ if (!discarded)
+ pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
+ cpu_buffer->cpu);
+ discarded++;
+ } else {
+ entries += ret;
+ entry_bytes += rb_page_size(head_page);
+ if (ret > 0)
+ local_inc(&cpu_buffer->pages_touched);
+ ts = head_page->page->time_stamp;
+ }
}
if (i)
pr_info("Ring buffer [%d] rewound %d pages\n", cpu_buffer->cpu, i);
@@ -2027,6 +2045,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Nothing more to do, the only page is the reader page */
goto done;
}
+ ts = head_page->page->time_stamp;
/* Iterate until finding the commit page */
for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) {
@@ -2035,15 +2054,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (head_page == orig_reader)
continue;
- ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu, meta);
+ ret = rb_validate_buffer(head_page, cpu_buffer->cpu, meta, ts, 0);
if (ret < 0) {
if (!discarded)
pr_info("Ring buffer meta [%d] invalid buffer page detected\n",
cpu_buffer->cpu);
discarded++;
- /* Instead of discard whole ring buffer, discard only this sub-buffer. */
- local_set(&head_page->entries, 0);
- local_set(&head_page->page->commit, 0);
} else {
/* If the buffer has content, update pages_touched */
if (ret)
@@ -2051,7 +2067,7 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
entries += ret;
entry_bytes += rb_page_size(head_page);
- local_set(&head_page->entries, ret);
+ ts = head_page->page->time_stamp;
}
if (head_page == cpu_buffer->commit_page)
break;
@@ -2079,12 +2095,12 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
/* Reset the reader page */
local_set(&cpu_buffer->reader_page->entries, 0);
- local_set(&cpu_buffer->reader_page->page->commit, 0);
+ rb_init_page(cpu_buffer->reader_page->page);
/* Reset all the subbuffers */
for (i = 0; i < meta->nr_subbufs - 1; i++, rb_inc_page(&head_page)) {
local_set(&head_page->entries, 0);
- local_set(&head_page->page->commit, 0);
+ rb_init_page(head_page->page);
}
}
^ permalink raw reply related
* Re: [PATCH 2/6] arm64: defconfig: Drop unused legacy netfilter options
From: Linus Walleij @ 2026-04-24 6:52 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: linux-kernel, linux-arm-kernel, Catalin Marinas, Will Deacon,
Arnd Bergmann, Alexandre Belloni, Drew Fustini, soc
In-Reply-To: <20260412-defconfig-v1-2-a46918286451@oss.qualcomm.com>
On Sun, Apr 12, 2026 at 6:21 PM Krzysztof Kozlowski
<krzysztof.kozlowski@oss.qualcomm.com> wrote:
> Drop all options which are ineffective now, because they depend on
> disabled IP_NF_IPTABLES_LEGACY or IP6_NF_IPTABLES_LEGACY. No impact on
> include/generated/autoconf.h.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH v18 5/8] ring-buffer: Add persistent ring buffer invalid-page inject test
From: Masami Hiramatsu (Google) @ 2026-04-24 6:52 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add a self-corrupting test for the persistent ring buffer.
This will inject an erroneous value to some sub-buffer pages (where
the index is even or multiples of 5) in the persistent ring buffer
when the kernel panics, and checks whether the number of detected
invalid pages and the total entry_bytes are the same as the recorded
values after reboot.
This ensures that the kernel can correctly recover a partially
corrupted persistent ring buffer after a reboot or panic.
The test only runs on the persistent ring buffer whose name is
"ptracingtest". The user has to fill it with events before a
kernel panic.
To run the test, enable CONFIG_RING_BUFFER_PERSISTENT_INJECT
and add the following kernel cmdline:
reserve_mem=20M:2M:trace trace_instance=ptracingtest^traceoff@trace
panic=1
Run the following commands after the 1st boot:
cd /sys/kernel/tracing/instances/ptracingtest
echo 1 > tracing_on
echo 1 > events/enable
sleep 3
echo c > /proc/sysrq-trigger
After panic message, the kernel will reboot and run the verification
on the persistent ring buffer, e.g.
Ring buffer meta [2] invalid buffer page detected
Ring buffer meta [2] is from previous boot! (318 pages discarded)
Ring buffer testing [2] invalid pages: PASSED (318/318)
Ring buffer testing [2] entry_bytes: PASSED (1300476/1300476)
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v18:
- Fix to mask RB_MISSED_* flags when counting entry_bytes.
Changes in v17:
- In rb_test_inject_invalid_pages(), changed entry_bytes and
idx to unsigned long
- Added NULL checks for cpu_buffer and meta.
- In allocate_trace_buffer(), added a NULL check for tr->name
before comparing it with strcmp.
Changes in v16:
- Update description and comments according to review comments.
Changes in v15:
- Use pr_warn() for test result.
- Inject errors on the page index is multiples of 5 so that
this can reproduce contiguous empty pages.
Changes in v14:
- Rename config to CONFIG_RING_BUFFER_PERSISTENT_INJECT.
- Clear meta->nr_invalid/entry_bytes after testing.
- Add test commands in config comment.
Changes in v10:
- Add entry_bytes test.
- Do not compile test code if CONFIG_RING_BUFFER_PERSISTENT_SELFTEST=n.
Changes in v9:
- Test also reader pages.
---
include/linux/ring_buffer.h | 1 +
kernel/trace/Kconfig | 34 +++++++++++++++++++
kernel/trace/ring_buffer.c | 79 +++++++++++++++++++++++++++++++++++++++++++
kernel/trace/trace.c | 4 ++
4 files changed, 118 insertions(+)
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index 994f52b34344..0670742b2d60 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -238,6 +238,7 @@ int ring_buffer_subbuf_size_get(struct trace_buffer *buffer);
enum ring_buffer_flags {
RB_FL_OVERWRITE = 1 << 0,
+ RB_FL_TESTING = 1 << 1,
};
#ifdef CONFIG_RING_BUFFER
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index e130da35808f..084f34dc6c9f 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -1202,6 +1202,40 @@ config RING_BUFFER_VALIDATE_TIME_DELTAS
Only say Y if you understand what this does, and you
still want it enabled. Otherwise say N
+config RING_BUFFER_PERSISTENT_INJECT
+ bool "Enable persistent ring buffer error injection test"
+ depends on RING_BUFFER
+ help
+ This option will have the kernel check if the persistent ring
+ buffer is named "ptracingtest". and if so, it will corrupt some
+ of its pages on a kernel panic. This is used to test if the
+ persistent ring buffer can recover from some of its sub-buffers
+ being corrupted.
+ To use this, boot a kernel with a "ptracingtest" persistent
+ ring buffer, e.g.
+
+ reserve_mem=20M:2M:trace trace_instance=ptracingtest@trace panic=1
+
+ And after the 1st boot, run the following commands:
+
+ cd /sys/kernel/tracing/instances/ptracingtest
+ echo 1 > events/enable
+ echo 1 > tracing_on
+ sleep 3
+ echo c > /proc/sysrq-trigger
+
+ After the panic message, the kernel will reboot and will show
+ the test results in the console output.
+
+ Note that events for the test ring buffer needs to be enabled
+ prior to crashing the kernel so that the ring buffer has content
+ that the test will corrupt.
+ As the test will corrupt events in the "ptracingtest" persistent
+ ring buffer, it should not be used for any other purpose other
+ than this test.
+
+ If unsure, say N
+
config MMIOTRACE_TEST
tristate "Test module for mmiotrace"
depends on MMIOTRACE && m
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 899a78b307e6..e8d2b3457d7f 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -64,6 +64,10 @@ struct ring_buffer_cpu_meta {
unsigned long commit_buffer;
__u32 subbuf_size;
__u32 nr_subbufs;
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT
+ __u32 nr_invalid;
+ __u32 entry_bytes;
+#endif
int buffers[];
};
@@ -2086,6 +2090,21 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer)
if (discarded)
pr_cont(" (%d pages discarded)", discarded);
pr_cont("\n");
+
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT
+ if (meta->nr_invalid)
+ pr_warn("Ring buffer testing [%d] invalid pages: %s (%d/%d)\n",
+ cpu_buffer->cpu,
+ (discarded == meta->nr_invalid) ? "PASSED" : "FAILED",
+ discarded, meta->nr_invalid);
+ if (meta->entry_bytes)
+ pr_warn("Ring buffer testing [%d] entry_bytes: %s (%ld/%ld)\n",
+ cpu_buffer->cpu,
+ (entry_bytes == meta->entry_bytes) ? "PASSED" : "FAILED",
+ (long)entry_bytes, (long)meta->entry_bytes);
+ meta->nr_invalid = 0;
+ meta->entry_bytes = 0;
+#endif
return;
invalid:
@@ -2566,12 +2585,72 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
kfree(cpu_buffer);
}
+#ifdef CONFIG_RING_BUFFER_PERSISTENT_INJECT
+static void rb_test_inject_invalid_pages(struct trace_buffer *buffer)
+{
+ struct ring_buffer_per_cpu *cpu_buffer;
+ struct ring_buffer_cpu_meta *meta;
+ struct buffer_data_page *dpage;
+ unsigned long entry_bytes = 0;
+ unsigned long ptr;
+ int subbuf_size;
+ int invalid = 0;
+ int cpu;
+ int i;
+
+ if (!(buffer->flags & RB_FL_TESTING))
+ return;
+
+ guard(preempt)();
+ cpu = smp_processor_id();
+
+ cpu_buffer = buffer->buffers[cpu];
+ if (!cpu_buffer)
+ return;
+ meta = cpu_buffer->ring_meta;
+ if (!meta)
+ return;
+
+ ptr = (unsigned long)rb_subbufs_from_meta(meta);
+ subbuf_size = meta->subbuf_size;
+
+ for (i = 0; i < meta->nr_subbufs; i++) {
+ unsigned long idx = meta->buffers[i];
+
+ dpage = (void *)(ptr + idx * subbuf_size);
+ /* Skip unused pages */
+ if (!local_read(&dpage->commit))
+ continue;
+
+ /*
+ * Invalidate even pages or multiples of 5. This will cause 3
+ * contiguous invalidated(empty) pages.
+ */
+ if (!(i & 0x1) || !(i % 5)) {
+ local_add(subbuf_size + 1, &dpage->commit);
+ invalid++;
+ } else {
+ /* Count total commit bytes. */
+ entry_bytes += local_read(&dpage->commit) & ~RB_MISSED_MASK;
+ }
+ }
+
+ pr_info("Inject invalidated %d pages on CPU%d, total size: %ld\n",
+ invalid, cpu, (long)entry_bytes);
+ meta->nr_invalid = invalid;
+ meta->entry_bytes = entry_bytes;
+}
+#else /* !CONFIG_RING_BUFFER_PERSISTENT_INJECT */
+#define rb_test_inject_invalid_pages(buffer) do { } while (0)
+#endif
+
/* Stop recording on a persistent buffer and flush cache if needed. */
static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data)
{
struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb);
ring_buffer_record_off(buffer);
+ rb_test_inject_invalid_pages(buffer);
arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end);
return NOTIFY_DONE;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e9455d46ec16..d972b24cd73b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -9436,6 +9436,8 @@ static void setup_trace_scratch(struct trace_array *tr,
memset(tscratch, 0, size);
}
+#define TRACE_TEST_PTRACING_NAME "ptracingtest"
+
static int
allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, unsigned long size)
{
@@ -9448,6 +9450,8 @@ allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, unsigned
buf->tr = tr;
if (tr->range_addr_start && tr->range_addr_size) {
+ if (tr->name && !strcmp(tr->name, TRACE_TEST_PTRACING_NAME))
+ rb_flags |= RB_FL_TESTING;
/* Add scratch buffer to handle 128 modules */
buf->buffer = ring_buffer_alloc_range(size, rb_flags, 0,
tr->range_addr_start,
^ permalink raw reply related
* [PATCH v18 6/8] ring-buffer: Show commit numbers in buffer_meta file
From: Masami Hiramatsu (Google) @ 2026-04-24 6:52 UTC (permalink / raw)
To: Steven Rostedt, Catalin Marinas, Will Deacon
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Ian Rogers, linux-arm-kernel
In-Reply-To: <177701351903.2223789.17087009302463188638.stgit@mhiramat.tok.corp.google.com>
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
In addition to the index number, show the commit numbers of
each data page in the per_cpu buffer_meta file.
This is useful for understanding the current status of the
persistent ring buffer. (Note that this file is shown
only for persistent ring buffer and its backup instance)
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
Changes in v17:
- Added NULL check for dpage in rbm_show in ring_buffer.c.
Changes in v16:
- update description.
---
kernel/trace/ring_buffer.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index e8d2b3457d7f..de653a8e3cec 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2216,6 +2216,7 @@ static int rbm_show(struct seq_file *m, void *v)
struct ring_buffer_per_cpu *cpu_buffer = m->private;
struct ring_buffer_cpu_meta *meta = cpu_buffer->ring_meta;
unsigned long val = (unsigned long)v;
+ struct buffer_data_page *dpage;
if (val == 1) {
seq_printf(m, "head_buffer: %d\n",
@@ -2228,7 +2229,9 @@ static int rbm_show(struct seq_file *m, void *v)
}
val -= 2;
- seq_printf(m, "buffer[%ld]: %d\n", val, meta->buffers[val]);
+ dpage = rb_range_buffer(cpu_buffer, val);
+ seq_printf(m, "buffer[%ld]: %d (commit: %ld)\n",
+ val, meta->buffers[val], dpage ? local_read(&dpage->commit) : -1);
return 0;
}
^ 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