mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs
@ 2026-09-26 19:03 Michal Pluta
  2026-09-26 19:04 ` [PATCH 1/8] perf bench futex: Retry futex_wait() when interrupted by a signal Michal Pluta
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:03 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

This series fixes several bugs in the futex benchmarks of 'perf bench'.
I found them by running the benchmarks and reading the code, and then
trying edge-case inputs. They include hangs, wrong timing and throughput
numbers, and inputs that were silently ignored.

I used an LLM to check whether what I suspected were actually bugs, and
later to look for more edge cases. I reproduced and reviewed each one
myself.

This is my first patch submission, so I am happy to reword, reorder or
split any of the patches.

How I checked each problem, before the patch:

1. futex_wait() never returns EINTR, so the signal check never worked.
   I confirmed the hang by sending SIGINT to one worker thread with
   tgkill(2) while 'perf bench futex wake -t 8' was waiting: it hung.
2. 'perf bench futex wake -b 128' fails with an error from
   futex_print_nbuckets().
3. Run 'perf bench futex hash -t 2 -r 30 -s' and send it SIGINT after
   about 3 s: the throughput is much higher than in a full '-r 3' run.
   The timing problem in the other benchmarks I confirmed by making
   gettimeofday() advance by 1.5 s per call with an LD_PRELOAD shim.
4. 'perf bench futex requeue -q 0' and 'perf bench futex hash -f 0'
   hang, and 'perf bench futex wake -b -128' silently ignores the -b.
5. I confirmed the hang by making FUTEX_WAKE fail with an LD_PRELOAD
   shim.
6. Only affects 32-bit builds, see below.

I can share the small test programs for items 1, 3 and 5 if they are
useful.

Not tested:
- Patch 6 (64-bit counter) was built and run only on x86-64, as I have
  no 32-bit setup.
- Patch 5 was tested with an injected failure, not with a real failing
  system call.
- Everything else was run on x86-64 with a 7.0 kernel.

Patches 7 and 8 are optional cleanups.

The series is based on perf-tools-next, commit 528b1475f9cf ("perf mem
record: Use the IBS swfilt filter when available").

Thank you!

Michal Pluta (8):
  perf bench futex: Retry futex_wait() when interrupted by a signal
  perf bench futex: Set the number of hash buckets in futex wake
  perf bench futex: Use the whole timeval for elapsed times
  perf bench futex: Reject invalid -q, -f and -b values
  perf bench futex: Stop when futex_wake() fails in futex wake
  perf bench futex: Use a 64-bit operation counter in hash and lock-pi
  perf bench futex: Clean up two nits in futex hash
  perf bench futex: Fix wording in futex bench messages

 tools/perf/bench/futex-hash.c          | 23 +++++++++++++-------
 tools/perf/bench/futex-lock-pi.c       | 16 ++++++++------
 tools/perf/bench/futex-requeue.c       | 12 ++++++++---
 tools/perf/bench/futex-wake-parallel.c | 15 +++++++------
 tools/perf/bench/futex-wake.c          | 30 ++++++++++++++++----------
 tools/perf/bench/futex.c               |  9 +++++---
 6 files changed, 68 insertions(+), 37 deletions(-)


base-commit: 528b1475f9cffbaffb37f490e86780662471ef3f
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/8] perf bench futex: Retry futex_wait() when interrupted by a signal
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 2/8] perf bench futex: Set the number of hash buckets in futex wake Michal Pluta
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

The worker threads in 'futex wake' and 'futex wake-parallel' are meant
to go back to sleep when a signal interrupts futex_wait(), but the check
never works. futex_wait() returns -1 and sets errno, so comparing its
return value with EINTR is always true and the loop ends at the first
interruption.

The worker then exits without having been woken. 'futex wake' keeps
calling futex_wake() forever, because the number of woken threads never
reaches the total, while 'futex wake-parallel' finishes but shows the
wrong number of threads.

Check errno instead.

Fixes: 598adc5c9c1c ("perf bench futex: Handle spurious wakeups")
Fixes: d65817b4e707 ("perf bench futex: Support parallel waker threads")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-wake-parallel.c | 7 +++----
 tools/perf/bench/futex-wake.c          | 7 +++----
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c
index 6aede7c46b33..a089d8ee4b02 100644
--- a/tools/perf/bench/futex-wake-parallel.c
+++ b/tools/perf/bench/futex-wake-parallel.c
@@ -139,10 +139,9 @@ static void *blocked_workerfn(void *arg __maybe_unused)
 	cond_wait(&thread_worker, &thread_lock);
 	mutex_unlock(&thread_lock);
 
-	while (1) { /* handle spurious wakeups */
-		if (futex_wait(&futex, 0, NULL, futex_flag) != EINTR)
-			break;
-	}
+	/* Retry if the wait was interrupted by a signal. */
+	while (futex_wait(&futex, 0, NULL, futex_flag) == -1 && errno == EINTR)
+		;
 
 	pthread_exit(NULL);
 	return NULL;
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index a31fc1563862..66d30ca91405 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -75,10 +75,9 @@ static void *workerfn(void *arg __maybe_unused)
 	cond_wait(&thread_worker, &thread_lock);
 	mutex_unlock(&thread_lock);
 
-	while (1) {
-		if (futex_wait(&futex1, 0, NULL, futex_flag) != EINTR)
-			break;
-	}
+	/* Retry if the wait was interrupted by a signal. */
+	while (futex_wait(&futex1, 0, NULL, futex_flag) == -1 && errno == EINTR)
+		;
 
 	pthread_exit(NULL);
 	return NULL;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/8] perf bench futex: Set the number of hash buckets in futex wake
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
  2026-09-26 19:04 ` [PATCH 1/8] perf bench futex: Retry futex_wait() when interrupted by a signal Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 3/8] perf bench futex: Use the whole timeval for elapsed times Michal Pluta
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

