* Re: [PATCH 2/4] firmware: arm_ffa: Register core as a platform driver
From: Nathan Chancellor @ 2026-05-23 0:11 UTC (permalink / raw)
To: Sudeep Holla
Cc: linux-security-module, linux-kernel, linux-integrity,
linux-arm-kernel, kvmarm, Yeoreum Yun
In-Reply-To: <20260508-b4-ffa_plat_dev-v1-2-c5a30f8cf7b8@kernel.org>
Hi Sudeep,
On Fri, May 08, 2026 at 06:54:16PM +0100, Sudeep Holla wrote:
> Move the FF-A core bring-up and teardown paths into platform driver
> probe and remove callbacks, and register a synthetic arm-ffa platform
> device to bind the driver.
>
> This makes the FF-A core lifetime follow the driver model while keeping
> the device creation internal to the FF-A core. Use normal platform driver
> registration so the probe path has standard driver-core semantics.
>
> The synthetic platform device is a temporary bridge until ACPI and
> devicetree describe the FF-A core device or object. Once those firmware
> description paths are defined, the internal platform device creation can
> be dropped and the driver can bind to the firmware-described device
> directly.
>
> Since the transport selection now happens from the platform probe path,
> drop the __init annotation from ffa_transport_init().
>
> Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
I am seeing
arm-ffa arm-ffa: probe with driver arm-ffa failed with error -95
on my two arm64 test machines after this change landed in -next as
commit e659fc8e537c ("firmware: arm_ffa: Register core as a platform
driver"), is this expected? If so, perhaps it should be silenced?
Cheers,
Nathan
# bad: [c1ecb239fa3456529a32255359fc78b69eb9d847] Add linux-next specific files for 20260522
# good: [6779b50faa562e6cca1aa6a4649a4d764c6c7e28] Merge tag 'pci-v7.1-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
git bisect start 'c1ecb239fa3456529a32255359fc78b69eb9d847' '6779b50faa562e6cca1aa6a4649a4d764c6c7e28'
# bad: [c09bb0d7c56d56daed51e374409472c3fece9931] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git
git bisect bad c09bb0d7c56d56daed51e374409472c3fece9931
# bad: [42b0664d6f7b3d04f961e0709811280e52d4d39c] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/geert/linux-m68k.git
git bisect bad 42b0664d6f7b3d04f961e0709811280e52d4d39c
# good: [63775c4b77cf7ac5863ae0e76f8626b80d217562] Merge branch 'mm-nonmm-unstable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
git bisect good 63775c4b77cf7ac5863ae0e76f8626b80d217562
# good: [f437936af5c74fddfdfcd2388a5f1eec2f9a105b] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-omap.git
git bisect good f437936af5c74fddfdfcd2388a5f1eec2f9a105b
# good: [01bf3f256f1ed15fa3884e4893e5a9d78fdfb2d6] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git
git bisect good 01bf3f256f1ed15fa3884e4893e5a9d78fdfb2d6
# bad: [bee649097c8de82d9dccd3660c41163a6507e3d4] Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/jenswi/linux-tee.git
git bisect bad bee649097c8de82d9dccd3660c41163a6507e3d4
# bad: [82ccfdd8dd1e29229a3ed7c95a76578706fc4a1d] Merge branch 'for-next' of https://github.com/sophgo/linux.git
git bisect bad 82ccfdd8dd1e29229a3ed7c95a76578706fc4a1d
# good: [32bc5496b48174dbca1f187f710955ee4d9527a1] firmware: arm_scmi: Validate SENSOR_UPDATE payload size
git bisect good 32bc5496b48174dbca1f187f710955ee4d9527a1
# bad: [64251369d33884b431a52b89c60fabf466f3913c] Merge branches 'for-next/scmi/updates' and 'for-next/ffa/updates' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into for-linux-next
git bisect bad 64251369d33884b431a52b89c60fabf466f3913c
# bad: [70492cfce2a4d41e87bf46989028a90f4bc6b38f] firmware: smccc: Fix Arm SMCCC SOC_ID name call
git bisect bad 70492cfce2a4d41e87bf46989028a90f4bc6b38f
# bad: [7fe2ec9fb8e9a78dad8d6b1e551cb4d126e36f1e] firmware: arm_ffa: Set the core device as FF-A device parent
git bisect bad 7fe2ec9fb8e9a78dad8d6b1e551cb4d126e36f1e
# bad: [e659fc8e537c7a21d5d693d6f30d8852f2fa8d91] firmware: arm_ffa: Register core as a platform driver
git bisect bad e659fc8e537c7a21d5d693d6f30d8852f2fa8d91
# good: [cc7e8f21b9f0c229d68cf19a837cba82b5ac2d87] Revert "firmware: arm_ffa: Change initcall level of ffa_init() to rootfs_initcall"
git bisect good cc7e8f21b9f0c229d68cf19a837cba82b5ac2d87
# first bad commit: [e659fc8e537c7a21d5d693d6f30d8852f2fa8d91] firmware: arm_ffa: Register core as a platform driver
^ permalink raw reply
* [PATCH] ARM: mm: fix kexec and hibernation with CONFIG_CPU_TTBR0_PAN
From: Florian Fainelli @ 2026-05-23 0:08 UTC (permalink / raw)
To: linux-arm-kernel
Cc: bcm-kernel-feedback-list, Florian Fainelli, Catalin Marinas,
Linus Walleij, Russell King, Linus Walleij, Russell King (Oracle),
Kees Cook, open list
Commit 7af5b901e847 ("ARM: 9358/2: Implement PAN for LPAE by TTBR0
page table walks disablement") implemented PAN for LPAE kernels by
setting TTBCR.EPD0 on every kernel entry, disabling TTBR0 page-table
walks while running in kernel mode. The commit correctly updated
cpu_suspend() in arch/arm/kernel/suspend.c, but missed two other code
paths that switch the CPU to the identity mapping before jumping to
low-PA (TTBR0-range) physical addresses:
1. setup_mm_for_reboot() in arch/arm/mm/idmap.c, used by the kexec
reboot path. With TTBCR.EPD0 still set, the subsequent branch to
the identity-mapped cpu_v7_reset causes a PrefetchAbort because the
TTBR0 page-table walk needed to resolve the identity-mapped address
is disabled. This manifests as a hard hang or "bad PC value" panic
on LPAE kernels booted on CPUs that strictly enforce EPD0 for
instruction fetch (e.g. Cortex-A53 in AArch32 mode) while the same
image may accidentally work on Cortex-A15 due to microarchitectural
differences in EPD0 enforcement.
2. arch_restore_image() in arch/arm/kernel/hibernate.c, which calls
cpu_switch_mm(idmap_pgd, &init_mm) directly without going through
setup_mm_for_reboot(), leaving TTBCR.EPD0 set while the identity
mapping is active.
Fix both sites by calling uaccess_save_and_enable() before switching
to the identity mapping, mirroring what the original commit did for
cpu_suspend().
Fixes: 7af5b901e847 ("ARM: 9358/2: Implement PAN for LPAE by TTBR0 page table walks disablement")
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Assisted-by: Cursor:claude-sonnet-4.6
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
arch/arm/kernel/hibernate.c | 10 ++++++++++
arch/arm/mm/idmap.c | 12 ++++++++++++
2 files changed, 22 insertions(+)
diff --git a/arch/arm/kernel/hibernate.c b/arch/arm/kernel/hibernate.c
index 38a90a3d12b2..231a76af09a0 100644
--- a/arch/arm/kernel/hibernate.c
+++ b/arch/arm/kernel/hibernate.c
@@ -21,6 +21,7 @@
#include <asm/suspend.h>
#include <asm/page.h>
#include <asm/sections.h>
+#include <asm/uaccess.h>
#include "reboot.h"
int pfn_is_nosave(unsigned long pfn)
@@ -82,6 +83,15 @@ static void notrace arch_restore_image(void *unused)
{
struct pbe *pbe;
+ /*
+ * With CONFIG_CPU_TTBR0_PAN enabled, TTBCR.EPD0 is set to block
+ * TTBR0 page-table walks. The identity mapping used here lives at
+ * low (user-space) virtual addresses and is only reachable via
+ * TTBR0, so re-enable those walks before switching page tables.
+ * On non-PAN kernels this is a no-op.
+ */
+ if (IS_ENABLED(CONFIG_CPU_TTBR0_PAN))
+ uaccess_save_and_enable();
cpu_switch_mm(idmap_pgd, &init_mm);
for (pbe = restore_pblist; pbe; pbe = pbe->next)
copy_page(pbe->orig_address, pbe->address);
diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c
index 4a833e89782a..70403e968d2a 100644
--- a/arch/arm/mm/idmap.c
+++ b/arch/arm/mm/idmap.c
@@ -11,6 +11,7 @@
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <asm/system_info.h>
+#include <asm/uaccess.h>
/*
* Note: accesses outside of the kernel image and the identity map area
@@ -133,6 +134,17 @@ early_initcall(init_static_idmap);
*/
void setup_mm_for_reboot(void)
{
+ /*
+ * With CONFIG_CPU_TTBR0_PAN enabled, TTBCR.EPD0 is set whenever
+ * user-space access is disabled in order to block TTBR0 page-table
+ * walks. The identity mapping lives at low (user-space) virtual
+ * addresses and can only be reached via TTBR0, so we must re-enable
+ * those walks before switching page tables. On non-PAN kernels this
+ * is a no-op.
+ */
+ if (IS_ENABLED(CONFIG_CPU_TTBR0_PAN))
+ uaccess_save_and_enable();
+
/* Switch to the identity mapping. */
cpu_switch_mm(idmap_pgd, &init_mm);
local_flush_bp_all();
--
2.34.1
^ permalink raw reply related
* [PATCH v4 09/12] crypto: atmel-i2c - move shared client allocation logic to core
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Migrate the I2C client allocation and runtime load-balancing routines out
of the ECC driver code and into the central atmel-i2c core library module.
Export the symmetric lifecycle helper interfaces atmel_i2c_client_alloc()
and atmel_i2c_client_free() using EXPORT_SYMBOL_GPL() to expose a unified
client management API. This consolidation enables the dynamic selection
subsystem (which chooses the least-loaded client device based on the active
transformation count) to be shared by both the ECC driver and upcoming
Atmel crypto modules.
Refactor the ECC driver's transformation context initialization (init_tfm)
and teardown (exit_tfm) paths to use this centralized core API.
No functional change is intended.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 51 +++-----------------------------------
drivers/crypto/atmel-i2c.c | 47 +++++++++++++++++++++++++++++++++++
drivers/crypto/atmel-i2c.h | 3 +++
3 files changed, 53 insertions(+), 48 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index d2490693a198..16e607cd06c4 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -205,51 +205,6 @@ static int atmel_ecdh_compute_shared_secret(struct kpp_request *req)
return ret;
}
-static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
-{
- struct atmel_i2c_client_priv *i2c_priv, *min_i2c_priv = NULL;
- struct i2c_client *client = ERR_PTR(-ENODEV);
- int min_tfm_cnt = INT_MAX;
- int tfm_cnt;
-
- spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
-
- if (list_empty(&atmel_i2c_mgmt.i2c_client_list)) {
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
- return ERR_PTR(-ENODEV);
- }
-
- list_for_each_entry(i2c_priv, &atmel_i2c_mgmt.i2c_client_list,
- i2c_client_list_node) {
- if (!i2c_priv->ready)
- continue;
- tfm_cnt = atomic_read(&i2c_priv->tfm_count);
- if (tfm_cnt < min_tfm_cnt) {
- min_tfm_cnt = tfm_cnt;
- min_i2c_priv = i2c_priv;
- }
- if (!min_tfm_cnt)
- break;
- }
-
- if (min_i2c_priv) {
- atomic_inc(&min_i2c_priv->tfm_count);
- client = min_i2c_priv->client;
- }
-
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
-
- return client;
-}
-
-static void atmel_ecc_i2c_client_free(struct i2c_client *client)
-{
- struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
-
- if (atomic_dec_and_test(&i2c_priv->tfm_count))
- complete(&i2c_priv->remove_done);
-}
-
static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
{
const char *alg = kpp_alg_name(tfm);
@@ -257,7 +212,7 @@ static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
ctx->curve_id = ECC_CURVE_NIST_P256;
- ctx->client = atmel_ecc_i2c_client_alloc();
+ ctx->client = atmel_i2c_client_alloc();
if (IS_ERR(ctx->client)) {
pr_err("tfm - i2c_client binding failed\n");
return PTR_ERR(ctx->client);
@@ -267,7 +222,7 @@ static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
if (IS_ERR(fallback)) {
dev_err(&ctx->client->dev, "Failed to allocate transformation for '%s': %ld\n",
alg, PTR_ERR(fallback));
- atmel_ecc_i2c_client_free(ctx->client);
+ atmel_i2c_client_free(ctx->client);
return PTR_ERR(fallback);
}
@@ -284,7 +239,7 @@ static void atmel_ecdh_exit_tfm(struct crypto_kpp *tfm)
kfree(ctx->public_key);
if (ctx->fallback)
crypto_free_kpp(ctx->fallback);
- atmel_ecc_i2c_client_free(ctx->client);
+ atmel_i2c_client_free(ctx->client);
}
static unsigned int atmel_ecdh_max_size(struct crypto_kpp *tfm)
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index db818ce55033..92d3e28f9d9a 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -57,6 +57,53 @@ static void atmel_i2c_checksum(struct atmel_i2c_cmd *cmd)
*__crc16 = cpu_to_le16(bitrev16(crc16(0, data, len)));
}
+struct i2c_client *atmel_i2c_client_alloc(void)
+{
+ struct atmel_i2c_client_priv *i2c_priv, *min_i2c_priv = NULL;
+ struct i2c_client *client = ERR_PTR(-ENODEV);
+ int min_tfm_cnt = INT_MAX;
+ int tfm_cnt;
+
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+
+ if (list_empty(&atmel_i2c_mgmt.i2c_client_list)) {
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ return ERR_PTR(-ENODEV);
+ }
+
+ list_for_each_entry(i2c_priv, &atmel_i2c_mgmt.i2c_client_list,
+ i2c_client_list_node) {
+ if (!i2c_priv->ready)
+ continue;
+ tfm_cnt = atomic_read(&i2c_priv->tfm_count);
+ if (tfm_cnt < min_tfm_cnt) {
+ min_tfm_cnt = tfm_cnt;
+ min_i2c_priv = i2c_priv;
+ }
+ if (!min_tfm_cnt)
+ break;
+ }
+
+ if (min_i2c_priv) {
+ atomic_inc(&min_i2c_priv->tfm_count);
+ client = min_i2c_priv->client;
+ }
+
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
+ return client;
+}
+EXPORT_SYMBOL_GPL(atmel_i2c_client_alloc);
+
+void atmel_i2c_client_free(struct i2c_client *client)
+{
+ struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+
+ if (atomic_dec_and_test(&i2c_priv->tfm_count))
+ complete(&i2c_priv->remove_done);
+}
+EXPORT_SYMBOL_GPL(atmel_i2c_client_free);
+
void atmel_i2c_init_read_config_cmd(struct atmel_i2c_cmd *cmd)
{
cmd->word_addr = COMMAND;
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 07fd2248e20b..ddab80bc1a72 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -193,6 +193,9 @@ void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid);
int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
struct scatterlist *pubkey);
+struct i2c_client *atmel_i2c_client_alloc(void);
+void atmel_i2c_client_free(struct i2c_client *client);
+
void atmel_i2c_deactivate_client(struct atmel_i2c_client_priv *i2c_priv);
void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv);
--
2.39.5
^ permalink raw reply related
* [PATCH v4 05/12] crypto: atmel - rename atmel_ecc_driver_data to atmel_i2c_client_mgmt
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Rename struct atmel_ecc_driver_data to atmel_i2c_client_mgmt to reflect its
generic role in shared I2C client tracking and locking. A subsequent change
will move the client management infrastructure into the atmel-i2c core
driver.
No functional changes intended.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 2 +-
drivers/crypto/atmel-i2c.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index d12a9dbe45a7..d6ae113c45df 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -28,7 +28,7 @@ static int atmel_ecc_kpp_refcnt;
DECLARE_COMPLETION(atmel_ecc_unreg_done);
static bool atmel_ecc_unreg_active;
-static struct atmel_ecc_driver_data atmel_i2c_mgmt;
+static struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
/**
* struct atmel_ecdh_ctx - transformation context
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index b320559e50eb..660ca861b705 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -115,7 +115,7 @@ struct atmel_i2c_cmd {
#define ECDH_PREFIX_MODE 0x00
/* Used for binding tfm objects to i2c clients. */
-struct atmel_ecc_driver_data {
+struct atmel_i2c_client_mgmt {
struct list_head i2c_client_list;
spinlock_t i2c_list_lock;
} ____cacheline_aligned;
--
2.39.5
^ permalink raw reply related
* [PATCH v2] ARM: OMAP2+: Add CFI type for omap4_finish_suspend
From: Mithil Bavishi @ 2026-05-22 23:30 UTC (permalink / raw)
To: Aaro Koskinen, Andreas Kemnade, Kevin Hilman, Roger Quadros,
Tony Lindgren, Russell King
Cc: Sami Tolvanen, Kees Cook, Nathan Chancellor, linux-arm-kernel,
linux-omap, llvm, linux-kernel, Mithil Bavishi
With CONFIG_CFI enabled, OMAP4 can trap in omap4_enter_lowpower()
because omap_pm_ops.finish_suspend points directly to the assembly
routine omap4_finish_suspend, which lacks the expected KCFI type
metadata.
Annotate omap4_finish_suspend with SYM_TYPED_FUNC_START so the assembly
routine carries the KCFI type metadata.
Signed-off-by: Mithil Bavishi <bavishimithil@gmail.com>
---
arch/arm/mach-omap2/sleep44xx.S | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
index f09c91978..0b48df6fc 100644
--- a/arch/arm/mach-omap2/sleep44xx.S
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -58,7 +58,7 @@
* stack frame and it expects the caller to take care of it. Hence the entire
* stack frame is saved to avoid possible stack corruption.
*/
-ENTRY(omap4_finish_suspend)
+SYM_TYPED_FUNC_START(omap4_finish_suspend)
stmfd sp!, {r4-r12, lr}
cmp r0, #0x0
beq do_WFI @ No lowpower state, jump to WFI
@@ -223,7 +223,7 @@ skip_scu_gp_clear:
isb
dsb
ldmfd sp!, {r4-r12, pc}
-ENDPROC(omap4_finish_suspend)
+SYM_FUNC_END(omap4_finish_suspend)
/*
* ============================
--
2.43.0
^ permalink raw reply related
* [PATCH v3 03/11] m68k: mcf5441x: initialize DAC clocks by iio DAC driver name
From: Angelo Dureghello @ 2026-05-22 21:20 UTC (permalink / raw)
To: Greg Ungerer, Geert Uytterhoeven, Steven King, Arnd Bergmann,
Maxime Coquelin, Alexandre Torgue, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko
Cc: Greg Ungerer, linux-m68k, linux-kernel, linux-stm32,
linux-arm-kernel, linux-iio, Angelo Dureghello
In-Reply-To: <20260522-wip-stmark2-dac-v3-0-16be0ad35a67@baylibre.com>
From: Angelo Dureghello <adureghello@baylibre.com>
Later in this patchset, the mcf54415 DAC driver is added.
Considering some other different ColdFire cpu DACs exists, the DAC driver
is named as "mcf54415_dac", related to the mcf5441x family SoCs with
DACs (mcf54415/6/7/8).
So updating DAC CLKDEDV_INIT with proper driver name.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
Changes in v2:
- none
Changes in v3
- roll back clock names as they was originally
---
arch/m68k/coldfire/m5441x.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/m68k/coldfire/m5441x.c b/arch/m68k/coldfire/m5441x.c
index 5b5e09ecf487..b02ca2eb55a4 100644
--- a/arch/m68k/coldfire/m5441x.c
+++ b/arch/m68k/coldfire/m5441x.c
@@ -106,8 +106,8 @@ static struct clk_lookup m5411x_clk_lookup[] = {
CLKDEV_INIT("mcfpit.3", NULL, &__clk_0_35),
CLKDEV_INIT("mcfeport.0", NULL, &__clk_0_36),
CLKDEV_INIT("mcfadc.0", NULL, &__clk_0_37),
- CLKDEV_INIT("mcfdac.0", NULL, &__clk_0_38),
- CLKDEV_INIT("mcfdac.1", NULL, &__clk_0_39),
+ CLKDEV_INIT("mcf54415_dac.0", NULL, &__clk_0_38),
+ CLKDEV_INIT("mcf54415_dac.1", NULL, &__clk_0_39),
CLKDEV_INIT("mcfrtc.0", NULL, &__clk_0_42),
CLKDEV_INIT("mcfsim.0", NULL, &__clk_0_43),
CLKDEV_INIT("mcfusb-otg.0", NULL, &__clk_0_44),
--
2.54.0
^ permalink raw reply related
* [PATCH v9 20/23] perf python: Add syscall name/id to convert syscall number and name
From: Ian Rogers @ 2026-05-22 22:04 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark, leo.yan,
linux-arm-kernel, linux-kernel, linux-perf-users, mingo, peterz,
tmricht
In-Reply-To: <20260522220435.2378363-1-irogers@google.com>
Use perf's syscalltbl support to convert syscall number to name
assuming the number is for the host machine. This avoids python
libaudit support as tools/perf/scripts/python/syscall-counts.py
requires.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
v2:
1. Guarded with HAVE_LIBTRACEEVENT : Wrapped the syscall functions and
their entries in perf__methods with #ifdef HAVE_LIBTRACEEVENT to
avoid potential linker errors if CONFIG_TRACE is disabled.
2. Changed Exception Type: Updated pyrf__syscall_id to raise a
ValueError instead of a TypeError when a syscall is not found.
3. Fixed Typo: Corrected "name number" to "name" in the docstring for
syscall_id.
v6:
- Added optional keyword-only `elf_machine` argument to `syscall_name`
and `syscall_id`.
v7:
- Made syscalltbl.o unconditional in Build to fix undefined symbol
when building without libtraceevent.
---
tools/perf/util/Build | 1 -
tools/perf/util/python.c | 48 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 4bbc78b1f741..e37779f9dfe4 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -75,7 +75,6 @@ perf-util-y += sample.o
perf-util-y += sample-raw.o
perf-util-y += s390-sample-raw.o
perf-util-y += amd-sample-raw.o
-
perf-util-y += ordered-events.o
perf-util-y += namespaces.o
perf-util-y += comm.o
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 218d772346e2..23eb65adb392 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -18,6 +18,7 @@
#include "data.h"
#include "debug.h"
#include "dso.h"
+#include "dwarf-regs.h"
#include "event.h"
#include "branch.h"
#include "evlist.h"
@@ -35,6 +36,7 @@
#include "srcline.h"
#include "strbuf.h"
#include "symbol.h"
+#include "trace/beauty/syscalltbl.h"
#include "thread.h"
#include "thread_map.h"
#include "tool.h"
@@ -3792,6 +3794,40 @@ static int pyrf_session__setup_types(void)
return PyType_Ready(&pyrf_session__type);
}
+static PyObject *pyrf__syscall_name(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *name;
+ int id;
+ int elf_machine = EM_HOST;
+ static char * const kwlist[] = { "id", "elf_machine", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|$i", kwlist, &id, &elf_machine))
+ return NULL;
+
+ name = syscalltbl__name(elf_machine, id);
+ if (!name)
+ Py_RETURN_NONE;
+ return PyUnicode_FromString(name);
+}
+
+static PyObject *pyrf__syscall_id(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *name;
+ int id;
+ int elf_machine = EM_HOST;
+ static char * const kwlist[] = { "name", "elf_machine", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|$i", kwlist, &name, &elf_machine))
+ return NULL;
+
+ id = syscalltbl__id(elf_machine, name);
+ if (id < 0) {
+ PyErr_Format(PyExc_ValueError, "Failed to find syscall %s", name);
+ return NULL;
+ }
+ return PyLong_FromLong(id);
+}
+
static PyMethodDef perf__methods[] = {
{
.ml_name = "metrics",
@@ -3825,6 +3861,18 @@ static PyMethodDef perf__methods[] = {
.ml_flags = METH_NOARGS,
.ml_doc = PyDoc_STR("Returns a sequence of pmus.")
},
+ {
+ .ml_name = "syscall_name",
+ .ml_meth = (PyCFunction) pyrf__syscall_name,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
+ .ml_doc = PyDoc_STR("Turns a syscall number to a string.")
+ },
+ {
+ .ml_name = "syscall_id",
+ .ml_meth = (PyCFunction) pyrf__syscall_id,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
+ .ml_doc = PyDoc_STR("Turns a syscall name to a number.")
+ },
{ .ml_name = NULL, }
};
--
2.54.0.794.g4f17f83d09-goog
^ permalink raw reply related
* [PATCH v4 06/12] crypto: atmel-i2c - move client management instance into core
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Move the global 'atmel_i2c_mgmt' tracking instance out of the ECC driver
and into the atmel-i2c core library.
This change consolidates the shared I2C client infrastructure into a
central core driver. This centralization allows both the ECC and
upcoming SHA204A driver modules to access and reference a unified,
common device-management context.
As part of this relocation, replace the explicit runtime initialization
calls inside the module init block with static, compile-time macros
(__SPIN_LOCK_UNLOCKED and LIST_HEAD_INIT). Export the tracking structure
via EXPORT_SYMBOL_GPL() to make it available to dependent sub-modules.
No functional change intended.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 4 ----
drivers/crypto/atmel-i2c.c | 6 ++++++
drivers/crypto/atmel-i2c.h | 1 +
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index d6ae113c45df..1ae9c52812df 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -28,8 +28,6 @@ static int atmel_ecc_kpp_refcnt;
DECLARE_COMPLETION(atmel_ecc_unreg_done);
static bool atmel_ecc_unreg_active;
-static struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
-
/**
* struct atmel_ecdh_ctx - transformation context
* @client : pointer to i2c client device
@@ -458,8 +456,6 @@ static struct i2c_driver atmel_ecc_driver = {
static int __init atmel_ecc_init(void)
{
- spin_lock_init(&atmel_i2c_mgmt.i2c_list_lock);
- INIT_LIST_HEAD(&atmel_i2c_mgmt.i2c_client_list);
return i2c_add_driver(&atmel_ecc_driver);
}
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index ff19857894d0..a42b0ea30033 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -21,6 +21,12 @@
#include <linux/workqueue.h>
#include "atmel-i2c.h"
+struct atmel_i2c_client_mgmt atmel_i2c_mgmt = {
+ .i2c_list_lock = __SPIN_LOCK_UNLOCKED(atmel_i2c_mgmt.i2c_list_lock),
+ .i2c_client_list = LIST_HEAD_INIT(atmel_i2c_mgmt.i2c_client_list),
+};
+EXPORT_SYMBOL_GPL(atmel_i2c_mgmt);
+
static const struct {
u8 value;
const char *error_text;
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 660ca861b705..82321c35c21f 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -119,6 +119,7 @@ struct atmel_i2c_client_mgmt {
struct list_head i2c_client_list;
spinlock_t i2c_list_lock;
} ____cacheline_aligned;
+extern struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
/**
* atmel_i2c_client_priv - i2c_client private data
--
2.39.5
^ permalink raw reply related
* [PATCH v4 07/12] crypto: atmel-i2c - introduce shared teardown helpers and fix queue flush
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Introduce atmel_i2c_deactivate_client() and atmel_i2c_unregister_client()
helpers in the atmel-i2c core library to modularize client teardown. This
encapsulates common client state tracking and list manipulation operations.
Convert the ECC driver's error recovery and device removal paths to utilize
these new helpers, ensuring consistent execution ordering when modifying
device-readiness states and deleting linked-list nodes.
Additionally, migrate the atmel_i2c_flush_queue() call out of the module
exit path. It now runs inside the core unregistration helper. Export both
new tracking symbols via EXPORT_SYMBOL_GPL() to match the existing core
driver licensing standard.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 25 ++++++-------------------
drivers/crypto/atmel-i2c.c | 20 ++++++++++++++++++++
drivers/crypto/atmel-i2c.h | 3 +++
3 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 1ae9c52812df..e6d3e6574251 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -351,12 +351,8 @@ static int atmel_ecc_probe(struct i2c_client *client)
msecs_to_jiffies(2000));
mutex_lock(&atmel_ecc_kpp_lock);
if (timeout == 0) {
- spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
- i2c_priv->ready = false;
- list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
- mutex_unlock(&atmel_ecc_kpp_lock);
-
+ atmel_i2c_deactivate_client(i2c_priv);
+ atmel_i2c_unregister_client(i2c_priv);
dev_err(&client->dev, "probe timed out, former driver instance not fully deregistered\n");
return -ETIMEDOUT;
}
@@ -365,12 +361,8 @@ static int atmel_ecc_probe(struct i2c_client *client)
if (atmel_ecc_kpp_refcnt == 0) {
ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
if (ret) {
- spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
- i2c_priv->ready = false;
- list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
- mutex_unlock(&atmel_ecc_kpp_lock);
-
+ atmel_i2c_deactivate_client(i2c_priv);
+ atmel_i2c_unregister_client(i2c_priv);
dev_err(&client->dev, "%s alg registration failed\n",
atmel_ecdh_nist_p256.base.cra_driver_name);
return ret;
@@ -388,9 +380,7 @@ static void atmel_ecc_remove(struct i2c_client *client)
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
bool trigger_unreg = false;
- spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
- i2c_priv->ready = false;
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ atmel_i2c_deactivate_client(i2c_priv);
/*
* The Linux crypto core automatically blocks until all active
@@ -410,9 +400,7 @@ static void atmel_ecc_remove(struct i2c_client *client)
if (atomic_read(&i2c_priv->tfm_count))
wait_for_completion(&i2c_priv->remove_done);
- spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
- list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+ atmel_i2c_unregister_client(i2c_priv);
/*
* The driver registers once an algorithm, but maintains a list of
@@ -461,7 +449,6 @@ static int __init atmel_ecc_init(void)
static void __exit atmel_ecc_exit(void)
{
- atmel_i2c_flush_queue();
i2c_del_driver(&atmel_ecc_driver);
}
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index a42b0ea30033..db818ce55033 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -354,6 +354,26 @@ static int device_sanity_check(struct i2c_client *client)
return ret;
}
+void atmel_i2c_deactivate_client(struct atmel_i2c_client_priv *i2c_priv)
+{
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ i2c_priv->ready = false;
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+}
+EXPORT_SYMBOL_GPL(atmel_i2c_deactivate_client);
+
+void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv)
+{
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ if (!list_empty(&i2c_priv->i2c_client_list_node))
+ list_del_init(&i2c_priv->i2c_client_list_node);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
+ /* don't sleep inside spin locks */
+ atmel_i2c_flush_queue();
+}
+EXPORT_SYMBOL_GPL(atmel_i2c_unregister_client);
+
int atmel_i2c_probe(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv;
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 82321c35c21f..07fd2248e20b 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -193,4 +193,7 @@ void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid);
int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
struct scatterlist *pubkey);
+void atmel_i2c_deactivate_client(struct atmel_i2c_client_priv *i2c_priv);
+void atmel_i2c_unregister_client(struct atmel_i2c_client_priv *i2c_priv);
+
#endif /* __ATMEL_I2C_H__ */
--
2.39.5
^ permalink raw reply related
* [PATCH v4 10/12] crypto: atmel-i2c - implement capability-based client selection
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Extend the shared I2C client allocation interface to support feature-aware
hardware selection by introducing capability filtering.
Add a 'caps' mask to 'struct atmel_i2c_client_priv' alongside an
'atmel_i2c_capability' enum. The allocator now explicitly filters hardware
nodes by a requested capability bit while retaining the least-loaded device
load-balancing scheme.
Update the ECC driver to advertise ATMEL_CAP_ECDH configuration capability
during probe, and adapt the tfm context setup execution path to request
this specific capability variant. Initialize the bitmask field to zero
inside the SHA204A driver context for now.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 4 +++-
drivers/crypto/atmel-i2c.c | 6 +++++-
drivers/crypto/atmel-i2c.h | 8 +++++++-
drivers/crypto/atmel-sha204a.c | 2 ++
4 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 16e607cd06c4..76b8f9e7c2e1 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -212,7 +212,7 @@ static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
ctx->curve_id = ECC_CURVE_NIST_P256;
- ctx->client = atmel_i2c_client_alloc();
+ ctx->client = atmel_i2c_client_alloc(ATMEL_CAP_ECDH);
if (IS_ERR(ctx->client)) {
pr_err("tfm - i2c_client binding failed\n");
return PTR_ERR(ctx->client);
@@ -287,6 +287,8 @@ static int atmel_ecc_probe(struct i2c_client *client)
i2c_priv = i2c_get_clientdata(client);
i2c_priv->ready = false;
+ i2c_priv->caps = BIT(ATMEL_CAP_ECDH);
+
spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
&atmel_i2c_mgmt.i2c_client_list);
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 92d3e28f9d9a..4953b8fcb02d 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -57,7 +57,7 @@ static void atmel_i2c_checksum(struct atmel_i2c_cmd *cmd)
*__crc16 = cpu_to_le16(bitrev16(crc16(0, data, len)));
}
-struct i2c_client *atmel_i2c_client_alloc(void)
+struct i2c_client *atmel_i2c_client_alloc(enum atmel_i2c_capability cap)
{
struct atmel_i2c_client_priv *i2c_priv, *min_i2c_priv = NULL;
struct i2c_client *client = ERR_PTR(-ENODEV);
@@ -75,6 +75,10 @@ struct i2c_client *atmel_i2c_client_alloc(void)
i2c_client_list_node) {
if (!i2c_priv->ready)
continue;
+
+ if (!(i2c_priv->caps & BIT(cap)))
+ continue;
+
tfm_cnt = atomic_read(&i2c_priv->tfm_count);
if (tfm_cnt < min_tfm_cnt) {
min_tfm_cnt = tfm_cnt;
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index ddab80bc1a72..af2e49332ab6 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -115,6 +115,10 @@ struct atmel_i2c_cmd {
#define ECDH_PREFIX_MODE 0x00
/* Used for binding tfm objects to i2c clients. */
+enum atmel_i2c_capability {
+ ATMEL_CAP_ECDH = 0,
+};
+
struct atmel_i2c_client_mgmt {
struct list_head i2c_client_list;
spinlock_t i2c_list_lock;
@@ -131,6 +135,7 @@ extern struct atmel_i2c_client_mgmt atmel_i2c_mgmt;
* @tfm_count : number of active crypto transformations on i2c client
* @hwrng : hold the hardware generated rng
* @ready : hw client is ready to use
+ * @caps : feature capability of the particular driver
*
* Reads and writes from/to the i2c client are sequential. The first byte
* transmitted to the device is treated as the byte size. Any attempt to send
@@ -149,6 +154,7 @@ struct atmel_i2c_client_priv {
struct hwrng hwrng;
struct completion remove_done;
bool ready;
+ u32 caps;
};
/**
@@ -193,7 +199,7 @@ void atmel_i2c_init_genkey_cmd(struct atmel_i2c_cmd *cmd, u16 keyid);
int atmel_i2c_init_ecdh_cmd(struct atmel_i2c_cmd *cmd,
struct scatterlist *pubkey);
-struct i2c_client *atmel_i2c_client_alloc(void);
+struct i2c_client *atmel_i2c_client_alloc(enum atmel_i2c_capability cap);
void atmel_i2c_client_free(struct i2c_client *client);
void atmel_i2c_deactivate_client(struct atmel_i2c_client_priv *i2c_priv);
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 33e5a66b843c..0c5b5cdbfcbc 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -178,6 +178,8 @@ static int atmel_sha204a_probe(struct i2c_client *client)
i2c_priv = i2c_get_clientdata(client);
+ i2c_priv->caps = 0;
+
memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
i2c_priv->hwrng.name = dev_name(&client->dev);
--
2.39.5
^ permalink raw reply related
* [PATCH v4 11/12] crypto: atmel-sha204a - integrate into core management tracking
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Register the SHA204A I2C device instance into the shared atmel_i2c client
management tracking list during the probe phase. This allows the driver to
participate in the central hardware selection infrastructure.
Rework the error-unwind paths inside atmel_sha204a_probe() to prevent stale
entries from remaining in the global tracking structures if a partial
initialization failure occurs. If sysfs group creation fails, explicitly
trigger devm_hwrng_unregister() to preserve the strict lifecycle ordering
introduced in previous stability fixes.
Convert the removal path to use the core teardown helpers. Ensure the
device readiness state is deactivated using atmel_i2c_deactivate_client()
and the tracking node is removed via atmel_i2c_unregister_client() before
local memory resources are freed. This guarantees that any in-flight work
queue items are unconditionally flushed, eliminating a potential
Use-After-Free (UAF) window during device removal.
No functional change intended beyond improved lifecycle handling.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-sha204a.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 0c5b5cdbfcbc..86a68f2a27e0 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -177,9 +177,15 @@ static int atmel_sha204a_probe(struct i2c_client *client)
return ret;
i2c_priv = i2c_get_clientdata(client);
+ i2c_priv->ready = false;
i2c_priv->caps = 0;
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ list_add_tail(&i2c_priv->i2c_client_list_node,
+ &atmel_i2c_mgmt.i2c_client_list);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
memset(&i2c_priv->hwrng, 0, sizeof(i2c_priv->hwrng));
i2c_priv->hwrng.name = dev_name(&client->dev);
@@ -192,15 +198,26 @@ static int atmel_sha204a_probe(struct i2c_client *client)
ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
if (ret) {
dev_err(&client->dev, "failed to register RNG (%d)\n", ret);
- return ret;
+ goto err_list_del;
}
ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups);
if (ret) {
dev_err(&client->dev, "failed to create sysfs group (%d)\n", ret);
- return ret;
+ goto err_hwrng_unregister;
}
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
+ i2c_priv->ready = true;
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
+
+ return 0;
+
+err_hwrng_unregister:
+ devm_hwrng_unregister(&client->dev, &i2c_priv->hwrng);
+err_list_del:
+ atmel_i2c_unregister_client(i2c_priv);
+
return ret;
}
@@ -208,9 +225,11 @@ static void atmel_sha204a_remove(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+ atmel_i2c_deactivate_client(i2c_priv);
+
sysfs_remove_group(&client->dev.kobj, &atmel_sha204a_groups);
devm_hwrng_unregister(&client->dev, &i2c_priv->hwrng);
- atmel_i2c_flush_queue();
+ atmel_i2c_unregister_client(i2c_priv);
kfree((void *)i2c_priv->hwrng.priv);
}
@@ -245,7 +264,6 @@ static int __init atmel_sha204a_init(void)
static void __exit atmel_sha204a_exit(void)
{
- atmel_i2c_flush_queue();
i2c_del_driver(&atmel_sha204a_driver);
}
--
2.39.5
^ permalink raw reply related
* [PATCH v4 08/12] crypto: atmel-ecc - switch to module_i2c_driver
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Remove custom boilerplate module configuration code and convert the module
init/exit paths to use the modern module_i2c_driver() helper macro.
This shortens and simplifies driver initialization. Custom structure setup
is no longer required here since management tracking context initialization
was already safely moved into the atmel-i2c core library module.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index e6d3e6574251..d2490693a198 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -442,18 +442,7 @@ static struct i2c_driver atmel_ecc_driver = {
.id_table = atmel_ecc_id,
};
-static int __init atmel_ecc_init(void)
-{
- return i2c_add_driver(&atmel_ecc_driver);
-}
-
-static void __exit atmel_ecc_exit(void)
-{
- i2c_del_driver(&atmel_ecc_driver);
-}
-
-module_init(atmel_ecc_init);
-module_exit(atmel_ecc_exit);
+module_i2c_driver(atmel_ecc_driver);
MODULE_AUTHOR("Tudor Ambarus");
MODULE_DESCRIPTION("Microchip / Atmel ECC (I2C) driver");
--
2.39.5
^ permalink raw reply related
* [PATCH v4 04/12] crypto: atmel-ecc - rename driver_data before moving it into atmel-i2c
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Rename the local driver_data instance to atmel_i2c_mgmt in
preparation for moving the shared I2C client management
infrastructure into the atmel-i2c core driver in a subsequent
change.
No functional changes intended.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 39 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 20 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 005a9a3d919c..d12a9dbe45a7 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -28,7 +28,7 @@ static int atmel_ecc_kpp_refcnt;
DECLARE_COMPLETION(atmel_ecc_unreg_done);
static bool atmel_ecc_unreg_active;
-static struct atmel_ecc_driver_data driver_data;
+static struct atmel_ecc_driver_data atmel_i2c_mgmt;
/**
* struct atmel_ecdh_ctx - transformation context
@@ -214,14 +214,14 @@ static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
int min_tfm_cnt = INT_MAX;
int tfm_cnt;
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
- if (list_empty(&driver_data.i2c_client_list)) {
- spin_unlock(&driver_data.i2c_list_lock);
+ if (list_empty(&atmel_i2c_mgmt.i2c_client_list)) {
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
return ERR_PTR(-ENODEV);
}
- list_for_each_entry(i2c_priv, &driver_data.i2c_client_list,
+ list_for_each_entry(i2c_priv, &atmel_i2c_mgmt.i2c_client_list,
i2c_client_list_node) {
if (!i2c_priv->ready)
continue;
@@ -239,7 +239,7 @@ static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
client = min_i2c_priv->client;
}
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
return client;
}
@@ -334,11 +334,11 @@ static int atmel_ecc_probe(struct i2c_client *client)
i2c_priv = i2c_get_clientdata(client);
i2c_priv->ready = false;
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
- &driver_data.i2c_client_list);
+ &atmel_i2c_mgmt.i2c_client_list);
i2c_priv->ready = true;
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
mutex_lock(&atmel_ecc_kpp_lock);
/*
@@ -352,12 +352,11 @@ static int atmel_ecc_probe(struct i2c_client *client)
timeout = wait_for_completion_timeout(&atmel_ecc_unreg_done,
msecs_to_jiffies(2000));
mutex_lock(&atmel_ecc_kpp_lock);
-
if (timeout == 0) {
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
i2c_priv->ready = false;
list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
mutex_unlock(&atmel_ecc_kpp_lock);
dev_err(&client->dev, "probe timed out, former driver instance not fully deregistered\n");
@@ -368,10 +367,10 @@ static int atmel_ecc_probe(struct i2c_client *client)
if (atmel_ecc_kpp_refcnt == 0) {
ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
if (ret) {
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
i2c_priv->ready = false;
list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
mutex_unlock(&atmel_ecc_kpp_lock);
dev_err(&client->dev, "%s alg registration failed\n",
@@ -391,9 +390,9 @@ static void atmel_ecc_remove(struct i2c_client *client)
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
bool trigger_unreg = false;
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
i2c_priv->ready = false;
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
/*
* The Linux crypto core automatically blocks until all active
@@ -413,9 +412,9 @@ static void atmel_ecc_remove(struct i2c_client *client)
if (atomic_read(&i2c_priv->tfm_count))
wait_for_completion(&i2c_priv->remove_done);
- spin_lock(&driver_data.i2c_list_lock);
+ spin_lock(&atmel_i2c_mgmt.i2c_list_lock);
list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&driver_data.i2c_list_lock);
+ spin_unlock(&atmel_i2c_mgmt.i2c_list_lock);
/*
* The driver registers once an algorithm, but maintains a list of
@@ -459,8 +458,8 @@ static struct i2c_driver atmel_ecc_driver = {
static int __init atmel_ecc_init(void)
{
- spin_lock_init(&driver_data.i2c_list_lock);
- INIT_LIST_HEAD(&driver_data.i2c_client_list);
+ spin_lock_init(&atmel_i2c_mgmt.i2c_list_lock);
+ INIT_LIST_HEAD(&atmel_i2c_mgmt.i2c_client_list);
return i2c_add_driver(&atmel_ecc_driver);
}
--
2.39.5
^ permalink raw reply related
* [PATCH v4 12/12] crypto: atmel-sha204a - switch to module_i2c_driver
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Replace explicit module init and exit boilerplate functions with the
module_i2c_driver() macro helper to simplify the driver registration
path.
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-sha204a.c | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 86a68f2a27e0..74f91e176713 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -257,18 +257,7 @@ static struct i2c_driver atmel_sha204a_driver = {
.driver.of_match_table = atmel_sha204a_dt_ids,
};
-static int __init atmel_sha204a_init(void)
-{
- return i2c_add_driver(&atmel_sha204a_driver);
-}
-
-static void __exit atmel_sha204a_exit(void)
-{
- i2c_del_driver(&atmel_sha204a_driver);
-}
-
-module_init(atmel_sha204a_init);
-module_exit(atmel_sha204a_exit);
+module_i2c_driver(atmel_sha204a_driver);
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
MODULE_DESCRIPTION("Microchip / Atmel SHA204A (I2C) driver");
--
2.39.5
^ permalink raw reply related
* [PATCH v4 02/12] crypto: atmel-ecc - fix multi-device kpp registration
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
When multiple atmel-ecc hardware accelerator chips are attached to the
same host, registering the same static kpp_alg structure multiple times
corrupts internal fields used by the crypto core's algorithm list. This
leads to immediate list corruption or kernel panics.
Additionally, removing an individual device via sysfs while active crypto
transformations (TFMs) are running triggers a use-after-free (UAF) bug.
Because the device driver core lacks unbind error-handling paths, the
underlying memory allocated via devm for the i2c_priv structure is freed
unconditionally, leaving active transformation context pointers dangling.
Fix these problems by implementing a centralized subsystem tracking matrix:
1. Introduce a global subsystem mutex and reference counter to ensure
that the static 'atmel_ecdh_nist_p256' structure is only registered by
the first probing device, and unregistered exclusively when the last
supporting device unbinds.
2. Maintain per-device allocation tracking with 'tfm_count'. On remove,
mark the device unready to halt load-balancing assignments, and block
via a completion barrier until all pending transformation contexts bound
to that specific physical hardware are freed.
3. Fix a critical re-registration race where a high-velocity unbind and
subsequent re-probe cycles occur while crypto core asynchronous users
are still purging. Establish a global 'atmel_ecc_unreg_active' state
fence to force concurrent probing threads to execute a 2-second timeout
bounded wait_for_completion_timeout() rather than unsafely mutating
static lists.
Fixes: 11105693fa05 ("crypto: atmel-ecc - introduce Microchip / Atmel ECC driver")
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 107 ++++++++++++++++++++++++++++---------
drivers/crypto/atmel-i2c.h | 1 +
2 files changed, 82 insertions(+), 26 deletions(-)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 94360d29f9f9..005a9a3d919c 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -23,6 +23,11 @@
#include <crypto/kpp.h>
#include "atmel-i2c.h"
+static DEFINE_MUTEX(atmel_ecc_kpp_lock);
+static int atmel_ecc_kpp_refcnt;
+DECLARE_COMPLETION(atmel_ecc_unreg_done);
+static bool atmel_ecc_unreg_active;
+
static struct atmel_ecc_driver_data driver_data;
/**
@@ -243,7 +248,8 @@ static void atmel_ecc_i2c_client_free(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
- atomic_dec(&i2c_priv->tfm_count);
+ if (atomic_dec_and_test(&i2c_priv->tfm_count))
+ complete(&i2c_priv->remove_done);
}
static int atmel_ecdh_init_tfm(struct crypto_kpp *tfm)
@@ -278,7 +284,8 @@ static void atmel_ecdh_exit_tfm(struct crypto_kpp *tfm)
struct atmel_ecdh_ctx *ctx = kpp_tfm_ctx(tfm);
kfree(ctx->public_key);
- crypto_free_kpp(ctx->fallback);
+ if (ctx->fallback)
+ crypto_free_kpp(ctx->fallback);
atmel_ecc_i2c_client_free(ctx->client);
}
@@ -317,6 +324,7 @@ static struct kpp_alg atmel_ecdh_nist_p256 = {
static int atmel_ecc_probe(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv;
+ unsigned long timeout;
int ret;
ret = atmel_i2c_probe(client);
@@ -332,50 +340,97 @@ static int atmel_ecc_probe(struct i2c_client *client)
i2c_priv->ready = true;
spin_unlock(&driver_data.i2c_list_lock);
- ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
- if (ret) {
- spin_lock(&driver_data.i2c_list_lock);
- i2c_priv->ready = false;
- list_del(&i2c_priv->i2c_client_list_node);
- spin_unlock(&driver_data.i2c_list_lock);
+ mutex_lock(&atmel_ecc_kpp_lock);
+ /*
+ * For cases where the same/last such device is still in unregistering,
+ * and now re-registering (refcnt is 0, but completion still exists).
+ * Safely capture the pointer, drop the lock and sleep until it
+ * terminates upon completion or retry limit reached.
+ */
+ while (atmel_ecc_unreg_active) {
+ mutex_unlock(&atmel_ecc_kpp_lock);
+ timeout = wait_for_completion_timeout(&atmel_ecc_unreg_done,
+ msecs_to_jiffies(2000));
+ mutex_lock(&atmel_ecc_kpp_lock);
+
+ if (timeout == 0) {
+ spin_lock(&driver_data.i2c_list_lock);
+ i2c_priv->ready = false;
+ list_del(&i2c_priv->i2c_client_list_node);
+ spin_unlock(&driver_data.i2c_list_lock);
+ mutex_unlock(&atmel_ecc_kpp_lock);
+
+ dev_err(&client->dev, "probe timed out, former driver instance not fully deregistered\n");
+ return -ETIMEDOUT;
+ }
+ }
- dev_err(&client->dev, "%s alg registration failed\n",
- atmel_ecdh_nist_p256.base.cra_driver_name);
- return ret;
- } else {
- dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
+ if (atmel_ecc_kpp_refcnt == 0) {
+ ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
+ if (ret) {
+ spin_lock(&driver_data.i2c_list_lock);
+ i2c_priv->ready = false;
+ list_del(&i2c_priv->i2c_client_list_node);
+ spin_unlock(&driver_data.i2c_list_lock);
+ mutex_unlock(&atmel_ecc_kpp_lock);
+
+ dev_err(&client->dev, "%s alg registration failed\n",
+ atmel_ecdh_nist_p256.base.cra_driver_name);
+ return ret;
+ }
}
+ atmel_ecc_kpp_refcnt++;
+ mutex_unlock(&atmel_ecc_kpp_lock);
+ dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
return ret;
}
static void atmel_ecc_remove(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+ bool trigger_unreg = false;
spin_lock(&driver_data.i2c_list_lock);
i2c_priv->ready = false;
spin_unlock(&driver_data.i2c_list_lock);
- /* Return EBUSY if i2c client already allocated. */
- if (atomic_read(&i2c_priv->tfm_count)) {
- /*
- * After we return here, the memory backing the device is freed.
- * That happens no matter what the return value of this function
- * is because in the Linux device model there is no error
- * handling for unbinding a driver.
- * If there is still some action pending, it probably involves
- * accessing the freed memory.
- */
- dev_emerg(&client->dev, "Device is busy, expect memory corruption.\n");
- return;
+ /*
+ * The Linux crypto core automatically blocks until all active
+ * transformations utilizing that specific algorithm structure
+ * are fully freed and closed.
+ */
+ mutex_lock(&atmel_ecc_kpp_lock);
+ atmel_ecc_kpp_refcnt--;
+
+ if (atmel_ecc_kpp_refcnt == 0) {
+ trigger_unreg = true;
+ atmel_ecc_unreg_active = true;
+ reinit_completion(&atmel_ecc_unreg_done);
}
+ mutex_unlock(&atmel_ecc_kpp_lock);
- crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+ if (atomic_read(&i2c_priv->tfm_count))
+ wait_for_completion(&i2c_priv->remove_done);
spin_lock(&driver_data.i2c_list_lock);
list_del(&i2c_priv->i2c_client_list_node);
spin_unlock(&driver_data.i2c_list_lock);
+
+ /*
+ * The driver registers once an algorithm, but maintains a list of
+ * supporting i2c devices. Unregister the algorithm only, when the last
+ * supporting device deregisters. Use completions to assure no inflight
+ * TFMs and/or re-registering driver probe will then loose memory
+ * by over initializing the global statics.
+ */
+ if (trigger_unreg) {
+ crypto_unregister_kpp(&atmel_ecdh_nist_p256);
+ mutex_lock(&atmel_ecc_kpp_lock);
+ atmel_ecc_unreg_active = false;
+ complete_all(&atmel_ecc_unreg_done);
+ mutex_unlock(&atmel_ecc_kpp_lock);
+ }
}
static const struct of_device_id atmel_ecc_dt_ids[] = {
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index e3b12030f9c4..b320559e50eb 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -146,6 +146,7 @@ struct atmel_i2c_client_priv {
size_t wake_token_sz;
atomic_t tfm_count ____cacheline_aligned;
struct hwrng hwrng;
+ struct completion remove_done;
bool ready;
};
--
2.39.5
^ permalink raw reply related
* [PATCH v4 03/12] crypto: atmel-sha204a - fix heap info leak on I2C transfer failure
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
When a non-blocking read operation is requested, the driver dynamically
allocates memory to track asynchronous transfer status. If the underlying
I2C transmission fails, atmel_sha204a_rng_done() logs a rate-limited
warning but incorrectly proceeds to cache the pointer to this uninitialized
buffer inside the rng->priv data field anyway.
On subsequent execution passes, atmel_sha204a_rng_read_nonblocking()
detects the stale rng->priv value, skips executing a hardware data read,
and copies up to 32 bytes of uninitialized kernel heap data from this
garbage memory pool straight back into the system's hwrng data stream.
Fix this information disclosure vector by immediately releasing the
allocated asynchronous work data buffer and explicitly clearing the
tracking pointer context whenever an I2C transaction returns a non-zero
error status.
Additionally, duplicate the tfm counter decrement within the new error
path to ensure the reference counter is properly released before executing
the early return, maintaining the driver's availability for subsequent
requests.
Fixes: da001fb651b0 ("crypto: atmel-i2c - add support for SHA204A random number generator")
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-sha204a.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/crypto/atmel-sha204a.c b/drivers/crypto/atmel-sha204a.c
index 12eb85b57380..33e5a66b843c 100644
--- a/drivers/crypto/atmel-sha204a.c
+++ b/drivers/crypto/atmel-sha204a.c
@@ -31,10 +31,15 @@ static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data,
struct atmel_i2c_client_priv *i2c_priv = work_data->ctx;
struct hwrng *rng = areq;
- if (status)
+ if (status) {
dev_warn_ratelimited(&i2c_priv->client->dev,
"i2c transaction failed (%d)\n",
status);
+ kfree(work_data);
+ rng->priv = 0;
+ atomic_dec(&i2c_priv->tfm_count);
+ return;
+ }
rng->priv = (unsigned long)work_data;
atomic_dec(&i2c_priv->tfm_count);
--
2.39.5
^ permalink raw reply related
* [PATCH v4 01/12] crypto: atmel-ecc - fix use after free situation
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
In-Reply-To: <20260522230134.32414-1-l.rubusch@gmail.com>
Fixes a possible race condition, when having multiple of such devices
attached (identified by sashiko feedback).
The Scenario:
Thread A (Device 1 Probe): Successfully adds i2c_priv to the global
list (Line 324). The lock is released.
Thread B (An active crypto request): Concurrently calls
atmel_ecc_i2c_client_alloc(). It scans the global list, sees
Device 1, and assigns a crypto job to it.
Thread A: Moves to line 332. crypto_register_kpp() fails (e.g., out of
memory or name clash).
Thread A: Enters the error path. It removes Device 1 from the list and
frees the i2c_priv memory.
Thread B: Is still actively trying to talk to the I2C hardware using
the i2c_priv pointer it grabbed in Step 2. The memory is now
gone. Result: Kernel crash (Use-After-Free).
Fixes: 11105693fa05 ("crypto: atmel-ecc - introduce Microchip / Atmel ECC driver")
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
drivers/crypto/atmel-ecc.c | 10 ++++++++++
drivers/crypto/atmel-i2c.h | 2 ++
2 files changed, 12 insertions(+)
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 9660f6426a84..94360d29f9f9 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -218,6 +218,8 @@ static struct i2c_client *atmel_ecc_i2c_client_alloc(void)
list_for_each_entry(i2c_priv, &driver_data.i2c_client_list,
i2c_client_list_node) {
+ if (!i2c_priv->ready)
+ continue;
tfm_cnt = atomic_read(&i2c_priv->tfm_count);
if (tfm_cnt < min_tfm_cnt) {
min_tfm_cnt = tfm_cnt;
@@ -322,20 +324,24 @@ static int atmel_ecc_probe(struct i2c_client *client)
return ret;
i2c_priv = i2c_get_clientdata(client);
+ i2c_priv->ready = false;
spin_lock(&driver_data.i2c_list_lock);
list_add_tail(&i2c_priv->i2c_client_list_node,
&driver_data.i2c_client_list);
+ i2c_priv->ready = true;
spin_unlock(&driver_data.i2c_list_lock);
ret = crypto_register_kpp(&atmel_ecdh_nist_p256);
if (ret) {
spin_lock(&driver_data.i2c_list_lock);
+ i2c_priv->ready = false;
list_del(&i2c_priv->i2c_client_list_node);
spin_unlock(&driver_data.i2c_list_lock);
dev_err(&client->dev, "%s alg registration failed\n",
atmel_ecdh_nist_p256.base.cra_driver_name);
+ return ret;
} else {
dev_info(&client->dev, "atmel ecc algorithms registered in /proc/crypto\n");
}
@@ -347,6 +353,10 @@ static void atmel_ecc_remove(struct i2c_client *client)
{
struct atmel_i2c_client_priv *i2c_priv = i2c_get_clientdata(client);
+ spin_lock(&driver_data.i2c_list_lock);
+ i2c_priv->ready = false;
+ spin_unlock(&driver_data.i2c_list_lock);
+
/* Return EBUSY if i2c client already allocated. */
if (atomic_read(&i2c_priv->tfm_count)) {
/*
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 72f04c15682f..e3b12030f9c4 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -129,6 +129,7 @@ struct atmel_ecc_driver_data {
* @wake_token_sz : size in bytes of the wake_token
* @tfm_count : number of active crypto transformations on i2c client
* @hwrng : hold the hardware generated rng
+ * @ready : hw client is ready to use
*
* Reads and writes from/to the i2c client are sequential. The first byte
* transmitted to the device is treated as the byte size. Any attempt to send
@@ -145,6 +146,7 @@ struct atmel_i2c_client_priv {
size_t wake_token_sz;
atomic_t tfm_count ____cacheline_aligned;
struct hwrng hwrng;
+ bool ready;
};
/**
--
2.39.5
^ permalink raw reply related
* [PATCH v4 00/12] crypto: atmel - introduce shared i2c core client management and capability-based selection framework
From: Lothar Rubusch @ 2026-05-22 23:01 UTC (permalink / raw)
To: thorsten.blum, herbert, davem, nicolas.ferre, alexandre.belloni,
claudiu.beznea, tudor.ambarus, ardb, linusw, krzk+dt
Cc: linux-crypto, linux-arm-kernel, linux-kernel, l.rubusch
This patch series introduces a staged refactoring of the Atmel crypto I2C
drivers in preparation for a shared core-based architecture. The goal is to
consolidate I2C client management and selection logic into a common
atmel-i2c core driver while keeping ECC (ECDH) and SHA204A client drivers
functionally separate but interoperating through shared infrastructure.
The series moves existing ECC-specific client tracking into a shared
management structure, relocates allocation and selection logic, and
introduces capability-based filtering for hardware selection. This allows
individual crypto drivers to request hardware clients based on supported
features while still benefiting from a unified least-loaded selection
strategy.
Subsequent patches extend this base by:
- migrating client management fully into the core driver,
- introducing explicit capability advertisement by each hardware client,
- updating ECC and SHA204A drivers to participate in capability-aware allocation,
- and cleaning up probe/remove paths to ensure consistent lifecycle handling.
No functional behavioral changes are intended at this stage beyond internal
refactoring and preparation for future feature expansion. The series is
designed to preserve existing crypto functionality while gradually
centralizing shared logic in the atmel-i2c core layer, reducing duplication
and improving maintainability across all Atmel crypto drivers.
Note, this series is a more elaborated part of a bigger refactoring,
sketched out here:
https://sashiko.dev/#/patchset/20260512224349.64621-1-l.rubusch%40gmail.com
Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
---
v3 -> v4:
- sashiko feedback[3]
- fixes and patches reordered
- fix: cover a possible memory leak at deregistration and subsequent reregistration
[3] https://sashiko.dev/#/patchset/20260520155703.23018-1-l.rubusch%40gmail.com
v2 -> v3:
- sashiko feedback[2]
- ecc: reorder setting ready flag in probe()
- i2c: unconditionally call flush in unregister client function, to avoid UAF for multiple devices
- i2c: fixed a sleep-in-atomic bug by moving atmel_i2c_flush_queue() outside the spin_lock section
- sha204a: reorder calls in remove() avoid UAF flagged risk
- sha204a: rephrase commit messages
[2] https://sashiko.dev/#/patchset/20260519204803.17034-1-l.rubusch%40gmail.com
v1 -> v2:
- going over Sashiko feedback[1]
- rephrasing commit messages and titles
- fix: introduce a ready/state flag to address the UAF risk
- fix: add kpp lock and refcnt to impede overwriting global driver struct
- fix: explicitely clearing rng cached buffer in return branch
- unregistering ready state by dedicated function
- reorder Atmel ECC related things and atmel I2C at beginning
- reorder Atmel SHA204a related things behind introduction of cap
- patches dropped: NULL checks in remove functions
- changed to EXPORT_SYMBOL_GPL
- additionally to alloc hw client also migrate freeing it to core driver
[1] https://sashiko.dev/#/patchset/20260517180639.9657-1-l.rubusch%40gmail.com
---
Lothar Rubusch (12):
crypto: atmel-ecc - fix use after free situation
crypto: atmel-ecc - fix multi-device kpp registration
crypto: atmel-sha204a - fix heap info leak on I2C transfer failure
crypto: atmel-ecc - rename driver_data before moving it into atmel-i2c
crypto: atmel - rename atmel_ecc_driver_data to atmel_i2c_client_mgmt
crypto: atmel-i2c - move client management instance into core
crypto: atmel-i2c - introduce shared teardown helpers and fix queue
flush
crypto: atmel-ecc - switch to module_i2c_driver
crypto: atmel-i2c - move shared client allocation logic to core
crypto: atmel-i2c - implement capability-based client selection
crypto: atmel-sha204a - integrate into core management tracking
crypto: atmel-sha204a - switch to module_i2c_driver
drivers/crypto/atmel-ecc.c | 177 ++++++++++++++++-----------------
drivers/crypto/atmel-i2c.c | 77 ++++++++++++++
drivers/crypto/atmel-i2c.h | 18 +++-
drivers/crypto/atmel-sha204a.c | 48 +++++----
4 files changed, 210 insertions(+), 110 deletions(-)
base-commit: 49e05bb00f2e8168695f7af4d694c39e1423e8a2
--
2.39.5
^ permalink raw reply
* [PATCH v9 17/23] perf python: Add callchain support
From: Ian Rogers @ 2026-05-22 22:04 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark, leo.yan,
linux-arm-kernel, linux-kernel, linux-perf-users, mingo, peterz,
tmricht
In-Reply-To: <20260522220435.2378363-1-irogers@google.com>
Implement pyrf_callchain_node and pyrf_callchain types for lazy
iteration over callchain frames. Add callchain property to
sample_event.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
v2:
1. Eager Callchain Resolution: Moved the callchain resolution from
deferred iteration to eager processing in
pyrf_session_tool__sample() . This avoids risks of reading from
unmapped memory or following dangling pointers to closed sessions.
2. Cached Callchain: Added a callchain field to struct pyrf_event to
store the resolved object.
3. Simplified Access: pyrf_sample_event__get_callchain() now just
returns the cached object if available.
4. Avoided Double Free: Handled lazy cleanups properly.
v6:
- Moved callchain resolution from `session_tool__sample` to
`pyrf_event__new`.
---
tools/perf/util/python.c | 241 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 240 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index daedd67f12d5..de7a51389572 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -66,6 +66,8 @@ struct pyrf_event {
struct addr_location al;
/** @al_resolved: True when machine__resolve been called. */
bool al_resolved;
+ /** @callchain: Resolved callchain, eagerly computed if requested. */
+ PyObject *callchain;
/** @event: The underlying perf_event that may be in a file or ring buffer. */
union perf_event event;
};
@@ -103,6 +105,7 @@ static void pyrf_event__delete(struct pyrf_event *pevent)
{
if (pevent->al_resolved)
addr_location__exit(&pevent->al);
+ Py_XDECREF(pevent->callchain);
perf_sample__exit(&pevent->sample);
Py_TYPE(pevent)->tp_free((PyObject *)pevent);
}
@@ -669,6 +672,181 @@ static PyObject *pyrf_sample_event__insn(PyObject *self, PyObject *args __maybe_
pevent->sample.insn_len);
}
+struct pyrf_callchain_node {
+ PyObject_HEAD
+ u64 ip;
+ struct map *map;
+ struct symbol *sym;
+};
+
+static void pyrf_callchain_node__delete(struct pyrf_callchain_node *pnode)
+{
+ map__put(pnode->map);
+ Py_TYPE(pnode)->tp_free((PyObject*)pnode);
+}
+
+static PyObject *pyrf_callchain_node__get_ip(struct pyrf_callchain_node *pnode,
+ void *closure __maybe_unused)
+{
+ return PyLong_FromUnsignedLongLong(pnode->ip);
+}
+
+static PyObject *pyrf_callchain_node__get_symbol(struct pyrf_callchain_node *pnode,
+ void *closure __maybe_unused)
+{
+ if (pnode->sym)
+ return PyUnicode_FromString(pnode->sym->name);
+ return PyUnicode_FromString("[unknown]");
+}
+
+static PyObject *pyrf_callchain_node__get_dso(struct pyrf_callchain_node *pnode,
+ void *closure __maybe_unused)
+{
+ const char *dsoname = "[unknown]";
+
+ if (pnode->map) {
+ struct dso *dso = map__dso(pnode->map);
+ if (dso) {
+ if (symbol_conf.show_kernel_path && dso__long_name(dso))
+ dsoname = dso__long_name(dso);
+ else
+ dsoname = dso__name(dso);
+ }
+ }
+ return PyUnicode_FromString(dsoname);
+}
+
+static PyGetSetDef pyrf_callchain_node__getset[] = {
+ { .name = "ip", .get = (getter)pyrf_callchain_node__get_ip, },
+ { .name = "symbol", .get = (getter)pyrf_callchain_node__get_symbol, },
+ { .name = "dso", .get = (getter)pyrf_callchain_node__get_dso, },
+ { .name = NULL, },
+};
+
+static PyTypeObject pyrf_callchain_node__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.callchain_node",
+ .tp_basicsize = sizeof(struct pyrf_callchain_node),
+ .tp_dealloc = (destructor)pyrf_callchain_node__delete,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_doc = "perf callchain node object.",
+ .tp_getset = pyrf_callchain_node__getset,
+};
+
+struct pyrf_callchain_frame {
+ u64 ip;
+ struct map *map;
+ struct symbol *sym;
+};
+
+struct pyrf_callchain {
+ PyObject_HEAD
+ struct pyrf_event *pevent;
+ struct pyrf_callchain_frame *frames;
+ u64 nr_frames;
+ u64 pos;
+ bool resolved;
+};
+
+static void pyrf_callchain__delete(struct pyrf_callchain *pchain)
+{
+ Py_XDECREF(pchain->pevent);
+ if (pchain->frames) {
+ for (u64 i = 0; i < pchain->nr_frames; i++)
+ map__put(pchain->frames[i].map);
+ free(pchain->frames);
+ }
+ Py_TYPE(pchain)->tp_free((PyObject*)pchain);
+}
+
+static PyObject *pyrf_callchain__next(struct pyrf_callchain *pchain)
+{
+ struct pyrf_callchain_node *pnode;
+
+ if (!pchain->resolved) {
+ struct evsel *evsel = pchain->pevent->sample.evsel;
+ struct evlist *evlist = evsel->evlist;
+ struct perf_session *session = evlist ? evlist__session(evlist) : NULL;
+ struct addr_location al;
+ struct callchain_cursor *cursor;
+ struct callchain_cursor_node *node;
+ u64 i;
+
+ if (!session || !pchain->pevent->sample.callchain)
+ return NULL;
+
+ addr_location__init(&al);
+ if (machine__resolve(&session->machines.host, &al, &pchain->pevent->sample) < 0) {
+ addr_location__exit(&al);
+ return NULL;
+ }
+
+ cursor = get_tls_callchain_cursor();
+ if (thread__resolve_callchain(al.thread, cursor,
+ &pchain->pevent->sample, NULL, NULL,
+ PERF_MAX_STACK_DEPTH) != 0) {
+ addr_location__exit(&al);
+ return NULL;
+ }
+ callchain_cursor_commit(cursor);
+
+ pchain->nr_frames = cursor->nr;
+ if (pchain->nr_frames > 0) {
+ pchain->frames = calloc(pchain->nr_frames, sizeof(*pchain->frames));
+ if (!pchain->frames) {
+ addr_location__exit(&al);
+ return PyErr_NoMemory();
+ }
+
+ for (i = 0; i < pchain->nr_frames; i++) {
+ node = callchain_cursor_current(cursor);
+ pchain->frames[i].ip = node->ip;
+ pchain->frames[i].map = map__get(node->ms.map);
+ pchain->frames[i].sym = node->ms.sym;
+ callchain_cursor_advance(cursor);
+ }
+ }
+ pchain->resolved = true;
+ addr_location__exit(&al);
+ }
+
+ if (pchain->pos >= pchain->nr_frames)
+ return NULL;
+
+ pnode = PyObject_New(struct pyrf_callchain_node, &pyrf_callchain_node__type);
+ if (!pnode)
+ return NULL;
+
+ pnode->ip = pchain->frames[pchain->pos].ip;
+ pnode->map = map__get(pchain->frames[pchain->pos].map);
+ pnode->sym = pchain->frames[pchain->pos].sym;
+
+ pchain->pos++;
+ return (PyObject *)pnode;
+}
+
+static PyTypeObject pyrf_callchain__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.callchain",
+ .tp_basicsize = sizeof(struct pyrf_callchain),
+ .tp_dealloc = (destructor)pyrf_callchain__delete,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_doc = "perf callchain object.",
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)pyrf_callchain__next,
+};
+
+static PyObject *pyrf_sample_event__get_callchain(PyObject *self, void *closure __maybe_unused)
+{
+ struct pyrf_event *pevent = (void *)self;
+
+ if (!pevent->callchain)
+ Py_RETURN_NONE;
+
+ Py_INCREF(pevent->callchain);
+ return pevent->callchain;
+}
+
static PyObject*
pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name)
{
@@ -683,6 +861,12 @@ pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name)
}
static PyGetSetDef pyrf_sample_event__getset[] = {
+ {
+ .name = "callchain",
+ .get = pyrf_sample_event__get_callchain,
+ .set = NULL,
+ .doc = "event callchain.",
+ },
{
.name = "raw_buf",
.get = (getter)pyrf_sample_event__get_raw_buf,
@@ -852,6 +1036,12 @@ static int pyrf_event__setup_types(void)
err = PyType_Ready(&pyrf_context_switch_event__type);
if (err < 0)
goto out;
+ err = PyType_Ready(&pyrf_callchain_node__type);
+ if (err < 0)
+ goto out;
+ err = PyType_Ready(&pyrf_callchain__type);
+ if (err < 0)
+ goto out;
out:
return err;
}
@@ -872,9 +1062,11 @@ static PyTypeObject *pyrf_event__type[] = {
};
static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *evsel,
- struct perf_session *session __maybe_unused)
+ struct perf_session *session)
{
struct pyrf_event *pevent;
+ struct perf_sample *sample;
+ struct machine *machine = session ? &session->machines.host : NULL;
size_t size;
int err;
size_t min_size = sizeof(struct perf_event_header);
@@ -1033,6 +1225,7 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
}
perf_sample__init(&pevent->sample, /*all=*/true);
+ pevent->callchain = NULL;
pevent->al_resolved = false;
addr_location__init(&pevent->al);
@@ -1046,6 +1239,49 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
return PyErr_Format(PyExc_OSError,
"perf: can't parse sample, err=%d", err);
}
+ sample = &pevent->sample;
+ if (machine && sample->callchain) {
+ struct addr_location al;
+ struct callchain_cursor *cursor;
+ u64 i;
+ struct pyrf_callchain *pchain;
+
+ addr_location__init(&al);
+ if (machine__resolve(machine, &al, sample) >= 0) {
+ cursor = get_tls_callchain_cursor();
+ if (thread__resolve_callchain(al.thread, cursor, sample,
+ NULL, NULL, PERF_MAX_STACK_DEPTH) == 0) {
+ callchain_cursor_commit(cursor);
+
+ pchain = PyObject_New(struct pyrf_callchain, &pyrf_callchain__type);
+ if (pchain) {
+ pchain->pevent = pevent;
+ Py_INCREF(pevent);
+ pchain->nr_frames = cursor->nr;
+ pchain->pos = 0;
+ pchain->resolved = true;
+ pchain->frames = calloc(pchain->nr_frames,
+ sizeof(*pchain->frames));
+ if (pchain->frames) {
+ struct callchain_cursor_node *node;
+
+ for (i = 0; i < pchain->nr_frames; i++) {
+ node = callchain_cursor_current(cursor);
+ pchain->frames[i].ip = node->ip;
+ pchain->frames[i].map =
+ map__get(node->ms.map);
+ pchain->frames[i].sym = node->ms.sym;
+ callchain_cursor_advance(cursor);
+ }
+ pevent->callchain = (PyObject *)pchain;
+ } else {
+ Py_DECREF(pchain);
+ }
+ }
+ }
+ addr_location__exit(&al);
+ }
+ }
return (PyObject *)pevent;
}
@@ -3145,6 +3381,9 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject
}
psession->session = session;
+ symbol_conf.use_callchain = true;
+ symbol_conf.show_kernel_path = true;
+ symbol_conf.inline_name = false;
if (symbol__init(perf_session__env(session)) < 0) {
PyErr_SetString(PyExc_OSError, "perf: symbol__init failed");
goto err_out;
--
2.54.0.794.g4f17f83d09-goog
^ permalink raw reply related
* Re: [PATCH v5 4/5] PCI: dwc: Use common D3cold eligibility helper in suspend path
From: Bjorn Helgaas @ 2026-05-22 22:48 UTC (permalink / raw)
To: Krishna Chaitanya Chundru
Cc: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Will Deacon, linux-pci, linux-kernel, linux-arm-msm,
linux-arm-kernel, jonathanh, bjorn.andersson, Frank Li, linux-pm
In-Reply-To: <20260520000153.GA14400@bhelgaas>
On Tue, May 19, 2026 at 07:01:53PM -0500, Bjorn Helgaas wrote:
> [+cc Frank, linux-pm]
>
> On Wed, Apr 29, 2026 at 12:12:26PM +0530, Krishna Chaitanya Chundru wrote:
> > Previously, the driver skipped putting the link into L2/device state in
> > D3cold whenever L1 ASPM was enabled, since some devices (e.g. NVMe) expect
> > low resume latency and may not tolerate deeper power states.
>
> I think "some devices expect low resume latency and may not tolerate
> deeper power states" conveys the wrong message. It's not that NVMe
> has a mysterious acceptable resume latency number that we have to meet
> or that NVMe has some inherent aversion to D3cold or L1SS or whatever
> "deeper power states" refers to.
>
> It could be that ASPM L1 was configured incorrectly (e.g., an L1->L0
> transition didn't happen within the advertised exit latency, leading
> to some device access failure) or a device lost internal context when
> the driver didn't expect it (e.g., the Qcom problem where L1SS exit
> takes too long and results in a link-down and device reset [1]).
>
> It sounds to me like the ASPM L1 check was a way to avoid problems
> like that, but I don't think we ever really had a root cause.
Possible updated commit log text:
Previously, the driver skipped putting the link into L2 and device
state in D3cold when L1 ASPM was enabled since some devices (e.g.
NVMe) failed to resume correctly if they were suspended with L1
enabled. The root cause of the failure is unknown.
> [1] https://lore.kernel.org/linux-pci/20260519-l1ss-fix-v2-0-b2c3a4bdeb15@oss.qualcomm.com/
>
> > However, such devices typically remain in D0 and are already covered
> > by the new helper's requirement that all endpoints be in D3hot
> > before the devices under host bridge may enter D3cold.
It makes me nervous when we assume "typical" things, but I don't have
any ideas about wording this.
This is all merged and in pci/next, so we can leave it as-is or amend
the commit log if anybody has better ideas.
> If we put the host bridge in D3cold, I assume the hierarchy below is
> either put in D3cold as well, or at least every device in the
> hierarchy will be reset as a consequence of the Root Port link going
> down.
>
> If the driver doesn't manage the device power state itself, I assume
> we have the freedom to put the hierarchy in D3cold or reset it.
>
> Do we have the same freedom if the driver *does* manage the power
> state itself? What if the driver put the device in D3hot, expecting
> it to *stay* in D3hot?
>
> I think pci_host_common_d3cold_possible() will see the device in D3hot
> and decide that D3cold is possible.
>
> (I'm looking at https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/power/pci.rst?id=v7.0#n746)
>
> > So, replace the local L1/L1SS-based check in dw_pcie_suspend_noirq() with
> > the shared pci_host_common_d3cold_possible() helper to decide whether the
> > devices under host bridge can safely transition to D3cold.
> >
> > In addition, propagate PME-from-D3cold capability information from the
> > helper and record it in skip_pwrctrl_off. Some devices (e.g. M.2 cards
> > without auxiliary power) may lose PME detection when main power is
> > removed, even if they advertise PME-from-D3cold support. This allows
> > controller power-off to be skipped when required to preserve wakeup
> > functionality.
> >
> > Update the suspended flag in dw_pcie_resume_noirq() only after the PCIe
> > link resumes successfully, to avoid marking the controller active when
> > link resume fails.
> >
> > Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> > ---
> > drivers/pci/controller/dwc/pcie-designware-host.c | 15 +++++++--------
> > drivers/pci/controller/dwc/pcie-designware.h | 1 +
> > 2 files changed, 8 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
> > index c9517a348836..9e409a1909e6 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware-host.c
> > +++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> > @@ -16,9 +16,11 @@
> > #include <linux/msi.h>
> > #include <linux/of_address.h>
> > #include <linux/of_pci.h>
> > +#include <linux/pci.h>
> > #include <linux/pci_regs.h>
> > #include <linux/platform_device.h>
> >
> > +#include "../pci-host-common.h"
> > #include "../../pci.h"
> > #include "pcie-designware.h"
> >
> > @@ -1218,18 +1220,14 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
> >
> > int dw_pcie_suspend_noirq(struct dw_pcie *pci)
> > {
> > - u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
> > + bool pme_capable = false;
> > int ret = 0;
> > u32 val;
> >
> > if (!dw_pcie_link_up(pci))
> > goto stop_link;
> >
> > - /*
> > - * If L1SS is supported, then do not put the link into L2 as some
> > - * devices such as NVMe expect low resume latency.
> > - */
> > - if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
> > + if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable))
> > return 0;
> >
> > if (pci->pp.ops->pme_turn_off) {
> > @@ -1273,6 +1271,7 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
> > udelay(1);
> >
> > stop_link:
> > + pci->pp.skip_pwrctrl_off = pme_capable;
> > dw_pcie_stop_link(pci);
> > if (pci->pp.ops->deinit)
> > pci->pp.ops->deinit(&pci->pp);
> > @@ -1290,8 +1289,6 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
> > if (!pci->suspended)
> > return 0;
> >
> > - pci->suspended = false;
> > -
> > if (pci->pp.ops->init) {
> > ret = pci->pp.ops->init(&pci->pp);
> > if (ret) {
> > @@ -1313,6 +1310,8 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
> > if (pci->pp.ops->post_init)
> > pci->pp.ops->post_init(&pci->pp);
> >
> > + pci->suspended = false;
> > +
> > return 0;
> >
> > err_stop_link:
> > diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> > index 3e69ef60165b..e759c5c7257e 100644
> > --- a/drivers/pci/controller/dwc/pcie-designware.h
> > +++ b/drivers/pci/controller/dwc/pcie-designware.h
> > @@ -450,6 +450,7 @@ struct dw_pcie_rp {
> > bool ecam_enabled;
> > bool native_ecam;
> > bool skip_l23_ready;
> > + bool skip_pwrctrl_off;
> > };
> >
> > struct dw_pcie_ep_ops {
> >
> > --
> > 2.34.1
> >
^ permalink raw reply
* [PATCH v9 03/23] perf tests: Sort includes and add missed explicit dependencies
From: Ian Rogers @ 2026-05-22 22:04 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark, leo.yan,
linux-arm-kernel, linux-kernel, linux-perf-users, mingo, peterz,
tmricht
In-Reply-To: <20260522220435.2378363-1-irogers@google.com>
Fix missing #includes found while cleaning the evsel/evlist header
files. Sort the remaining header files for consistency with the rest
of the code.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/hwmon_pmu.c | 14 +++++++++-----
tools/perf/tests/mmap-basic.c | 18 +++++++++++-------
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/tools/perf/tests/hwmon_pmu.c b/tools/perf/tests/hwmon_pmu.c
index 4aa4aac94f09..ada6e445c4c4 100644
--- a/tools/perf/tests/hwmon_pmu.c
+++ b/tools/perf/tests/hwmon_pmu.c
@@ -1,15 +1,19 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
-#include "debug.h"
-#include "evlist.h"
#include "hwmon_pmu.h"
-#include "parse-events.h"
-#include "tests.h"
+
#include <errno.h>
+
#include <fcntl.h>
-#include <sys/stat.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <sys/stat.h>
+
+#include "debug.h"
+#include "evlist.h"
+#include "parse-events.h"
+#include "pmus.h"
+#include "tests.h"
static const struct test_event {
const char *name;
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index a18d84d858aa..a69cd1046e9a 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -1,25 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
-#include <fcntl.h>
#include <inttypes.h>
#include <stdlib.h>
+
+#include <fcntl.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
#include <perf/cpumap.h>
+#include <perf/evlist.h>
+#include <perf/mmap.h>
#include "cpumap.h"
#include "debug.h"
#include "event.h"
#include "evlist.h"
#include "evsel.h"
-#include "thread_map.h"
+#include "pmu.h"
+#include "pmus.h"
#include "tests.h"
+#include "thread_map.h"
#include "util/affinity.h"
#include "util/mmap.h"
#include "util/sample.h"
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <perf/evlist.h>
-#include <perf/mmap.h>
/*
* This test will generate random numbers of calls to some getpid syscalls,
--
2.54.0.794.g4f17f83d09-goog
^ permalink raw reply related
* [PATCH v9 14/23] perf python: Add python session abstraction wrapping perf's session
From: Ian Rogers @ 2026-05-22 22:04 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark, leo.yan,
linux-arm-kernel, linux-kernel, linux-perf-users, mingo, peterz,
tmricht
In-Reply-To: <20260522220435.2378363-1-irogers@google.com>
Sessions are necessary to be able to use perf.data files within a
tool. Add a wrapper python type that incorporates the tool. Allow a
sample callback to be passed when creating the session. When
process_events is run this callback will be called, if supplied, for
sample events.
An example use looks like:
```
$ perf record -e cycles,instructions -a sleep 3
$ PYTHONPATH=..../perf/python python3
Python 3.13.7 (main, Aug 20 2025, 22:17:40) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import perf
>>> count=0
... def handle_sample(x):
... global count
... if count < 3:
... print(dir(x))
... count = count + 1
... perf.session(perf.data("perf.data"),sample=handle_sample).process_events()
...
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'sample_addr', 'sample_cpu', 'sample_id', 'sample_ip', 'sample_period', 'sample_pid', 'sample_stream_id', 'sample_tid', 'sample_time', 'type']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'sample_addr', 'sample_cpu', 'sample_id', 'sample_ip', 'sample_period', 'sample_pid', 'sample_stream_id', 'sample_tid', 'sample_time', 'type']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'sample_addr', 'sample_cpu', 'sample_id', 'sample_ip', 'sample_period', 'sample_pid', 'sample_stream_id', 'sample_tid', 'sample_time', 'type']
```
Also, add the ability to get the thread associated with a session. For
threads, allow the comm string to be retrieved. This can be useful for
filtering threads. Connect up some of the standard event handling in
psession->tool to better support queries of the machine. Also connect
up the symbols.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
v2:
1. Fixed Potential Crash in pyrf_thread__comm : Used
thread__comm_str() to safely retrieve the command name, avoiding a
crash if thread__comm() returns NULL.
2. Fixed Double Free Risk: Zeroed out user_regs , intr_regs , and
callchain in the shallow copy of perf_sample to prevent Python from
attempting to free pointers it doesn't own.
3. Fixed Memory Leak & Exception Handling in Callback: Handled the
return value of PyObject_CallFunction() to avoid leaks, and checked
for failure to abort the loop and propagate Python exceptions
cleanly.
4. Enforced Type Safety: Used O! with &pyrf_data__type in
PyArg_ParseTupleAndKeywords to prevent bad casts from passing
arbitrary objects as perf.data.
5. Added Missing Build ID Handler: Registered
perf_event__process_build_id to allow correct symbol resolution.
6. Fixed Double Free Crash on Init Failure: Set session and pdata to
NULL on failure to prevent tp_dealloc from double-freeing them.
7. Preserved C-level Errors: Made pyrf_session__process_events return
the error code integer rather than always returning None .
v7:
- Fixed NULL comm handling.
- Avoided swallowing exceptions in module init.
- Fixed checkpatch warning for missing blank line.
v8:
- Switch from pyrf_session__init to pyrf_session__new to avoid dealing with a
potentially NULL session variable.
- Added pid, tid, ppid, and cpu attributes to perf.thread.
---
tools/perf/util/python.c | 310 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 304 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 82adc59ecd94..18a48779c591 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -11,8 +11,10 @@
#include <structmember.h>
#include "callchain.h"
+#include "comm.h"
#include "counts.h"
#include "data.h"
+#include "debug.h"
#include "event.h"
#include "evlist.h"
#include "evsel.h"
@@ -22,8 +24,12 @@
#include "pmus.h"
#include "print_binary.h"
#include "record.h"
+#include "session.h"
#include "strbuf.h"
+#include "symbol.h"
+#include "thread.h"
#include "thread_map.h"
+#include "tool.h"
#include "tp_pmu.h"
#include "trace-event.h"
#include "util/sample.h"
@@ -2417,6 +2423,287 @@ static int pyrf_data__setup_types(void)
return PyType_Ready(&pyrf_data__type);
}
+struct pyrf_thread {
+ PyObject_HEAD
+
+ struct thread *thread;
+};
+
+static void pyrf_thread__delete(struct pyrf_thread *pthread)
+{
+ thread__put(pthread->thread);
+ Py_TYPE(pthread)->tp_free((PyObject *)pthread);
+}
+
+static PyObject *pyrf_thread__comm(PyObject *obj)
+{
+ struct pyrf_thread *pthread = (void *)obj;
+ const char *str = thread__comm_str(pthread->thread);
+
+ if (!str)
+ Py_RETURN_NONE;
+
+ return PyUnicode_FromString(str);
+}
+
+static PyMethodDef pyrf_thread__methods[] = {
+ {
+ .ml_name = "comm",
+ .ml_meth = (PyCFunction)pyrf_thread__comm,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = PyDoc_STR("Comm(and) associated with this thread.")
+ },
+ { .ml_name = NULL, }
+};
+
+static PyObject *pyrf_thread__get_pid(struct pyrf_thread *pthread, void *closure __maybe_unused)
+{
+ return PyLong_FromLong(thread__pid(pthread->thread));
+}
+
+static PyObject *pyrf_thread__get_tid(struct pyrf_thread *pthread, void *closure __maybe_unused)
+{
+ return PyLong_FromLong(thread__tid(pthread->thread));
+}
+
+static PyObject *pyrf_thread__get_ppid(struct pyrf_thread *pthread, void *closure __maybe_unused)
+{
+ return PyLong_FromLong(thread__ppid(pthread->thread));
+}
+
+static PyObject *pyrf_thread__get_cpu(struct pyrf_thread *pthread, void *closure __maybe_unused)
+{
+ return PyLong_FromLong(thread__cpu(pthread->thread));
+}
+
+static PyGetSetDef pyrf_thread__getset[] = {
+ { .name = "pid", .get = (getter)pyrf_thread__get_pid, .doc = "process ID" },
+ { .name = "tid", .get = (getter)pyrf_thread__get_tid, .doc = "thread ID" },
+ { .name = "ppid", .get = (getter)pyrf_thread__get_ppid, .doc = "parent process ID" },
+ { .name = "cpu", .get = (getter)pyrf_thread__get_cpu, .doc = "cpu number" },
+ { .name = NULL }
+};
+
+static const char pyrf_thread__doc[] = PyDoc_STR("perf thread object.");
+
+static PyTypeObject pyrf_thread__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.thread",
+ .tp_basicsize = sizeof(struct pyrf_thread),
+ .tp_dealloc = (destructor)pyrf_thread__delete,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_methods = pyrf_thread__methods,
+ .tp_getset = pyrf_thread__getset,
+ .tp_doc = pyrf_thread__doc,
+};
+
+static int pyrf_thread__setup_types(void)
+{
+ return PyType_Ready(&pyrf_thread__type);
+}
+
+static PyObject *pyrf_thread__from_thread(struct thread *thread)
+{
+ struct pyrf_thread *pthread = PyObject_New(struct pyrf_thread, &pyrf_thread__type);
+
+ if (!pthread)
+ return NULL;
+
+ pthread->thread = thread__get(thread);
+ return (PyObject *)pthread;
+}
+
+struct pyrf_session {
+ PyObject_HEAD
+
+ struct perf_session *session;
+ struct perf_tool tool;
+ struct pyrf_data *pdata;
+ PyObject *sample;
+};
+
+static int pyrf_session_tool__sample(const struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine __maybe_unused)
+{
+ struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool);
+ PyObject *pyevent = pyrf_event__new(event);
+ struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
+ PyObject *ret;
+
+ if (pyevent == NULL)
+ return -ENOMEM;
+
+ memcpy(&pevent->event, event, event->header.size);
+ if (evsel__parse_sample(sample->evsel, &pevent->event, &pevent->sample) < 0) {
+ Py_DECREF(pyevent);
+ return -1;
+ }
+
+ ret = PyObject_CallFunction(psession->sample, "O", pyevent);
+ if (!ret) {
+ Py_DECREF(pyevent);
+ return -1;
+ }
+ Py_DECREF(ret);
+ Py_DECREF(pyevent);
+ return 0;
+}
+
+static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, PyObject *args)
+{
+ struct machine *machine;
+ struct thread *thread = NULL;
+ PyObject *result;
+ int pid;
+
+ if (!PyArg_ParseTuple(args, "i", &pid))
+ return NULL;
+
+ machine = &psession->session->machines.host;
+ thread = machine__find_thread(machine, pid, pid);
+
+ if (!thread) {
+ machine = perf_session__find_machine(psession->session, pid);
+ if (machine)
+ thread = machine__find_thread(machine, pid, pid);
+ }
+
+ if (!thread) {
+ PyErr_Format(PyExc_TypeError, "Failed to find thread %d", pid);
+ return NULL;
+ }
+ result = pyrf_thread__from_thread(thread);
+ thread__put(thread);
+ return result;
+}
+
+static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ struct pyrf_data *pdata;
+ PyObject *sample = NULL;
+ static char *kwlist[] = { "data", "sample", NULL };
+ struct pyrf_session *psession;
+ struct perf_session *session;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &pyrf_data__type, &pdata,
+ &sample))
+ return NULL;
+
+ psession = PyObject_New(struct pyrf_session, type);
+ if (!psession)
+ return NULL;
+
+ psession->session = NULL;
+ psession->sample = NULL;
+ psession->pdata = NULL;
+
+ Py_INCREF(pdata);
+ psession->pdata = pdata;
+
+ perf_tool__init(&psession->tool, /*ordered_events=*/true);
+ psession->tool.ordering_requires_timestamps = true;
+
+ #define ADD_TOOL(name) \
+ do { \
+ if (name) { \
+ if (!PyCallable_Check(name)) { \
+ PyErr_SetString(PyExc_TypeError, #name " must be callable"); \
+ goto err_out; \
+ } \
+ psession->tool.name = pyrf_session_tool__##name; \
+ Py_INCREF(name); \
+ psession->name = name; \
+ } \
+ } while (0)
+
+ ADD_TOOL(sample);
+ #undef ADD_TOOL
+
+ psession->tool.comm = perf_event__process_comm;
+ psession->tool.mmap = perf_event__process_mmap;
+ psession->tool.mmap2 = perf_event__process_mmap2;
+ psession->tool.namespaces = perf_event__process_namespaces;
+ psession->tool.cgroup = perf_event__process_cgroup;
+ psession->tool.exit = perf_event__process_exit;
+ psession->tool.fork = perf_event__process_fork;
+ psession->tool.ksymbol = perf_event__process_ksymbol;
+ psession->tool.text_poke = perf_event__process_text_poke;
+ psession->tool.build_id = perf_event__process_build_id;
+ session = perf_session__new(&pdata->data, &psession->tool);
+ if (IS_ERR(session)) {
+ PyErr_Format(PyExc_IOError, "failed to create session: %ld", PTR_ERR(session));
+ goto err_out;
+ }
+ psession->session = session;
+
+ if (symbol__init(perf_session__env(session)) < 0) {
+ PyErr_SetString(PyExc_OSError, "perf: symbol__init failed");
+ goto err_out;
+ }
+
+ if (perf_session__create_kernel_maps(session) < 0)
+ pr_warning("Cannot read kernel map\n");
+
+ return (PyObject *)psession;
+err_out:
+ Py_DECREF(psession);
+ return NULL;
+}
+
+static void pyrf_session__delete(struct pyrf_session *psession)
+{
+ perf_session__delete(psession->session);
+ Py_XDECREF(psession->pdata);
+ Py_XDECREF(psession->sample);
+ Py_TYPE(psession)->tp_free((PyObject *)psession);
+}
+
+static PyObject *pyrf_session__find_thread_events(struct pyrf_session *psession)
+{
+ int err = perf_session__process_events(psession->session);
+
+ if (PyErr_Occurred())
+ return NULL;
+
+ return PyLong_FromLong(err);
+}
+
+static PyMethodDef pyrf_session__methods[] = {
+ {
+ .ml_name = "process_events",
+ .ml_meth = (PyCFunction)pyrf_session__find_thread_events,
+ .ml_flags = METH_NOARGS,
+ .ml_doc = PyDoc_STR("Iterate and process events.")
+ },
+ {
+ .ml_name = "find_thread",
+ .ml_meth = (PyCFunction)pyrf_session__find_thread,
+ .ml_flags = METH_VARARGS,
+ .ml_doc = PyDoc_STR("Returns the thread associated with a pid.")
+ },
+ { .ml_name = NULL, }
+};
+
+static const char pyrf_session__doc[] = PyDoc_STR("perf session object.");
+
+static PyTypeObject pyrf_session__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.session",
+ .tp_basicsize = sizeof(struct pyrf_session),
+ .tp_dealloc = (destructor)pyrf_session__delete,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_methods = pyrf_session__methods,
+ .tp_doc = pyrf_session__doc,
+ .tp_new = pyrf_session__new,
+};
+
+static int pyrf_session__setup_types(void)
+{
+ return PyType_Ready(&pyrf_session__type);
+}
+
static PyMethodDef perf__methods[] = {
{
.ml_name = "metrics",
@@ -2471,8 +2758,10 @@ PyMODINIT_FUNC PyInit_perf(void)
};
PyObject *module = PyModule_Create(&moduledef);
- if (module == NULL ||
- pyrf_event__setup_types() < 0 ||
+ if (module == NULL)
+ return NULL;
+
+ if (pyrf_event__setup_types() < 0 ||
pyrf_evlist__setup_types() < 0 ||
pyrf_evsel__setup_types() < 0 ||
pyrf_thread_map__setup_types() < 0 ||
@@ -2480,8 +2769,12 @@ PyMODINIT_FUNC PyInit_perf(void)
pyrf_pmu_iterator__setup_types() < 0 ||
pyrf_pmu__setup_types() < 0 ||
pyrf_counts_values__setup_types() < 0 ||
- pyrf_data__setup_types() < 0)
- return module;
+ pyrf_data__setup_types() < 0 ||
+ pyrf_session__setup_types() < 0 ||
+ pyrf_thread__setup_types() < 0) {
+ Py_DECREF(module);
+ return NULL;
+ }
/* The page_size is placed in util object. */
page_size = sysconf(_SC_PAGE_SIZE);
@@ -2531,6 +2824,9 @@ PyMODINIT_FUNC PyInit_perf(void)
Py_INCREF(&pyrf_data__type);
PyModule_AddObject(module, "data", (PyObject *)&pyrf_data__type);
+ Py_INCREF(&pyrf_session__type);
+ PyModule_AddObject(module, "session", (PyObject *)&pyrf_session__type);
+
dict = PyModule_GetDict(module);
if (dict == NULL)
goto error;
@@ -2544,7 +2840,9 @@ PyMODINIT_FUNC PyInit_perf(void)
}
error:
- if (PyErr_Occurred())
- PyErr_SetString(PyExc_ImportError, "perf: Init failed!");
+ if (PyErr_Occurred()) {
+ Py_XDECREF(module);
+ return NULL;
+ }
return module;
}
--
2.54.0.794.g4f17f83d09-goog
^ permalink raw reply related
* [PATCH v3 00/11] add mcf54415 DAC driver
From: Angelo Dureghello @ 2026-05-22 21:20 UTC (permalink / raw)
To: Greg Ungerer, Geert Uytterhoeven, Steven King, Arnd Bergmann,
Maxime Coquelin, Alexandre Torgue, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko
Cc: Greg Ungerer, linux-m68k, linux-kernel, linux-stm32,
linux-arm-kernel, linux-iio, Angelo Dureghello
This patchset adds a minimalistic DAC driver for the NXP mcf54415/6/7/8
ibuiltin DACs.
Currently the driver enables the raw write only. Feature as dma, sync, or
format are not supoprted for this version.
Additional options suppoerted by the DAC module will be added to the driver
later on, as needed.
The same patchset prepares the m68k/coldfire architecture to support
the driver.
Below soem basic tests done on stmark2 mcf54415-based board, voltage check
on DAC0 and DAC1:
~ # cd /sys/bus/iio/devices/iio:device0/
/sys/bus/iio/devices/iio:device0 # ls
name out_voltage_scale uevent
out_voltage_raw subsystem
/sys/bus/iio/devices/iio:device0 # cat name
mcf54415
/sys/bus/iio/devices/iio:device0 # echo 4095 > out_voltage_raw
/sys/bus/iio/devices/iio:device0 # echo 2048 > out_voltage_raw
/sys/bus/iio/devices/iio:device0 # echo 4096 > out_voltage_raw
sh: write error: Invalid argument
/sys/bus/iio/devices/iio:device0 # cat out_voltage_raw
2048
/sys/bus/iio/devices/iio:device0 #
Same behavior for /sys/bus/iio/devices/iio:device1.
Generated a sine wave by shell script, sine shape is good.
is actually in progress:
Note: this patchset depends on mew mcf_read/mcf_write implementation that
Link: https://lore.kernel.org/linux-m68k/209d0653-6386-4b64-9e15-e358f84453ab@app.fastmail.com/T/#t
Link: https://lore.kernel.org/linux-m68k/20260506142644.3234270-2-gerg@kernel.org/
---
Changes in v3:
- keeping changelog in each single patch, where any
- Link to v2: https://patch.msgid.link/20260513-wip-stmark2-dac-v2-0-fcdae50cf51a@baylibre.com
Changes in v2:
- keeping changelog in each single patch, where any
- Link to v1: https://patch.msgid.link/20260504-wip-stmark2-dac-v1-0-874c36a4910d@baylibre.com
To: Greg Ungerer <gerg@linux-m68k.org>
To: Geert Uytterhoeven <geert@linux-m68k.org>
To: Steven King <sfking@fdwdc.com>
To: Arnd Bergmann <arnd@arndb.de>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
To: Alexandre Torgue <alexandre.torgue@foss.st.com>
To: Jonathan Cameron <jic23@kernel.org>
To: David Lechner <dlechner@baylibre.com>
To: Nuno Sá <nuno.sa@analog.com>
To: Andy Shevchenko <andy@kernel.org>
Cc: Greg Ungerer <gerg@uclinux.org>
Cc: linux-m68k@lists.linux-m68k.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-stm32@st-md-mailman.stormreply.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-iio@vger.kernel.org
---
Angelo Dureghello (11):
m68k: mcf5441x: fix clocks numbering
m68k: mcf5441x: add clock for DAC channel 1
m68k: mcf5441x: initialize DAC clocks by iio DAC driver name
m68k: defconfig: update stmark2 defconfig
m68k: add DAC modules base addresses
m68k: mcf5441x: add CCM registers
m68k: mcf5441x: add CCR MISCCR2 bitfields
m68k: stmark2: use ioport.h macros for resources
m68k: stmark2: add mcf5441x DAC platform devices
m68k: stmark2: enable DACs outputs
iio: dac: add mcf54415 DAC
arch/m68k/coldfire/m5441x.c | 21 ++--
arch/m68k/coldfire/stmark2.c | 47 +++++---
arch/m68k/configs/stmark2_defconfig | 2 +
arch/m68k/include/asm/m5441xsim.h | 42 ++++++++
drivers/iio/dac/Kconfig | 11 ++
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/mcf54415_dac.c | 207 ++++++++++++++++++++++++++++++++++++
7 files changed, 307 insertions(+), 24 deletions(-)
---
base-commit: 9edf4183f0d32146b708220d56ee2d35905ba516
change-id: 20260430-wip-stmark2-dac-7060f49dd94f
Best regards,
--
Angelo Dureghello <adureghello@baylibre.com>
^ permalink raw reply
* [PATCH v3 02/11] m68k: mcf5441x: add clock for DAC channel 1
From: Angelo Dureghello @ 2026-05-22 21:20 UTC (permalink / raw)
To: Greg Ungerer, Geert Uytterhoeven, Steven King, Arnd Bergmann,
Maxime Coquelin, Alexandre Torgue, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko
Cc: Greg Ungerer, linux-m68k, linux-kernel, linux-stm32,
linux-arm-kernel, linux-iio, Angelo Dureghello
In-Reply-To: <20260522-wip-stmark2-dac-v3-0-16be0ad35a67@baylibre.com>
From: Angelo Dureghello <adureghello@baylibre.com>
Add missing clock for mcf5441x DAC channel 1.
Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
---
arch/m68k/coldfire/m5441x.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/m68k/coldfire/m5441x.c b/arch/m68k/coldfire/m5441x.c
index 613b0275d9d8..5b5e09ecf487 100644
--- a/arch/m68k/coldfire/m5441x.c
+++ b/arch/m68k/coldfire/m5441x.c
@@ -44,6 +44,7 @@ DEFINE_CLK(0, "mcfpit.3", 35, MCF_BUSCLK);
DEFINE_CLK(0, "mcfeport.0", 36, MCF_CLK);
DEFINE_CLK(0, "mcfadc.0", 37, MCF_CLK);
DEFINE_CLK(0, "mcfdac.0", 38, MCF_CLK);
+DEFINE_CLK(0, "mcfdac.1", 39, MCF_CLK);
DEFINE_CLK(0, "mcfrtc.0", 42, MCF_CLK);
DEFINE_CLK(0, "mcfsim.0", 43, MCF_CLK);
DEFINE_CLK(0, "mcfusb-otg.0", 44, MCF_CLK);
@@ -106,6 +107,7 @@ static struct clk_lookup m5411x_clk_lookup[] = {
CLKDEV_INIT("mcfeport.0", NULL, &__clk_0_36),
CLKDEV_INIT("mcfadc.0", NULL, &__clk_0_37),
CLKDEV_INIT("mcfdac.0", NULL, &__clk_0_38),
+ CLKDEV_INIT("mcfdac.1", NULL, &__clk_0_39),
CLKDEV_INIT("mcfrtc.0", NULL, &__clk_0_42),
CLKDEV_INIT("mcfsim.0", NULL, &__clk_0_43),
CLKDEV_INIT("mcfusb-otg.0", NULL, &__clk_0_44),
@@ -176,6 +178,7 @@ static struct clk * const disable_clks[] __initconst = {
&__clk_0_35, /* pit.3 */
&__clk_0_37, /* adc */
&__clk_0_38, /* dac.0 */
+ &__clk_0_39, /* dac.1 */
&__clk_0_44, /* usb otg */
&__clk_0_45, /* usb host */
&__clk_0_47, /* ssi.0 */
--
2.54.0
^ permalink raw reply related
* [PATCH v9 16/23] perf python: Add mmap2 event
From: Ian Rogers @ 2026-05-22 22:04 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, alice.mei.rogers, dapeng1.mi, james.clark, leo.yan,
linux-arm-kernel, linux-kernel, linux-perf-users, mingo, peterz,
tmricht
In-Reply-To: <20260522220435.2378363-1-irogers@google.com>
If mmap is handled so should mmap2 events. Add support as a distinct
python event type.
Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/python.c | 60 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 029915b7a1fe..daedd67f12d5 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -153,6 +153,54 @@ static PyTypeObject pyrf_mmap_event__type = {
.tp_repr = (reprfunc)pyrf_mmap_event__repr,
};
+static const char pyrf_mmap2_event__doc[] = PyDoc_STR("perf mmap2 event object.");
+
+static PyMemberDef pyrf_mmap2_event__members[] = {
+ sample_members
+ member_def(perf_event_header, type, T_UINT, "event type"),
+ member_def(perf_event_header, misc, T_UINT, "event misc"),
+ member_def(perf_record_mmap2, pid, T_UINT, "event pid"),
+ member_def(perf_record_mmap2, tid, T_UINT, "event tid"),
+ member_def(perf_record_mmap2, start, T_ULONGLONG, "start of the map"),
+ member_def(perf_record_mmap2, len, T_ULONGLONG, "map length"),
+ member_def(perf_record_mmap2, pgoff, T_ULONGLONG, "page offset"),
+ member_def(perf_record_mmap2, prot, T_UINT, "protection"),
+ member_def(perf_record_mmap2, flags, T_UINT, "flags"),
+ member_def(perf_record_mmap2, filename, T_STRING_INPLACE, "backing store"),
+ { .name = NULL, },
+};
+
+static PyObject *pyrf_mmap2_event__repr(const struct pyrf_event *pevent)
+{
+ PyObject *ret;
+ char *s;
+
+ if (asprintf(&s, "{ type: mmap2, pid: %u, tid: %u, start: %#" PRI_lx64 ", "
+ "length: %#" PRI_lx64 ", offset: %#" PRI_lx64 ", "
+ "flags: %#x, prot: %#x, filename: %s }",
+ pevent->event.mmap2.pid, pevent->event.mmap2.tid,
+ pevent->event.mmap2.start, pevent->event.mmap2.len,
+ pevent->event.mmap2.pgoff, pevent->event.mmap2.flags,
+ pevent->event.mmap2.prot, pevent->event.mmap2.filename) < 0)
+ return PyErr_NoMemory();
+
+ ret = PyUnicode_FromString(s);
+ free(s);
+ return ret;
+}
+
+static PyTypeObject pyrf_mmap2_event__type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "perf.mmap2_event",
+ .tp_basicsize = sizeof(struct pyrf_event),
+ .tp_dealloc = (destructor)pyrf_event__delete,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_doc = pyrf_mmap2_event__doc,
+ .tp_members = pyrf_mmap2_event__members,
+ .tp_getset = pyrf_event__getset,
+ .tp_repr = (reprfunc)pyrf_mmap2_event__repr,
+};
+
static const char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object.");
static PyMemberDef pyrf_task_event__members[] = {
@@ -778,6 +826,9 @@ static int pyrf_event__setup_types(void)
int err;
err = PyType_Ready(&pyrf_mmap_event__type);
+ if (err < 0)
+ goto out;
+ err = PyType_Ready(&pyrf_mmap2_event__type);
if (err < 0)
goto out;
err = PyType_Ready(&pyrf_lost_event__type);
@@ -807,6 +858,7 @@ static int pyrf_event__setup_types(void)
static PyTypeObject *pyrf_event__type[] = {
[PERF_RECORD_MMAP] = &pyrf_mmap_event__type,
+ [PERF_RECORD_MMAP2] = &pyrf_mmap2_event__type,
[PERF_RECORD_LOST] = &pyrf_lost_event__type,
[PERF_RECORD_COMM] = &pyrf_comm_event__type,
[PERF_RECORD_EXIT] = &pyrf_task_event__type,
@@ -973,6 +1025,11 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
size_t max_len = pevent->event.header.size - offsetof(struct perf_record_mmap, filename);
pevent->event.mmap.filename[max_len - 1] = '\0';
+ } else if (event->header.type == PERF_RECORD_MMAP2) {
+ /* Ensure '\0' string termination. */
+ size_t max_len = pevent->event.header.size - offsetof(struct perf_record_mmap2, filename);
+
+ pevent->event.mmap2.filename[max_len - 1] = '\0';
}
perf_sample__init(&pevent->sample, /*all=*/true);
@@ -3238,6 +3295,9 @@ PyMODINIT_FUNC PyInit_perf(void)
Py_INCREF(&pyrf_mmap_event__type);
PyModule_AddObject(module, "mmap_event", (PyObject *)&pyrf_mmap_event__type);
+ Py_INCREF(&pyrf_mmap2_event__type);
+ PyModule_AddObject(module, "mmap2_event", (PyObject *)&pyrf_mmap2_event__type);
+
Py_INCREF(&pyrf_lost_event__type);
PyModule_AddObject(module, "lost_event", (PyObject *)&pyrf_lost_event__type);
--
2.54.0.794.g4f17f83d09-goog
^ 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