[PATCH 2/3] perf sched stats: Fix SIGCHLD race in schedstat_live()

Swapnil Sapkal
2026-04-01T06:42:24Z

The signal race that exists in perf_sched__schedstat_record() also
affects perf_sched__schedstat_live(). A very short-lived workload can
exit and deliver SIGCHLD before pause() is entered, causing an
indefinite hang.

Apply the same fix: block SIGCHLD before starting the workload and
replace pause() with sigsuspend() to atomically unblock and wait.

Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
---
tools/perf/builtin-sched.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index eb3702d98fd1..1d8f4ceda1eb 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -4655,6 +4655,7 @@ static int perf_sched__schedstat_live(struct perf_sched *sched,
int argc, const char **argv)
{
struct cpu_domain_map **cd_map = NULL;
+ sigset_t sigchld_mask, oldmask;
struct target target = {};
u32 __maybe_unused md;
struct evlist *evlist;
@@ -4666,6 +4667,10 @@ static int perf_sched__schedstat_live(struct perf_sched *sched,
signal(SIGCHLD, sighandler);
signal(SIGTERM, sighandler);

+ sigemptyset(&sigchld_mask);
+ sigaddset(&sigchld_mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigchld_mask, &oldmask);
+
evlist = evlist__new();
if (!evlist)
return -ENOMEM;
@@ -4707,8 +4712,8 @@ static int perf_sched__schedstat_live(struct perf_sched *sched,
if (argc)
evlist__start_workload(evlist);

- /* wait for signal */
- pause();
+ sigsuspend(&oldmask);
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);

if (reset) {
err = disable_sched_schedstat();
--
2.43.0