]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tools/perf: Allow to select the number of hash buckets
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Wed, 16 Apr 2025 16:29:19 +0000 (18:29 +0200)
committerPeter Zijlstra <peterz@infradead.org>
Sat, 3 May 2025 10:02:10 +0000 (12:02 +0200)
Add the -b/ --buckets argument to specify the number of hash buckets for
the private futex hash. This is directly passed to
    prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, buckets, immutable)

and must return without an error if specified. The `immutable' is 0 by
default and can be set to 1 via the -I/ --immutable argument.
The size of the private hash is verified with PR_FUTEX_HASH_GET_SLOTS.
If PR_FUTEX_HASH_GET_SLOTS failed then it is assumed that an older
kernel was used without the support and that the global hash is used.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20250416162921.513656-20-bigeasy@linutronix.de
tools/perf/bench/Build
tools/perf/bench/futex-hash.c
tools/perf/bench/futex-lock-pi.c
tools/perf/bench/futex-requeue.c
tools/perf/bench/futex-wake-parallel.c
tools/perf/bench/futex-wake.c
tools/perf/bench/futex.c [new file with mode: 0644]
tools/perf/bench/futex.h

index 279ab2ab4abe42f91277f5cea4e3bcc3467850f9..b558ab98719f923303d341526159ef0aac988f2b 100644 (file)
@@ -3,6 +3,7 @@ perf-bench-y += sched-pipe.o
 perf-bench-y += sched-seccomp-notify.o
 perf-bench-y += syscall.o
 perf-bench-y += mem-functions.o
+perf-bench-y += futex.o
 perf-bench-y += futex-hash.o
 perf-bench-y += futex-wake.o
 perf-bench-y += futex-wake-parallel.o
index b472eded521b1c714c1bad921394bc1b28311efc..fdf133c9520f73a47fa5c0358608908ea09f144d 100644 (file)
 #include <stdlib.h>
 #include <linux/compiler.h>
 #include <linux/kernel.h>
+#include <linux/prctl.h>
 #include <linux/zalloc.h>
 #include <sys/time.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 #include <perf/cpumap.h>
 
 #include "../util/mutex.h"
@@ -50,9 +52,12 @@ struct worker {
 static struct bench_futex_parameters params = {
        .nfutexes = 1024,
        .runtime  = 10,
+       .nbuckets = -1,
 };
 
 static const struct option options[] = {
+       OPT_INTEGER( 'b', "buckets", &params.nbuckets, "Specify amount of hash buckets"),
+       OPT_BOOLEAN( 'I', "immutable", &params.buckets_immutable, "Make the hash buckets immutable"),
        OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
        OPT_UINTEGER('r', "runtime", &params.runtime, "Specify runtime (in seconds)"),
        OPT_UINTEGER('f', "futexes", &params.nfutexes, "Specify amount of futexes per threads"),
@@ -118,6 +123,7 @@ static void print_summary(void)
        printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
               !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
               (int)bench__runtime.tv_sec);
+       futex_print_nbuckets(&params);
 }
 
 int bench_futex_hash(int argc, const char **argv)
@@ -161,6 +167,7 @@ int bench_futex_hash(int argc, const char **argv)
 
        if (!params.fshared)
                futex_flag = FUTEX_PRIVATE_FLAG;
+       futex_set_nbuckets_param(&params);
 
        printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n",
               getpid(), params.nthreads, params.nfutexes, params.fshared ? "shared":"private", params.runtime);
index 0416120c091b2490a5062755d1b5ce7cce8d1de5..5144a158512cc8bff1642932c63cf9ddbcb13ddf 100644 (file)
@@ -41,10 +41,13 @@ static struct stats throughput_stats;
 static struct cond thread_parent, thread_worker;
 
 static struct bench_futex_parameters params = {
+       .nbuckets = -1,
        .runtime  = 10,
 };
 
 static const struct option options[] = {
+       OPT_INTEGER( 'b', "buckets", &params.nbuckets, "Specify amount of hash buckets"),
+       OPT_BOOLEAN( 'I', "immutable", &params.buckets_immutable, "Make the hash buckets immutable"),
        OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
        OPT_UINTEGER('r', "runtime", &params.runtime, "Specify runtime (in seconds)"),
        OPT_BOOLEAN( 'M', "multi",   &params.multi, "Use multiple futexes"),
@@ -67,6 +70,7 @@ static void print_summary(void)
        printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
               !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
               (int)bench__runtime.tv_sec);
+       futex_print_nbuckets(&params);
 }
 
 static void toggle_done(int sig __maybe_unused,
@@ -203,6 +207,7 @@ int bench_futex_lock_pi(int argc, const char **argv)
        mutex_init(&thread_lock);
        cond_init(&thread_parent);
        cond_init(&thread_worker);
+       futex_set_nbuckets_param(&params);
 
        threads_starting = params.nthreads;
        gettimeofday(&bench__start, NULL);
index aad5bfc4fe188d48690b3e88edc4f47c76354586..a2f91ee1950b3482fef78341df92f0f2ee56fe2e 100644 (file)
@@ -42,6 +42,7 @@ static unsigned int threads_starting;
 static int futex_flag = 0;
 
 static struct bench_futex_parameters params = {
+       .nbuckets = -1,
        /*
         * How many tasks to requeue at a time.
         * Default to 1 in order to make the kernel work more.
@@ -50,6 +51,8 @@ static struct bench_futex_parameters params = {
 };
 
 static const struct option options[] = {
+       OPT_INTEGER( 'b', "buckets", &params.nbuckets, "Specify amount of hash buckets"),
+       OPT_BOOLEAN( 'I', "immutable", &params.buckets_immutable, "Make the hash buckets immutable"),
        OPT_UINTEGER('t', "threads",  &params.nthreads, "Specify amount of threads"),
        OPT_UINTEGER('q', "nrequeue", &params.nrequeue, "Specify amount of threads to requeue at once"),
        OPT_BOOLEAN( 's', "silent",   &params.silent, "Silent mode: do not display data/details"),
@@ -77,6 +80,7 @@ static void print_summary(void)
               params.nthreads,
               requeuetime_avg / USEC_PER_MSEC,
               rel_stddev_stats(requeuetime_stddev, requeuetime_avg));
+       futex_print_nbuckets(&params);
 }
 
 static void *workerfn(void *arg __maybe_unused)
@@ -204,6 +208,8 @@ int bench_futex_requeue(int argc, const char **argv)
        if (params.broadcast)
                params.nrequeue = params.nthreads;
 
+       futex_set_nbuckets_param(&params);
+
        printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %s%p), "
               "%d at a time.\n\n",  getpid(), params.nthreads,
               params.fshared ? "shared":"private", &futex1,
index 4352e318631e991d97744eda9e2f720576edec18..ee66482c29fd1c930c4e12c7dc2b09cb5e3a03a9 100644 (file)
@@ -57,9 +57,13 @@ static struct stats waketime_stats, wakeup_stats;
 static unsigned int threads_starting;
 static int futex_flag = 0;
 
-static struct bench_futex_parameters params;
+static struct bench_futex_parameters params = {
+       .nbuckets = -1,
+};
 
 static const struct option options[] = {
+       OPT_INTEGER( 'b', "buckets", &params.nbuckets, "Specify amount of hash buckets"),
+       OPT_BOOLEAN( 'I', "immutable", &params.buckets_immutable, "Make the hash buckets immutable"),
        OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
        OPT_UINTEGER('w', "nwakers", &params.nwakes, "Specify amount of waking threads"),
        OPT_BOOLEAN( 's', "silent",  &params.silent, "Silent mode: do not display data/details"),
@@ -218,6 +222,7 @@ static void print_summary(void)
               params.nthreads,
               waketime_avg / USEC_PER_MSEC,
               rel_stddev_stats(waketime_stddev, waketime_avg));
+       futex_print_nbuckets(&params);
 }
 
 
@@ -291,6 +296,8 @@ int bench_futex_wake_parallel(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), %d threads waking up %d at a time.\n\n",
               getpid(), params.nthreads, params.fshared ? "shared":"private",
index 49b3c89b0b35d6b23183d78358a0d3ef88ff6853..8d6107f7cd941000cede1832c6694de483a89592 100644 (file)
@@ -42,6 +42,7 @@ static unsigned int threads_starting;
 static int futex_flag = 0;
 
 static struct bench_futex_parameters params = {
+       .nbuckets = -1,
        /*
         * How many wakeups to do at a time.
         * Default to 1 in order to make the kernel work more.
@@ -50,6 +51,8 @@ static struct bench_futex_parameters params = {
 };
 
 static const struct option options[] = {
+       OPT_INTEGER( 'b', "buckets", &params.nbuckets, "Specify amount of hash buckets"),
+       OPT_BOOLEAN( 'I', "immutable", &params.buckets_immutable, "Make the hash buckets immutable"),
        OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
        OPT_UINTEGER('w', "nwakes",  &params.nwakes, "Specify amount of threads to wake at once"),
        OPT_BOOLEAN( 's', "silent",  &params.silent, "Silent mode: do not display data/details"),
@@ -93,6 +96,7 @@ static void print_summary(void)
               params.nthreads,
               waketime_avg / USEC_PER_MSEC,
               rel_stddev_stats(waketime_stddev, waketime_avg));
+       futex_print_nbuckets(&params);
 }
 
 static void block_threads(pthread_t *w, struct perf_cpu_map *cpu)
diff --git a/tools/perf/bench/futex.c b/tools/perf/bench/futex.c
new file mode 100644 (file)
index 0000000..02ae6c5
--- /dev/null
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/prctl.h>
+#include <sys/prctl.h>
+
+#include "futex.h"
+
+void futex_set_nbuckets_param(struct bench_futex_parameters *params)
+{
+       int ret;
+
+       if (params->nbuckets < 0)
+               return;
+
+       ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_SET_SLOTS, params->nbuckets, params->buckets_immutable);
+       if (ret) {
+               printf("Requesting %d hash buckets failed: %d/%m\n",
+                      params->nbuckets, ret);
+               err(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)");
+       }
+}
+
+void futex_print_nbuckets(struct bench_futex_parameters *params)
+{
+       char *futex_hash_mode;
+       int ret;
+
+       ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_SLOTS);
+       if (params->nbuckets >= 0) {
+               if (ret != params->nbuckets) {
+                       if (ret < 0) {
+                               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)");
+               }
+               if (params->nbuckets == 0) {
+                       ret = asprintf(&futex_hash_mode, "Futex hashing: global hash");
+               } else {
+                       ret = prctl(PR_FUTEX_HASH, PR_FUTEX_HASH_GET_IMMUTABLE);
+                       if (ret < 0) {
+                               printf("Can't check if the hash is immutable: %m\n");
+                               err(EXIT_FAILURE, "prctl(PR_FUTEX_HASH)");
+                       }
+                       ret = asprintf(&futex_hash_mode, "Futex hashing: %d hash buckets %s",
+                                      params->nbuckets,
+                                      ret == 1 ? "(immutable)" : "");
+               }
+       } else {
+               if (ret <= 0) {
+                       ret = asprintf(&futex_hash_mode, "Futex hashing: global hash");
+               } else {
+                       ret = asprintf(&futex_hash_mode, "Futex hashing: auto resized to %d buckets",
+                                      ret);
+               }
+       }
+       if (ret < 0)
+               err(EXIT_FAILURE, "ENOMEM, futex_hash_mode");
+       printf("%s\n", futex_hash_mode);
+       free(futex_hash_mode);
+}
index ebdc2b032afc16432b9415985ffb433f2ff55bd3..9c9a73f9d865e71b15f0ded8ce174a9b8a9a2e93 100644 (file)
@@ -25,6 +25,8 @@ struct bench_futex_parameters {
        unsigned int nfutexes;
        unsigned int nwakes;
        unsigned int nrequeue;
+       int nbuckets;
+       bool buckets_immutable;
 };
 
 /**
@@ -143,4 +145,7 @@ futex_cmp_requeue_pi(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2,
                                        val, opflags);
 }
 
+void futex_set_nbuckets_param(struct bench_futex_parameters *params);
+void futex_print_nbuckets(struct bench_futex_parameters *params);
+
 #endif /* _FUTEX_H */