'perf bench futex wake -b N' fails unless N happens to be the number of
buckets the kernel is already using:

  $ perf bench futex wake -b 128
  Requested number of hash buckets does not currently used.
  Requested: 128 in usage: 64
  perf: prctl(PR_FUTEX_HASH): No such file or directory

The other futex benchmarks call futex_set_nbuckets_param() to ask the
kernel for the requested size, but 'futex wake' only checks afterwards
which size is in use, so the request never reaches the kernel. The call
now happens before the run summary, as in the other benchmarks.

Fixes: 60035a3981a7 ("tools/perf: Allow to select the number of hash buckets")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-wake.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 66d30ca91405..58427bb55c03 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -175,6 +175,8 @@ int bench_futex_wake(int argc, const char **argv)
 	if (!params.fshared)
 		futex_flag = FUTEX_PRIVATE_FLAG;
 
+	futex_set_nbuckets_param(&params);
+
 	printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
 	       "waking up %d at a time.\n\n",
 	       getpid(), params.nthreads, params.fshared ? "shared":"private",
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 3/8] perf bench futex: Use the whole timeval for elapsed times
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
  2026-09-26 19:04 ` [PATCH 1/8] perf bench futex: Retry futex_wait() when interrupted by a signal Michal Pluta
  2026-09-26 19:04 ` [PATCH 2/8] perf bench futex: Set the number of hash buckets in futex wake Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 4/8] perf bench futex: Reject invalid -q, -f and -b values Michal Pluta
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

Several futex benchmarks read only one field of the struct timeval that
timersub() produces, so part of the elapsed time is lost.

'futex wake', 'futex wake-parallel' and 'futex requeue' print tv_usec,
so a run that takes 1.5 s is shown as 500 ms. 'futex hash' and 'futex
lock-pi' divide their operation count by tv_sec, which overstates the
throughput when a run is stopped early, and gives 0 if that happens
within the first second. Their "total secs" is truncated in the same
way.

Use the whole timeval and print "total secs" with two decimals. With
--runtime=0 the rate is now computed over the time that actually
elapsed, instead of showing 0.

Fixes: 27db78307481 ("perf bench: Add futex-wake microbenchmark")
Fixes: 0fb298cf95c0 ("perf bench: Add futex-requeue microbenchmark")
Fixes: a043971141f1 ("perf bench: Add futex-hash microbenchmark")
Fixes: d2f3f5d2e9ca ("perf bench futex: Add lock_pi stresser")
Fixes: d65817b4e707 ("perf bench futex: Support parallel waker threads")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-hash.c          | 12 ++++++++----
 tools/perf/bench/futex-lock-pi.c       | 12 ++++++++----
 tools/perf/bench/futex-requeue.c       |  8 +++++---
 tools/perf/bench/futex-wake-parallel.c |  8 ++++++--
 tools/perf/bench/futex-wake.c          |  6 ++++--
 5 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index 7e29f04da744..32b88df8ee6d 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <linux/compiler.h>
 #include <linux/kernel.h>
