Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests: futex2: Add wake/wait test
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
1 parent
641c681
commit 96e0a14
Showing
5 changed files
with
296 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ futex_wait_private_mapped_file | |
futex_wait_timeout | ||
futex_wait_uninitialized_heap | ||
futex_wait_wouldblock | ||
futex2_wait |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |