diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-09-05 21:56:18 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-09-05 21:56:18 +0300 |
commit | 27151f177827d478508e756c7657273261aaf8a9 (patch) | |
tree | 393ee6f1b12842c87461c471c0da70e4ecd93da8 /tools/perf/bench | |
parent | 58ca24158758f1784400d32743373d7d6227d018 (diff) | |
parent | c7a3828d98db2730079265b5f51933dfcef8bb5f (diff) | |
download | linux-27151f177827d478508e756c7657273261aaf8a9.tar.xz |
Merge tag 'perf-tools-for-v5.15-2021-09-04' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux
Pull perf tool updates from Arnaldo Carvalho de Melo:
"New features:
- Improvements for the flamegraph python script, including:
- Display perf.data header
- Display PIDs of user stacks
- Added option to change color scheme
- Default to blue/green color scheme to improve accessibility
- Correctly identify kernel stacks when debuginfo is available
- Improvements for 'perf bench futex':
- Add --mlockall parameter
- Add --broadcast and --pi to the 'requeue' sub benchmark
- Add support for PMU aliases.
- Introduce an ARM Coresight ETE decoder.
- Add a 'perf bench' entry for evlist open/close operations, to help
quantify improvements with multithreading 'perf record'.
- Allow reporting the [un]throttle PERF_RECORD_ meta event in 'perf
script's python scripting.
- Add a 'perf test' entry for PMU aliases.
- Add a 'perf test' entry for 'perf record/perf report/perf script'
pipe mode.
Fixes:
- perf script dlfilter (API for filtering via dynamically loaded
shared object introduced in v5.14) fixes and a 'perf test' entry
for it.
- Fix get_current_dir_name() compilation on Android.
- Fix issues with asciidoc and double dashes uses.
- Fix memory leaks in the BTF handling code.
- Fix leftover problems in the Documentation from the infrastructure
originally lifted from the git codebase.
- Fix *probe_vfs_getname.sh 'perf test' failures.
- Handle fd gaps in 'perf test's test__dso_data_reopen().
- Make sure to show disasembly warnings for 'perf annotate --stdio'.
- Fix output from pipe to file and vice-versa in 'perf
record/report/script'.
- Correct 'perf data -h' output.
- Fix wrong comm in system-wide mode with 'perf record --delay'.
- Do not allow --for-each-cgroup without cpu in 'perf stat'
- Make 'perf test --skip' work on shell tests.
- Fix libperf's verbose printing.
Misc improvements:
- Preparatory patches for multithreading various 'perf record' phases
(synthesizing, opening, recording, etc).
- Add sparse context/locking annotations in compiler-types.h, also to
help with the multithreading effort.
- Optimize the generation of the arch specific erno tables used in
'perf trace'.
- Optimize libperf's perf_cpu_map__max().
- Improve ARM's CoreSight warnings.
- Report collisions in AUX records.
- Improve warnings for the LLVM 'perf test' entry.
- Improve the PMU events 'perf test' codebase.
- perf test: Do not compare overheads in the zstd comp test
- Better support annotation on ARM.
- Update 'perf trace's cmd string table to decode sys_bpf() first
arg.
Vendor events:
- Add JSON events and metrics for Intel's Ice Lake, Tiger Lake and
Elhart Lake.
- Update JSON eventsand metrics for Intel's Cascade Lake and Sky Lake
servers.
Hardware tracing:
- Improvements for the ARM hardware tracing auxtrace support"
* tag 'perf-tools-for-v5.15-2021-09-04' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux: (130 commits)
perf tests: Add test for PMU aliases
perf pmu: Add PMU alias support
perf session: Report collisions in AUX records
perf script python: Allow reporting the [un]throttle PERF_RECORD_ meta event
perf build: Report failure for testing feature libopencsd
perf cs-etm: Show a warning for an unknown magic number
perf cs-etm: Print the decoder name
perf cs-etm: Create ETE decoder
perf cs-etm: Update OpenCSD decoder for ETE
perf cs-etm: Fix typo
perf cs-etm: Save TRCDEVARCH register
perf cs-etm: Refactor out ETMv4 header saving
perf cs-etm: Initialise architecture based on TRCIDR1
perf cs-etm: Refactor initialisation of decoder params.
tools build: Fix feature detect clean for out of source builds
perf evlist: Add evlist__for_each_entry_from() macro
perf evsel: Handle precise_ip fallback in evsel__open_cpu()
perf evsel: Move bpf_counter__install_pe() to success path in evsel__open_cpu()
perf evsel: Move test_attr__open() to success path in evsel__open_cpu()
perf evsel: Move ignore_missing_thread() to fallback code
...
Diffstat (limited to 'tools/perf/bench')
-rw-r--r-- | tools/perf/bench/Build | 1 | ||||
-rw-r--r-- | tools/perf/bench/bench.h | 1 | ||||
-rw-r--r-- | tools/perf/bench/evlist-open-close.c | 258 | ||||
-rw-r--r-- | tools/perf/bench/futex-hash.c | 62 | ||||
-rw-r--r-- | tools/perf/bench/futex-lock-pi.c | 60 | ||||
-rw-r--r-- | tools/perf/bench/futex-requeue.c | 159 | ||||
-rw-r--r-- | tools/perf/bench/futex-wake-parallel.c | 66 | ||||
-rw-r--r-- | tools/perf/bench/futex-wake.c | 65 | ||||
-rw-r--r-- | tools/perf/bench/futex.h | 52 | ||||
-rw-r--r-- | tools/perf/bench/inject-buildid.c | 52 | ||||
-rw-r--r-- | tools/perf/bench/synthesize.c | 4 |
11 files changed, 607 insertions, 173 deletions
diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build index e43f46931b41..61d45fcb4057 100644 --- a/tools/perf/bench/Build +++ b/tools/perf/bench/Build @@ -13,6 +13,7 @@ perf-y += synthesize.o perf-y += kallsyms-parse.o perf-y += find-bit-bench.o perf-y += inject-buildid.o +perf-y += evlist-open-close.o perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o perf-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index eac36afab2b3..b3480bc33fe8 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h @@ -48,6 +48,7 @@ int bench_epoll_ctl(int argc, const char **argv); int bench_synthesize(int argc, const char **argv); int bench_kallsyms_parse(int argc, const char **argv); int bench_inject_build_id(int argc, const char **argv); +int bench_evlist_open_close(int argc, const char **argv); #define BENCH_FORMAT_DEFAULT_STR "default" #define BENCH_FORMAT_DEFAULT 0 diff --git a/tools/perf/bench/evlist-open-close.c b/tools/perf/bench/evlist-open-close.c new file mode 100644 index 000000000000..83e9897c64a1 --- /dev/null +++ b/tools/perf/bench/evlist-open-close.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include "bench.h" +#include "../util/debug.h" +#include "../util/stat.h" +#include "../util/evlist.h" +#include "../util/evsel.h" +#include "../util/strbuf.h" +#include "../util/record.h" +#include "../util/parse-events.h" +#include "internal/threadmap.h" +#include "internal/cpumap.h" +#include <linux/perf_event.h> +#include <linux/kernel.h> +#include <linux/time64.h> +#include <linux/string.h> +#include <subcmd/parse-options.h> + +#define MMAP_FLUSH_DEFAULT 1 + +static int iterations = 100; +static int nr_events = 1; +static const char *event_string = "dummy"; + +static struct record_opts opts = { + .sample_time = true, + .mmap_pages = UINT_MAX, + .user_freq = UINT_MAX, + .user_interval = ULLONG_MAX, + .freq = 4000, + .target = { + .uses_mmap = true, + .default_per_cpu = true, + }, + .mmap_flush = MMAP_FLUSH_DEFAULT, + .nr_threads_synthesize = 1, + .ctl_fd = -1, + .ctl_fd_ack = -1, +}; + +static const struct option options[] = { + OPT_STRING('e', "event", &event_string, "event", "event selector. use 'perf list' to list available events"), + OPT_INTEGER('n', "nr-events", &nr_events, + "number of dummy events to create (default 1). If used with -e, it clones those events n times (1 = no change)"), + OPT_INTEGER('i', "iterations", &iterations, "Number of iterations used to compute average (default=100)"), + OPT_BOOLEAN('a', "all-cpus", &opts.target.system_wide, "system-wide collection from all CPUs"), + OPT_STRING('C', "cpu", &opts.target.cpu_list, "cpu", "list of cpus where to open events"), + OPT_STRING('p', "pid", &opts.target.pid, "pid", "record events on existing process id"), + OPT_STRING('t', "tid", &opts.target.tid, "tid", "record events on existing thread id"), + OPT_STRING('u', "uid", &opts.target.uid_str, "user", "user to profile"), + OPT_BOOLEAN(0, "per-thread", &opts.target.per_thread, "use per-thread mmaps"), + OPT_END() +}; + +static const char *const bench_usage[] = { + "perf bench internals evlist-open-close <options>", + NULL +}; + +static int evlist__count_evsel_fds(struct evlist *evlist) +{ + struct evsel *evsel; + int cnt = 0; + + evlist__for_each_entry(evlist, evsel) + cnt += evsel->core.threads->nr * evsel->core.cpus->nr; + + return cnt; +} + +static struct evlist *bench__create_evlist(char *evstr) +{ + struct parse_events_error err = { .idx = 0, }; + struct evlist *evlist = evlist__new(); + int ret; + + if (!evlist) { + pr_err("Not enough memory to create evlist\n"); + return NULL; + } + + ret = parse_events(evlist, evstr, &err); + if (ret) { + parse_events_print_error(&err, evstr); + pr_err("Run 'perf list' for a list of valid events\n"); + ret = 1; + goto out_delete_evlist; + } + + ret = evlist__create_maps(evlist, &opts.target); + if (ret < 0) { + pr_err("Not enough memory to create thread/cpu maps\n"); + goto out_delete_evlist; + } + + evlist__config(evlist, &opts, NULL); + + return evlist; + +out_delete_evlist: + evlist__delete(evlist); + return NULL; +} + +static int bench__do_evlist_open_close(struct evlist *evlist) +{ + char sbuf[STRERR_BUFSIZE]; + int err = evlist__open(evlist); + + if (err < 0) { + pr_err("evlist__open: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); + return err; + } + + err = evlist__mmap(evlist, opts.mmap_pages); + if (err < 0) { + pr_err("evlist__mmap: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); + return err; + } + + evlist__enable(evlist); + evlist__disable(evlist); + evlist__munmap(evlist); + evlist__close(evlist); + + return 0; +} + +static int bench_evlist_open_close__run(char *evstr) +{ + // used to print statistics only + struct evlist *evlist = bench__create_evlist(evstr); + double time_average, time_stddev; + struct timeval start, end, diff; + struct stats time_stats; + u64 runtime_us; + int i, err; + + if (!evlist) + return -ENOMEM; + + init_stats(&time_stats); + + printf(" Number of cpus:\t%d\n", evlist->core.cpus->nr); + printf(" Number of threads:\t%d\n", evlist->core.threads->nr); + printf(" Number of events:\t%d (%d fds)\n", + evlist->core.nr_entries, evlist__count_evsel_fds(evlist)); + printf(" Number of iterations:\t%d\n", iterations); + + evlist__delete(evlist); + + for (i = 0; i < iterations; i++) { + pr_debug("Started iteration %d\n", i); + evlist = bench__create_evlist(evstr); + if (!evlist) + return -ENOMEM; + + gettimeofday(&start, NULL); + err = bench__do_evlist_open_close(evlist); + if (err) { + evlist__delete(evlist); + return err; + } + + gettimeofday(&end, NULL); + timersub(&end, &start, &diff); + runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; + update_stats(&time_stats, runtime_us); + + evlist__delete(evlist); + pr_debug("Iteration %d took:\t%" PRIu64 "us\n", i, runtime_us); + } + + time_average = avg_stats(&time_stats); + time_stddev = stddev_stats(&time_stats); + printf(" Average open-close took: %.3f usec (+- %.3f usec)\n", time_average, time_stddev); + + return 0; +} + +static char *bench__repeat_event_string(const char *evstr, int n) +{ + char sbuf[STRERR_BUFSIZE]; + struct strbuf buf; + int i, str_size = strlen(evstr), + final_size = str_size * n + n, + err = strbuf_init(&buf, final_size); + + if (err) { + pr_err("strbuf_init: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); + goto out_error; + } + + for (i = 0; i < n; i++) { + err = strbuf_add(&buf, evstr, str_size); + if (err) { + pr_err("strbuf_add: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); + goto out_error; + } + + err = strbuf_addch(&buf, i == n-1 ? '\0' : ','); + if (err) { + pr_err("strbuf_addch: %s\n", str_error_r(err, sbuf, sizeof(sbuf))); + goto out_error; + } + } + + return strbuf_detach(&buf, NULL); + +out_error: + strbuf_release(&buf); + return NULL; +} + + +int bench_evlist_open_close(int argc, const char **argv) +{ + char *evstr, errbuf[BUFSIZ]; + int err; + + argc = parse_options(argc, argv, options, bench_usage, 0); + if (argc) { + usage_with_options(bench_usage, options); + exit(EXIT_FAILURE); + } + + err = target__validate(&opts.target); + if (err) { + target__strerror(&opts.target, err, errbuf, sizeof(errbuf)); + pr_err("%s\n", errbuf); + goto out; + } + + err = target__parse_uid(&opts.target); + if (err) { + target__strerror(&opts.target, err, errbuf, sizeof(errbuf)); + pr_err("%s", errbuf); + goto out; + } + + /* Enable ignoring missing threads when -u/-p option is defined. */ + opts.ignore_missing_thread = opts.target.uid != UINT_MAX || opts.target.pid; + + evstr = bench__repeat_event_string(event_string, nr_events); + if (!evstr) { + err = -ENOMEM; + goto out; + } + + err = bench_evlist_open_close__run(evstr); + + free(evstr); +out: + return err; +} diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index b65373ce5c4f..fcdea3e44937 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c @@ -20,6 +20,7 @@ #include <linux/kernel.h> #include <linux/zalloc.h> #include <sys/time.h> +#include <sys/mman.h> #include <perf/cpumap.h> #include "../util/stat.h" @@ -29,11 +30,7 @@ #include <err.h> -static unsigned int nthreads = 0; -static unsigned int nsecs = 10; -/* amount of futexes per thread */ -static unsigned int nfutexes = 1024; -static bool fshared = false, done = false, silent = false; +static bool done = false; static int futex_flag = 0; struct timeval bench__start, bench__end, bench__runtime; @@ -49,12 +46,18 @@ struct worker { unsigned long ops; }; +static struct bench_futex_parameters params = { + .nfutexes = 1024, + .runtime = 10, +}; + static const struct option options[] = { - OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), - OPT_UINTEGER('r', "runtime", &nsecs, "Specify runtime (in seconds)"), - OPT_UINTEGER('f', "futexes", &nfutexes, "Specify amount of futexes per threads"), - OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), - OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), + OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), + OPT_UINTEGER('r', "runtime", ¶ms.runtime, "Specify runtime (in seconds)"), + OPT_UINTEGER('f', "futexes", ¶ms.nfutexes, "Specify amount of futexes per threads"), + OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), + OPT_BOOLEAN( 'S', "shared", ¶ms.fshared, "Use shared futexes instead of private ones"), + OPT_BOOLEAN( 'm', "mlockall", ¶ms.mlockall, "Lock all current and future memory"), OPT_END() }; @@ -78,7 +81,7 @@ static void *workerfn(void *arg) pthread_mutex_unlock(&thread_lock); do { - for (i = 0; i < nfutexes; i++, ops++) { + for (i = 0; i < params.nfutexes; i++, ops++) { /* * We want the futex calls to fail in order to stress * the hashing of uaddr and not measure other steps, @@ -86,7 +89,7 @@ static void *workerfn(void *arg) * the critical region protected by hb->lock. */ ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag); - if (!silent && + if (!params.silent && (!ret || errno != EAGAIN || errno != EWOULDBLOCK)) warn("Non-expected futex return call"); } @@ -112,7 +115,7 @@ static void print_summary(void) double stddev = stddev_stats(&throughput_stats); printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n", - !silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg), + !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg), (int)bench__runtime.tv_sec); } @@ -141,30 +144,35 @@ int bench_futex_hash(int argc, const char **argv) act.sa_sigaction = toggle_done; sigaction(SIGINT, &act, NULL); - if (!nthreads) /* default to the number of CPUs */ - nthreads = cpu->nr; + if (params.mlockall) { + if (mlockall(MCL_CURRENT | MCL_FUTURE)) + err(EXIT_FAILURE, "mlockall"); + } + + if (!params.nthreads) /* default to the number of CPUs */ + params.nthreads = cpu->nr; - worker = calloc(nthreads, sizeof(*worker)); + worker = calloc(params.nthreads, sizeof(*worker)); if (!worker) goto errmem; - if (!fshared) + if (!params.fshared) futex_flag = FUTEX_PRIVATE_FLAG; printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n", - getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs); + getpid(), params.nthreads, params.nfutexes, params.fshared ? "shared":"private", params.runtime); init_stats(&throughput_stats); pthread_mutex_init(&thread_lock, NULL); pthread_cond_init(&thread_parent, NULL); pthread_cond_init(&thread_worker, NULL); - threads_starting = nthreads; + threads_starting = params.nthreads; pthread_attr_init(&thread_attr); gettimeofday(&bench__start, NULL); - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { worker[i].tid = i; - worker[i].futex = calloc(nfutexes, sizeof(*worker[i].futex)); + worker[i].futex = calloc(params.nfutexes, sizeof(*worker[i].futex)); if (!worker[i].futex) goto errmem; @@ -189,10 +197,10 @@ int bench_futex_hash(int argc, const char **argv) pthread_cond_broadcast(&thread_worker); pthread_mutex_unlock(&thread_lock); - sleep(nsecs); + sleep(params.runtime); toggle_done(0, NULL, NULL); - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { ret = pthread_join(worker[i].thread, NULL); if (ret) err(EXIT_FAILURE, "pthread_join"); @@ -203,18 +211,18 @@ int bench_futex_hash(int argc, const char **argv) pthread_cond_destroy(&thread_worker); pthread_mutex_destroy(&thread_lock); - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { unsigned long t = bench__runtime.tv_sec > 0 ? worker[i].ops / bench__runtime.tv_sec : 0; update_stats(&throughput_stats, t); - if (!silent) { - if (nfutexes == 1) + if (!params.silent) { + if (params.nfutexes == 1) printf("[thread %2d] futex: %p [ %ld ops/sec ]\n", worker[i].tid, &worker[i].futex[0], t); else printf("[thread %2d] futexes: %p ... %p [ %ld ops/sec ]\n", worker[i].tid, &worker[i].futex[0], - &worker[i].futex[nfutexes-1], t); + &worker[i].futex[params.nfutexes-1], t); } zfree(&worker[i].futex); diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c index 89c6d160379c..5d1fe9c35807 100644 --- a/tools/perf/bench/futex-lock-pi.c +++ b/tools/perf/bench/futex-lock-pi.c @@ -21,6 +21,7 @@ #include <err.h> #include <stdlib.h> #include <sys/time.h> +#include <sys/mman.h> struct worker { int tid; @@ -31,22 +32,24 @@ struct worker { static u_int32_t global_futex = 0; static struct worker *worker; -static unsigned int nsecs = 10; -static bool silent = false, multi = false; -static bool done = false, fshared = false; -static unsigned int nthreads = 0; +static bool done = false; static int futex_flag = 0; static pthread_mutex_t thread_lock; static unsigned int threads_starting; static struct stats throughput_stats; static pthread_cond_t thread_parent, thread_worker; +static struct bench_futex_parameters params = { + .runtime = 10, +}; + static const struct option options[] = { - OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), - OPT_UINTEGER('r', "runtime", &nsecs, "Specify runtime (in seconds)"), - OPT_BOOLEAN( 'M', "multi", &multi, "Use multiple futexes"), - OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), - OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), + OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), + OPT_UINTEGER('r', "runtime", ¶ms.runtime, "Specify runtime (in seconds)"), + OPT_BOOLEAN( 'M', "multi", ¶ms.multi, "Use multiple futexes"), + OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), + OPT_BOOLEAN( 'S', "shared", ¶ms.fshared, "Use shared futexes instead of private ones"), + OPT_BOOLEAN( 'm', "mlockall", ¶ms.mlockall, "Lock all current and future memory"), OPT_END() }; @@ -61,7 +64,7 @@ static void print_summary(void) double stddev = stddev_stats(&throughput_stats); printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n", - !silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg), + !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg), (int)bench__runtime.tv_sec); } @@ -93,7 +96,7 @@ static void *workerfn(void *arg) ret = futex_lock_pi(w->futex, NULL, futex_flag); if (ret) { /* handle lock acquisition */ - if (!silent) + if (!params.silent) warn("thread %d: Could not lock pi-lock for %p (%d)", w->tid, w->futex, ret); if (done) @@ -104,7 +107,7 @@ static void *workerfn(void *arg) usleep(1); ret = futex_unlock_pi(w->futex, futex_flag); - if (ret && !silent) + if (ret && !params.silent) warn("thread %d: Could not unlock pi-lock for %p (%d)", w->tid, w->futex, ret); ops++; /* account for thread's share of work */ @@ -120,12 +123,12 @@ static void create_threads(struct worker *w, pthread_attr_t thread_attr, cpu_set_t cpuset; unsigned int i; - threads_starting = nthreads; + threads_starting = params.nthreads; - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { worker[i].tid = i; - if (multi) { + if (params.multi) { worker[i].futex = calloc(1, sizeof(u_int32_t)); if (!worker[i].futex) err(EXIT_FAILURE, "calloc"); @@ -164,25 +167,30 @@ int bench_futex_lock_pi(int argc, const char **argv) act.sa_sigaction = toggle_done; sigaction(SIGINT, &act, NULL); - if (!nthreads) - nthreads = cpu->nr; + if (params.mlockall) { + if (mlockall(MCL_CURRENT | MCL_FUTURE)) + err(EXIT_FAILURE, "mlockall"); + } + + if (!params.nthreads) + params.nthreads = cpu->nr; - worker = calloc(nthreads, sizeof(*worker)); + worker = calloc(params.nthreads, sizeof(*worker)); if (!worker) err(EXIT_FAILURE, "calloc"); - if (!fshared) + if (!params.fshared) futex_flag = FUTEX_PRIVATE_FLAG; printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n", - getpid(), nthreads, nsecs); + getpid(), params.nthreads, params.runtime); init_stats(&throughput_stats); pthread_mutex_init(&thread_lock, NULL); pthread_cond_init(&thread_parent, NULL); pthread_cond_init(&thread_worker, NULL); - threads_starting = nthreads; + threads_starting = params.nthreads; pthread_attr_init(&thread_attr); gettimeofday(&bench__start, NULL); @@ -195,10 +203,10 @@ int bench_futex_lock_pi(int argc, const char **argv) pthread_cond_broadcast(&thread_worker); pthread_mutex_unlock(&thread_lock); - sleep(nsecs); + sleep(params.runtime); toggle_done(0, NULL, NULL); - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { ret = pthread_join(worker[i].thread, NULL); if (ret) err(EXIT_FAILURE, "pthread_join"); @@ -209,16 +217,16 @@ int bench_futex_lock_pi(int argc, const char **argv) pthread_cond_destroy(&thread_worker); pthread_mutex_destroy(&thread_lock); - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { unsigned long t = bench__runtime.tv_sec > 0 ? worker[i].ops / bench__runtime.tv_sec : 0; update_stats(&throughput_stats, t); - if (!silent) + if (!params.silent) printf("[thread %3d] futex: %p [ %ld ops/sec ]\n", worker[i].tid, worker[i].futex, t); - if (multi) + if (params.multi) zfree(&worker[i].futex); } diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index 5fa23295ee5f..97fe31fd3a23 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c @@ -6,7 +6,8 @@ * on futex2, N at a time. * * This program is particularly useful to measure the latency of nthread - * requeues without waking up any tasks -- thus mimicking a regular futex_wait. + * requeues without waking up any tasks (in the non-pi case) -- thus + * mimicking a regular futex_wait. */ /* For the CLR_() macros */ @@ -27,28 +28,35 @@ #include <err.h> #include <stdlib.h> #include <sys/time.h> +#include <sys/mman.h> static u_int32_t futex1 = 0, futex2 = 0; -/* - * How many tasks to requeue at a time. - * Default to 1 in order to make the kernel work more. - */ -static unsigned int nrequeue = 1; - static pthread_t *worker; -static bool done = false, silent = false, fshared = false; +static bool done = false; static pthread_mutex_t thread_lock; static pthread_cond_t thread_parent, thread_worker; static struct stats requeuetime_stats, requeued_stats; -static unsigned int threads_starting, nthreads = 0; +static unsigned int threads_starting; static int futex_flag = 0; +static struct bench_futex_parameters params = { + /* + * How many tasks to requeue at a time. + * Default to 1 in order to make the kernel work more. + */ + .nrequeue = 1, +}; + static const struct option options[] = { - OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), - OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"), - OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), - OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), + OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), + OPT_UINTEGER('q', "nrequeue", ¶ms.nrequeue, "Specify amount of threads to requeue at once"), + OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), + OPT_BOOLEAN( 'S', "shared", ¶ms.fshared, "Use shared futexes instead of private ones"), + OPT_BOOLEAN( 'm', "mlockall", ¶ms.mlockall, "Lock all current and future memory"), + OPT_BOOLEAN( 'B', "broadcast", ¶ms.broadcast, "Requeue all threads at once"), + OPT_BOOLEAN( 'p', "pi", ¶ms.pi, "Use PI-aware variants of FUTEX_CMP_REQUEUE"), + OPT_END() }; @@ -65,13 +73,15 @@ static void print_summary(void) printf("Requeued %d of %d threads in %.4f ms (+-%.2f%%)\n", requeued_avg, - nthreads, + params.nthreads, requeuetime_avg / USEC_PER_MSEC, rel_stddev_stats(requeuetime_stddev, requeuetime_avg)); } static void *workerfn(void *arg __maybe_unused) { + int ret; + pthread_mutex_lock(&thread_lock); threads_starting--; if (!threads_starting) @@ -79,7 +89,34 @@ static void *workerfn(void *arg __maybe_unused) pthread_cond_wait(&thread_worker, &thread_lock); pthread_mutex_unlock(&thread_lock); - futex_wait(&futex1, 0, NULL, futex_flag); + while (1) { + if (!params.pi) { + ret = futex_wait(&futex1, 0, NULL, futex_flag); + if (!ret) + break; + + if (ret && errno != EAGAIN) { + if (!params.silent) + warnx("futex_wait"); + break; + } + } else { + ret = futex_wait_requeue_pi(&futex1, 0, &futex2, + NULL, futex_flag); + if (!ret) { + /* got the lock at futex2 */ + futex_unlock_pi(&futex2, futex_flag); + break; + } + + if (ret && errno != EAGAIN) { + if (!params.silent) + warnx("futex_wait_requeue_pi"); + break; + } + } + } + return NULL; } @@ -89,10 +126,10 @@ static void block_threads(pthread_t *w, cpu_set_t cpuset; unsigned int i; - threads_starting = nthreads; + threads_starting = params.nthreads; /* create and block all threads */ - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { CPU_ZERO(&cpuset); CPU_SET(cpu->map[i % cpu->nr], &cpuset); @@ -132,22 +169,31 @@ int bench_futex_requeue(int argc, const char **argv) act.sa_sigaction = toggle_done; sigaction(SIGINT, &act, NULL); - if (!nthreads) - nthreads = cpu->nr; + if (params.mlockall) { + if (mlockall(MCL_CURRENT | MCL_FUTURE)) + err(EXIT_FAILURE, "mlockall"); + } + + if (!params.nthreads) + params.nthreads = cpu->nr; - worker = calloc(nthreads, sizeof(*worker)); + worker = calloc(params.nthreads, sizeof(*worker)); if (!worker) err(EXIT_FAILURE, "calloc"); - if (!fshared) + if (!params.fshared) futex_flag = FUTEX_PRIVATE_FLAG; - if (nrequeue > nthreads) - nrequeue = nthreads; + if (params.nrequeue > params.nthreads) + params.nrequeue = params.nthreads; - printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %p), " - "%d at a time.\n\n", getpid(), nthreads, - fshared ? "shared":"private", &futex1, &futex2, nrequeue); + if (params.broadcast) + params.nrequeue = params.nthreads; + + 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, + params.pi ? "PI ": "", &futex2, params.nrequeue); init_stats(&requeued_stats); init_stats(&requeuetime_stats); @@ -157,7 +203,7 @@ int bench_futex_requeue(int argc, const char **argv) pthread_cond_init(&thread_worker, NULL); for (j = 0; j < bench_repeat && !done; j++) { - unsigned int nrequeued = 0; + unsigned int nrequeued = 0, wakeups = 0; struct timeval start, end, runtime; /* create, launch & block all threads */ @@ -174,13 +220,31 @@ int bench_futex_requeue(int argc, const char **argv) /* Ok, all threads are patiently blocked, start requeueing */ gettimeofday(&start, NULL); - while (nrequeued < nthreads) { + while (nrequeued < params.nthreads) { + int r; + /* - * Do not wakeup any tasks blocked on futex1, allowing - * us to really measure futex_wait functionality. + * For the regular non-pi case, do not wakeup any tasks + * blocked on futex1, allowing us to really measure + * futex_wait functionality. For the PI case the first + * waiter is always awoken. */ - nrequeued += futex_cmp_requeue(&futex1, 0, &futex2, 0, - nrequeue, futex_flag); + if (!params.pi) { + r = futex_cmp_requeue(&futex1, 0, &futex2, 0, + params.nrequeue, + futex_flag); + } else { + r = futex_cmp_requeue_pi(&futex1, 0, &futex2, + params.nrequeue, + futex_flag); + wakeups++; /* assume no error */ + } + + if (r < 0) + err(EXIT_FAILURE, "couldn't requeue from %p to %p", + &futex1, &futex2); + + nrequeued += r; } gettimeofday(&end, NULL); @@ -189,17 +253,32 @@ int bench_futex_requeue(int argc, const char **argv) update_stats(&requeued_stats, nrequeued); update_stats(&requeuetime_stats, runtime.tv_usec); - if (!silent) { - printf("[Run %d]: Requeued %d of %d threads in %.4f ms\n", - j + 1, nrequeued, nthreads, runtime.tv_usec / (double)USEC_PER_MSEC); + 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); + 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); + } + } - /* everybody should be blocked on futex2, wake'em up */ - nrequeued = futex_wake(&futex2, nrequeued, futex_flag); - if (nthreads != nrequeued) - warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads); + if (!params.pi) { + /* everybody should be blocked on futex2, wake'em up */ + nrequeued = futex_wake(&futex2, nrequeued, futex_flag); + if (params.nthreads != nrequeued) + warnx("couldn't wakeup all tasks (%d/%d)", + nrequeued, params.nthreads); + } - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { ret = pthread_join(worker[i], NULL); if (ret) err(EXIT_FAILURE, "pthread_join"); diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c index 6e6f5247e1fe..e970e6b9ad53 100644 --- a/tools/perf/bench/futex-wake-parallel.c +++ b/tools/perf/bench/futex-wake-parallel.c @@ -34,6 +34,7 @@ int bench_futex_wake_parallel(int argc __maybe_unused, const char **argv __maybe #include <err.h> #include <stdlib.h> #include <sys/time.h> +#include <sys/mman.h> struct thread_data { pthread_t worker; @@ -47,8 +48,7 @@ static unsigned int nwakes = 1; static u_int32_t futex = 0; static pthread_t *blocked_worker; -static bool done = false, silent = false, fshared = false; -static unsigned int nblocked_threads = 0, nwaking_threads = 0; +static bool done = false; static pthread_mutex_t thread_lock; static pthread_cond_t thread_parent, thread_worker; static pthread_barrier_t barrier; @@ -56,11 +56,15 @@ static struct stats waketime_stats, wakeup_stats; static unsigned int threads_starting; static int futex_flag = 0; +static struct bench_futex_parameters params; + static const struct option options[] = { - OPT_UINTEGER('t', "threads", &nblocked_threads, "Specify amount of threads"), - OPT_UINTEGER('w', "nwakers", &nwaking_threads, "Specify amount of waking threads"), - OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), - OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), + OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), + OPT_UINTEGER('w', "nwakers", ¶ms.nwakes, "Specify amount of waking threads"), + OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), + OPT_BOOLEAN( 'S', "shared", ¶ms.fshared, "Use shared futexes instead of private ones"), + OPT_BOOLEAN( 'm', "mlockall", ¶ms.mlockall, "Lock all current and future memory"), + OPT_END() }; @@ -96,10 +100,10 @@ static void wakeup_threads(struct thread_data *td, pthread_attr_t thread_attr) pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); - pthread_barrier_init(&barrier, NULL, nwaking_threads + 1); + pthread_barrier_init(&barrier, NULL, params.nwakes + 1); /* create and block all threads */ - for (i = 0; i < nwaking_threads; i++) { + for (i = 0; i < params.nwakes; i++) { /* * Thread creation order will impact per-thread latency * as it will affect the order to acquire the hb spinlock. @@ -112,7 +116,7 @@ static void wakeup_threads(struct thread_data *td, pthread_attr_t thread_attr) pthread_barrier_wait(&barrier); - for (i = 0; i < nwaking_threads; i++) + for (i = 0; i < params.nwakes; i++) if (pthread_join(td[i].worker, NULL)) err(EXIT_FAILURE, "pthread_join"); @@ -143,10 +147,10 @@ static void block_threads(pthread_t *w, pthread_attr_t thread_attr, cpu_set_t cpuset; unsigned int i; - threads_starting = nblocked_threads; + threads_starting = params.nthreads; /* create and block all threads */ - for (i = 0; i < nblocked_threads; i++) { + for (i = 0; i < params.nthreads; i++) { CPU_ZERO(&cpuset); CPU_SET(cpu->map[i % cpu->nr], &cpuset); @@ -167,7 +171,7 @@ static void print_run(struct thread_data *waking_worker, unsigned int run_num) init_stats(&__wakeup_stats); init_stats(&__waketime_stats); - for (i = 0; i < nwaking_threads; i++) { + for (i = 0; i < params.nwakes; i++) { update_stats(&__waketime_stats, waking_worker[i].runtime.tv_usec); update_stats(&__wakeup_stats, waking_worker[i].nwoken); } @@ -178,7 +182,7 @@ static void print_run(struct thread_data *waking_worker, unsigned int run_num) printf("[Run %d]: Avg per-thread latency (waking %d/%d threads) " "in %.4f ms (+-%.2f%%)\n", run_num + 1, wakeup_avg, - nblocked_threads, waketime_avg / USEC_PER_MSEC, + params.nthreads, waketime_avg / USEC_PER_MSEC, rel_stddev_stats(waketime_stddev, waketime_avg)); } @@ -193,7 +197,7 @@ static void print_summary(void) printf("Avg per-thread latency (waking %d/%d threads) in %.4f ms (+-%.2f%%)\n", wakeup_avg, - nblocked_threads, + params.nthreads, waketime_avg / USEC_PER_MSEC, rel_stddev_stats(waketime_stddev, waketime_avg)); } @@ -203,7 +207,7 @@ static void do_run_stats(struct thread_data *waking_worker) { unsigned int i; - for (i = 0; i < nwaking_threads; i++) { + for (i = 0; i < params.nwakes; i++) { update_stats(&waketime_stats, waking_worker[i].runtime.tv_usec); update_stats(&wakeup_stats, waking_worker[i].nwoken); } @@ -238,36 +242,42 @@ int bench_futex_wake_parallel(int argc, const char **argv) act.sa_sigaction = toggle_done; sigaction(SIGINT, &act, NULL); + if (params.mlockall) { + if (mlockall(MCL_CURRENT | MCL_FUTURE)) + err(EXIT_FAILURE, "mlockall"); + } + cpu = perf_cpu_map__new(NULL); if (!cpu) err(EXIT_FAILURE, "calloc"); - if (!nblocked_threads) - nblocked_threads = cpu->nr; + if (!params.nthreads) + params.nthreads = cpu->nr; /* some sanity checks */ - if (nwaking_threads > nblocked_threads || !nwaking_threads) - nwaking_threads = nblocked_threads; + if (params.nwakes > params.nthreads || + !params.nwakes) + params.nwakes = params.nthreads; - if (nblocked_threads % nwaking_threads) + if (params.nthreads % params.nwakes) errx(EXIT_FAILURE, "Must be perfectly divisible"); /* * Each thread will wakeup nwakes tasks in * a single futex_wait call. */ - nwakes = nblocked_threads/nwaking_threads; + nwakes = params.nthreads/params.nwakes; - blocked_worker = calloc(nblocked_threads, sizeof(*blocked_worker)); + blocked_worker = calloc(params.nthreads, sizeof(*blocked_worker)); if (!blocked_worker) err(EXIT_FAILURE, "calloc"); - if (!fshared) + if (!params.fshared) futex_flag = FUTEX_PRIVATE_FLAG; printf("Run summary [PID %d]: blocking on %d threads (at [%s] " "futex %p), %d threads waking up %d at a time.\n\n", - getpid(), nblocked_threads, fshared ? "shared":"private", - &futex, nwaking_threads, nwakes); + getpid(), params.nthreads, params.fshared ? "shared":"private", + &futex, params.nwakes, nwakes); init_stats(&wakeup_stats); init_stats(&waketime_stats); @@ -278,7 +288,7 @@ int bench_futex_wake_parallel(int argc, const char **argv) pthread_cond_init(&thread_worker, NULL); for (j = 0; j < bench_repeat && !done; j++) { - waking_worker = calloc(nwaking_threads, sizeof(*waking_worker)); + waking_worker = calloc(params.nwakes, sizeof(*waking_worker)); if (!waking_worker) err(EXIT_FAILURE, "calloc"); @@ -297,14 +307,14 @@ int bench_futex_wake_parallel(int argc, const char **argv) /* Ok, all threads are patiently blocked, start waking folks up */ wakeup_threads(waking_worker, thread_attr); - for (i = 0; i < nblocked_threads; i++) { + for (i = 0; i < params.nthreads; i++) { ret = pthread_join(blocked_worker[i], NULL); if (ret) err(EXIT_FAILURE, "pthread_join"); } do_run_stats(waking_worker); - if (!silent) + if (!params.silent) print_run(waking_worker, j); free(waking_worker); diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index 6d217868f53c..77f058a47790 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c @@ -27,29 +27,34 @@ #include <err.h> #include <stdlib.h> #include <sys/time.h> +#include <sys/mman.h> /* all threads will block on the same futex */ static u_int32_t futex1 = 0; -/* - * How many wakeups to do at a time. - * Default to 1 in order to make the kernel work more. - */ -static unsigned int nwakes = 1; - -pthread_t *worker; -static bool done = false, silent = false, fshared = false; +static pthread_t *worker; +static bool done = false; static pthread_mutex_t thread_lock; static pthread_cond_t thread_parent, thread_worker; static struct stats waketime_stats, wakeup_stats; -static unsigned int threads_starting, nthreads = 0; +static unsigned int threads_starting; static int futex_flag = 0; +static struct bench_futex_parameters params = { + /* + * How many wakeups to do at a time. + * Default to 1 in order to make the kernel work more. + */ + .nwakes = 1, +}; + static const struct option options[] = { - OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), - OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"), - OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), - OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), + OPT_UINTEGER('t', "threads", ¶ms.nthreads, "Specify amount of threads"), + OPT_UINTEGER('w', "nwakes", ¶ms.nwakes, "Specify amount of threads to wake at once"), + OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"), + OPT_BOOLEAN( 'S', "shared", ¶ms.fshared, "Use shared futexes instead of private ones"), + OPT_BOOLEAN( 'm', "mlockall", ¶ms.mlockall, "Lock all current and future memory"), + OPT_END() }; @@ -84,7 +89,7 @@ static void print_summary(void) printf("Wokeup %d of %d threads in %.4f ms (+-%.2f%%)\n", wakeup_avg, - nthreads, + params.nthreads, waketime_avg / USEC_PER_MSEC, rel_stddev_stats(waketime_stddev, waketime_avg)); } @@ -95,10 +100,10 @@ static void block_threads(pthread_t *w, cpu_set_t cpuset; unsigned int i; - threads_starting = nthreads; + threads_starting = params.nthreads; /* create and block all threads */ - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { CPU_ZERO(&cpuset); CPU_SET(cpu->map[i % cpu->nr], &cpuset); @@ -140,19 +145,25 @@ int bench_futex_wake(int argc, const char **argv) act.sa_sigaction = toggle_done; sigaction(SIGINT, &act, NULL); - if (!nthreads) - nthreads = cpu->nr; + if (params.mlockall) { + if (mlockall(MCL_CURRENT | MCL_FUTURE)) + err(EXIT_FAILURE, "mlockall"); + } + + if (!params.nthreads) + params.nthreads = cpu->nr; - worker = calloc(nthreads, sizeof(*worker)); + worker = calloc(params.nthreads, sizeof(*worker)); if (!worker) err(EXIT_FAILURE, "calloc"); - if (!fshared) + if (!params.fshared) futex_flag = FUTEX_PRIVATE_FLAG; printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), " "waking up %d at a time.\n\n", - getpid(), nthreads, fshared ? "shared":"private", &futex1, nwakes); + getpid(), params.nthreads, params.fshared ? "shared":"private", + &futex1, params.nwakes); init_stats(&wakeup_stats); init_stats(&waketime_stats); @@ -179,20 +190,22 @@ int bench_futex_wake(int argc, const char **argv) /* Ok, all threads are patiently blocked, start waking folks up */ gettimeofday(&start, NULL); - while (nwoken != nthreads) - nwoken += futex_wake(&futex1, nwakes, futex_flag); + while (nwoken != params.nthreads) + nwoken += futex_wake(&futex1, + params.nwakes, futex_flag); gettimeofday(&end, NULL); timersub(&end, &start, &runtime); update_stats(&wakeup_stats, nwoken); update_stats(&waketime_stats, runtime.tv_usec); - if (!silent) { + if (!params.silent) { printf("[Run %d]: Wokeup %d of %d threads in %.4f ms\n", - j + 1, nwoken, nthreads, runtime.tv_usec / (double)USEC_PER_MSEC); + j + 1, nwoken, params.nthreads, + runtime.tv_usec / (double)USEC_PER_MSEC); } - for (i = 0; i < nthreads; i++) { + for (i = 0; i < params.nthreads; i++) { ret = pthread_join(worker[i], NULL); if (ret) err(EXIT_FAILURE, "pthread_join"); diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h index 31b53cc7d5bc..b3853aac3021 100644 --- a/tools/perf/bench/futex.h +++ b/tools/perf/bench/futex.h @@ -13,6 +13,20 @@ #include <sys/types.h> #include <linux/futex.h> +struct bench_futex_parameters { + bool silent; + bool fshared; + bool mlockall; + bool multi; /* lock-pi */ + bool pi; /* requeue-pi */ + bool broadcast; /* requeue */ + unsigned int runtime; /* seconds*/ + unsigned int nthreads; + unsigned int nfutexes; + unsigned int nwakes; + unsigned int nrequeue; +}; + /** * futex() - SYS_futex syscall wrapper * @uaddr: address of first futex @@ -20,7 +34,7 @@ * @val: typically expected value of uaddr, but varies by op * @timeout: typically an absolute struct timespec (except where noted * otherwise). Overloaded by some ops - * @uaddr2: address of second futex for some ops\ + * @uaddr2: address of second futex for some ops * @val3: varies by op * @opflags: flags to be bitwise OR'd with op, such as FUTEX_PRIVATE_FLAG * @@ -77,7 +91,7 @@ futex_unlock_pi(u_int32_t *uaddr, int opflags) /** * futex_cmp_requeue() - requeue tasks from uaddr to uaddr2 * @nr_wake: wake up to this many tasks -* @nr_requeue: requeue up to this many tasks +* @nr_requeue: requeue up to this many tasks */ static inline int futex_cmp_requeue(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, int nr_wake, @@ -86,4 +100,38 @@ futex_cmp_requeue(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, int nr_wak return futex(uaddr, FUTEX_CMP_REQUEUE, nr_wake, nr_requeue, uaddr2, val, opflags); } + +/** + * futex_wait_requeue_pi() - block on uaddr and prepare to requeue to uaddr2 + * @uaddr: non-PI futex source + * @uaddr2: PI futex target + * + * This is the first half of the requeue_pi mechanism. It shall always be + * paired with futex_cmp_requeue_pi(). + */ +static inline int +futex_wait_requeue_pi(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, + struct timespec *timeout, int opflags) +{ + return futex(uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0, + opflags); +} + +/** + * futex_cmp_requeue_pi() - requeue tasks from uaddr to uaddr2 + * @uaddr: non-PI futex source + * @uaddr2: PI futex target + * @nr_requeue: requeue up to this many tasks + * + * This is the second half of the requeue_pi mechanism. It shall always be + * paired with futex_wait_requeue_pi(). The first waker is always awoken. + */ +static inline int +futex_cmp_requeue_pi(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, + int nr_requeue, int opflags) +{ + return futex(uaddr, FUTEX_CMP_REQUEUE_PI, 1, nr_requeue, uaddr2, + val, opflags); +} + #endif /* _FUTEX_H */ diff --git a/tools/perf/bench/inject-buildid.c b/tools/perf/bench/inject-buildid.c index 55d373b75791..17672790f123 100644 --- a/tools/perf/bench/inject-buildid.c +++ b/tools/perf/bench/inject-buildid.c @@ -133,7 +133,7 @@ static u64 dso_map_addr(struct bench_dso *dso) return 0x400000ULL + dso->ino * 8192ULL; } -static u32 synthesize_attr(struct bench_data *data) +static ssize_t synthesize_attr(struct bench_data *data) { union perf_event event; @@ -151,7 +151,7 @@ static u32 synthesize_attr(struct bench_data *data) return writen(data->input_pipe[1], &event, event.header.size); } -static u32 synthesize_fork(struct bench_data *data) +static ssize_t synthesize_fork(struct bench_data *data) { union perf_event event; @@ -169,8 +169,7 @@ static u32 synthesize_fork(struct bench_data *data) return writen(data->input_pipe[1], &event, event.header.size); } -static u32 synthesize_mmap(struct bench_data *data, struct bench_dso *dso, - u64 timestamp) +static ssize_t synthesize_mmap(struct bench_data *data, struct bench_dso *dso, u64 timestamp) { union perf_event event; size_t len = offsetof(struct perf_record_mmap2, filename); @@ -198,23 +197,25 @@ static u32 synthesize_mmap(struct bench_data *data, struct bench_dso *dso, if (len > sizeof(event.mmap2)) { /* write mmap2 event first */ - writen(data->input_pipe[1], &event, len - bench_id_hdr_size); + if (writen(data->input_pipe[1], &event, len - bench_id_hdr_size) < 0) + return -1; /* zero-fill sample id header */ memset(id_hdr_ptr, 0, bench_id_hdr_size); /* put timestamp in the right position */ ts_idx = (bench_id_hdr_size / sizeof(u64)) - 2; id_hdr_ptr[ts_idx] = timestamp; - writen(data->input_pipe[1], id_hdr_ptr, bench_id_hdr_size); - } else { - ts_idx = (len / sizeof(u64)) - 2; - id_hdr_ptr[ts_idx] = timestamp; - writen(data->input_pipe[1], &event, len); + if (writen(data->input_pipe[1], id_hdr_ptr, bench_id_hdr_size) < 0) + return -1; + + return len; } - return len; + + ts_idx = (len / sizeof(u64)) - 2; + id_hdr_ptr[ts_idx] = timestamp; + return writen(data->input_pipe[1], &event, len); } -static u32 synthesize_sample(struct bench_data *data, struct bench_dso *dso, - u64 timestamp) +static ssize_t synthesize_sample(struct bench_data *data, struct bench_dso *dso, u64 timestamp) { union perf_event event; struct perf_sample sample = { @@ -233,7 +234,7 @@ static u32 synthesize_sample(struct bench_data *data, struct bench_dso *dso, return writen(data->input_pipe[1], &event, event.header.size); } -static u32 synthesize_flush(struct bench_data *data) +static ssize_t synthesize_flush(struct bench_data *data) { struct perf_event_header header = { .size = sizeof(header), @@ -348,14 +349,16 @@ static int inject_build_id(struct bench_data *data, u64 *max_rss) int status; unsigned int i, k; struct rusage rusage; - u64 len = 0; /* this makes the child to run */ if (perf_header__write_pipe(data->input_pipe[1]) < 0) return -1; - len += synthesize_attr(data); - len += synthesize_fork(data); + if (synthesize_attr(data) < 0) + return -1; + + if (synthesize_fork(data) < 0) + return -1; for (i = 0; i < nr_mmaps; i++) { int idx = rand() % (nr_dsos - 1); @@ -363,13 +366,18 @@ static int inject_build_id(struct bench_data *data, u64 *max_rss) u64 timestamp = rand() % 1000000; pr_debug2(" [%d] injecting: %s\n", i+1, dso->name); - len += synthesize_mmap(data, dso, timestamp); + if (synthesize_mmap(data, dso, timestamp) < 0) + return -1; - for (k = 0; k < nr_samples; k++) - len += synthesize_sample(data, dso, timestamp + k * 1000); + for (k = 0; k < nr_samples; k++) { + if (synthesize_sample(data, dso, timestamp + k * 1000) < 0) + return -1; + } - if ((i + 1) % 10 == 0) - len += synthesize_flush(data); + if ((i + 1) % 10 == 0) { + if (synthesize_flush(data) < 0) + return -1; + } } /* this makes the child to finish */ diff --git a/tools/perf/bench/synthesize.c b/tools/perf/bench/synthesize.c index b2924e3181dc..05f7c923c745 100644 --- a/tools/perf/bench/synthesize.c +++ b/tools/perf/bench/synthesize.c @@ -117,7 +117,7 @@ static int run_single_threaded(void) int err; perf_set_singlethreaded(); - session = perf_session__new(NULL, false, NULL); + session = perf_session__new(NULL, NULL); if (IS_ERR(session)) { pr_err("Session creation failed.\n"); return PTR_ERR(session); @@ -161,7 +161,7 @@ static int do_run_multi_threaded(struct target *target, init_stats(&time_stats); init_stats(&event_stats); for (i = 0; i < multi_iterations; i++) { - session = perf_session__new(NULL, false, NULL); + session = perf_session__new(NULL, NULL); if (IS_ERR(session)) return PTR_ERR(session); |