Skip to content

Commit

Permalink
selftests: futex2: Add wake/wait test
Browse files Browse the repository at this point in the history
Add a simple file to test wake/wait mechanism using futex2 interface.
Test three scenarios: using a common local int variable as private
futex, a shm futex as shared futex and a file-backed shared memory as a
shared futex. This should test all branches of futex_get_key().

Create helper files so more tests can evaluate futex2. While 32bit ABIs
from glibc aren't yet able to use 64 bit sized time variables, add a
temporary workaround that implements the required types and calls the
appropriated syscalls, since futex2 doesn't supports 32 bit sized time.

Signed-off-by: André Almeida <andrealmeid@collabora.com>
  • Loading branch information
andrealmeid authored and xanmod committed Jun 29, 2021
1 parent 641c681 commit 96e0a14
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 2 deletions.
1 change: 1 addition & 0 deletions tools/testing/selftests/futex/functional/.gitignore
Expand Up @@ -6,3 +6,4 @@ futex_wait_private_mapped_file
futex_wait_timeout
futex_wait_uninitialized_heap
futex_wait_wouldblock
futex2_wait
6 changes: 4 additions & 2 deletions tools/testing/selftests/futex/functional/Makefile
@@ -1,10 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
INCLUDES := -I../include -I../../
INCLUDES := -I../include -I../../ -I../../../../../usr/include/
CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE -pthread $(INCLUDES)
LDLIBS := -lpthread -lrt

HEADERS := \
../include/futextest.h \
../include/futex2test.h \
../include/atomic.h \
../include/logging.h
TEST_GEN_FILES := \
Expand All @@ -14,7 +15,8 @@ TEST_GEN_FILES := \
futex_requeue_pi_signal_restart \
futex_requeue_pi_mismatched_ops \
futex_wait_uninitialized_heap \
futex_wait_private_mapped_file
futex_wait_private_mapped_file \
futex2_wait

TEST_PROGS := run.sh

Expand Down
209 changes: 209 additions & 0 deletions tools/testing/selftests/futex/functional/futex2_wait.c
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/******************************************************************************
*
* Copyright Collabora Ltd., 2021
*
* DESCRIPTION
* Test wait/wake mechanism of futex2, using 32bit sized futexes.
*
* AUTHOR
* André Almeida <andrealmeid@collabora.com>
*
* HISTORY
* 2021-Feb-5: Initial version by André <andrealmeid@collabora.com>
*
*****************************************************************************/

#include <errno.h>
#include <error.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include "futex2test.h"
#include "logging.h"

#define TEST_NAME "futex2-wait"
#define timeout_ns 30000000
#define WAKE_WAIT_US 10000
#define SHM_PATH "futex2_shm_file"
futex_t *f1;

void usage(char *prog)
{
printf("Usage: %s\n", prog);
printf(" -c Use color\n");
printf(" -h Display this help message\n");
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
VQUIET, VCRITICAL, VINFO);
}

void *waiterfn(void *arg)
{
struct timespec64 to64;
unsigned int flags = 0;

if (arg)
flags = *((unsigned int *) arg);

/* setting absolute timeout for futex2 */
if (gettime64(CLOCK_MONOTONIC, &to64))
error("gettime64 failed\n", errno);

to64.tv_nsec += timeout_ns;

if (to64.tv_nsec >= 1000000000) {
to64.tv_sec++;
to64.tv_nsec -= 1000000000;
}

if (futex2_wait(f1, *f1, FUTEX_32 | flags, &to64))
printf("waiter failed errno %d\n", errno);

return NULL;
}

void *waitershm(void *arg)
{
futex2_wait(arg, 0, FUTEX_32 | FUTEX_SHARED_FLAG, NULL);

return NULL;
}

int main(int argc, char *argv[])
{
pthread_t waiter;
unsigned int flags = FUTEX_SHARED_FLAG;
int res, ret = RET_PASS;
int c;
futex_t f_private = 0;

f1 = &f_private;

while ((c = getopt(argc, argv, "cht:v:")) != -1) {
switch (c) {
case 'c':
log_color(1);
break;
case 'h':
usage(basename(argv[0]));
exit(0);
case 'v':
log_verbosity(atoi(optarg));
break;
default:
usage(basename(argv[0]));
exit(1);
}
}

ksft_print_header();
ksft_set_plan(3);
ksft_print_msg("%s: Test FUTEX2_WAIT\n",
basename(argv[0]));

/* Testing a private futex */
info("Calling private futex2_wait on f1: %u @ %p with val=%u\n", *f1, f1, *f1);

if (pthread_create(&waiter, NULL, waiterfn, NULL))
error("pthread_create failed\n", errno);

usleep(WAKE_WAIT_US);

info("Calling private futex2_wake on f1: %u @ %p with val=%u\n", *f1, f1, *f1);
res = futex2_wake(f1, 1, FUTEX_32);
if (res != 1) {
ksft_test_result_fail("futex2_wake private returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex2_wake private succeeds\n");
}

int shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);

