Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2015 Davidlohr Bueso.
0004  */
0005 
0006 /* For the CLR_() macros */
0007 #include <string.h>
0008 #include <pthread.h>
0009 
0010 #include <signal.h>
0011 #include "../util/stat.h"
0012 #include <subcmd/parse-options.h>
0013 #include <linux/compiler.h>
0014 #include <linux/kernel.h>
0015 #include <linux/zalloc.h>
0016 #include <errno.h>
0017 #include <perf/cpumap.h>
0018 #include "bench.h"
0019 #include "futex.h"
0020 
0021 #include <err.h>
0022 #include <stdlib.h>
0023 #include <sys/time.h>
0024 #include <sys/mman.h>
0025 
0026 struct worker {
0027     int tid;
0028     u_int32_t *futex;
0029     pthread_t thread;
0030     unsigned long ops;
0031 };
0032 
0033 static u_int32_t global_futex = 0;
0034 static struct worker *worker;
0035 static bool done = false;
0036 static int futex_flag = 0;
0037 static pthread_mutex_t thread_lock;
0038 static unsigned int threads_starting;
0039 static struct stats throughput_stats;
0040 static pthread_cond_t thread_parent, thread_worker;
0041 
0042 static struct bench_futex_parameters params = {
0043     .runtime  = 10,
0044 };
0045 
0046 static const struct option options[] = {
0047     OPT_UINTEGER('t', "threads", &params.nthreads, "Specify amount of threads"),
0048     OPT_UINTEGER('r', "runtime", &params.runtime, "Specify runtime (in seconds)"),
0049     OPT_BOOLEAN( 'M', "multi",   &params.multi, "Use multiple futexes"),
0050     OPT_BOOLEAN( 's', "silent",  &params.silent, "Silent mode: do not display data/details"),
0051     OPT_BOOLEAN( 'S', "shared",  &params.fshared, "Use shared futexes instead of private ones"),
0052     OPT_BOOLEAN( 'm', "mlockall", &params.mlockall, "Lock all current and future memory"),
0053     OPT_END()
0054 };
0055 
0056 static const char * const bench_futex_lock_pi_usage[] = {
0057     "perf bench futex lock-pi <options>",
0058     NULL
0059 };
0060 
0061 static void print_summary(void)
0062 {
0063     unsigned long avg = avg_stats(&throughput_stats);
0064     double stddev = stddev_stats(&throughput_stats);
0065 
0066     printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
0067            !params.silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
0068            (int)bench__runtime.tv_sec);
0069 }
0070 
0071 static void toggle_done(int sig __maybe_unused,
0072             siginfo_t *info __maybe_unused,
0073             void *uc __maybe_unused)
0074 {
0075     /* inform all threads that we're done for the day */
0076     done = true;
0077     gettimeofday(&bench__end, NULL);
0078     timersub(&bench__end, &bench__start, &bench__runtime);
0079 }
0080 
0081 static void *workerfn(void *arg)
0082 {
0083     struct worker *w = (struct worker *) arg;
0084     unsigned long ops = w->ops;
0085 
0086     pthread_mutex_lock(&thread_lock);
0087     threads_starting--;
0088     if (!threads_starting)
0089         pthread_cond_signal(&thread_parent);
0090     pthread_cond_wait(&thread_worker, &thread_lock);
0091     pthread_mutex_unlock(&thread_lock);
0092 
0093     do {
0094         int ret;
0095     again:
0096         ret = futex_lock_pi(w->futex, NULL, futex_flag);
0097 
0098         if (ret) { /* handle lock acquisition */
0099             if (!params.silent)
0100                 warn("thread %d: Could not lock pi-lock for %p (%d)",
0101                      w->tid, w->futex, ret);
0102             if (done)
0103                 break;
0104 
0105             goto again;
0106         }
0107 
0108         usleep(1);
0109         ret = futex_unlock_pi(w->futex, futex_flag);
0110         if (ret && !params.silent)
0111             warn("thread %d: Could not unlock pi-lock for %p (%d)",
0112                  w->tid, w->futex, ret);
0113         ops++; /* account for thread's share of work */
0114     }  while (!done);
0115 
0116     w->ops = ops;
0117     return NULL;
0118 }
0119 
0120 static void create_threads(struct worker *w, pthread_attr_t thread_attr,
0121                struct perf_cpu_map *cpu)
0122 {
0123     cpu_set_t *cpuset;
0124     unsigned int i;
0125     int nrcpus =  perf_cpu_map__nr(cpu);
0126     size_t size;
0127 
0128     threads_starting = params.nthreads;
0129 
0130     cpuset = CPU_ALLOC(nrcpus);
0131     BUG_ON(!cpuset);
0132     size = CPU_ALLOC_SIZE(nrcpus);
0133 
0134     for (i = 0; i < params.nthreads; i++) {
0135         worker[i].tid = i;
0136 
0137         if (params.multi) {
0138             worker[i].futex = calloc(1, sizeof(u_int32_t));
0139             if (!worker[i].futex)
0140                 err(EXIT_FAILURE, "calloc");
0141         } else
0142             worker[i].futex = &global_futex;
0143 
0144         CPU_ZERO_S(size, cpuset);
0145         CPU_SET_S(perf_cpu_map__cpu(cpu, i % perf_cpu_map__nr(cpu)).cpu, size, cpuset);
0146 
0147         if (pthread_attr_setaffinity_np(&thread_attr, size, cpuset)) {
0148             CPU_FREE(cpuset);
0149             err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
0150         }
0151 
0152         if (pthread_create(&w[i].thread, &thread_attr, workerfn, &worker[i])) {
0153             CPU_FREE(cpuset);
0154             err(EXIT_FAILURE, "pthread_create");
0155         }
0156     }
0157     CPU_FREE(cpuset);
0158 }
0159 
0160 int bench_futex_lock_pi(int argc, const char **argv)
0161 {
0162     int ret = 0;
0163     unsigned int i;
0164     struct sigaction act;
0165     pthread_attr_t thread_attr;
0166     struct perf_cpu_map *cpu;
0167 
0168     argc = parse_options(argc, argv, options, bench_futex_lock_pi_usage, 0);
0169     if (argc)
0170         goto err;
0171 
0172     cpu = perf_cpu_map__new(NULL);
0173     if (!cpu)
0174         err(EXIT_FAILURE, "calloc");
0175 
0176     memset(&act, 0, sizeof(act));
0177     sigfillset(&act.sa_mask);
0178     act.sa_sigaction = toggle_done;
0179     sigaction(SIGINT, &act, NULL);
0180 
0181     if (params.mlockall) {
0182         if (mlockall(MCL_CURRENT | MCL_FUTURE))
0183             err(EXIT_FAILURE, "mlockall");
0184     }
0185 
0186     if (!params.nthreads)
0187         params.nthreads = perf_cpu_map__nr(cpu);
0188 
0189     worker = calloc(params.nthreads, sizeof(*worker));
0190     if (!worker)
0191         err(EXIT_FAILURE, "calloc");
0192 
0193     if (!params.fshared)
0194         futex_flag = FUTEX_PRIVATE_FLAG;
0195 
0196     printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n",
0197            getpid(), params.nthreads, params.runtime);
0198 
0199     init_stats(&throughput_stats);
0200     pthread_mutex_init(&thread_lock, NULL);
0201     pthread_cond_init(&thread_parent, NULL);
0202     pthread_cond_init(&thread_worker, NULL);
0203 
0204     threads_starting = params.nthreads;
0205     pthread_attr_init(&thread_attr);
0206     gettimeofday(&bench__start, NULL);
0207 
0208     create_threads(worker, thread_attr, cpu);
0209     pthread_attr_destroy(&thread_attr);
0210 
0211     pthread_mutex_lock(&thread_lock);
0212     while (threads_starting)
0213         pthread_cond_wait(&thread_parent, &thread_lock);
0214     pthread_cond_broadcast(&thread_worker);
0215     pthread_mutex_unlock(&thread_lock);
0216 
0217     sleep(params.runtime);
0218     toggle_done(0, NULL, NULL);
0219 
0220     for (i = 0; i < params.nthreads; i++) {
0221         ret = pthread_join(worker[i].thread, NULL);
0222         if (ret)
0223             err(EXIT_FAILURE, "pthread_join");
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 
0231     for (i = 0; i < params.nthreads; i++) {
0232         unsigned long t = bench__runtime.tv_sec > 0 ?
0233             worker[i].ops / bench__runtime.tv_sec : 0;
0234 
0235         update_stats(&throughput_stats, t);
0236         if (!params.silent)
0237             printf("[thread %3d] futex: %p [ %ld ops/sec ]\n",
0238                    worker[i].tid, worker[i].futex, t);
0239 
0240         if (params.multi)
0241             zfree(&worker[i].futex);
0242     }
0243 
0244     print_summary();
0245 
0246     free(worker);
0247     perf_cpu_map__put(cpu);
0248     return ret;
0249 err:
0250     usage_with_options(bench_futex_lock_pi_usage, options);
0251     exit(EXIT_FAILURE);
0252 }