poll-many.c 4.3 KB

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