poll-mshot-update.c 5.8 KB

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