+#include <linux/time64.h>
 #include <linux/zalloc.h>
 #include <sys/time.h>
 #include <sys/mman.h>
@@ -118,18 +119,19 @@ static void print_summary(void)
 	unsigned long avg = avg_stats(&throughput_stats);
 	double stddev = stddev_stats(&throughput_stats);
 
-	printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
+	printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %.2f\n",
 	       !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
-	       (int)bench__runtime.tv_sec);
+	       bench__runtime.tv_sec + bench__runtime.tv_usec / (double)USEC_PER_SEC);
 	futex_print_nbuckets(&params);
 }
 
 int bench_futex_hash(int argc, const char **argv)
 {
 	int ret = 0;
 	cpu_set_t *cpuset;
 	struct sigaction act;
 	unsigned int i;
+	u64 runtime_us;
 	pthread_attr_t thread_attr;
 	struct worker *worker = NULL;
 	struct perf_cpu_map *cpu;
@@ -229,9 +231,11 @@ int bench_futex_hash(int argc, const char **argv)
 	cond_destroy(&thread_worker);
 	mutex_destroy(&thread_lock);
 
+	runtime_us = (u64)bench__runtime.tv_sec * USEC_PER_SEC + bench__runtime.tv_usec;
+
 	for (i = 0; i < params.nthreads; i++) {
-		unsigned long t = bench__runtime.tv_sec > 0 ?
-			worker[i].ops / bench__runtime.tv_sec : 0;
+		unsigned long t = runtime_us ?
+			(u64)worker[i].ops * USEC_PER_SEC / runtime_us : 0;
 		update_stats(&throughput_stats, t);
 		if (!params.silent) {
 			if (params.nfutexes == 1)
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index 40640b674427..7190f5102e09 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -13,6 +13,7 @@
 #include <subcmd/parse-options.h>
 #include <linux/compiler.h>
 #include <linux/kernel.h>
+#include <linux/time64.h>
 #include <linux/zalloc.h>
 #include <errno.h>
 #include <perf/cpumap.h>
@@ -66,9 +67,9 @@ static void print_summary(void)
 	unsigned long avg = avg_stats(&throughput_stats);
 	double stddev = stddev_stats(&throughput_stats);
 
-	printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
+	printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %.2f\n",
 	       !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
-	       (int)bench__runtime.tv_sec);
+	       bench__runtime.tv_sec + bench__runtime.tv_usec / (double)USEC_PER_SEC);
 	futex_print_nbuckets(&params);
 }
 
