* [PATCH 0/3 v8] Add CPU hotplug support for PAI PMU
@ 2026-09-01 8:54 Thomas Richter
2026-09-01 8:54 ` [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event Thomas Richter
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Thomas Richter @ 2026-09-01 8:54 UTC (permalink / raw)
To: linux-s390; +Cc: Thomas Richter
V8: Add patch version 8 (v8) to each subject line
V7: Add findings from Jan Polensky
- Fixed spelling in code and commit messages.
- Added correct Cc: stable tag.
V6: Add findings from Sashiko
- Remove cpus_read_lock() and cpus_read_unlock() invocations
from CPU hotplug handlers in patch 3.
V5: Add findings from Sashiko
- Add cpus_read_lock() and cpus_read_unlock() in patch 3.
V4: Add findings from Sumanth and updated patch 3/3.
- Fix reference count for CPU hotplug remove while multiple
per-task events are active.
V3: Add findings from Sumanth and updated patch 3/3.
- Refined Fixes: commit id and messages
- Handle cpuhp_setup_state() failure
V2: Add findings from Sumanth and updated patch 3/3.
- handle possible NULL pointer dereference in pai_event_destroy_cpu()
- handle possibe memory allocation failure in pai_online_cpu()
- add Reviewed-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
to patch 1/3 and patch 2/3.
Add support for CPU hotplug add and remove for PAI PMU.
Patch 1: Change function argument list
Patch 2: Move locking from CPU scope to event scope
Patch 3: Add CPU hotplug handler
Thomas Richter (3):
s390/pai: Use PAI PMU index as parameter replacing event
s390/pai: Move locking to event init and delete
s390/pai: Support CPU hotplug for PMU PAI
arch/s390/include/asm/pai.h | 1 -
arch/s390/kernel/perf_pai.c | 182 +++++++++++++++++++++++++-----------
2 files changed, 128 insertions(+), 55 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event
2026-09-01 8:54 [PATCH 0/3 v8] Add CPU hotplug support for PAI PMU Thomas Richter
@ 2026-09-01 8:54 ` Thomas Richter
2026-09-01 9:12 ` sashiko-bot
2026-09-01 8:54 ` [PATCH 2/3 v8] s390/pai: Move locking to event init and delete Thomas Richter
2026-09-01 8:54 ` [PATCH 3/3 v8] s390/pai: Support CPU hotplug for PMU PAI Thomas Richter
2 siblings, 1 reply; 8+ messages in thread
From: Thomas Richter @ 2026-09-01 8:54 UTC (permalink / raw)
To: linux-s390; +Cc: Thomas Richter, Sumanth Korikkar
Use PAI PMU index value as function argument instead of pointer
to struct perf_event. Only that index value is used inside
functions pai_alloc_cpu() and pai_event_destroy_cpu().
No functional change.
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
arch/s390/kernel/perf_pai.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
index cdb8006220ca..7c13f5586c79 100644
--- a/arch/s390/kernel/perf_pai.c
+++ b/arch/s390/kernel/perf_pai.c
@@ -140,16 +140,14 @@ static void pai_free(struct pai_mapptr *mp)
/* Adjust usage counters and remove allocated memory when all users are
* gone.
*/
-static void pai_event_destroy_cpu(struct perf_event *event, int cpu)
+static void pai_event_destroy_cpu(int idx, int cpu)
{
- int idx = PAI_PMU_IDX(event);
struct pai_mapptr *mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
struct pai_map *cpump = mp->mapptr;
mutex_lock(&pai_reserve_mutex);
- debug_sprintf_event(paidbg, 5, "%s event %#llx idx %d cpu %d users %d "
- "refcnt %u\n", __func__, event->attr.config, idx,
- event->cpu, cpump->active_events,
+ debug_sprintf_event(paidbg, 5, "%s users %d refcnt %u\n",
+ __func__, cpump->active_events,
refcount_read(&cpump->refcnt));
if (refcount_dec_and_test(&cpump->refcnt))
pai_free(mp);
@@ -159,17 +157,17 @@ static void pai_event_destroy_cpu(struct perf_event *event, int cpu)
static void pai_event_destroy(struct perf_event *event)
{
- int cpu;
+ int cpu = 0, idx = PAI_PMU_IDX(event);
free_page(PAI_SAVE_AREA(event));
if (event->cpu == -1) {
struct cpumask *mask = PAI_CPU_MASK(event);
for_each_cpu(cpu, mask)
- pai_event_destroy_cpu(event, cpu);
+ pai_event_destroy_cpu(idx, cpu);
kfree(mask);
} else {
- pai_event_destroy_cpu(event, event->cpu);
+ pai_event_destroy_cpu(idx, event->cpu);
}
}
@@ -241,12 +239,12 @@ static u64 paicrypt_getall(struct perf_event *event)
*
* Allocate the memory for the event.
*/
-static int pai_alloc_cpu(struct perf_event *event, int cpu)
+static int pai_alloc_cpu(int idx, int cpu)
{
- int rc, idx = PAI_PMU_IDX(event);
struct pai_map *cpump = NULL;
bool need_paiext_cb = false;
struct pai_mapptr *mp;
+ int rc;
mutex_lock(&pai_reserve_mutex);
/* Allocate root node */
@@ -318,6 +316,7 @@ static int pai_alloc_cpu(struct perf_event *event, int cpu)
static int pai_alloc(struct perf_event *event)
{
+ int idx = PAI_PMU_IDX(event);
struct cpumask *maskptr;
int cpu, rc = -ENOMEM;
@@ -326,10 +325,10 @@ static int pai_alloc(struct perf_event *event)
goto out;
for_each_online_cpu(cpu) {
- rc = pai_alloc_cpu(event, cpu);
+ rc = pai_alloc_cpu(idx, cpu);
if (rc) {
for_each_cpu(cpu, maskptr)
- pai_event_destroy_cpu(event, cpu);
+ pai_event_destroy_cpu(idx, cpu);
kfree(maskptr);
goto out;
}
@@ -392,7 +391,7 @@ static int pai_event_init(struct perf_event *event, int idx)
}
if (event->cpu >= 0)
- rc = pai_alloc_cpu(event, event->cpu);
+ rc = pai_alloc_cpu(idx, event->cpu);
else
rc = pai_alloc(event);
if (rc) {
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3 v8] s390/pai: Move locking to event init and delete
2026-09-01 8:54 [PATCH 0/3 v8] Add CPU hotplug support for PAI PMU Thomas Richter
2026-09-01 8:54 ` [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event Thomas Richter
@ 2026-09-01 8:54 ` Thomas Richter
2026-09-01 9:12 ` sashiko-bot
2026-09-01 8:54 ` [PATCH 3/3 v8] s390/pai: Support CPU hotplug for PMU PAI Thomas Richter
2 siblings, 1 reply; 8+ messages in thread
From: Thomas Richter @ 2026-09-01 8:54 UTC (permalink / raw)
To: linux-s390; +Cc: Thomas Richter, Sumanth Korikkar
Move mutex locking from per CPU allocation to event allocation.
No functional change.
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Sumanth Korikkar <sumanthk@linux.ibm.com>
---
arch/s390/kernel/perf_pai.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
index 7c13f5586c79..52d9f654346a 100644
--- a/arch/s390/kernel/perf_pai.c
+++ b/arch/s390/kernel/perf_pai.c
@@ -138,21 +138,19 @@ static void pai_free(struct pai_mapptr *mp)
}
/* Adjust usage counters and remove allocated memory when all users are
- * gone.
+ * gone. Called under mutex_lock.
*/
static void pai_event_destroy_cpu(int idx, int cpu)
{
struct pai_mapptr *mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
struct pai_map *cpump = mp->mapptr;
- mutex_lock(&pai_reserve_mutex);
debug_sprintf_event(paidbg, 5, "%s users %d refcnt %u\n",
__func__, cpump->active_events,
refcount_read(&cpump->refcnt));
if (refcount_dec_and_test(&cpump->refcnt))
pai_free(mp);
pai_root_free(idx);
- mutex_unlock(&pai_reserve_mutex);
}
static void pai_event_destroy(struct perf_event *event)
@@ -160,6 +158,7 @@ static void pai_event_destroy(struct perf_event *event)
int cpu = 0, idx = PAI_PMU_IDX(event);
free_page(PAI_SAVE_AREA(event));
+ mutex_lock(&pai_reserve_mutex);
if (event->cpu == -1) {
struct cpumask *mask = PAI_CPU_MASK(event);
@@ -169,6 +168,7 @@ static void pai_event_destroy(struct perf_event *event)
} else {
pai_event_destroy_cpu(idx, event->cpu);
}
+ mutex_unlock(&pai_reserve_mutex);
}
static void paicrypt_event_destroy(struct perf_event *event)
@@ -232,12 +232,10 @@ static u64 paicrypt_getall(struct perf_event *event)
return sum;
}
-/* Check concurrent access of counting and sampling for crypto events.
- * This function is called in process context and it is save to block.
- * When the event initialization functions fails, no other call back will
- * be invoked.
- *
- * Allocate the memory for the event.
+/* Allocate all per-CPU data structures. This function is called in
+ * process context and can block. In case of error all partly allocated
+ * memory is released and the reference counters adjusted correctly.
+ * Called under mutex_lock.
*/
static int pai_alloc_cpu(int idx, int cpu)
{
@@ -246,11 +244,10 @@ static int pai_alloc_cpu(int idx, int cpu)
struct pai_mapptr *mp;
int rc;
- mutex_lock(&pai_reserve_mutex);
/* Allocate root node */
rc = pai_root_alloc(idx);
if (rc)
- goto unlock;
+ goto out;
/* Allocate node for this event */
mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
@@ -308,12 +305,12 @@ static int pai_alloc_cpu(int idx, int cpu)
*/
pai_root_free(idx);
}
-unlock:
- mutex_unlock(&pai_reserve_mutex);
+out:
/* If rc is non-zero, no increment of counter/sampler was done. */
return rc;
}
+/* Called under mutex_lock */
static int pai_alloc(struct perf_event *event)
{
int idx = PAI_PMU_IDX(event);
@@ -390,10 +387,12 @@ static int pai_event_init(struct perf_event *event, int idx)
}
}
+ mutex_lock(&pai_reserve_mutex);
if (event->cpu >= 0)
rc = pai_alloc_cpu(idx, event->cpu);
else
rc = pai_alloc(event);
+ mutex_unlock(&pai_reserve_mutex);
if (rc) {
free_page(PAI_SAVE_AREA(event));
goto out;
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3 v8] s390/pai: Support CPU hotplug for PMU PAI
2026-09-01 8:54 [PATCH 0/3 v8] Add CPU hotplug support for PAI PMU Thomas Richter
2026-09-01 8:54 ` [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event Thomas Richter
2026-09-01 8:54 ` [PATCH 2/3 v8] s390/pai: Move locking to event init and delete Thomas Richter
@ 2026-09-01 8:54 ` Thomas Richter
2026-09-01 9:03 ` sashiko-bot
2 siblings, 1 reply; 8+ messages in thread
From: Thomas Richter @ 2026-09-01 8:54 UTC (permalink / raw)
To: linux-s390; +Cc: Thomas Richter, stable, Jan Polensky
The command 'perf stat -e pai_crypto/CRYPTO_ALL/ -- <command>'
crashes the kernel when CPUs are hotplug added during that run.
Root cause is the missing allocation of per-CPU data structures
for that new CPU. The allocation is dynamic and the first
event that has task context creates such a structure for
each online CPU. This is not sufficient. CPUs may be offline
during event creation and can be set online during the
perf run time. For example commands
# echo 0 > /sys/devices/system/cpu/cpu1/online
# perf stat -e cycles -i -- stress-ng -t10s --matrix X
# sleep 1
# echo 1 > /sys/devices/system/cpu/cpu1/online
Currently without a CPU hotplug handler, that new CPU has no
per-CPU data infrastructure. The scheduler runs PMU call back
function pai_add() to install the PMU support for that CPU before
the task is being scheduled on that new CPU.
In pai_add() instructions
mp = this_cpu_ptr(pai_root[idx].mapptr);
cpump = mp->mapptr;
return a NULL pointer and the result is a kernel panic as variable
cpump is used inside that function.
Add CPU hotplug support for CPU add and delete and create
the necessary per-CPU data infrastructure during CPU hotplug
add processing. Same for CPU hotplug remove.
This is done when the CPU is offline to ensure the data structures
are available when CPU is made online and tasks are scheduled on it.
Cc: stable@vger.kernel.org # v6.19
Fixes: 582cc1b28e8c ("s390/pai_ext: Enable per-task and system-wide sampling event")
Fixes: 9f66572f2889 ("s390/pai_crypto: Enable per-task and system-wide sampling event")
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Jan Polensky <japo@linux.ibm.com>
---
arch/s390/include/asm/pai.h | 1 -
arch/s390/kernel/perf_pai.c | 164 ++++++++++++++++++++++++++----------
2 files changed, 120 insertions(+), 45 deletions(-)
diff --git a/arch/s390/include/asm/pai.h b/arch/s390/include/asm/pai.h
index 534d0320e2aa..a3456a36aaa7 100644
--- a/arch/s390/include/asm/pai.h
+++ b/arch/s390/include/asm/pai.h
@@ -76,7 +76,6 @@ static __always_inline void pai_kernel_exit(struct pt_regs *regs)
}
#define PAI_SAVE_AREA(x) ((x)->hw.event_base)
-#define PAI_CPU_MASK(x) ((x)->hw.addr_filters)
#define PAI_PMU_IDX(x) ((x)->hw.last_tag)
#define PAI_SWLIST(x) (&(x)->hw.tp_list)
diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
index 52d9f654346a..794ce69a2fd5 100644
--- a/arch/s390/kernel/perf_pai.c
+++ b/arch/s390/kernel/perf_pai.c
@@ -67,6 +67,7 @@ struct pai_mapptr {
static struct pai_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */
+ atomic_t tskctx; /* Overall per-task events */
struct pai_mapptr __percpu *mapptr;
} pai_root[PAI_PMU_MAX];
@@ -93,14 +94,15 @@ struct pai_pmu { /* Define PAI PMU characteristics */
static struct pai_pmu pai_pmu[]; /* Forward declaration */
/* Free per CPU data when the last event is removed. */
-static void pai_root_free(int idx)
+static void pai_root_free(int idx, int tasks)
{
- if (refcount_dec_and_test(&pai_root[idx].refcnt)) {
+ if (refcount_sub_and_test(tasks, &pai_root[idx].refcnt)) {
free_percpu(pai_root[idx].mapptr);
pai_root[idx].mapptr = NULL;
}
- debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d\n", __func__,
- idx, refcount_read(&pai_root[idx].refcnt));
+ debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d tskctx %d\n",
+ __func__, idx, refcount_read(&pai_root[idx].refcnt),
+ atomic_read(&pai_root[idx].tskctx));
}
/*
@@ -137,20 +139,36 @@ static void pai_free(struct pai_mapptr *mp)
mp->mapptr = NULL;
}
-/* Adjust usage counters and remove allocated memory when all users are
- * gone. Called under mutex_lock.
- */
-static void pai_event_destroy_cpu(int idx, int cpu)
+/* Called under mutex_lock */
+static void pai_event_destroy_cpu(int idx, int cpu, bool hotplug)
{
- struct pai_mapptr *mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
- struct pai_map *cpump = mp->mapptr;
+ struct pai_mapptr *mp;
+ struct pai_map *cpump;
+ int tasks = 1;
- debug_sprintf_event(paidbg, 5, "%s users %d refcnt %u\n",
- __func__, cpump->active_events,
- refcount_read(&cpump->refcnt));
- if (refcount_dec_and_test(&cpump->refcnt))
+ /* Check reference count and return when all gone.
+ * 1. An event is installed on online CPU X.
+ * 2. CPU x is offlined and the per-CPU data is removed.
+ * 3. Event is destroyed via close system call.
+ */
+ if (!refcount_read(&pai_root[idx].refcnt))
+ return; /* No events at all */
+ mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
+ if (!mp || !mp->mapptr) /* No events on that CPU */
+ return;
+
+ /* When hotplug is true, invocation is from CPU hotplug callback.
+ * Delete per-CPU resource and adjust refcnt when per-task events
+ * are currently active. This can be more than one.
+ * In this case adjust counters.
+ */
+ if (hotplug)
+ tasks = atomic_read(&pai_root[idx].tskctx);
+
+ cpump = mp->mapptr;
+ if (refcount_sub_and_test(tasks, &cpump->refcnt))
pai_free(mp);
- pai_root_free(idx);
+ pai_root_free(idx, tasks);
}
static void pai_event_destroy(struct perf_event *event)
@@ -158,17 +176,17 @@ static void pai_event_destroy(struct perf_event *event)
int cpu = 0, idx = PAI_PMU_IDX(event);
free_page(PAI_SAVE_AREA(event));
+ cpus_read_lock();
mutex_lock(&pai_reserve_mutex);
if (event->cpu == -1) {
- struct cpumask *mask = PAI_CPU_MASK(event);
-
- for_each_cpu(cpu, mask)
- pai_event_destroy_cpu(idx, cpu);
- kfree(mask);
+ atomic_dec(&pai_root[idx].tskctx);
+ for_each_online_cpu(cpu)
+ pai_event_destroy_cpu(idx, cpu, false);
} else {
- pai_event_destroy_cpu(idx, event->cpu);
+ pai_event_destroy_cpu(idx, event->cpu, false);
}
mutex_unlock(&pai_reserve_mutex);
+ cpus_read_unlock();
}
static void paicrypt_event_destroy(struct perf_event *event)
@@ -232,17 +250,25 @@ static u64 paicrypt_getall(struct perf_event *event)
return sum;
}
-/* Allocate all per-CPU data structures. This function is called in
- * process context and can block. In case of error all partly allocated
- * memory is released and the reference counters adjusted correctly.
- * Called under mutex_lock.
- */
-static int pai_alloc_cpu(int idx, int cpu)
+/* Called under mutex_lock */
+static int pai_alloc_cpu(int idx, int cpu, bool hotplug)
{
struct pai_map *cpump = NULL;
bool need_paiext_cb = false;
struct pai_mapptr *mp;
- int rc;
+ int tasks = 1, rc = 0;
+
+ /* When hotplug is true, invocation is from CPU hotplug callback.
+ * Allocate per-CPU resource when per-task events are currently active.
+ * This can be more than one. In this case adjust all reference
+ * counters. Otherwise return, this ensures memory is only allocated
+ * when needed.
+ */
+ if (hotplug) {
+ tasks = atomic_read(&pai_root[idx].tskctx);
+ if (!tasks)
+ goto out;
+ }
/* Allocate root node */
rc = pai_root_alloc(idx);
@@ -291,26 +317,42 @@ static int pai_alloc_cpu(int idx, int cpu)
goto undo;
}
INIT_LIST_HEAD(&cpump->syswide_list);
- refcount_set(&cpump->refcnt, 1);
+ refcount_set(&cpump->refcnt, tasks);
rc = 0;
} else {
- refcount_inc(&cpump->refcnt);
+ refcount_add(tasks, &cpump->refcnt);
}
+ /* If tasks is greater than 1, we are called from CPU hotplug path
+ * and need to adjust the pai_root[idx].refcnt by the number of
+ * per-process events. Function pai_root_alloc(idx) already
+ * incremented by one. Adjust for the rest.
+ */
+ if (tasks > 1)
+ refcount_add(tasks - 1, &pai_root[idx].refcnt);
undo:
if (rc) {
/* Error in allocation of event, decrement anchor. Since
* the event in not created, its destroy() function is never
* invoked. Adjust the reference counter for the anchor.
+ * The failure happened in the case of variable
+ * cpump == NULL branch above. The pai_root[XXX].refcnt has
+ * been incremented by one. Then the per-CPU allocation
+ * failed, so decrement it by one, regardless of tasks.
*/
- pai_root_free(idx);
+ pai_root_free(idx, 1);
}
out:
/* If rc is non-zero, no increment of counter/sampler was done. */
return rc;
}
-/* Called under mutex_lock */
+/* Check concurrent access of counting and sampling for PAI events.
+ * This function is called in process context and it is safe to block.
+ * When the event initialization functions fails, no other call back will
+ * be invoked.
+ * Called under mutex_lock.
+ */
static int pai_alloc(struct perf_event *event)
{
int idx = PAI_PMU_IDX(event);
@@ -322,24 +364,20 @@ static int pai_alloc(struct perf_event *event)
goto out;
for_each_online_cpu(cpu) {
- rc = pai_alloc_cpu(idx, cpu);
+ rc = pai_alloc_cpu(idx, cpu, false);
if (rc) {
for_each_cpu(cpu, maskptr)
- pai_event_destroy_cpu(idx, cpu);
- kfree(maskptr);
- goto out;
+ pai_event_destroy_cpu(idx, cpu, false);
+ goto undo;
}
cpumask_set_cpu(cpu, maskptr);
}
- /*
- * On error all cpumask are freed and all events have been destroyed.
- * Save of which CPUs data structures have been allocated for.
- * Release them in pai_event_destroy call back function
- * for this event.
- */
- PAI_CPU_MASK(event) = maskptr;
rc = 0;
+ /* Trace per-task events for CPU hotplug. */
+ atomic_inc(&pai_root[idx].tskctx);
+undo:
+ kfree(maskptr);
out:
return rc;
}
@@ -387,12 +425,14 @@ static int pai_event_init(struct perf_event *event, int idx)
}
}
+ cpus_read_lock();
mutex_lock(&pai_reserve_mutex);
if (event->cpu >= 0)
- rc = pai_alloc_cpu(idx, event->cpu);
+ rc = pai_alloc_cpu(idx, event->cpu, false);
else
rc = pai_alloc(event);
mutex_unlock(&pai_reserve_mutex);
+ cpus_read_unlock();
if (rc) {
free_page(PAI_SAVE_AREA(event));
goto out;
@@ -1216,8 +1256,34 @@ static int __init paipmu_setup(void)
return install_ok;
}
+static int pai_online_cpu(unsigned int cpu)
+{
+ int rc;
+
+ mutex_lock(&pai_reserve_mutex);
+ rc = pai_alloc_cpu(PAI_PMU_CRYPTO, cpu, true);
+ if (!rc) {
+ rc = pai_alloc_cpu(PAI_PMU_EXT, cpu, true);
+ if (rc)
+ pai_event_destroy_cpu(PAI_PMU_CRYPTO, cpu, true);
+ }
+ mutex_unlock(&pai_reserve_mutex);
+ return rc;
+}
+
+static int pai_offline_cpu(unsigned int cpu)
+{
+ mutex_lock(&pai_reserve_mutex);
+ pai_event_destroy_cpu(PAI_PMU_CRYPTO, cpu, true);
+ pai_event_destroy_cpu(PAI_PMU_EXT, cpu, true);
+ mutex_unlock(&pai_reserve_mutex);
+ return 0;
+}
+
static int __init pai_init(void)
{
+ int rc;
+
/* Setup s390dbf facility */
paidbg = debug_register("pai", 32, 256, 128);
if (!paidbg) {
@@ -1226,7 +1292,17 @@ static int __init pai_init(void)
}
debug_register_view(paidbg, &debug_sprintf_view);
+ /* CPUHP_BP_PREPARE_DYN --> before CPU is brought online */
+ rc = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "perf/pai:prepare",
+ pai_online_cpu, pai_offline_cpu);
+ if (rc < 0) {
+ debug_unregister_view(paidbg, &debug_sprintf_view);
+ debug_unregister(paidbg);
+ return rc;
+ }
+
if (!paipmu_setup()) {
+ cpuhp_remove_state(rc);
/* No PMU registration, no need for debug buffer */
debug_unregister_view(paidbg, &debug_sprintf_view);
debug_unregister(paidbg);
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3 v8] s390/pai: Support CPU hotplug for PMU PAI
2026-09-01 8:54 ` [PATCH 3/3 v8] s390/pai: Support CPU hotplug for PMU PAI Thomas Richter
@ 2026-09-01 9:03 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-09-01 9:03 UTC (permalink / raw)
To: Thomas Richter
Cc: linux-s390, Vasily Gorbik, Christian Borntraeger, Heiko Carstens,
Alexander Gordeev
> The command 'perf stat -e pai_crypto/CRYPTO_ALL/ -- <command>'
> crashes the kernel when CPUs are hotplug added during that run.
>
> Root cause is the missing allocation of per-CPU data structures
> for that new CPU. The allocation is dynamic and the first
> event that has task context creates such a structure for
> each online CPU. This is not sufficient. CPUs may be offline
> during event creation and can be set online during the
> perf run time. For example commands
>
> # echo 0 > /sys/devices/system/cpu/cpu1/online
> # perf stat -e cycles -i -- stress-ng -t10s --matrix X
> # sleep 1
> # echo 1 > /sys/devices/system/cpu/cpu1/online
>
> [ ... ]
>
> Cc: stable@vger.kernel.org # v6.19
> Fixes: 582cc1b28e8c ("s390/pai_ext: Enable per-task and system-wide sampling event")
> Fixes: 9f66572f2889 ("s390/pai_crypto: Enable per-task and system-wide sampling event")
> Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260901085448.2260658-1-tmricht@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event
2026-09-01 8:54 ` [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event Thomas Richter
@ 2026-09-01 9:12 ` sashiko-bot
2026-09-01 9:56 ` Thomas Richter
0 siblings, 1 reply; 8+ messages in thread
From: sashiko-bot @ 2026-09-01 9:12 UTC (permalink / raw)
To: Thomas Richter
Cc: linux-s390, Alexander Gordeev, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing bounds check on the hardware-reported NNPA counter count (`ib.num_nnpa`) leads to an out-of-bounds kernel heap read.
--
commit a6f12edfe6f84375133dd3bb77d40f4e8c67385e
Author: Thomas Richter <tmricht@linux.ibm.com>
s390/pai: Use PAI PMU index as parameter replacing event
This patch updates pai_alloc_cpu() and pai_event_destroy_cpu() to use
the PAI PMU index value as a parameter instead of a struct perf_event
pointer. Only the index value is actually needed inside these functions.
> diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
> index cdb8006220ca0..7c13f5586c793 100644
> --- a/arch/s390/kernel/perf_pai.c
> +++ b/arch/s390/kernel/perf_pai.c
[ ... ]
> @@ -241,12 +239,12 @@ static u64 paicrypt_getall(struct perf_event *event)
> *
> * Allocate the memory for the event.
> */
> -static int pai_alloc_cpu(struct perf_event *event, int cpu)
> +static int pai_alloc_cpu(int idx, int cpu)
> {
> - int rc, idx = PAI_PMU_IDX(event);
> struct pai_map *cpump = NULL;
> bool need_paiext_cb = false;
> struct pai_mapptr *mp;
> + int rc;
[Severity: High]
This is a pre-existing issue, but I have a question about how the buffer
allocated in this function is accessed.
In paipmu_setup(), the hardware-reported counter count (ib.num_nnpa) can be
up to 127. The code then artificially increments it for the software ALL event:
arch/s390/kernel/perf_pai.c:paipmu_setup() {
...
case PAI_PMU_EXT:
p->num_avail = ib.num_nnpa;
break;
}
p->num_avail += 1; /* Add xxx_ALL event */
...
}
This means p->num_avail can reach 128.
Later in pai_alloc_cpu(), cpump->area is allocated with a fixed size:
arch/s390/kernel/perf_pai.c:pai_alloc_cpu() {
...
cpump->area = kzalloc(pai_pmu[idx].area_size, GFP_KERNEL);
...
}
For PAI_PMU_EXT, area_size is 1024 bytes, which holds exactly 128 elements.
However, pai_getdata() iterates up to p->num_avail:
arch/s390/kernel/perf_pai.c:pai_getdata() {
...
for (i = 1; i <= pp->num_avail; i++) {
u64 val = pai_getctr(cpump->area, i,
kernel ? pp->kernel_offset : 0);
...
}
If ib.num_nnpa is 127, p->num_avail becomes 128. The loop evaluates i = 128
and pai_getctr() accesses cpump->area[128] (the 129th element).
Can this cause an out-of-bounds heap read if cpump->area is strictly 1024
bytes?
>
> mutex_lock(&pai_reserve_mutex);
> /* Allocate root node */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260901085448.2260658-1-tmricht@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3 v8] s390/pai: Move locking to event init and delete
2026-09-01 8:54 ` [PATCH 2/3 v8] s390/pai: Move locking to event init and delete Thomas Richter
@ 2026-09-01 9:12 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-09-01 9:12 UTC (permalink / raw)
To: Thomas Richter
Cc: Alexander Gordeev, Vasily Gorbik, Christian Borntraeger,
linux-s390, Heiko Carstens
> Move mutex locking from per CPU allocation to event allocation.
> No functional change.
>
> Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260901085448.2260658-1-tmricht@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event
2026-09-01 9:12 ` sashiko-bot
@ 2026-09-01 9:56 ` Thomas Richter
0 siblings, 0 replies; 8+ messages in thread
From: Thomas Richter @ 2026-09-01 9:56 UTC (permalink / raw)
To: sashiko-reviews
Cc: linux-s390, Alexander Gordeev, Heiko Carstens, Vasily Gorbik,
Christian Borntraeger, Thomas Richter, Jan Polensky,
Sumanth Korikkar
On 9/1/26 11:12, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] Missing bounds check on the hardware-reported NNPA counter count (`ib.num_nnpa`) leads to an out-of-bounds kernel heap read.
> --
This is not correct. The value of ib.num_nnpa is read out from hadware and should be correct.
It is assigned to member pai_pmu::num_avail.
Later in function attr_event_init() that value is compared to pai_pmu::num_named
which contains the number of supported events. And the minimum of both values is taken:
In attr_event_init()
unsigned int min_attr = min_t(unsigned int, p->num_named, p->num_avail);
So this is protected.
--
Thomas Richter, Dept 3303, IBM s390 Linux Development, Boeblingen, Germany
--
IBM Deutschland Research & Development GmbH
Vorsitzender des Aufsichtsrats: Wolfgang Wendt
Geschäftsführung: David Faller
Sitz der Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB 243294
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-01 9:56 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-01 8:54 [PATCH 0/3 v8] Add CPU hotplug support for PAI PMU Thomas Richter
2026-09-01 8:54 ` [PATCH 1/3 v8] s390/pai: Use PAI PMU index as parameter replacing event Thomas Richter
2026-09-01 9:12 ` sashiko-bot
2026-09-01 9:56 ` Thomas Richter
2026-09-01 8:54 ` [PATCH 2/3 v8] s390/pai: Move locking to event init and delete Thomas Richter
2026-09-01 9:12 ` sashiko-bot
2026-09-01 8:54 ` [PATCH 3/3 v8] s390/pai: Support CPU hotplug for PMU PAI Thomas Richter
2026-09-01 9:03 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox