poll-mshot-update.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: test many files being polled for and updated
  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 <pthread.h>
  17. #include "liburing.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 has_poll_update(void)
  28. {
  29. struct io_uring ring;
  30. struct io_uring_cqe *cqe;
  31. struct io_uring_sqe *sqe;
  32. bool has_update = false;
  33. int ret;
  34. ret = io_uring_queue_init(8, &ring, 0);
  35. if (ret)
  36. return -1;
  37. sqe = io_uring_get_sqe(&ring);
  38. io_uring_prep_poll_update(sqe, 0, 0, POLLIN, IORING_TIMEOUT_UPDATE);
  39. ret = io_uring_submit(&ring);
  40. if (ret != 1)
  41. return -1;
  42. ret = io_uring_wait_cqe(&ring, &cqe);
  43. if (!ret) {
  44. if (cqe->res == -ENOENT)
  45. has_update = true;
  46. else if (cqe->res != -EINVAL)
  47. return -1;
  48. io_uring_cqe_seen(&ring, cqe);
  49. }
  50. io_uring_queue_exit(&ring);
  51. return has_update;
  52. }
  53. static int arm_poll(struct io_uring *ring, int off)
  54. {
  55. struct io_uring_sqe *sqe;
  56. sqe = io_uring_get_sqe(ring);
  57. if (!sqe) {
  58. fprintf(stderr, "failed getting sqe\n");
  59. return 1;
  60. }
  61. io_uring_prep_poll_multishot(sqe, p[off].fd[0], POLLIN);
  62. sqe->user_data = off;
  63. return 0;
  64. }
  65. static int submit_arm_poll(struct io_uring *ring, int off)
  66. {
  67. int ret;
  68. ret = arm_poll(ring, off);
  69. if (ret)
  70. return ret;
  71. ret = io_uring_submit(ring);
  72. if (ret < 0)
  73. return ret;
  74. return ret == 1 ? 0 : -1;
  75. }
  76. static int reap_polls(struct io_uring *ring)
  77. {
  78. struct io_uring_cqe *cqe;
  79. int i, ret, off;
  80. char c;
  81. for (i = 0; i < BATCH; i++) {
  82. struct io_uring_sqe *sqe;
  83. sqe = io_uring_get_sqe(ring);
  84. /* update event */
  85. io_uring_prep_poll_update(sqe, i, 0, POLLIN,
  86. IORING_POLL_UPDATE_EVENTS);
  87. sqe->user_data = 0x12345678;
  88. }
  89. ret = io_uring_submit(ring);
  90. if (ret != BATCH) {
  91. fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
  92. return 1;
  93. }
  94. for (i = 0; i < 2 * BATCH; i++) {
  95. ret = io_uring_wait_cqe(ring, &cqe);
  96. if (ret) {
  97. fprintf(stderr, "wait cqe %d\n", ret);
  98. return ret;
  99. }
  100. off = cqe->user_data;
  101. if (off == 0x12345678)
  102. goto seen;
  103. if (!(cqe->flags & IORING_CQE_F_MORE)) {
  104. /* need to re-arm poll */
  105. ret = submit_arm_poll(ring, off);
  106. if (ret)
  107. break;
  108. if (cqe->res <= 0) {
  109. /* retry this one */
  110. i--;
  111. goto seen;
  112. }
  113. }
  114. ret = read(p[off].fd[0], &c, 1);
  115. if (ret != 1) {
  116. if (ret == -1 && errno == EAGAIN)
  117. goto seen;
  118. fprintf(stderr, "read got %d/%d\n", ret, errno);
  119. break;
  120. }
  121. seen:
  122. io_uring_cqe_seen(ring, cqe);
  123. }
  124. if (i != 2 * BATCH) {
  125. fprintf(stderr, "gave up at %d\n", i);
  126. return 1;
  127. }
  128. return 0;
  129. }
  130. static int trigger_polls(void)
  131. {
  132. char c = 89;
  133. int i, ret;
  134. for (i = 0; i < BATCH; i++) {
  135. int off;
  136. do {
  137. off = rand() % NFILES;
  138. if (!p[off].triggered)
  139. break;
  140. } while (1);
  141. p[off].triggered = 1;
  142. ret = write(p[off].fd[1], &c, 1);
  143. if (ret != 1) {
  144. fprintf(stderr, "write got %d/%d\n", ret, errno);
  145. return 1;
  146. }
  147. }
  148. return 0;
  149. }
  150. static void *trigger_polls_fn(void *data)
  151. {
  152. trigger_polls();
  153. return NULL;
  154. }
  155. static int arm_polls(struct io_uring *ring)
  156. {
  157. int ret, to_arm = NFILES, i, off;
  158. off = 0;
  159. while (to_arm) {
  160. int this_arm;
  161. this_arm = to_arm;
  162. if (this_arm > RING_SIZE)
  163. this_arm = RING_SIZE;
  164. for (i = 0; i < this_arm; i++) {
  165. if (arm_poll(ring, off)) {
  166. fprintf(stderr, "arm failed at %d\n", off);
  167. return 1;
  168. }
  169. off++;
  170. }
  171. ret = io_uring_submit(ring);
  172. if (ret != this_arm) {
  173. fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
  174. return 1;
  175. }
  176. to_arm -= this_arm;
  177. }
  178. return 0;
  179. }
  180. static int run(int cqe)
  181. {
  182. struct io_uring ring;
  183. struct io_uring_params params = { };
  184. pthread_t thread;
  185. int i, j, ret;
  186. for (i = 0; i < NFILES; i++) {
  187. if (pipe(p[i].fd) < 0) {
  188. perror("pipe");
  189. return 1;
  190. }
  191. fcntl(p[i].fd[0], F_SETFL, O_NONBLOCK);
  192. }
  193. params.flags = IORING_SETUP_CQSIZE;
  194. params.cq_entries = cqe;
  195. ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
  196. if (ret) {
  197. if (ret == -EINVAL) {
  198. fprintf(stdout, "No CQSIZE, trying without\n");
  199. ret = io_uring_queue_init(RING_SIZE, &ring, 0);
  200. if (ret) {
  201. fprintf(stderr, "ring setup failed: %d\n", ret);
  202. return 1;
  203. }
  204. }
  205. }
  206. if (arm_polls(&ring))
  207. goto err;
  208. for (i = 0; i < NLOOPS; i++) {
  209. pthread_create(&thread, NULL, trigger_polls_fn, NULL);
  210. ret = reap_polls(&ring);
  211. if (ret)
  212. goto err;
  213. pthread_join(thread, NULL);
  214. for (j = 0; j < NFILES; j++)
  215. p[j].triggered = 0;
  216. }
  217. io_uring_queue_exit(&ring);
  218. for (i = 0; i < NFILES; i++) {
  219. close(p[i].fd[0]);
  220. close(p[i].fd[1]);
  221. }
  222. return 0;
  223. err:
  224. io_uring_queue_exit(&ring);
  225. return 1;
  226. }
  227. int main(int argc, char *argv[])
  228. {
  229. struct rlimit rlim;
  230. int ret;
  231. if (argc > 1)
  232. return 0;
  233. ret = has_poll_update();
  234. if (ret < 0) {
  235. fprintf(stderr, "poll update check failed %i\n", ret);
  236. return -1;
  237. } else if (!ret) {
  238. fprintf(stderr, "no poll update, skip\n");
  239. return 0;
  240. }
  241. if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
  242. perror("getrlimit");
  243. goto err;
  244. }
  245. if (rlim.rlim_cur < (2 * NFILES + 5)) {
  246. rlim.rlim_cur = (2 * NFILES + 5);
  247. rlim.rlim_max = rlim.rlim_cur;
  248. if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
  249. if (errno == EPERM)
  250. goto err_nofail;
  251. perror("setrlimit");
  252. goto err;
  253. }
  254. }
  255. ret = run(1024);
  256. if (ret) {
  257. fprintf(stderr, "run(1024) failed\n");
  258. goto err;
  259. }
  260. ret = run(8192);
  261. if (ret) {
  262. fprintf(stderr, "run(8192) failed\n");
  263. goto err;
  264. }
  265. return 0;
  266. err:
  267. fprintf(stderr, "poll-many failed\n");
  268. return 1;
  269. err_nofail:
  270. fprintf(stderr, "poll-many: not enough files available (and not root), "
  271. "skipped\n");
  272. return 0;
  273. }