@@ -168,6 +169,7 @@ int bench_futex_lock_pi(int argc, const char **argv)
 {
 	int ret = 0;
 	unsigned int i;
+	u64 runtime_us;
 	struct sigaction act;
 	struct perf_cpu_map *cpu;
 
@@ -233,9 +235,11 @@ int bench_futex_lock_pi(int argc, const char **argv)
 	cond_destroy(&thread_worker);
 	mutex_destroy(&thread_lock);
 
+	runtime_us = (u64)bench__runtime.tv_sec * USEC_PER_SEC + bench__runtime.tv_usec;
+
 	for (i = 0; i < params.nthreads; i++) {
-		unsigned long t = bench__runtime.tv_sec > 0 ?
-			worker[i].ops / bench__runtime.tv_sec : 0;
+		unsigned long t = runtime_us ?
+			(u64)worker[i].ops * USEC_PER_SEC / runtime_us : 0;
 
 		update_stats(&throughput_stats, t);
 		if (!params.silent)
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index 0748b0fd689e..5d4708f40c60 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -223,6 +223,7 @@ int bench_futex_requeue(int argc, const char **argv)
 	for (j = 0; j < bench_repeat && !done; j++) {
 		unsigned int nrequeued = 0, wakeups = 0;
 		struct timeval start, end, runtime;
+		u64 runtime_us;
 
 		/* create, launch & block all threads */
 		block_threads(worker, cpu);
@@ -267,23 +268,24 @@ int bench_futex_requeue(int argc, const char **argv)
 
 		gettimeofday(&end, NULL);
 		timersub(&end, &start, &runtime);
+		runtime_us = (u64)runtime.tv_sec * USEC_PER_SEC + runtime.tv_usec;
 
 		update_stats(&requeued_stats, nrequeued);
-		update_stats(&requeuetime_stats, runtime.tv_usec);
+		update_stats(&requeuetime_stats, runtime_us);
 
 		if (!params.silent) {
 			if (!params.pi)
 				printf("[Run %d]: Requeued %d of %d threads in "
 				       "%.4f ms\n", j + 1, nrequeued,
 				       params.nthreads,
-				       runtime.tv_usec / (double)USEC_PER_MSEC);
+				       runtime_us / (double)USEC_PER_MSEC);
 			else {
 				nrequeued -= wakeups;
 				printf("[Run %d]: Awoke and Requeued (%d+%d) of "
 				       "%d threads in %.4f ms\n",
 				       j + 1, wakeups, nrequeued,
 				       params.nthreads,
-				       runtime.tv_usec / (double)USEC_PER_MSEC);
+				       runtime_us / (double)USEC_PER_MSEC);
 			}
 
 		}
diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c
index a089d8ee4b02..fa70b7d4b473 100644
--- a/tools/perf/bench/futex-wake-parallel.c
+++ b/tools/perf/bench/futex-wake-parallel.c
@@ -192,7 +192,9 @@ static void print_run(struct thread_data *waking_worker, unsigned int run_num)
 	init_stats(&__waketime_stats);
 
 	for (i = 0; i < params.nwakes; i++) {
-		update_stats(&__waketime_stats, waking_worker[i].runtime.tv_usec);
+		update_stats(&__waketime_stats,
+			     (u64)waking_worker[i].runtime.tv_sec * USEC_PER_SEC +
+			     waking_worker[i].runtime.tv_usec);
 		update_stats(&__wakeup_stats, waking_worker[i].nwoken);
 	}
 
@@ -229,7 +231,9 @@ static void do_run_stats(struct thread_data *waking_worker)
 	unsigned int i;
 
 	for (i = 0; i < params.nwakes; i++) {
-		update_stats(&waketime_stats, waking_worker[i].runtime.tv_usec);
+		update_stats(&waketime_stats,
+			     (u64)waking_worker[i].runtime.tv_sec * USEC_PER_SEC +
+			     waking_worker[i].runtime.tv_usec);
 		update_stats(&wakeup_stats, waking_worker[i].nwoken);
 	}
 
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 58427bb55c03..12a75be077a0 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -191,6 +191,7 @@ int bench_futex_wake(int argc, const char **argv)
 	for (j = 0; j < bench_repeat && !done; j++) {
 		unsigned int nwoken = 0;
 		struct timeval start, end, runtime;
+		u64 runtime_us;
 
 		/* create, launch & block all threads */
 		block_threads(worker, cpu);
@@ -211,14 +212,15 @@ int bench_futex_wake(int argc, const char **argv)
 					     params.nwakes, futex_flag);
 		gettimeofday(&end, NULL);
 		timersub(&end, &start, &runtime);
+		runtime_us = (u64)runtime.tv_sec * USEC_PER_SEC + runtime.tv_usec;
 
 		update_stats(&wakeup_stats, nwoken);
-		update_stats(&waketime_stats, runtime.tv_usec);
+		update_stats(&waketime_stats, runtime_us);
 
 		if (!params.silent) {
 			printf("[Run %d]: Wokeup %d of %d threads in %.4f ms\n",
 			       j + 1, nwoken, params.nthreads,
-			       runtime.tv_usec / (double)USEC_PER_MSEC);
+			       runtime_us / (double)USEC_PER_MSEC);
 		}
 
 		for (i = 0; i < params.nthreads; i++) {
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 4/8] perf bench futex: Reject invalid -q, -f and -b values
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
                   ` (2 preceding siblings ...)
  2026-09-26 19:04 ` [PATCH 3/8] perf bench futex: Use the whole timeval for elapsed times Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 5/8] perf bench futex: Stop when futex_wake() fails in futex wake Michal Pluta
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

Some values make a futex benchmark hang or are silently ignored.

