0001
0002
0003
0004
0005
0006
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", ¶ms.nthreads, "Specify amount of threads"),
0048 OPT_UINTEGER('r', "runtime", ¶ms.runtime, "Specify runtime (in seconds)"),
0049 OPT_BOOLEAN( 'M', "multi", ¶ms.multi, "Use multiple futexes"),
0050 OPT_BOOLEAN( 's', "silent", ¶ms.silent, "Silent mode: do not display data/details"),
0051 OPT_BOOLEAN( 'S', "shared", ¶ms.fshared, "Use shared futexes instead of private ones"),
0052 OPT_BOOLEAN( 'm', "mlockall", ¶ms.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
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) {
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++;
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
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 }