123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- #include "../config-host.h"
- /* SPDX-License-Identifier: MIT */
- /*
- * Description: tests for getevents timeout
- *
- */
- #include <stdio.h>
- #include <sys/time.h>
- #include <unistd.h>
- #include <pthread.h>
- #include "liburing.h"
- #include "helpers.h"
- #define TIMEOUT_MSEC 200
- #define TIMEOUT_SEC 10
- static int thread_ret0, thread_ret1;
- static int cnt = 0;
- static pthread_mutex_t mutex;
- static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
- {
- ts->tv_sec = msec / 1000;
- ts->tv_nsec = (msec % 1000) * 1000000;
- }
- static int test_return_before_timeout(struct io_uring *ring)
- {
- struct io_uring_cqe *cqe;
- struct io_uring_sqe *sqe;
- int ret;
- bool retried = false;
- struct __kernel_timespec ts;
- msec_to_ts(&ts, TIMEOUT_MSEC);
- sqe = io_uring_get_sqe(ring);
- io_uring_prep_nop(sqe);
- ret = io_uring_submit(ring);
- if (ret <= 0) {
- fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
- return 1;
- }
- again:
- ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
- if (ret == -ETIME && (ring->flags & IORING_SETUP_SQPOLL) && !retried) {
- /*
- * there is a small chance SQPOLL hasn't been waked up yet,
- * give it one more try.
- */
- printf("warning: funky SQPOLL timing\n");
- sleep(1);
- retried = true;
- goto again;
- } else if (ret < 0) {
- fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
- return 1;
- }
- io_uring_cqe_seen(ring, cqe);
- return 0;
- }
- static int test_return_after_timeout(struct io_uring *ring)
- {
- struct io_uring_cqe *cqe;
- int ret;
- struct __kernel_timespec ts;
- struct timeval tv;
- unsigned long long exp;
- msec_to_ts(&ts, TIMEOUT_MSEC);
- gettimeofday(&tv, NULL);
- ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
- exp = mtime_since_now(&tv);
- if (ret != -ETIME) {
- fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
- return 1;
- }
- if (exp < TIMEOUT_MSEC / 2 || exp > (TIMEOUT_MSEC * 3) / 2) {
- fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
- return 1;
- }
- return 0;
- }
- static int __reap_thread_fn(void *data)
- {
- struct io_uring *ring = (struct io_uring *)data;
- struct io_uring_cqe *cqe;
- struct __kernel_timespec ts;
- msec_to_ts(&ts, TIMEOUT_SEC);
- pthread_mutex_lock(&mutex);
- cnt++;
- pthread_mutex_unlock(&mutex);
- return io_uring_wait_cqe_timeout(ring, &cqe, &ts);
- }
- static void *reap_thread_fn0(void *data)
- {
- thread_ret0 = __reap_thread_fn(data);
- return NULL;
- }
- static void *reap_thread_fn1(void *data)
- {
- thread_ret1 = __reap_thread_fn(data);
- return NULL;
- }
- /*
- * This is to test issuing a sqe in main thread and reaping it in two child-thread
- * at the same time. To see if timeout feature works or not.
- */
- static int test_multi_threads_timeout(void)
- {
- struct io_uring ring;
- int ret;
- bool both_wait = false;
- pthread_t reap_thread0, reap_thread1;
- struct io_uring_sqe *sqe;
- ret = io_uring_queue_init(8, &ring, 0);
- if (ret) {
- fprintf(stderr, "%s: ring setup failed: %d\n", __FUNCTION__, ret);
- return 1;
- }
- pthread_create(&reap_thread0, NULL, reap_thread_fn0, &ring);
- pthread_create(&reap_thread1, NULL, reap_thread_fn1, &ring);
- /*
- * make two threads both enter io_uring_wait_cqe_timeout() before issuing the sqe
- * as possible as we can. So that there are two threads in the ctx->wait queue.
- * In this way, we can test if a cqe wakes up two threads at the same time.
- */
- while(!both_wait) {
- pthread_mutex_lock(&mutex);
- if (cnt == 2)
- both_wait = true;
- pthread_mutex_unlock(&mutex);
- sleep(1);
- }
- sqe = io_uring_get_sqe(&ring);
- if (!sqe) {
- fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
- goto err;
- }
- io_uring_prep_nop(sqe);
- ret = io_uring_submit(&ring);
- if (ret <= 0) {
- fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
- goto err;
- }
- pthread_join(reap_thread0, NULL);
- pthread_join(reap_thread1, NULL);
- if ((thread_ret0 && thread_ret0 != -ETIME) || (thread_ret1 && thread_ret1 != -ETIME)) {
- fprintf(stderr, "%s: thread wait cqe timeout failed: %d %d\n",
- __FUNCTION__, thread_ret0, thread_ret1);
- goto err;
- }
- return 0;
- err:
- return 1;
- }
- int main(int argc, char *argv[])
- {
- struct io_uring ring_normal, ring_sq;
- int ret;
- if (argc > 1)
- return 0;
- ret = io_uring_queue_init(8, &ring_normal, 0);
- if (ret) {
- fprintf(stderr, "ring_normal setup failed: %d\n", ret);
- return 1;
- }
- if (!(ring_normal.features & IORING_FEAT_EXT_ARG)) {
- fprintf(stderr, "feature IORING_FEAT_EXT_ARG not supported, skipping.\n");
- return 0;
- }
- ret = test_return_before_timeout(&ring_normal);
- if (ret) {
- fprintf(stderr, "ring_normal: test_return_before_timeout failed\n");
- return ret;
- }
- ret = test_return_after_timeout(&ring_normal);
- if (ret) {
- fprintf(stderr, "ring_normal: test_return_after_timeout failed\n");
- return ret;
- }
- ret = io_uring_queue_init(8, &ring_sq, IORING_SETUP_SQPOLL);
- if (ret) {
- fprintf(stderr, "ring_sq setup failed: %d\n", ret);
- return 1;
- }
- ret = test_return_before_timeout(&ring_sq);
- if (ret) {
- fprintf(stderr, "ring_sq: test_return_before_timeout failed\n");
- return ret;
- }
- ret = test_return_after_timeout(&ring_sq);
- if (ret) {
- fprintf(stderr, "ring_sq: test_return_after_timeout failed\n");
- return ret;
- }
- ret = test_multi_threads_timeout();
- if (ret) {
- fprintf(stderr, "test_multi_threads_timeout failed\n");
- return ret;
- }
- return 0;
- }
|