'futex requeue -q 0' never finishes, because no thread is requeued.
'futex hash -f 0' never finishes either, because with no futexes the
workers never notice that the run is over. A negative -b is treated as
if it had not been given, and the kernel simply auto-sizes the hash.

Print an error for these values, except for -b -1, which still means
automatic sizing.

Fixes: 0fb298cf95c0 ("perf bench: Add futex-requeue microbenchmark")
Fixes: a043971141f1 ("perf bench: Add futex-hash microbenchmark")
Fixes: 60035a3981a7 ("tools/perf: Allow to select the number of hash buckets")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-hash.c    | 3 +++
 tools/perf/bench/futex-requeue.c | 4 ++++
 tools/perf/bench/futex.c         | 3 +++
 3 files changed, 10 insertions(+)

diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index 32b88df8ee6d..710c253cfe17 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -161,6 +161,9 @@ int bench_futex_hash(int argc, const char **argv)
 	if (!params.nthreads) /* default to the number of CPUs */
 		params.nthreads = perf_cpu_map__nr(cpu);
 
+	if (!params.nfutexes)
+		errx(EXIT_FAILURE, "-f/--futexes must be at least 1");
+
 	worker = calloc(params.nthreads, sizeof(*worker));
 	if (!worker)
 		goto errmem;
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index 5d4708f40c60..bc6efc2ecadb 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -207,6 +207,10 @@ int bench_futex_requeue(int argc, const char **argv)
 	if (params.broadcast)
 		params.nrequeue = params.nthreads;
 
+	/* A regular requeue that moves no threads would never finish. */
+	if (!params.pi && !params.nrequeue)
+		errx(EXIT_FAILURE, "-q/--nrequeue must be at least 1");
+
 	futex_set_nbuckets_param(&params);
 
 	printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %s%p), "
diff --git a/tools/perf/bench/futex.c b/tools/perf/bench/futex.c
index 1968c9d00b5b..5757d47f8f7c 100644
--- a/tools/perf/bench/futex.c
+++ b/tools/perf/bench/futex.c
@@ -17,6 +17,9 @@ void futex_set_nbuckets_param(struct bench_futex_parameters *params)
 {
 	int ret;
 
+	if (params->nbuckets < -1)
+		errx(EXIT_FAILURE, "-b/--buckets must be -1 (auto), 0 or more");
+
 	if (params->nbuckets < 0)
 		return;
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 5/8] perf bench futex: Stop when futex_wake() fails in futex wake
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
                   ` (3 preceding siblings ...)
  2026-09-26 19:04 ` [PATCH 4/8] perf bench futex: Reject invalid -q, -f and -b values Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 6/8] perf bench futex: Use a 64-bit operation counter in hash and lock-pi Michal Pluta
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

If futex_wake() fails, 'futex wake' adds -1 to its unsigned counter of
woken threads and retries forever. Exit with an error instead, as
'futex requeue' already does.

Fixes: 27db78307481 ("perf bench: Add futex-wake microbenchmark")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-wake.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 12a75be077a0..7674622639ca 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -207,9 +207,14 @@ int bench_futex_wake(int argc, const char **argv)
 
 		/* Ok, all threads are patiently blocked, start waking folks up */
 		gettimeofday(&start, NULL);
