Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2013  Davidlohr Bueso <davidlohr@hp.com>
0004  *
0005  * futex-wake: Block a bunch of threads on a futex and wake'em up, N at a time.
0006  *
0007  * This program is particularly useful to measure the latency of nthread wakeups
0008  * in non-error situations:  all waiters are queued and all wake calls wakeup
0009  * one or more tasks, and thus the waitqueue is never empty.
0010  */
0011 
0012 /* For the CLR_() macros */
0013 #include <string.h>
0014 #include <pthread.h>
0015 
0016 #include <signal.h>
0017 #include "../util/stat.h"
0018 #include <subcmd/parse-options.h>
0019 #include <linux/compiler.h>
0020 #include <linux/kernel.h>
0021 #include <linux/time64.h>
0022 #include <errno.h>
0023 #include <perf/cpumap.h>
0024 #include "bench.h"
0025 #include "futex.h"
0026 
0027 #include <err.h>
0028 #include <stdlib.h>
0029 #include <sys/time.h>
0030 #include <sys/mman.h>
0031 
0032 /* all threads will block on the same futex */
0033 static u_int32_t futex1 = 0;
0034 
0035 static pthread_t *worker;
0036 static bool done = false;
0037 static pthread_mutex_t thread_lock;
0038 static pthread_cond_t thread_parent, thread_worker;
0039 static struct stats waketime_stats, wakeup_stats;
0040 static unsigned int threads_starting;
0041 static int futex_flag = 0;
0042 
0043 static struct bench_futex_parameters params = {
0044     /*
0045      * How many wakeups to do at a time.
0046      * Default to 1 in order to make the kernel work more.
0047      */
0048     .nwakes  = 1,
0049 };
0050 
0051 static const struct option options[] = {
0052     OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
0053     OPT_UINTEGER('w', "nwakes",  &params.nwakes, "Specify amount of threads to wake at once"),
0054     OPT_BOOLEAN( 's', "silent",  &params.silent, "Silent mode: do not display data/details"),
0055     OPT_BOOLEAN( 'S', "shared",  &params.fshared, "Use shared futexes instead of private ones"),
0056     OPT_BOOLEAN( 'm', "mlockall", &params.mlockall, "Lock all current and future memory"),
0057 
0058     OPT_END()
0059 };
0060 
0061 static const char * const bench_futex_wake_usage[] = {
0062     "perf bench futex wake <options>",
0063     NULL
0064 };
0065 
0066 static void *workerfn(void *arg __maybe_unused)
0067 {
0068     pthread_mutex_lock(&thread_lock);
0069     threads_starting--;
0070     if (!threads_starting)
0071         pthread_cond_signal(&thread_parent);
0072     pthread_cond_wait(&thread_worker, &thread_lock);
0073     pthread_mutex_unlock(&thread_lock);
0074 
0075     while (1) {
0076         if (futex_wait(&futex1, 0, NULL, futex_flag) != EINTR)
0077             break;
0078     }
0079 
0080     pthread_exit(NULL);
0081     return NULL;
0082 }
0083 
0084 static void print_summary(void)
0085 {
0086     double waketime_avg = avg_stats(&waketime_stats);
0087     double waketime_stddev = stddev_stats(&waketime_stats);
0088     unsigned int wakeup_avg = avg_stats(&wakeup_stats);
0089 
0090     printf("Wokeup %d of %d threads in %.4f ms (+-%.2f%%)\n",
0091            wakeup_avg,
0092            params.nthreads,
0093            waketime_avg / USEC_PER_MSEC,
0094            rel_stddev_stats(waketime_stddev, waketime_avg));
0095 }
0096 
0097 static void block_threads(pthread_t *w,
0098               pthread_attr_t thread_attr, struct perf_cpu_map *cpu)
0099 {
0100     cpu_set_t *cpuset;
0101     unsigned int i;
0102     size_t size;
0103     int nrcpus = perf_cpu_map__nr(cpu);
0104     threads_starting = params.nthreads;
0105 
0106     cpuset = CPU_ALLOC(nrcpus);
0107     BUG_ON(!cpuset);
0108     size = CPU_ALLOC_SIZE(nrcpus);
0109 
0110     /* create and block all threads */
0111     for (i = 0; i < params.nthreads; i++) {
0112         CPU_ZERO_S(size, cpuset);
0113         CPU_SET_S(perf_cpu_map__cpu(cpu, i % perf_cpu_map__nr(cpu)).cpu, size, cpuset);
0114 
0115         if (pthread_attr_setaffinity_np(&thread_attr, size, cpuset)) {
0116             CPU_FREE(cpuset);
0117             err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
0118         }
0119 
0120         if (pthread_create(&w[i], &thread_attr, workerfn, NULL)) {
0121             CPU_FREE(cpuset);
0122             err(EXIT_FAILURE, "pthread_create");
0123         }
0124     }
0125     CPU_FREE(cpuset);
0126 }
0127 
0128 static void toggle_done(int sig __maybe_unused,
0129             siginfo_t *info __maybe_unused,
0130             void *uc __maybe_unused)
0131 {
0132     done = true;
0133 }
0134 
0135 int bench_futex_wake(int argc, const char **argv)
0136 {
0137     int ret = 0;
0138     unsigned int i, j;
0139     struct sigaction act;
0140     pthread_attr_t thread_attr;
0141     struct perf_cpu_map *cpu;
0142 
0143     argc = parse_options(argc, argv, options, bench_futex_wake_usage, 0);
0144     if (argc) {
0145         usage_with_options(bench_futex_wake_usage, options);
0146         exit(EXIT_FAILURE);
0147     }
0148 
0149     cpu = perf_cpu_map__new(NULL);
0150     if (!cpu)
0151         err(EXIT_FAILURE, "calloc");
0152 
0153     memset(&act, 0, sizeof(act));
0154     sigfillset(&act.sa_mask);
0155     act.sa_sigaction = toggle_done;
0156     sigaction(SIGINT, &act, NULL);
0157 
0158     if (params.mlockall) {
0159         if (mlockall(MCL_CURRENT | MCL_FUTURE))
0160             err(EXIT_FAILURE, "mlockall");
0161     }
0162 
0163     if (!params.nthreads)
0164         params.nthreads = perf_cpu_map__nr(cpu);
0165 
0166     worker = calloc(params.nthreads, sizeof(*worker));
0167     if (!worker)
0168         err(EXIT_FAILURE, "calloc");
0169 
0170     if (!params.fshared)
0171         futex_flag = FUTEX_PRIVATE_FLAG;
0172 
0173     printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), "
0174            "waking up %d at a time.\n\n",
0175            getpid(), params.nthreads, params.fshared ? "shared":"private",
0176            &futex1, params.nwakes);
0177 
0178     init_stats(&wakeup_stats);
0179     init_stats(&waketime_stats);
0180     pthread_attr_init(&thread_attr);
0181     pthread_mutex_init(&thread_lock, NULL);
0182     pthread_cond_init(&thread_parent, NULL);
0183     pthread_cond_init(&thread_worker, NULL);
0184 
0185     for (j = 0; j < bench_repeat && !done; j++) {
0186         unsigned int nwoken = 0;
0187         struct timeval start, end, runtime;
0188 
0189         /* create, launch & block all threads */
0190         block_threads(worker, thread_attr, cpu);
0191 
0192         /* make sure all threads are already blocked */
0193         pthread_mutex_lock(&thread_lock);
0194         while (threads_starting)
0195             pthread_cond_wait(&thread_parent, &thread_lock);
0196         pthread_cond_broadcast(&thread_worker);
0197         pthread_mutex_unlock(&thread_lock);
0198 
0199         usleep(100000);
0200 
0201         /* Ok, all threads are patiently blocked, start waking folks up */
0202         gettimeofday(&start, NULL);
0203         while (nwoken != params.nthreads)
0204             nwoken += futex_wake(&futex1,
0205                          params.nwakes, futex_flag);
0206         gettimeofday(&end, NULL);
0207         timersub(&end, &start, &runtime);
0208 
0209         update_stats(&wakeup_stats, nwoken);
0210         update_stats(&waketime_stats, runtime.tv_usec);
0211 
0212         if (!params.silent) {
0213             printf("[Run %d]: Wokeup %d of %d threads in %.4f ms\n",
0214                    j + 1, nwoken, params.nthreads,
0215                    runtime.tv_usec / (double)USEC_PER_MSEC);
0216         }
0217 
0218         for (i = 0; i < params.nthreads; i++) {
0219             ret = pthread_join(worker[i], NULL);
0220             if (ret)
0221                 err(EXIT_FAILURE, "pthread_join");
0222         }
0223 
0224     }
0225 
0226     /* cleanup & report results */
0227     pthread_cond_destroy(&thread_parent);
0228     pthread_cond_destroy(&thread_worker);
0229     pthread_mutex_destroy(&thread_lock);
0230     pthread_attr_destroy(&thread_attr);
0231 
0232     print_summary();
0233 
0234     free(worker);
0235     perf_cpu_map__put(cpu);
0236     return ret;
0237 }