Module Name: src
Committed By: thorpej
Date: Sun Nov 1 15:22:58 UTC 2020
Modified Files:
src/tests/lib/libc/sys [thorpej-futex]: t_futex_ops.c
Log Message:
- Re-factor the code that sets up real-time LWPs for various tests.
- Add tests for the RW_HANDOFF operations.
To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.5.2.1 src/tests/lib/libc/sys/t_futex_ops.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/tests/lib/libc/sys/t_futex_ops.c
diff -u src/tests/lib/libc/sys/t_futex_ops.c:1.5 src/tests/lib/libc/sys/t_futex_ops.c:1.5.2.1
--- src/tests/lib/libc/sys/t_futex_ops.c:1.5 Wed May 6 05:14:27 2020
+++ src/tests/lib/libc/sys/t_futex_ops.c Sun Nov 1 15:22:58 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: t_futex_ops.c,v 1.5 2020/05/06 05:14:27 thorpej Exp $ */
+/* $NetBSD: t_futex_ops.c,v 1.5.2.1 2020/11/01 15:22:58 thorpej Exp $ */
/*-
* Copyright (c) 2019, 2020 The NetBSD Foundation, Inc.
@@ -29,7 +29,7 @@
#include <sys/cdefs.h>
__COPYRIGHT("@(#) Copyright (c) 2019, 2020\
The NetBSD Foundation, inc. All rights reserved.");
-__RCSID("$NetBSD: t_futex_ops.c,v 1.5 2020/05/06 05:14:27 thorpej Exp $");
+__RCSID("$NetBSD: t_futex_ops.c,v 1.5.2.1 2020/11/01 15:22:58 thorpej Exp $");
#include <sys/fcntl.h>
#include <sys/mman.h>
@@ -78,6 +78,7 @@ struct lwp_data {
volatile int *futex_ptr;
volatile int *error_ptr;
int block_val;
+ pri_t rt_prio;
void (*exit_func)(void);
@@ -101,6 +102,20 @@ static void *bs_source_buffer = NULL;
static void *bs_verify_buffer = NULL;
static long bs_pagesize;
+static int pri_min;
+static int pri_max;
+
+static void
+setup_rt_params(void)
+{
+ long pri;
+
+ ATF_REQUIRE((pri = sysconf(_SC_SCHED_PRI_MIN)) != -1);
+ pri_min = (int)pri;
+ ATF_REQUIRE((pri = sysconf(_SC_SCHED_PRI_MAX)) != -1);
+ pri_max = (int)pri;
+}
+
static void
create_lwp_waiter(struct lwp_data *d)
{
@@ -188,6 +203,23 @@ simple_test_waiter_lwp(void *arg)
_lwp_exit();
}
+static void
+rt_simple_test_waiter_lwp(void *arg)
+{
+ struct lwp_data *d = arg;
+ struct sched_param sp;
+ int policy;
+
+ d->threadid = _lwp_self();
+
+ ATF_REQUIRE(_sched_getparam(getpid(), d->threadid, &policy, &sp) == 0);
+ policy = SCHED_RR;
+ sp.sched_priority = d->rt_prio;
+ ATF_REQUIRE(_sched_setparam(getpid(), d->threadid, policy, &sp) == 0);
+
+ simple_test_waiter_lwp(arg);
+}
+
static bool
verify_zero_bs(void)
{
@@ -826,7 +858,7 @@ do_futex_requeue_test(int flags, int op)
/* Move all waiters from 0 to 1. */
ATF_REQUIRE(__futex(&futex_word, op | flags,
- 0, NULL, &futex_word1, INT_MAX, good_val3) == 0);
+ 0, NULL, &futex_word1, INT_MAX, good_val3) == 4);
/*
* FUTEX 0: 0 LWPs
@@ -847,7 +879,7 @@ do_futex_requeue_test(int flags, int op)
/* Wake one waiter on 1, move one waiter to 0. */
ATF_REQUIRE(__futex(&futex_word1, op | flags,
- 1, NULL, &futex_word, 1, good_val3) == 1);
+ 1, NULL, &futex_word, 1, good_val3) == 2);
/*
* FUTEX 0: 1 LWP
@@ -1335,59 +1367,19 @@ ATF_TC_CLEANUP(futex_wait_evil_unmapped_
/*****************************************************************************/
-static int pri_min;
-static int pri_max;
-
-static void
-lowpri_simple_test_waiter_lwp(void *arg)
-{
- struct lwp_data *d = arg;
- struct sched_param sp;
- int policy;
-
- d->threadid = _lwp_self();
-
- ATF_REQUIRE(_sched_getparam(getpid(), d->threadid, &policy, &sp) == 0);
- policy = SCHED_RR;
- sp.sched_priority = pri_min;
- ATF_REQUIRE(_sched_setparam(getpid(), d->threadid, policy, &sp) == 0);
-
- simple_test_waiter_lwp(arg);
-}
-
-static void
-highpri_simple_test_waiter_lwp(void *arg)
-{
- struct lwp_data *d = arg;
- struct sched_param sp;
- int policy;
-
- d->threadid = _lwp_self();
-
- ATF_REQUIRE(_sched_getparam(getpid(), d->threadid, &policy, &sp) == 0);
- policy = SCHED_RR;
- sp.sched_priority = pri_max;
- ATF_REQUIRE(_sched_setparam(getpid(), d->threadid, policy, &sp) == 0);
-
- simple_test_waiter_lwp(arg);
-}
-
static void
do_test_wake_highest_pri(void)
{
lwpid_t waiter;
int tries;
- long pri;
- ATF_REQUIRE((pri = sysconf(_SC_SCHED_PRI_MIN)) != -1);
- pri_min = (int)pri;
- ATF_REQUIRE((pri = sysconf(_SC_SCHED_PRI_MAX)) != -1);
- pri_max = (int)pri;
+ setup_rt_params();
futex_word = 0;
membar_sync();
- setup_lwp_context(&lwp_data[0], lowpri_simple_test_waiter_lwp);
+ setup_lwp_context(&lwp_data[0], rt_simple_test_waiter_lwp);
+ lwp_data[0].rt_prio = pri_min;
lwp_data[0].op_flags = FUTEX_PRIVATE_FLAG;
lwp_data[0].futex_error = -1;
lwp_data[0].futex_ptr = &futex_word;
@@ -1409,7 +1401,8 @@ do_test_wake_highest_pri(void)
/* Ensure it's blocked. */
ATF_REQUIRE(lwp_data[0].futex_error == -1);
- setup_lwp_context(&lwp_data[1], highpri_simple_test_waiter_lwp);
+ setup_lwp_context(&lwp_data[1], rt_simple_test_waiter_lwp);
+ lwp_data[1].rt_prio = pri_max;
lwp_data[1].op_flags = FUTEX_PRIVATE_FLAG;
lwp_data[1].futex_error = -1;
lwp_data[1].futex_ptr = &futex_word;
@@ -1471,7 +1464,6 @@ ATF_TC_HEAD(futex_wake_highest_pri, tc)
}
ATF_TC_BODY(futex_wake_highest_pri, tc)
{
- atf_tc_expect_fail("PR kern/55230");
do_test_wake_highest_pri();
}
ATF_TC_CLEANUP(futex_wake_highest_pri, tc)
@@ -1481,6 +1473,828 @@ ATF_TC_CLEANUP(futex_wake_highest_pri, t
/*****************************************************************************/
+static void
+do_test_rw_handoff_read(void)
+{
+ int i, tries;
+ int rv;
+
+ futex_word = FUTEX_WAITERS;
+ membar_sync();
+
+ for (i = 0; i < 3; i++) {
+ setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp);
+ lwp_data[i].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[i].futex_error = -1;
+ lwp_data[i].futex_ptr = &futex_word;
+ lwp_data[i].block_val = futex_word;
+ lwp_data[i].bitset = FUTEX_RW_READER;
+ lwp_data[i].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+ &lwp_data[i].lwpid) == 0);
+ }
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 3)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 3, "read-waiters failed to start");
+
+ /* Ensure they're all blocked. */
+ ATF_REQUIRE(lwp_data[0].futex_error == -1);
+ ATF_REQUIRE(lwp_data[1].futex_error == -1);
+ ATF_REQUIRE(lwp_data[2].futex_error == -1);
+
+ /* Ensure a regular wake errors out. */
+ rv = __futex(&futex_word,
+ FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT_MAX, NULL,
+ NULL, 0, 0);
+ ATF_REQUIRE(rv == -1 && errno == EINVAL);
+
+ /*
+ * Issue a hand-off. It should wake all 3 readers and update
+ * the futex word.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ FUTEX_WAITERS, NULL, NULL, 0, 0) == 3);
+ ATF_REQUIRE(futex_word == 3);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 0)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "read-waiters failed to exit");
+
+ /* Ensure they all exited error-free. */
+ ATF_REQUIRE(lwp_data[0].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[0]);
+
+ ATF_REQUIRE(lwp_data[1].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[1]);
+
+ ATF_REQUIRE(lwp_data[2].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[2]);
+}
+
+ATF_TC_WITH_CLEANUP(futex_rw_handoff_read);
+ATF_TC_HEAD(futex_rw_handoff_read, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "tests rwlock direct hand-off to readers");
+}
+ATF_TC_BODY(futex_rw_handoff_read, tc)
+{
+ atf_tc_skip("futex_rw_handoff is currently broken");
+ do_test_rw_handoff_read();
+}
+ATF_TC_CLEANUP(futex_rw_handoff_read, tc)
+{
+ do_cleanup();
+}
+
+/*****************************************************************************/
+
+static void
+do_test_rw_handoff_write(void)
+{
+ unsigned int i, tries;
+ lwpid_t lid;
+
+ /*
+ * The kernel should not care about the WRITE_WANTED bit, and
+ * should use the contents of the sleepqs as the truth.
+ */
+ futex_word = FUTEX_WAITERS;
+ membar_sync();
+
+ for (i = 0; i < 3; i++) {
+ setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp);
+ lwp_data[i].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[i].futex_error = -1;
+ lwp_data[i].futex_ptr = &futex_word;
+ lwp_data[i].block_val = futex_word;
+ lwp_data[i].bitset = FUTEX_RW_WRITER;
+ lwp_data[i].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+ &lwp_data[i].lwpid) == 0);
+
+ /*
+ * Wait for each one to start in-turn, because we want
+ * to know the order in which the LWPs block on the futex.
+ */
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == i + 1)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ }
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 3, "write-waiters failed to start");
+
+ /* Ensure they're all blocked. */
+ ATF_REQUIRE(lwp_data[0].futex_error == -1);
+ ATF_REQUIRE(lwp_data[1].futex_error == -1);
+ ATF_REQUIRE(lwp_data[2].futex_error == -1);
+
+ /*
+ * Issue a hand-off for each waiter. This should awaken one
+ * at a time and update the futex word.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ FUTEX_WAITERS, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_WANTED);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[0].lwpid);
+
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_WANTED);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[1].lwpid);
+
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_WANTED) == 0);
+ ATF_REQUIRE((futex_word & FUTEX_WAITERS) == 0);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+
+ /*
+ * Issue one final hand-off; it should result in a fully
+ * released lock word.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 0);
+ ATF_REQUIRE(futex_word == 0);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 0)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "read-waiters failed to exit");
+
+ /* Ensure they all exited error-free. */
+ ATF_REQUIRE(lwp_data[0].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[0]);
+
+ ATF_REQUIRE(lwp_data[1].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[1]);
+
+ ATF_REQUIRE(lwp_data[2].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[2]);
+}
+
+ATF_TC_WITH_CLEANUP(futex_rw_handoff_write);
+ATF_TC_HEAD(futex_rw_handoff_write, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "tests rwlock direct hand-off to writers");
+}
+ATF_TC_BODY(futex_rw_handoff_write, tc)
+{
+ atf_tc_skip("futex_rw_handoff is currently broken");
+ do_test_rw_handoff_write();
+}
+ATF_TC_CLEANUP(futex_rw_handoff_write, tc)
+{
+ do_cleanup();
+}
+
+/*****************************************************************************/
+
+static void
+do_test_rw_handoff_write_preferred(void)
+{
+ unsigned int i, tries;
+ lwpid_t lid;
+
+ /*
+ * The kernel should not care about the WRITE_WANTED bit, and
+ * should use the contents of the sleepqs as the truth.
+ */
+ futex_word = FUTEX_WAITERS;
+ membar_sync();
+
+ for (i = 0; i < 2; i++) {
+ setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp);
+ lwp_data[i].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[i].futex_error = -1;
+ lwp_data[i].futex_ptr = &futex_word;
+ lwp_data[i].block_val = futex_word;
+ lwp_data[i].bitset = FUTEX_RW_READER;
+ lwp_data[i].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+ &lwp_data[i].lwpid) == 0);
+
+ /*
+ * Wait for each one to start in-turn, because we want
+ * to know the order in which the LWPs block on the futex.
+ */
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == i + 1)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ }
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 2, "read-waiters failed to start");
+
+ setup_lwp_context(&lwp_data[2], simple_test_waiter_lwp);
+ lwp_data[2].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[2].futex_error = -1;
+ lwp_data[2].futex_ptr = &futex_word;
+ lwp_data[2].block_val = futex_word;
+ lwp_data[2].bitset = FUTEX_RW_WRITER;
+ lwp_data[2].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[2].context, 0,
+ &lwp_data[2].lwpid) == 0);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 3)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 3, "write-waiter failed to start");
+
+ /* Ensure they're all blocked. */
+ ATF_REQUIRE(lwp_data[0].futex_error == -1);
+ ATF_REQUIRE(lwp_data[1].futex_error == -1);
+ ATF_REQUIRE(lwp_data[2].futex_error == -1);
+
+ /*
+ * Issue a hand-off. It should select the writer despite
+ * the fact that it is the most recent waiter.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ FUTEX_WAITERS, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_WANTED) == 0);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+
+ /*
+ * Issue another hand-off. It should awaken both waiting
+ * readers.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 2);
+ ATF_REQUIRE(futex_word == 2);
+
+ /*
+ * Issue one final hand-off; it should result in a fully
+ * released lock word. Note that we don't have any
+ * outstaning waiters waiting, and therefore there will
+ * not be a kernel futex; this exercises a specific code
+ * path in the kernel designed to handle this.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 0);
+ ATF_REQUIRE(futex_word == 0);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 0)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "read-waiters failed to exit");
+
+ /* Ensure they all exited error-free. */
+ ATF_REQUIRE(lwp_data[0].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[0]);
+
+ ATF_REQUIRE(lwp_data[1].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[1]);
+
+ ATF_REQUIRE(lwp_data[2].futex_error == 0);
+ reap_lwp_waiter(&lwp_data[2]);
+}
+
+ATF_TC_WITH_CLEANUP(futex_rw_handoff_write_preferred);
+ATF_TC_HEAD(futex_rw_handoff_write_preferred, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "tests that rwlock direct hand-off prefers writers");
+}
+ATF_TC_BODY(futex_rw_handoff_write_preferred, tc)
+{
+ atf_tc_skip("futex_rw_handoff is currently broken");
+ do_test_rw_handoff_write_preferred();
+}
+ATF_TC_CLEANUP(futex_rw_handoff_write_preferred, tc)
+{
+ do_cleanup();
+}
+
+/*****************************************************************************/
+
+static void
+do_test_rw_handoff_write_locked_rt_reader_preferred(void)
+{
+ unsigned int i, tries;
+ lwpid_t lid;
+
+ setup_rt_params();
+
+ /*
+ * We need to show the rwlock as write-locked to ensure
+ * the rt read-waiter blocks.
+ */
+ futex_word =
+ FUTEX_WAITERS | FUTEX_RW_WRITE_WANTED | FUTEX_RW_WRITE_LOCKED;
+ membar_sync();
+
+ /*
+ * Create LWPs in the following order:
+ *
+ * 0 - non-RT reader
+ * 1 - RT reader
+ * 2 - non-RT writer
+ *
+ * We expect them to be awakened in this order:
+ *
+ * 1 -> 2 -> 0
+ */
+ for (i = 0; i < 3; i++) {
+ if (i == 1) {
+ setup_lwp_context(&lwp_data[i],
+ rt_simple_test_waiter_lwp);
+ lwp_data[i].rt_prio = pri_min;
+ } else {
+ setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp);
+ }
+ lwp_data[i].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[i].futex_error = -1;
+ lwp_data[i].futex_ptr = &futex_word;
+ lwp_data[i].block_val = futex_word;
+ lwp_data[i].bitset = i == 2 ? FUTEX_RW_WRITER : FUTEX_RW_READER;
+ lwp_data[i].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+ &lwp_data[i].lwpid) == 0);
+
+ /*
+ * Wait for each one to start in-turn, because we want
+ * to know the order in which the LWPs block on the futex.
+ */
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == i + 1)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ }
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 3, "waiters failed to start");
+
+ /* Ensure they're all blocked. */
+ ATF_REQUIRE(lwp_data[0].futex_error == -1);
+ ATF_REQUIRE(lwp_data[1].futex_error == -1);
+ ATF_REQUIRE(lwp_data[2].futex_error == -1);
+
+ /*
+ * Issue a hand-off. It should select the RT reader, and should
+ * indicate that there are still waiters, including a write-waiter.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_LOCKED) == 0);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_WANTED);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == 1);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[1].lwpid);
+
+ /*
+ * Issue another hand-off. It should awaken the writer and
+ * indicate only readers waiting.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_WANTED) == 0);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+
+ /*
+ * Issue another hand-off. It would awaken the non-rt
+ * reader and indicate no waiters waiting.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word == 1);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[0].lwpid);
+
+ /*
+ * Issue one final hand-off; it should result in a fully
+ * released lock word. Note that we don't have any
+ * outstaning waiters waiting, and therefore there will
+ * not be a kernel futex; this exercises a specific code
+ * path in the kernel designed to handle this.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 0);
+ ATF_REQUIRE(futex_word == 0);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 0)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "threads failed to exit");
+
+ /* Ensure they all exited error-free. */
+ ATF_REQUIRE(lwp_data[0].futex_error == 0);
+ ATF_REQUIRE(lwp_data[1].futex_error == 0);
+ ATF_REQUIRE(lwp_data[2].futex_error == 0);
+}
+
+ATF_TC_WITH_CLEANUP(futex_rw_handoff_write_locked_rt_reader_preferred);
+ATF_TC_HEAD(futex_rw_handoff_write_locked_rt_reader_preferred, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "tests that write-locked rwlock direct hand-off "
+ "prefers rt readers over non-rt writers");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(futex_rw_handoff_write_locked_rt_reader_preferred, tc)
+{
+ atf_tc_skip("futex_rw_handoff is currently broken");
+ do_test_rw_handoff_write_locked_rt_reader_preferred();
+}
+ATF_TC_CLEANUP(futex_rw_handoff_write_locked_rt_reader_preferred, tc)
+{
+ do_cleanup();
+}
+
+/*****************************************************************************/
+
+static void
+do_test_rw_wait_write_wanted_rt_reader_preferred(void)
+{
+ unsigned int i, tries;
+ lwpid_t lid;
+
+ setup_rt_params();
+
+ /*
+ * The kernel should not care about the WRITE_WANTED bit, and
+ * should use the contents of the sleepqs as the truth.
+ */
+ futex_word = FUTEX_WAITERS;
+ membar_sync();
+
+ /*
+ * Create LWPs in the following order:
+ *
+ * 0 - non-RT writer
+ * 1 - non-RT reader
+ * 2 - RT reader
+ *
+ * We expect:
+ *
+ * ==> non-RT reader blocks because WRITE_WANTED.
+ * ==> RT reader does not block and acquires the read-lock.
+ *
+ * We then expect hand-offs to awaken the remaining waiters
+ * in this order:
+ *
+ * 0 -> 1
+ */
+ for (i = 0; i < 3; i++) {
+ if (i == 2) {
+ setup_lwp_context(&lwp_data[i],
+ rt_simple_test_waiter_lwp);
+ lwp_data[i].rt_prio = pri_min;
+ } else {
+ setup_lwp_context(&lwp_data[i], simple_test_waiter_lwp);
+ }
+ lwp_data[i].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[i].futex_error = -1;
+ lwp_data[i].futex_ptr = &futex_word;
+ lwp_data[i].block_val = futex_word;
+ lwp_data[i].bitset = i == 0 ? FUTEX_RW_WRITER : FUTEX_RW_READER;
+ lwp_data[i].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+ &lwp_data[i].lwpid) == 0);
+
+ /*
+ * Wait for the first 2 to start in-turn, because we want
+ * to know the order in which the LWPs block on the futex.
+ */
+ for (tries = 0; i < 2 && tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == i + 1)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ }
+
+ sleep(2);
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 2, "waiters failed to start");
+
+ /* Ensure the first two are blocked and the 3rd one succeeded. */
+ ATF_REQUIRE(lwp_data[0].futex_error == -1);
+ ATF_REQUIRE(lwp_data[1].futex_error == -1);
+ ATF_REQUIRE(lwp_data[2].futex_error == 0);
+
+ /*
+ * Verify the locked-ness and waiter-status of the rwlock.
+ * Also verify it was the expected LWP that made it through
+ * the gate.
+ */
+ ATF_REQUIRE(futex_word == (FUTEX_WAITERS | FUTEX_RW_WRITE_WANTED | 1));
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+
+ /*
+ * Issue a hand-off. It should awaken the writer and indicate only
+ * readers waiting.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_WANTED) == 0);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[0].lwpid);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[0].lwpid);
+
+ /*
+ * Issue another hand-off. It would awaken the non-rt
+ * reader and indicate no waiters waiting.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word == 1);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[1].lwpid);
+
+ /*
+ * Issue one final hand-off; it should result in a fully
+ * released lock word. Note that we don't have any
+ * outstaning waiters waiting, and therefore there will
+ * not be a kernel futex; this exercises a specific code
+ * path in the kernel designed to handle this.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 0);
+ ATF_REQUIRE(futex_word == 0);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 0)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "threads failed to exit");
+
+ /* Ensure they all exited error-free. */
+ ATF_REQUIRE(lwp_data[0].futex_error == 0);
+ ATF_REQUIRE(lwp_data[1].futex_error == 0);
+}
+
+ATF_TC_WITH_CLEANUP(futex_rw_wait_write_wanted_rt_reader_preferred);
+ATF_TC_HEAD(futex_rw_wait_write_wanted_rt_reader_preferred, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "tests that a wait by a rt-reader acquires "
+ "even if the rwlock is wanted by a non-rt writer");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(futex_rw_wait_write_wanted_rt_reader_preferred, tc)
+{
+ atf_tc_skip("futex_rw_handoff is currently broken");
+ do_test_rw_wait_write_wanted_rt_reader_preferred();
+}
+ATF_TC_CLEANUP(futex_rw_wait_write_wanted_rt_reader_preferred, tc)
+{
+ do_cleanup();
+}
+
+/*****************************************************************************/
+
+static void
+do_test_rw_handoff_rt_prio_order(void)
+{
+ unsigned int i, tries;
+ lwpid_t lid;
+
+ setup_rt_params();
+
+ /*
+ * We need to show the rwlock as write-locked to ensure
+ * the rt read-waiter blocks.
+ */
+ futex_word =
+ FUTEX_WAITERS | FUTEX_RW_WRITE_WANTED | FUTEX_RW_WRITE_LOCKED;
+ membar_sync();
+
+ /*
+ * Create LWPs in the following order:
+ *
+ * 0 - pri_min+0 writer
+ * 1 - pri_min+1 reader
+ * 2 - pri_min+2 writer
+ * 3 - pri_min+3 reader
+ *
+ * We expect only one to be awakened at each hand-off and for them
+ * to be awakened in this order:
+ *
+ * 3 -> 2 -> 1 -> 0
+ */
+ for (i = 0; i < 4; i++) {
+ setup_lwp_context(&lwp_data[i], rt_simple_test_waiter_lwp);
+ lwp_data[i].rt_prio = pri_min + i;
+ lwp_data[i].op_flags = FUTEX_PRIVATE_FLAG;
+ lwp_data[i].futex_error = -1;
+ lwp_data[i].futex_ptr = &futex_word;
+ lwp_data[i].block_val = futex_word;
+ lwp_data[i].bitset = i & 1 ? FUTEX_RW_READER : FUTEX_RW_WRITER;
+ lwp_data[i].wait_op = FUTEX_NETBSD_RW_WAIT;
+ ATF_REQUIRE(_lwp_create(&lwp_data[i].context, 0,
+ &lwp_data[i].lwpid) == 0);
+
+ /*
+ * Wait for each one to start in-turn, because we want
+ * to know the order in which the LWPs block on the futex.
+ */
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == i + 1)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ }
+
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 4, "waiters failed to start");
+
+ /* Ensure they're all blocked. */
+ ATF_REQUIRE(lwp_data[0].futex_error == -1);
+ ATF_REQUIRE(lwp_data[1].futex_error == -1);
+ ATF_REQUIRE(lwp_data[2].futex_error == -1);
+ ATF_REQUIRE(lwp_data[3].futex_error == -1);
+
+ /*
+ * Issue a hand-off. It should select the pri_min+3 reader, and should
+ * indicate that there are still waiters, including a write-waiter.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_LOCKED) == 0);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_WANTED);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == 1);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[3].lwpid);
+
+ /*
+ * Issue another hand-off. It should awaken the pri_min+2 writer and
+ * indicate that there are still waiters, including a write-waiter.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_WANTED);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[2].lwpid);
+
+ /*
+ * Issue another a hand-off. It should select the pri_min+1 reader,
+ * and should indicate that there are still waiters, including a
+ * write-waiter.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_LOCKED) == 0);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_WANTED);
+ ATF_REQUIRE(futex_word & FUTEX_WAITERS);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == 1);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[1].lwpid);
+
+ /*
+ * Issue another hand-off. It should awaken the pri_min+0 writer and
+ * indicate that there are no more waiters.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 1);
+ ATF_REQUIRE(futex_word & FUTEX_RW_WRITE_LOCKED);
+ ATF_REQUIRE((futex_word & FUTEX_RW_WRITE_WANTED) == 0);
+ ATF_REQUIRE((futex_word & FUTEX_WAITERS) == 0);
+ lid = futex_word & FUTEX_TID_MASK;
+ ATF_REQUIRE(lid == lwp_data[0].lwpid);
+ ATF_REQUIRE(_lwp_wait(0, &lid) == 0);
+ ATF_REQUIRE(lid == lwp_data[0].lwpid);
+
+ /*
+ * Issue one final hand-off; it should result in a fully
+ * released lock word. Note that we don't have any
+ * outstaning waiters waiting, and therefore there will
+ * not be a kernel futex; this exercises a specific code
+ * path in the kernel designed to handle this.
+ */
+ ATF_REQUIRE(__futex(&futex_word,
+ FUTEX_NETBSD_RW_HANDOFF | FUTEX_PRIVATE_FLAG,
+ futex_word, NULL, NULL, 0, 0) == 0);
+ ATF_REQUIRE(futex_word == 0);
+
+ for (tries = 0; tries < 5; tries++) {
+ membar_sync();
+ if (nlwps_running == 0)
+ break;
+ sleep(1);
+ }
+ membar_sync();
+ ATF_REQUIRE_EQ_MSG(nlwps_running, 0, "threads failed to exit");
+
+ /* Ensure they all exited error-free. */
+ ATF_REQUIRE(lwp_data[0].futex_error == 0);
+ ATF_REQUIRE(lwp_data[1].futex_error == 0);
+ ATF_REQUIRE(lwp_data[2].futex_error == 0);
+ ATF_REQUIRE(lwp_data[3].futex_error == 0);
+}
+
+ATF_TC_WITH_CLEANUP(futex_rw_handoff_rt_prio_order);
+ATF_TC_HEAD(futex_rw_handoff_rt_prio_order, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "tests that hand-off to mixed readers/writer occurs "
+ "in strict priority order");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(futex_rw_handoff_rt_prio_order, tc)
+{
+ atf_tc_skip("futex_rw_handoff is currently broken");
+ do_test_rw_handoff_rt_prio_order();
+}
+ATF_TC_CLEANUP(futex_rw_handoff_rt_prio_order, tc)
+{
+ do_cleanup();
+}
+
+/*****************************************************************************/
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, futex_basic_wait_wake_private);
@@ -1513,5 +2327,12 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, futex_wake_highest_pri);
+ ATF_TP_ADD_TC(tp, futex_rw_handoff_read);
+ ATF_TP_ADD_TC(tp, futex_rw_handoff_write);
+ ATF_TP_ADD_TC(tp, futex_rw_handoff_write_preferred);
+ ATF_TP_ADD_TC(tp, futex_rw_handoff_write_locked_rt_reader_preferred);
+ ATF_TP_ADD_TC(tp, futex_rw_wait_write_wanted_rt_reader_preferred);
+ ATF_TP_ADD_TC(tp, futex_rw_handoff_rt_prio_order);
+
return atf_no_error();
}