if (shm_id < 0) {
perror("shmget");
exit(1);
}

/* Testing an anon page shared memory */
unsigned int *shared_data = shmat(shm_id, NULL, 0);

*shared_data = 0;
f1 = shared_data;

info("Calling shared futex2_wait on f1: %u @ %p with val=%u\n", *f1, f1, *f1);

if (pthread_create(&waiter, NULL, waiterfn, &flags))
error("pthread_create failed\n", errno);

usleep(WAKE_WAIT_US);

info("Calling shared futex2_wake on f1: %u @ %p with val=%u\n", *f1, f1, *f1);
res = futex2_wake(f1, 1, FUTEX_32 | FUTEX_SHARED_FLAG);
if (res != 1) {
ksft_test_result_fail("futex2_wake shared (shmget) returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex2_wake shared (shmget) succeeds\n");
}

shmdt(shared_data);

/* Testing a file backed shared memory */
void *shm;
int fd, pid;

f_private = 0;

fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
perror("open");
exit(1);
}

res = ftruncate(fd, sizeof(f_private));
if (res) {
perror("ftruncate");
exit(1);
}

shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm == MAP_FAILED) {
perror("mmap");
exit(1);
}

memcpy(shm, &f_private, sizeof(f_private));

pthread_create(&waiter, NULL, waitershm, shm);

usleep(WAKE_WAIT_US);

res = futex2_wake(shm, 1, FUTEX_32 | FUTEX_SHARED_FLAG);
if (res != 1) {
ksft_test_result_fail("futex2_wake shared (mmap) returned: %d %s\n",
res ? errno : res,
res ? strerror(errno) : "");
ret = RET_FAIL;
} else {
ksft_test_result_pass("futex2_wake shared (mmap) succeeds\n");
}

munmap(shm, sizeof(f_private));

remove(SHM_PATH);

ksft_print_cnts();
return ret;
}
3 changes: 3 additions & 0 deletions tools/testing/selftests/futex/functional/run.sh
Expand Up @@ -73,3 +73,6 @@ echo
echo
./futex_wait_uninitialized_heap $COLOR
./futex_wait_private_mapped_file $COLOR

echo
./futex2_wait $COLOR
79 changes: 79 additions & 0 deletions tools/testing/selftests/futex/include/futex2test.h
@@ -0,0 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/******************************************************************************
*
* Copyright Collabora Ltd., 2021
*
* DESCRIPTION
* Futex2 library addons for old futex library
*
* AUTHOR
* André Almeida <andrealmeid@collabora.com>
*
* HISTORY
* 2021-Feb-5: Initial version by André <andrealmeid@collabora.com>
*
*****************************************************************************/
#include "futextest.h"
#include <stdio.h>

#define NSEC_PER_SEC 1000000000L

#ifndef FUTEX_8
# define FUTEX_8 0
#endif
#ifndef FUTEX_16
# define FUTEX_16 1
#endif
#ifndef FUTEX_32
# define FUTEX_32 2
#endif

/*
* - Y2038 section for 32-bit applications -
*
* Remove this when glibc is ready for y2038. Then, always compile with
* `-DTIME_BITS=64` or `-D__USE_TIME_BITS64`. glibc will provide both
* timespec64 and clock_gettime64 so we won't need to define here.
*/
#if defined(__i386__) || __TIMESIZE == 32
# define NR_gettime __NR_clock_gettime64
#else
# define NR_gettime __NR_clock_gettime
#endif

struct timespec64 {
long long tv_sec; /* seconds */
long long tv_nsec; /* nanoseconds */
};

int gettime64(clock_t clockid, struct timespec64 *tv)
{
return syscall(NR_gettime, clockid, tv);
}
/*
* - End of Y2038 section -
*/

/**
* futex2_wait - If (*uaddr == val), wait at uaddr until timo
* @uaddr: User address to wait on
* @val: Expected value at uaddr, return if is not equal
* @flags: Operation flags
* @timo: Optional timeout for operation
*/
static inline int futex2_wait(volatile void *uaddr, unsigned long val,
unsigned long flags, struct timespec64 *timo)
{
return syscall(__NR_futex_wait, uaddr, val, flags, timo);
}

/**
* futex2_wake - Wake a number of waiters at uaddr
* @uaddr: Address to wake
* @nr: Number of waiters to wake
* @flags: Operation flags
*/
static inline int futex2_wake(volatile void *uaddr, unsigned int nr, unsigned long flags)
{
return syscall(__NR_futex_wake, uaddr, nr, flags);
}

0 comments on commit 96e0a14

Please sign in to comment.