提交 8871eed8 编写于 作者: P Peter Oskolkov 提交者: briansun

selftests/futex: add futex_swap selftest

openeuler inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I4L9RU
CVE: NA

-------------------

This is the final patch in FUTEX_SWAP patchset. It
adds a test/benchmark to validate behavior and
compare performance of a new FUTEX_SWAP futex operation.

Detailed API design and behavior considerations are provided
in the commit messages of the previous two patches.
Signed-off-by: NPeter Oskolkov <posk@google.com>
上级 fc4a2354
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
futex_requeue_pi futex_requeue_pi
futex_requeue_pi_mismatched_ops futex_requeue_pi_mismatched_ops
futex_requeue_pi_signal_restart futex_requeue_pi_signal_restart
futex_swap
futex_wait_private_mapped_file futex_wait_private_mapped_file
futex_wait_timeout futex_wait_timeout
futex_wait_uninitialized_heap futex_wait_uninitialized_heap
......
...@@ -13,6 +13,7 @@ TEST_GEN_FILES := \ ...@@ -13,6 +13,7 @@ TEST_GEN_FILES := \
futex_requeue_pi \ futex_requeue_pi \
futex_requeue_pi_signal_restart \ futex_requeue_pi_signal_restart \
futex_requeue_pi_mismatched_ops \ futex_requeue_pi_mismatched_ops \
futex_swap \
futex_wait_uninitialized_heap \ futex_wait_uninitialized_heap \
futex_wait_private_mapped_file futex_wait_private_mapped_file
......
// 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;
}
...@@ -38,6 +38,9 @@ typedef volatile u_int32_t futex_t; ...@@ -38,6 +38,9 @@ typedef volatile u_int32_t futex_t;
#ifndef FUTEX_CMP_REQUEUE_PI #ifndef FUTEX_CMP_REQUEUE_PI
#define FUTEX_CMP_REQUEUE_PI 12 #define FUTEX_CMP_REQUEUE_PI 12
#endif #endif
#ifndef FUTEX_SWAP
#define FUTEX_SWAP 13
#endif
#ifndef FUTEX_WAIT_REQUEUE_PI_PRIVATE #ifndef FUTEX_WAIT_REQUEUE_PI_PRIVATE
#define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_WAIT_REQUEUE_PI | \ #define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_WAIT_REQUEUE_PI | \
FUTEX_PRIVATE_FLAG) FUTEX_PRIVATE_FLAG)
...@@ -46,6 +49,9 @@ typedef volatile u_int32_t futex_t; ...@@ -46,6 +49,9 @@ typedef volatile u_int32_t futex_t;
#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | \ #define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | \
FUTEX_PRIVATE_FLAG) FUTEX_PRIVATE_FLAG)
#endif #endif
#ifndef FUTEX_SWAP_PRIVATE
#define FUTEX_SWAP_PRIVATE (FUTEX_WAIT_WAKE | FUTEX_PRIVATE_FLAG)
#endif
/** /**
* futex() - SYS_futex syscall wrapper * futex() - SYS_futex syscall wrapper
...@@ -204,6 +210,19 @@ futex_cmp_requeue_pi(futex_t *uaddr, futex_t val, futex_t *uaddr2, int nr_wake, ...@@ -204,6 +210,19 @@ futex_cmp_requeue_pi(futex_t *uaddr, futex_t val, futex_t *uaddr2, int nr_wake,
val, opflags); val, opflags);
} }
/**
* futex_swap() - block on uaddr and wake one task blocked on uaddr2.
* @uaddr: futex to block the current task on
* @timeout: relative timeout for the current task block
* @uaddr2: futex to wake tasks at (can be the same as uaddr)
*/
static inline int
futex_swap(futex_t *uaddr, futex_t val, struct timespec *timeout,
futex_t *uaddr2, int opflags)
{
return futex(uaddr, FUTEX_SWAP, val, timeout, uaddr2, 0, opflags);
}
/** /**
* futex_cmpxchg() - atomic compare and exchange * futex_cmpxchg() - atomic compare and exchange
* @uaddr: The address of the futex to be modified * @uaddr: The address of the futex to be modified
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册