poll-many.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: test many files being polled for
  5. *
  6. */
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <signal.h>
  13. #include <poll.h>
  14. #include <sys/resource.h>
  15. #include <fcntl.h>
  16. #include "liburing.h"
  17. #include "helpers.h"
  18. #define NFILES 5000
  19. #define BATCH 500
  20. #define NLOOPS 1000
  21. #define RING_SIZE 512
  22. struct p {
  23. int fd[2];
  24. int triggered;
  25. };
  26. static struct p p[NFILES];
  27. static int arm_poll(struct io_uring *ring, int off)
  28. {
  29. struct io_uring_sqe *sqe;
  30. sqe = io_uring_get_sqe(ring);
  31. if (!sqe) {
  32. fprintf(stderr, "failed getting sqe\n");
  33. return 1;
  34. }
  35. io_uring_prep_poll_add(sqe, p[off].fd[0], POLLIN);
  36. sqe->user_data = off;
  37. return 0;
  38. }
  39. static int reap_polls(struct io_uring *ring)
  40. {
  41. struct io_uring_cqe *cqe;
  42. int i, ret, off;
  43. char c;
  44. for (i = 0; i < BATCH; i++) {
  45. ret = io_uring_wait_cqe(ring, &cqe);
  46. if (ret) {
  47. fprintf(stderr, "wait cqe %d\n", ret);
  48. return ret;
  49. }
  50. off = cqe->user_data;
  51. p[off].triggered = 0;
  52. ret = read(p[off].fd[0], &c, 1);
  53. if (ret != 1) {
  54. fprintf(stderr, "read got %d/%d\n", ret, errno);
  55. break;
  56. }
  57. if (arm_poll(ring, off))
  58. break;
  59. io_uring_cqe_seen(ring, cqe);
  60. }
  61. if (i != BATCH) {
  62. fprintf(stderr, "gave up at %d\n", i);
  63. return 1;
  64. }
  65. ret = io_uring_submit(ring);
  66. if (ret != BATCH) {
  67. fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
  68. return 1;
  69. }
  70. return 0;
  71. }
  72. static int trigger_polls(void)
  73. {
  74. char c = 89;
  75. int i, ret;
  76. for (i = 0; i < BATCH; i++) {
  77. int off;
  78. do {
  79. off = rand() % NFILES;
  80. if (!p[off].triggered)
  81. break;
  82. } while (1);
  83. p[off].triggered = 1;
  84. ret = write(p[off].fd[1], &c, 1);
  85. if (ret != 1) {
  86. fprintf(stderr, "write got %d/%d\n", ret, errno);
  87. return 1;
  88. }
  89. }
  90. return 0;
  91. }
  92. static int arm_polls(struct io_uring *ring)
  93. {
  94. int ret, to_arm = NFILES, i, off;
  95. off = 0;
  96. while (to_arm) {
  97. int this_arm;
  98. this_arm = to_arm;
  99. if (this_arm > RING_SIZE)
  100. this_arm = RING_SIZE;
  101. for (i = 0; i < this_arm; i++) {
  102. if (arm_poll(ring, off)) {
  103. fprintf(stderr, "arm failed at %d\n", off);
  104. return 1;
  105. }
  106. off++;
  107. }
  108. ret = io_uring_submit(ring);
  109. if (ret != this_arm) {
  110. fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
  111. return 1;
  112. }
  113. to_arm -= this_arm;
  114. }
  115. return 0;
  116. }
  117. static int do_test(struct io_uring *ring)
  118. {
  119. int i;
  120. if (arm_polls(ring))
  121. return 1;
  122. for (i = 0; i < NLOOPS; i++) {
  123. trigger_polls();
  124. if (reap_polls(ring))
  125. return 1;
  126. }
  127. return 0;
  128. }
  129. int main(int argc, char *argv[])
  130. {
  131. struct io_uring ring;
  132. struct io_uring_params params = { };
  133. struct rlimit rlim;
  134. int i, ret;
  135. if (argc > 1)
  136. return 0;
  137. if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
  138. perror("getrlimit");
  139. return T_EXIT_FAIL;
  140. }
  141. if (rlim.rlim_cur < (2 * NFILES + 5)) {
  142. rlim.rlim_cur = (2 * NFILES + 5);
  143. rlim.rlim_max = rlim.rlim_cur;
  144. if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
  145. if (errno == EPERM)
  146. goto err_nofail;
  147. perror("setrlimit");
  148. return T_EXIT_FAIL;
  149. }
  150. }
  151. for (i = 0; i < NFILES; i++) {
  152. if (pipe(p[i].fd) < 0) {
  153. perror("pipe");
  154. return T_EXIT_FAIL;
  155. }
  156. }
  157. params.flags = IORING_SETUP_CQSIZE;
  158. params.cq_entries = 4096;
  159. ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
  160. if (ret) {
  161. if (ret == -EINVAL) {
  162. fprintf(stdout, "No CQSIZE, trying without\n");
  163. params.flags &= ~IORING_SETUP_CQSIZE;
  164. params.cq_entries = 0;
  165. ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
  166. if (ret) {
  167. fprintf(stderr, "ring setup failed: %d\n", ret);
  168. return T_EXIT_FAIL;
  169. }
  170. }
  171. }
  172. if (do_test(&ring)) {
  173. fprintf(stderr, "test (normal) failed\n");
  174. return T_EXIT_FAIL;
  175. }
  176. io_uring_queue_exit(&ring);
  177. if (t_probe_defer_taskrun()) {
  178. params.flags |= IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN;
  179. ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
  180. if (ret) {
  181. fprintf(stderr, "ring DEFER setup failed: %d\n", ret);
  182. return T_EXIT_FAIL;
  183. }
  184. if (do_test(&ring)) {
  185. fprintf(stderr, "test (DEFER) failed\n");
  186. return T_EXIT_FAIL;
  187. }
  188. io_uring_queue_exit(&ring);
  189. }
  190. return 0;
  191. err_nofail:
  192. fprintf(stderr, "poll-many: not enough files available (and not root), "
  193. "skipped\n");
  194. return 0;
  195. }