-		while (nwoken != params.nthreads)
-			nwoken += futex_wake(&futex1,
-					     params.nwakes, futex_flag);
+		while (nwoken != params.nthreads) {
+			int r = futex_wake(&futex1, params.nwakes, futex_flag);
+
+			if (r < 0)
+				err(EXIT_FAILURE, "couldn't wakeup from %p", &futex1);
+
+			nwoken += r;
+		}
 		gettimeofday(&end, NULL);
 		timersub(&end, &start, &runtime);
 		runtime_us = (u64)runtime.tv_sec * USEC_PER_SEC + runtime.tv_usec;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 6/8] perf bench futex: Use a 64-bit operation counter in hash and lock-pi
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
                   ` (4 preceding siblings ...)
  2026-09-26 19:04 ` [PATCH 5/8] perf bench futex: Stop when futex_wake() fails in futex wake Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 7/8] perf bench futex: Clean up two nits in futex hash Michal Pluta
  2026-09-26 19:04 ` [PATCH 8/8] perf bench futex: Fix wording in futex bench messages Michal Pluta
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

The per-thread operation counters in 'futex hash' and 'futex lock-pi'
are unsigned longs, which could overflow on 32-bit systems. Use u64
instead.

Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-hash.c    | 6 +++---
 tools/perf/bench/futex-lock-pi.c | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index 710c253cfe17..e18d98664d27 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -46,7 +46,7 @@ struct worker {
 	int tid;
 	u_int32_t *futex;
 	pthread_t thread;
-	unsigned long ops;
+	u64 ops;
 };
 
 static struct bench_futex_parameters params = {
@@ -76,7 +76,7 @@ static void *workerfn(void *arg)
 	int ret;
 	struct worker *w = (struct worker *) arg;
 	unsigned int i;
-	unsigned long ops = w->ops; /* avoid cacheline bouncing */
+	u64 ops = w->ops; /* avoid cacheline bouncing */
 
 	mutex_lock(&thread_lock);
 	threads_starting--;
@@ -238,7 +238,7 @@ int bench_futex_hash(int argc, const char **argv)
 
 	for (i = 0; i < params.nthreads; i++) {
 		unsigned long t = runtime_us ?
-			(u64)worker[i].ops * USEC_PER_SEC / runtime_us : 0;
+			worker[i].ops * USEC_PER_SEC / runtime_us : 0;
 		update_stats(&throughput_stats, t);
 		if (!params.silent) {
 			if (params.nfutexes == 1)
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index 7190f5102e09..8016a31b198d 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -29,7 +29,7 @@ struct worker {
 	int tid;
 	u_int32_t *futex;
 	pthread_t thread;
-	unsigned long ops;
+	u64 ops;
 };
 
 static u_int32_t global_futex = 0;
@@ -86,7 +86,7 @@ static void toggle_done(int sig __maybe_unused,
 static void *workerfn(void *arg)
 {
 	struct worker *w = (struct worker *) arg;
-	unsigned long ops = w->ops;
+	u64 ops = w->ops;
 
 	mutex_lock(&thread_lock);
 	threads_starting--;
@@ -239,7 +239,7 @@ int bench_futex_lock_pi(int argc, const char **argv)
 
 	for (i = 0; i < params.nthreads; i++) {
 		unsigned long t = runtime_us ?
-			(u64)worker[i].ops * USEC_PER_SEC / runtime_us : 0;
+			worker[i].ops * USEC_PER_SEC / runtime_us : 0;
 
 		update_stats(&throughput_stats, t);
 		if (!params.silent)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 7/8] perf bench futex: Clean up two nits in futex hash
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
                   ` (5 preceding siblings ...)
  2026-09-26 19:04 ` [PATCH 6/8] perf bench futex: Use a 64-bit operation counter in hash and lock-pi Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  2026-09-26 19:04 ` [PATCH 8/8] perf bench futex: Fix wording in futex bench messages Michal Pluta
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

The worker's check for an unexpected futex_wait() result tests
'errno != EAGAIN || errno != EWOULDBLOCK', which only works because the
two are equal, so use '&&' instead.

The CPU map is reference counted, so release it with perf_cpu_map__put()
instead of free().

Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-hash.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index e18d98664d27..6f4af1a52d1b 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -95,7 +95,7 @@ static void *workerfn(void *arg)
 			 */
 			ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag);
 			if (!params.silent &&
-			    (!ret || errno != EAGAIN || errno != EWOULDBLOCK))
+			    (!ret || (errno != EAGAIN && errno != EWOULDBLOCK)))
 				warn("Non-expected futex return call");
 		}
 	}  while (!done);
@@ -256,7 +256,7 @@ int bench_futex_hash(int argc, const char **argv)
 	print_summary();
 
 	free(worker);
-	free(cpu);
+	perf_cpu_map__put(cpu);
 	return ret;
 errmem:
 	err(EXIT_FAILURE, "calloc");
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 8/8] perf bench futex: Fix wording in futex bench messages
  2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
                   ` (6 preceding siblings ...)
  2026-09-26 19:04 ` [PATCH 7/8] perf bench futex: Clean up two nits in futex hash Michal Pluta
