Skip to content

Commit

Permalink
selftests: futex: Expand timeout test
Browse files Browse the repository at this point in the history
Improve futex timeout testing by checking all the operations that
supports timeout and their available modes.

Signed-off-by: André Almeida <andrealmeid@collabora.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20210427135328.11013-3-andrealmeid@collabora.com
  • Loading branch information
andrealmeid authored and Ingo Molnar committed May 12, 2021
1 parent c7d84e7 commit f4addd5
Showing 1 changed file with 110 additions and 16 deletions.
126 changes: 110 additions & 16 deletions tools/testing/selftests/futex/functional/futex_wait_timeout.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,18 @@
*
* HISTORY
* 2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com>
* 2021-Apr-26: More test cases by André Almeida <andrealmeid@collabora.com>
*
*****************************************************************************/

#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "futextest.h"
#include "logging.h"

#define TEST_NAME "futex-wait-timeout"

static long timeout_ns = 100000; /* 100us default timeout */
static futex_t futex_pi;

void usage(char *prog)
{
Expand All @@ -37,11 +34,67 @@ void usage(char *prog)
VQUIET, VCRITICAL, VINFO);
}

/*
* Get a PI lock and hold it forever, so the main thread lock_pi will block
* and we can test the timeout
*/
void *get_pi_lock(void *arg)
{
int ret;
volatile futex_t lock = 0;

ret = futex_lock_pi(&futex_pi, NULL, 0, 0);
if (ret != 0)
error("futex_lock_pi failed\n", ret);

/* Blocks forever */
ret = futex_wait(&lock, 0, NULL, 0);
error("futex_wait failed\n", ret);

return NULL;
}

/*
* Check if the function returned the expected error
*/
static void test_timeout(int res, int *ret, char *test_name, int err)
{
if (!res || errno != err) {
ksft_test_result_fail("%s returned %d\n", test_name,
res < 0 ? errno : res);
*ret = RET_FAIL;
} else {
ksft_test_result_pass("%s succeeds\n", test_name);
}
}

/*
* Calculate absolute timeout and correct overflow
*/
static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to,
long timeout_ns)
{
if (clock_gettime(clockid, to)) {
error("clock_gettime failed\n", errno);
return errno;
}

to->tv_nsec += timeout_ns;

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

return 0;
}

int main(int argc, char *argv[])
{
futex_t f1 = FUTEX_INITIALIZER;
struct timespec to;
int res, ret = RET_PASS;
struct timespec to;
pthread_t thread;
int c;

while ((c = getopt(argc, argv, "cht:v:")) != -1) {
Expand All @@ -65,22 +118,63 @@ int main(int argc, char *argv[])
}

ksft_print_header();
ksft_set_plan(1);
ksft_set_plan(7);
ksft_print_msg("%s: Block on a futex and wait for timeout\n",
basename(argv[0]));
ksft_print_msg("\tArguments: timeout=%ldns\n", timeout_ns);

/* initialize timeout */
pthread_create(&thread, NULL, get_pi_lock, NULL);

/* initialize relative timeout */
to.tv_sec = 0;
to.tv_nsec = timeout_ns;

info("Calling futex_wait on f1: %u @ %p\n", f1, &f1);
res = futex_wait(&f1, f1, &to, FUTEX_PRIVATE_FLAG);
if (!res || errno != ETIMEDOUT) {
fail("futex_wait returned %d\n", ret < 0 ? errno : ret);
ret = RET_FAIL;
}
res = futex_wait(&f1, f1, &to, 0);
test_timeout(res, &ret, "futex_wait relative", ETIMEDOUT);

/* FUTEX_WAIT_BITSET with CLOCK_REALTIME */
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
return RET_FAIL;
res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME);
test_timeout(res, &ret, "futex_wait_bitset realtime", ETIMEDOUT);

/* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */
if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
return RET_FAIL;
res = futex_wait_bitset(&f1, f1, &to, 1, 0);
test_timeout(res, &ret, "futex_wait_bitset monotonic", ETIMEDOUT);

/* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
return RET_FAIL;
res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME);
test_timeout(res, &ret, "futex_wait_requeue_pi realtime", ETIMEDOUT);

/* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */
if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns))
return RET_FAIL;
res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0);
test_timeout(res, &ret, "futex_wait_requeue_pi monotonic", ETIMEDOUT);

/*
* FUTEX_LOCK_PI with CLOCK_REALTIME
* Due to historical reasons, FUTEX_LOCK_PI supports only realtime
* clock, but requires the caller to not set CLOCK_REALTIME flag.
*
* If you call FUTEX_LOCK_PI with a monotonic clock, it'll be
* interpreted as a realtime clock, and (unless you mess your machine's
* time or your time machine) the monotonic clock value is always
* smaller than realtime and the syscall will timeout immediately.
*/
if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns))
return RET_FAIL;
res = futex_lock_pi(&futex_pi, &to, 0, 0);
test_timeout(res, &ret, "futex_lock_pi realtime", ETIMEDOUT);

/* Test operations that don't support FUTEX_CLOCK_REALTIME */
res = futex_lock_pi(&futex_pi, NULL, 0, FUTEX_CLOCK_REALTIME);
test_timeout(res, &ret, "futex_lock_pi invalid timeout flag", ENOSYS);

print_result(TEST_NAME, ret);
ksft_print_cnts();
return ret;
}

0 comments on commit f4addd5

Please sign in to comment.