defer.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. #include <errno.h>
  4. #include <stdio.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <fcntl.h>
  9. #include <sys/uio.h>
  10. #include <stdbool.h>
  11. #include "helpers.h"
  12. #include "liburing.h"
  13. #define RING_SIZE 128
  14. enum {
  15. OP_NOP,
  16. OP_REMOVE_BUFFERS
  17. };
  18. struct test_context {
  19. struct io_uring *ring;
  20. struct io_uring_sqe **sqes;
  21. struct io_uring_cqe *cqes;
  22. int nr;
  23. };
  24. static void free_context(struct test_context *ctx)
  25. {
  26. free(ctx->sqes);
  27. free(ctx->cqes);
  28. memset(ctx, 0, sizeof(*ctx));
  29. }
  30. static int init_context(struct test_context *ctx, struct io_uring *ring, int nr,
  31. int op)
  32. {
  33. struct io_uring_sqe *sqe;
  34. int i;
  35. memset(ctx, 0, sizeof(*ctx));
  36. ctx->nr = nr;
  37. ctx->ring = ring;
  38. ctx->sqes = t_malloc(nr * sizeof(*ctx->sqes));
  39. ctx->cqes = t_malloc(nr * sizeof(*ctx->cqes));
  40. if (!ctx->sqes || !ctx->cqes)
  41. goto err;
  42. for (i = 0; i < nr; i++) {
  43. sqe = io_uring_get_sqe(ring);
  44. if (!sqe)
  45. goto err;
  46. switch (op) {
  47. case OP_NOP:
  48. io_uring_prep_nop(sqe);
  49. break;
  50. case OP_REMOVE_BUFFERS:
  51. io_uring_prep_remove_buffers(sqe, 10, 1);
  52. break;
  53. }
  54. sqe->user_data = i;
  55. ctx->sqes[i] = sqe;
  56. }
  57. return 0;
  58. err:
  59. free_context(ctx);
  60. printf("init context failed\n");
  61. return 1;
  62. }
  63. static int wait_cqes(struct test_context *ctx)
  64. {
  65. int ret, i;
  66. struct io_uring_cqe *cqe;
  67. for (i = 0; i < ctx->nr; i++) {
  68. ret = io_uring_wait_cqe(ctx->ring, &cqe);
  69. if (ret < 0) {
  70. printf("wait_cqes: wait completion %d\n", ret);
  71. return 1;
  72. }
  73. memcpy(&ctx->cqes[i], cqe, sizeof(*cqe));
  74. io_uring_cqe_seen(ctx->ring, cqe);
  75. }
  76. return 0;
  77. }
  78. static int test_canceled_userdata(struct io_uring *ring)
  79. {
  80. struct test_context ctx;
  81. int ret, i, nr = 100;
  82. if (init_context(&ctx, ring, nr, OP_NOP))
  83. return 1;
  84. for (i = 0; i < nr - 1; i++)
  85. ctx.sqes[i]->flags |= IOSQE_IO_LINK;
  86. ret = io_uring_submit(ring);
  87. if (ret <= 0) {
  88. printf("sqe submit failed: %d\n", ret);
  89. goto err;
  90. }
  91. if (wait_cqes(&ctx))
  92. goto err;
  93. for (i = 0; i < nr; i++) {
  94. if (i != ctx.cqes[i].user_data) {
  95. printf("invalid user data\n");
  96. goto err;
  97. }
  98. }
  99. free_context(&ctx);
  100. return 0;
  101. err:
  102. free_context(&ctx);
  103. return 1;
  104. }
  105. static int test_thread_link_cancel(struct io_uring *ring)
  106. {
  107. struct test_context ctx;
  108. int ret, i, nr = 100;
  109. if (init_context(&ctx, ring, nr, OP_REMOVE_BUFFERS))
  110. return 1;
  111. for (i = 0; i < nr - 1; i++)
  112. ctx.sqes[i]->flags |= IOSQE_IO_LINK;
  113. ret = io_uring_submit(ring);
  114. if (ret <= 0) {
  115. printf("sqe submit failed: %d\n", ret);
  116. goto err;
  117. }
  118. if (wait_cqes(&ctx))
  119. goto err;
  120. for (i = 0; i < nr; i++) {
  121. bool fail = false;
  122. if (i == 0)
  123. fail = (ctx.cqes[i].res != -ENOENT);
  124. else
  125. fail = (ctx.cqes[i].res != -ECANCELED);
  126. if (fail) {
  127. printf("invalid status %d\n", ctx.cqes[i].res);
  128. goto err;
  129. }
  130. }
  131. free_context(&ctx);
  132. return 0;
  133. err:
  134. free_context(&ctx);
  135. return 1;
  136. }
  137. static int test_drain_with_linked_timeout(struct io_uring *ring)
  138. {
  139. const int nr = 3;
  140. struct __kernel_timespec ts = { .tv_sec = 1, .tv_nsec = 0, };
  141. struct test_context ctx;
  142. int ret, i;
  143. if (init_context(&ctx, ring, nr * 2, OP_NOP))
  144. return 1;
  145. for (i = 0; i < nr; i++) {
  146. io_uring_prep_timeout(ctx.sqes[2 * i], &ts, 0, 0);
  147. ctx.sqes[2 * i]->flags |= IOSQE_IO_LINK | IOSQE_IO_DRAIN;
  148. io_uring_prep_link_timeout(ctx.sqes[2 * i + 1], &ts, 0);
  149. }
  150. ret = io_uring_submit(ring);
  151. if (ret <= 0) {
  152. printf("sqe submit failed: %d\n", ret);
  153. goto err;
  154. }
  155. if (wait_cqes(&ctx))
  156. goto err;
  157. free_context(&ctx);
  158. return 0;
  159. err:
  160. free_context(&ctx);
  161. return 1;
  162. }
  163. static int run_drained(struct io_uring *ring, int nr)
  164. {
  165. struct test_context ctx;
  166. int ret, i;
  167. if (init_context(&ctx, ring, nr, OP_NOP))
  168. return 1;
  169. for (i = 0; i < nr; i++)
  170. ctx.sqes[i]->flags |= IOSQE_IO_DRAIN;
  171. ret = io_uring_submit(ring);
  172. if (ret <= 0) {
  173. printf("sqe submit failed: %d\n", ret);
  174. goto err;
  175. }
  176. if (wait_cqes(&ctx))
  177. goto err;
  178. free_context(&ctx);
  179. return 0;
  180. err:
  181. free_context(&ctx);
  182. return 1;
  183. }
  184. static int test_overflow_hung(struct io_uring *ring)
  185. {
  186. struct io_uring_sqe *sqe;
  187. int ret, nr = 10;
  188. while (*ring->cq.koverflow != 1000) {
  189. sqe = io_uring_get_sqe(ring);
  190. if (!sqe) {
  191. printf("get sqe failed\n");
  192. return 1;
  193. }
  194. io_uring_prep_nop(sqe);
  195. ret = io_uring_submit(ring);
  196. if (ret <= 0) {
  197. printf("sqe submit failed: %d\n", ret);
  198. return 1;
  199. }
  200. }
  201. return run_drained(ring, nr);
  202. }
  203. static int test_dropped_hung(struct io_uring *ring)
  204. {
  205. int nr = 10;
  206. *ring->sq.kdropped = 1000;
  207. return run_drained(ring, nr);
  208. }
  209. int main(int argc, char *argv[])
  210. {
  211. struct io_uring ring, poll_ring, sqthread_ring;
  212. struct io_uring_params p;
  213. int ret;
  214. if (argc > 1)
  215. return T_EXIT_SKIP;
  216. memset(&p, 0, sizeof(p));
  217. ret = io_uring_queue_init_params(RING_SIZE, &ring, &p);
  218. if (ret) {
  219. printf("ring setup failed %i\n", ret);
  220. return T_EXIT_FAIL;
  221. }
  222. ret = io_uring_queue_init(RING_SIZE, &poll_ring, IORING_SETUP_IOPOLL);
  223. if (ret) {
  224. printf("poll_ring setup failed\n");
  225. return T_EXIT_FAIL;
  226. }
  227. ret = test_canceled_userdata(&poll_ring);
  228. if (ret) {
  229. printf("test_canceled_userdata failed\n");
  230. return ret;
  231. }
  232. if (!(p.features & IORING_FEAT_NODROP)) {
  233. ret = test_overflow_hung(&ring);
  234. if (ret) {
  235. printf("test_overflow_hung failed\n");
  236. return ret;
  237. }
  238. }
  239. ret = test_dropped_hung(&ring);
  240. if (ret) {
  241. printf("test_dropped_hung failed\n");
  242. return ret;
  243. }
  244. ret = test_drain_with_linked_timeout(&ring);
  245. if (ret) {
  246. printf("test_drain_with_linked_timeout failed\n");
  247. return ret;
  248. }
  249. ret = t_create_ring(RING_SIZE, &sqthread_ring,
  250. IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
  251. if (ret == T_SETUP_SKIP)
  252. return T_EXIT_SKIP;
  253. else if (ret < 0)
  254. return T_EXIT_FAIL;
  255. ret = test_thread_link_cancel(&sqthread_ring);
  256. if (ret) {
  257. printf("test_thread_link_cancel failed\n");
  258. return ret;
  259. }
  260. return T_EXIT_PASS;
  261. }