@ 2026-09-26 19:04 ` Michal Pluta
  7 siblings, 0 replies; 9+ messages in thread
From: Michal Pluta @ 2026-09-26 19:04 UTC (permalink / raw)
  To: acme, namhyung
  Cc: Thomas Gleixner, Ingo Molnar, Peter Zijlstra, Darren Hart,
	Davidlohr Bueso, André Almeida, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, Sebastian Andrzej Siewior, linux-kernel,
	linux-perf-users

When the kernel is using a different number of hash buckets than
requested with -b, the message printed is ungrammatical, and it ends
with a stale errno even though nothing failed:

  Requested number of hash buckets does not currently used.
  Requested: 3 in usage: 64
  perf: prctl(PR_FUTEX_HASH): No such file or directory

Fix the wording and use errx(), so that no errno text is printed:

  Requested number of buckets differs from the number in use.
  Requested: 3, in use: 64
  perf: prctl(PR_FUTEX_HASH)

Also fix "Wokeup" in the 'futex wake' output, which should be "Woke up".

Fixes: 60035a3981a7 ("tools/perf: Allow to select the number of hash buckets")
Assisted-by: LLM
Signed-off-by: Michal Pluta <michalpl2003@gmail.com>
---
 tools/perf/bench/futex-wake.c | 4 ++--
 tools/perf/bench/futex.c      | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 7674622639ca..2cbf31e121d5 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -89,7 +89,7 @@ static void print_summary(void)
 	double waketime_stddev = stddev_stats(&waketime_stats);
 	unsigned int wakeup_avg = avg_stats(&wakeup_stats);
 
-	printf("Wokeup %d of %d threads in %.4f ms (+-%.2f%%)\n",
+	printf("Woke up %d of %d threads in %.4f ms (+-%.2f%%)\n",
 	       wakeup_avg,
 	       params.nthreads,
 	       waketime_avg / USEC_PER_MSEC,
@@ -223,7 +223,7 @@ int bench_futex_wake(int argc, const char **argv)
 		update_stats(&waketime_stats, runtime_us);
 
 		if (!params.silent) {
-			printf("[Run %d]: Wokeup %d of %d threads in %.4f ms\n",
+			printf("[Run %d]: Woke up %d of %d threads in %.4f ms\n",
 			       j + 1, nwoken, params.nthreads,
 			       runtime_us / (double)USEC_PER_MSEC);
 		}
diff --git a/tools/perf/bench/futex.c b/tools/perf/bench/futex.c
index 5757d47f8f7c..be0cbc03682c 100644
--- a/tools/perf/bench/futex.c
+++ b/tools/perf/bench/futex.c
@@ -43,9 +43,9 @@ void futex_print_nbuckets(struct bench_futex_parameters *params)
 				printf("Can't query number of buckets: %m\n");
 				err(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)");
 			}
-			printf("Requested number of hash buckets does not currently used.\n");
-			printf("Requested: %d in usage: %d\n", params->nbuckets, ret);
-			err(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)");
+			printf("Requested number of buckets differs from the number in use.\n");
+			printf("Requested: %d, in use: %d\n", params->nbuckets, ret);
+			errx(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)");
 		}
 		if (params->nbuckets == 0)
 			ret = asprintf(&futex_hash_mode, "Futex hashing: global hash");
-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-09-26 19:05 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-26 19:03 [PATCH 0/8] perf bench futex: Fix several bugs and bad inputs Michal Pluta
2026-09-26 19:04 ` [PATCH 1/8] perf bench futex: Retry futex_wait() when interrupted by a signal Michal Pluta
2026-09-26 19:04 ` [PATCH 2/8] perf bench futex: Set the number of hash buckets in futex wake Michal Pluta
2026-09-26 19:04 ` [PATCH 3/8] perf bench futex: Use the whole timeval for elapsed times Michal Pluta
2026-09-26 19:04 ` [PATCH 4/8] perf bench futex: Reject invalid -q, -f and -b values Michal Pluta
2026-09-26 19:04 ` [PATCH 5/8] perf bench futex: Stop when futex_wake() fails in futex wake Michal Pluta
2026-09-26 19:04 ` [PATCH 6/8] perf bench futex: Use a 64-bit operation counter in hash and lock-pi Michal Pluta
2026-09-26 19:04 ` [PATCH 7/8] perf bench futex: Clean up two nits in futex hash Michal Pluta
2026-09-26 19:04 ` [PATCH 8/8] perf bench futex: Fix wording in futex bench messages Michal Pluta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®