min-timeout-wait.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: run various min_timeout tests
  5. *
  6. */
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <sys/time.h>
  14. #include <pthread.h>
  15. #include "liburing.h"
  16. #include "helpers.h"
  17. struct data {
  18. pthread_barrier_t startup;
  19. unsigned long usec_sleep;
  20. int out_fds[8];
  21. int nr_fds;
  22. };
  23. static int time_pass(struct timeval *start, unsigned long min_t,
  24. unsigned long max_t, const char *name)
  25. {
  26. unsigned long elapsed;
  27. elapsed = mtime_since_now(start);
  28. if (elapsed < min_t || elapsed > max_t) {
  29. fprintf(stderr, "%s fails time check\n", name);
  30. fprintf(stderr, " elapsed=%lu, min=%lu, max=%lu\n", elapsed,
  31. min_t, max_t);
  32. return T_EXIT_FAIL;
  33. }
  34. return T_EXIT_PASS;
  35. }
  36. static void *pipe_write(void *data)
  37. {
  38. struct data *d = data;
  39. char buf[32];
  40. int i;
  41. memset(buf, 0x55, sizeof(buf));
  42. pthread_barrier_wait(&d->startup);
  43. if (d->usec_sleep)
  44. usleep(d->usec_sleep);
  45. for (i = 0; i < d->nr_fds; i++) {
  46. int ret;
  47. ret = write(d->out_fds[i], buf, sizeof(buf));
  48. if (ret < 0) {
  49. perror("write");
  50. return NULL;
  51. }
  52. }
  53. return NULL;
  54. }
  55. static int __test_writes(struct io_uring *ring, int npipes, int usec_sleep,
  56. int usec_wait, int min_t, int max_t, const char *name)
  57. {
  58. struct __kernel_timespec ts;
  59. struct io_uring_cqe *cqe;
  60. struct io_uring_sqe *sqe;
  61. struct timeval tv;
  62. int ret, i, fds[4][2];
  63. pthread_t thread;
  64. struct data d;
  65. char buf[32];
  66. void *tret;
  67. for (i = 0; i < npipes; i++) {
  68. if (pipe(fds[i]) < 0) {
  69. perror("pipe");
  70. return T_EXIT_FAIL;
  71. }
  72. d.out_fds[i] = fds[i][1];
  73. }
  74. d.nr_fds = npipes;
  75. pthread_barrier_init(&d.startup, NULL, 2);
  76. d.usec_sleep = usec_sleep;
  77. pthread_create(&thread, NULL, pipe_write, &d);
  78. pthread_barrier_wait(&d.startup);
  79. for (i = 0; i < npipes; i++) {
  80. sqe = io_uring_get_sqe(ring);
  81. io_uring_prep_read(sqe, fds[i][0], buf, sizeof(buf), 0);
  82. }
  83. io_uring_submit(ring);
  84. ts.tv_sec = 1;
  85. ts.tv_nsec = 0;
  86. gettimeofday(&tv, NULL);
  87. ret = io_uring_wait_cqes_min_timeout(ring, &cqe, 4, &ts, usec_wait, NULL);
  88. if (ret) {
  89. fprintf(stderr, "wait_cqes: %d\n", ret);
  90. return T_EXIT_FAIL;
  91. }
  92. ret = time_pass(&tv, min_t, max_t, name);
  93. io_uring_cq_advance(ring, npipes);
  94. pthread_join(thread, &tret);
  95. for (i = 0; i < npipes; i++) {
  96. close(fds[i][0]);
  97. close(fds[i][1]);
  98. }
  99. return ret;
  100. }
  101. /*
  102. * Test doing min_wait for N events, where 0 events are already available
  103. * on wait enter but N/2 are posted within the min_wait window. We'll expect to
  104. * return when the min_wait window expires.
  105. */
  106. static int test_some_wait(struct io_uring *ring)
  107. {
  108. return __test_writes(ring, 2, 1000, 100000, 95, 120, __FUNCTION__);
  109. }
  110. /*
  111. * Test doing min_wait for N events, where 0 events are already available
  112. * on wait enter but N are posted within the min_wait window. We'll expect to
  113. * return upon arrival of the N events, not the full min_wait window.
  114. */
  115. static int test_post_wait(struct io_uring *ring)
  116. {
  117. return __test_writes(ring, 4, 10000, 200000, 9, 12, __FUNCTION__);
  118. }
  119. /*
  120. * Test doing min_wait for N events, where 0 events are already available
  121. * on wait enter and one is posted after the min_wait timeout has expired.
  122. * That first event should cause wait to abort, even if the task has asked
  123. * for more to wait on.
  124. */
  125. static int test_late(struct io_uring *ring)
  126. {
  127. return __test_writes(ring, 1, 100000, 10000, 95, 120, __FUNCTION__);
  128. }
  129. static int __test_nop(struct io_uring *ring, int nr_nops, int min_t, int max_t,
  130. unsigned long long_wait, const char *name)
  131. {
  132. struct __kernel_timespec ts;
  133. struct io_uring_cqe *cqe;
  134. struct timeval tv;
  135. int i, ret;
  136. for (i = 0; i < nr_nops; i++) {
  137. struct io_uring_sqe *sqe;
  138. sqe = io_uring_get_sqe(ring);
  139. io_uring_prep_nop(sqe);
  140. }
  141. if (nr_nops)
  142. io_uring_submit(ring);
  143. ts.tv_sec = 0;
  144. ts.tv_nsec = long_wait * 1000;
  145. gettimeofday(&tv, NULL);
  146. ret = io_uring_wait_cqes_min_timeout(ring, &cqe, 4, &ts, 50000, NULL);
  147. io_uring_cq_advance(ring, nr_nops);
  148. if (nr_nops) {
  149. if (ret) {
  150. fprintf(stderr, "wait_cqes: %d\n", ret);
  151. return T_EXIT_FAIL;
  152. }
  153. } else {
  154. if (ret != -ETIME) {
  155. fprintf(stderr, "wait_cqes: %d\n", ret);
  156. return T_EXIT_FAIL;
  157. }
  158. }
  159. return time_pass(&tv, min_t, max_t, name);
  160. }
  161. /*
  162. * Test doing min_wait for N events, where N/2 events are already available
  163. * on wait enter. This should abort waiting after min_wait, not do the full
  164. * wait.
  165. */
  166. static int test_some(struct io_uring *ring)
  167. {
  168. return __test_nop(ring, 2, 45, 55, 100000, __FUNCTION__);
  169. }
  170. /*
  171. * Test doing min_wait for N events, where N events are already available
  172. * on wait enter.
  173. */
  174. static int test_already(struct io_uring *ring)
  175. {
  176. return __test_nop(ring, 4, 0, 1, 100000, __FUNCTION__);
  177. }
  178. /*
  179. * Test doing min_wait for N events, and nothing ever gets posted. We'd
  180. * expect the time to be the normal wait time, not the min_wait time.
  181. */
  182. static int test_nothing(struct io_uring *ring)
  183. {
  184. return __test_nop(ring, 0, 95, 110, 100000, __FUNCTION__);
  185. }
  186. /*
  187. * Test doing min_wait for N events, and nothing ever gets posted, and use
  188. * a min_wait time that's bigger than the total wait. We only expect the
  189. * min_wait to elapse.
  190. */
  191. static int test_min_wait_biggest(struct io_uring *ring)
  192. {
  193. return __test_nop(ring, 0, 45, 55, 20000, __FUNCTION__);
  194. }
  195. /*
  196. * Test doing min_wait for N events, and nothing ever gets posted, and use
  197. * a min_wait time that's roughly equal to the total wait. We only expect the
  198. * min_wait to elapse.
  199. */
  200. static int test_min_wait_equal(struct io_uring *ring)
  201. {
  202. return __test_nop(ring, 0, 45, 55, 50001, __FUNCTION__);
  203. }
  204. int main(int argc, char *argv[])
  205. {
  206. struct io_uring ring1, ring2;
  207. struct io_uring_params p = { };
  208. int ret;
  209. if (argc > 1)
  210. return 0;
  211. ret = t_create_ring_params(8, &ring1, &p);
  212. if (ret == T_SETUP_SKIP)
  213. return T_EXIT_SKIP;
  214. else if (ret != T_SETUP_OK)
  215. return ret;
  216. if (!(p.features & IORING_FEAT_MIN_TIMEOUT))
  217. return T_EXIT_SKIP;
  218. p.flags = IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN;
  219. ret = t_create_ring_params(8, &ring2, &p);
  220. if (ret == T_SETUP_SKIP)
  221. return T_EXIT_SKIP;
  222. else if (ret != T_SETUP_OK)
  223. return ret;
  224. ret = test_already(&ring1);
  225. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  226. return ret;
  227. ret = test_already(&ring2);
  228. if (ret == T_EXIT_FAIL)
  229. return T_EXIT_FAIL;
  230. ret = test_some(&ring1);
  231. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  232. return ret;
  233. ret = test_some(&ring2);
  234. if (ret == T_EXIT_FAIL)
  235. return T_EXIT_FAIL;
  236. ret = test_late(&ring1);
  237. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  238. return ret;
  239. ret = test_late(&ring2);
  240. if (ret == T_EXIT_FAIL)
  241. return T_EXIT_FAIL;
  242. ret = test_post_wait(&ring1);
  243. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  244. return ret;
  245. ret = test_post_wait(&ring2);
  246. if (ret == T_EXIT_FAIL)
  247. return T_EXIT_FAIL;
  248. ret = test_some_wait(&ring1);
  249. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  250. return ret;
  251. ret = test_some_wait(&ring2);
  252. if (ret == T_EXIT_FAIL)
  253. return T_EXIT_FAIL;
  254. ret = test_nothing(&ring1);
  255. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  256. return ret;
  257. ret = test_nothing(&ring2);
  258. if (ret == T_EXIT_FAIL)
  259. return T_EXIT_FAIL;
  260. ret = test_min_wait_biggest(&ring1);
  261. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  262. return ret;
  263. ret = test_min_wait_biggest(&ring2);
  264. if (ret == T_EXIT_FAIL)
  265. return T_EXIT_FAIL;
  266. ret = test_min_wait_equal(&ring1);
  267. if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP)
  268. return ret;
  269. ret = test_min_wait_equal(&ring2);
  270. if (ret == T_EXIT_FAIL)
  271. return T_EXIT_FAIL;
  272. io_uring_queue_exit(&ring1);
  273. io_uring_queue_exit(&ring2);
  274. return T_EXIT_PASS;
  275. }