tests: posix: eventfd: add stress test
This test simply counts how many times `eventfd_read()` and `eventfd_write()` can be called on an `eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK)` file descriptor. Prior to the recent changes in `eventfd`, we were seeing approximately < 1000 writes / s. However, the previous `eventfd` implementation would fail this test with the result that the number of successful reads was far greater than the number of successful writes. This should be impossible, and with the recent `eventfd` changes that was fixed. Additionally, we are seeing an increase in over 40x for non-blocking eventfd reads and writes. ``` START - test_stress I: BOARD: qemu_riscv64_smp I: TEST_DURATION_S: 5 I: UPDATE_INTERVAL_S: 1 I: avg: 48537 reads/s I: avg: 48575 writes/s PASS - test_stress in 5.002 seconds ``` Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
parent
3e27c7f4a7
commit
386f6c7006
43
tests/posix/eventfd/Kconfig
Normal file
43
tests/posix/eventfd/Kconfig
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Copyright (c) 2023, Meta
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
source "Kconfig.zephyr"
|
||||
|
||||
config TEST_DURATION_S
|
||||
int "Number of seconds to run the test"
|
||||
range 1 21600
|
||||
default 5
|
||||
help
|
||||
Duration for the test, in seconds. The range has a reblatively high
|
||||
upper bound because we should expect that eventfd_read() and
|
||||
eventfd_write() are stable enough to run for an arbitrarily long
|
||||
period of time without encountering any race conditions.
|
||||
|
||||
config TEST_TIMEOUT_S
|
||||
int "Number of seconds to run the test"
|
||||
range 1 21600
|
||||
default 10
|
||||
|
||||
config TEST_STACK_SIZE
|
||||
int "Size of each thread stack in this test"
|
||||
default 2048
|
||||
help
|
||||
The minimal stack size required to run a no-op thread.
|
||||
|
||||
config TEST_EXTRA_ASSERTIONS
|
||||
bool "Add extra assertions into the hot path"
|
||||
help
|
||||
In order to get a true benchmark, there should be as few branches
|
||||
as possible on the hot path. Say 'y' here to add extra assertions
|
||||
on the hot path as well to verify functionality.
|
||||
|
||||
config TEST_EXTRA_QUIET
|
||||
bool "Do not print out regular reports"
|
||||
help
|
||||
In order to get a true benchmark, there should be as few branches
|
||||
as possible on the hot path. Say 'y' here to skip reporting.
|
||||
|
||||
module = EVENTFD_TEST
|
||||
module-str = eventfd
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
|
@ -71,7 +71,7 @@ ZTEST_F(eventfd, test_unset_poll_event_block)
|
|||
eventfd_poll_unset_common(fixture->fd);
|
||||
}
|
||||
|
||||
K_THREAD_STACK_DEFINE(thread_stack, 2048);
|
||||
K_THREAD_STACK_DEFINE(thread_stack, CONFIG_TEST_STACK_SIZE);
|
||||
|
||||
static void thread_fun(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
|
|
112
tests/posix/eventfd/src/stress.c
Normal file
112
tests/posix/eventfd/src/stress.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "_main.h"
|
||||
|
||||
/* update interval for printing stats */
|
||||
#if CONFIG_TEST_DURATION_S >= 60
|
||||
#define UPDATE_INTERVAL_S 10
|
||||
#elif CONFIG_TEST_DURATION_S >= 30
|
||||
#define UPDATE_INTERVAL_S 5
|
||||
#else
|
||||
#define UPDATE_INTERVAL_S 1
|
||||
#endif
|
||||
|
||||
enum th_id {
|
||||
WRITER,
|
||||
READER,
|
||||
};
|
||||
|
||||
typedef int (*eventfd_op_t)(int fd);
|
||||
|
||||
static size_t count[2];
|
||||
static struct k_thread th[2];
|
||||
static const char *msg[2] = {
|
||||
[READER] = "reads",
|
||||
[WRITER] = "writes",
|
||||
};
|
||||
|
||||
static int read_op(int fd);
|
||||
static int write_op(int fd);
|
||||
|
||||
static const eventfd_op_t op[2] = {
|
||||
[READER] = read_op,
|
||||
[WRITER] = write_op,
|
||||
};
|
||||
static K_THREAD_STACK_ARRAY_DEFINE(th_stack, 2, CONFIG_TEST_STACK_SIZE);
|
||||
|
||||
static int read_op(int fd)
|
||||
{
|
||||
eventfd_t value;
|
||||
|
||||
return eventfd_read(fd, &value);
|
||||
}
|
||||
|
||||
static int write_op(int fd)
|
||||
{
|
||||
return eventfd_write(fd, 1);
|
||||
}
|
||||
|
||||
static void th_fun(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
|
||||
int ret;
|
||||
uint64_t now;
|
||||
uint64_t end;
|
||||
uint64_t report;
|
||||
enum th_id id = POINTER_TO_UINT(arg1);
|
||||
struct eventfd_fixture *fixture = arg2;
|
||||
const uint64_t report_ms = UPDATE_INTERVAL_S * MSEC_PER_SEC;
|
||||
const uint64_t end_ms = CONFIG_TEST_DURATION_S * MSEC_PER_SEC;
|
||||
|
||||
for (now = k_uptime_get(), end = now + end_ms, report = now + report_ms; now < end;
|
||||
now = k_uptime_get()) {
|
||||
|
||||
ret = op[id](fixture->fd);
|
||||
if (IS_ENABLED(CONFIG_TEST_EXTRA_ASSERTIONS)) {
|
||||
zassert_true(ret == 0 || (ret == -1 && errno == EAGAIN),
|
||||
"ret: %d errno: %d", ret, errno);
|
||||
}
|
||||
count[id] += (ret == 0);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_TEST_EXTRA_QUIET)) {
|
||||
if (now >= report) {
|
||||
printk("%zu %s\n", count[id], msg[id]);
|
||||
report += report_ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printk("avg: %zu %s/s\n", (size_t)((count[id] * MSEC_PER_SEC) / end_ms), msg[id]);
|
||||
}
|
||||
|
||||
ZTEST_F(eventfd, test_stress)
|
||||
{
|
||||
enum th_id i;
|
||||
enum th_id begin = MIN(READER, WRITER);
|
||||
enum th_id end = MAX(READER, WRITER) + 1;
|
||||
|
||||
printk("BOARD: %s\n", CONFIG_BOARD);
|
||||
printk("TEST_DURATION_S: %u\n", CONFIG_TEST_DURATION_S);
|
||||
printk("UPDATE_INTERVAL_S: %u\n", UPDATE_INTERVAL_S);
|
||||
|
||||
reopen(&fixture->fd, 0, EFD_NONBLOCK | EFD_SEMAPHORE);
|
||||
|
||||
for (i = begin; i < end; ++i) {
|
||||
k_thread_create(&th[i], th_stack[i], K_THREAD_STACK_SIZEOF(th_stack[0]), th_fun,
|
||||
UINT_TO_POINTER(i), fixture, NULL, K_LOWEST_APPLICATION_THREAD_PRIO,
|
||||
0, K_NO_WAIT);
|
||||
}
|
||||
|
||||
for (i = begin; i < end; ++i) {
|
||||
zassert_ok(k_thread_join(&th[i], K_FOREVER));
|
||||
}
|
||||
|
||||
zassert_true(count[READER] > 0, "read count is zero");
|
||||
zassert_true(count[WRITER] > 0, "write count is zero");
|
||||
zassert_true(count[WRITER] >= count[READER], "read count (%zu) > write count (%zu)",
|
||||
count[READER], count[WRITER]);
|
||||
}
|
Loading…
Reference in a new issue