blob: 9034d04372d3f34b45e1f76e853bcf81990d275f [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
#include <errno.h>
#include <getopt.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "atomic.h"
#include "futextest.h"
/* The futex the main thread waits on. */
futex_t futex_main = FUTEX_INITIALIZER;
/* The futex the other thread wats on. */
futex_t futex_other = FUTEX_INITIALIZER;
/* The number of iterations to run (>1 => run benchmarks. */
static int cfg_iterations = 1;
/* If != 0, print diagnostic messages. */
static int cfg_verbose;
/* If == 0, do not use validation_counter. Useful for benchmarking. */
static int cfg_validate = 1;
/* How to swap threads. */
#define SWAP_WAKE_WAIT 1
#define SWAP_SWAP 2
/* Futex values. */
#define FUTEX_WAITING 0
#define FUTEX_WAKEUP 1
/* An atomic counter used to validate proper swapping. */
static atomic_t validation_counter;
void futex_swap_op(int mode, futex_t *futex_this, futex_t *futex_that)
{
int ret;
switch (mode) {
case SWAP_WAKE_WAIT:
futex_set(futex_this, FUTEX_WAITING);
futex_set(futex_that, FUTEX_WAKEUP);
futex_wake(futex_that, 1, FUTEX_PRIVATE_FLAG);
futex_wait(futex_this, FUTEX_WAITING, NULL, FUTEX_PRIVATE_FLAG);
if (*futex_this != FUTEX_WAKEUP) {
fprintf(stderr, "unexpected futex_this value on wakeup\n");
exit(1);
}
break;
case SWAP_SWAP:
futex_set(futex_this, FUTEX_WAITING);
futex_set(futex_that, FUTEX_WAKEUP);
ret = futex_swap(futex_this, FUTEX_WAITING, NULL,
futex_that, FUTEX_PRIVATE_FLAG);
if (ret < 0 && errno == ENOSYS) {
/* futex_swap not implemented */
perror("futex_swap");
exit(1);
}
if (*futex_this != FUTEX_WAKEUP) {
fprintf(stderr, "unexpected futex_this value on wakeup\n");
exit(1);
}
break;
default:
fprintf(stderr, "unknown mode in %s\n", __func__);
exit(1);
}
}
void *other_thread(void *arg)
{
int mode = *((int *)arg);
int counter;
if (cfg_verbose)
printf("%s started\n", __func__);
futex_wait(&futex_other, 0, NULL, FUTEX_PRIVATE_FLAG);
for (counter = 0; counter < cfg_iterations; ++counter) {
if (cfg_validate) {
int prev = 2 * counter + 1;
if (prev != atomic_cmpxchg(&validation_counter, prev,
prev + 1)) {
fprintf(stderr, "swap validation failed\n");
exit(1);
}
}
futex_swap_op(mode, &futex_other, &futex_main);
}
if (cfg_verbose)
printf("%s finished: %d iteration(s)\n", __func__, counter);
return NULL;
}
void run_test(int mode)
{
struct timespec start, stop;
int ret, counter;
pthread_t thread;
uint64_t duration;
futex_set(&futex_other, FUTEX_WAITING);
atomic_set(&validation_counter, 0);
ret = pthread_create(&thread, NULL, &other_thread, &mode);
if (ret) {
perror("pthread_create");
exit(1);
}
ret = clock_gettime(CLOCK_MONOTONIC, &start);
if (ret) {
perror("clock_gettime");
exit(1);
}
for (counter = 0; counter < cfg_iterations; ++counter) {
if (cfg_validate) {
int prev = 2 * counter;
if (prev != atomic_cmpxchg(&validation_counter, prev,
prev + 1)) {
fprintf(stderr, "swap validation failed\n");
exit(1);
}
}
futex_swap_op(mode, &futex_main, &futex_other);
}
if (cfg_validate && validation_counter.val != 2 * cfg_iterations) {
fprintf(stderr, "final swap validation failed\n");
exit(1);
}
ret = clock_gettime(CLOCK_MONOTONIC, &stop);
if (ret) {
perror("clock_gettime");
exit(1);
}
duration = (stop.tv_sec - start.tv_sec) * 1000000000LL +
stop.tv_nsec - start.tv_nsec;
if (cfg_verbose || cfg_iterations > 1) {
printf("completed %d swap and back iterations in %lu ns: %lu ns per swap\n",
cfg_iterations, duration,
duration / (cfg_iterations * 2));
}
/* The remote thread is blocked; send it the final wake. */
futex_set(&futex_other, FUTEX_WAKEUP);
futex_wake(&futex_other, 1, FUTEX_PRIVATE_FLAG);
if (pthread_join(thread, NULL)) {
perror("pthread_join");
exit(1);
}
}
void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -h Display this help message\n");
printf(" -i N Use N iterations to benchmark\n");
printf(" -n Do not validate swapping correctness\n");
printf(" -v Print diagnostic messages\n");
}
int main(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "hi:nv")) != -1) {
switch (c) {
case 'h':
usage(basename(argv[0]));
exit(0);
case 'i':
cfg_iterations = atoi(optarg);
break;
case 'n':
cfg_validate = 0;
break;
case 'v':
cfg_verbose = 1;
break;
default:
usage(basename(argv[0]));
exit(1);
}
}
printf("\n\n------- running SWAP_WAKE_WAIT -----------\n\n");
run_test(SWAP_WAKE_WAIT);
printf("PASS\n");
printf("\n\n------- running SWAP_SWAP -----------\n\n");
run_test(SWAP_SWAP);
printf("PASS\n");
return 0;
}