iopoll.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: basic read/write tests with polled IO
  5. */
  6. #include <errno.h>
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <fcntl.h>
  12. #include <sys/types.h>
  13. #include <poll.h>
  14. #include <sys/eventfd.h>
  15. #include <sys/resource.h>
  16. #include "helpers.h"
  17. #include "liburing.h"
  18. #include "../src/syscall.h"
  19. #define FILE_SIZE (128 * 1024)
  20. #define BS 4096
  21. #define BUFFERS (FILE_SIZE / BS)
  22. static struct iovec *vecs;
  23. static int no_buf_select;
  24. static int no_iopoll;
  25. static int no_hybrid;
  26. static int provide_buffers(struct io_uring *ring)
  27. {
  28. struct io_uring_sqe *sqe;
  29. struct io_uring_cqe *cqe;
  30. int ret, i;
  31. for (i = 0; i < BUFFERS; i++) {
  32. sqe = io_uring_get_sqe(ring);
  33. io_uring_prep_provide_buffers(sqe, vecs[i].iov_base,
  34. vecs[i].iov_len, 1, 1, i);
  35. }
  36. ret = io_uring_submit(ring);
  37. if (ret != BUFFERS) {
  38. fprintf(stderr, "submit: %d\n", ret);
  39. return 1;
  40. }
  41. for (i = 0; i < BUFFERS; i++) {
  42. ret = io_uring_wait_cqe(ring, &cqe);
  43. if (cqe->res < 0) {
  44. fprintf(stderr, "cqe->res=%d\n", cqe->res);
  45. return 1;
  46. }
  47. io_uring_cqe_seen(ring, cqe);
  48. }
  49. return 0;
  50. }
  51. static int __test_io(const char *file, struct io_uring *ring, int write, int sqthread,
  52. int fixed, int buf_select)
  53. {
  54. struct io_uring_sqe *sqe;
  55. struct io_uring_cqe *cqe;
  56. int open_flags;
  57. int i, fd = -1, ret;
  58. off_t offset;
  59. if (buf_select) {
  60. write = 0;
  61. fixed = 0;
  62. }
  63. if (buf_select && provide_buffers(ring))
  64. return 1;
  65. if (write)
  66. open_flags = O_WRONLY;
  67. else
  68. open_flags = O_RDONLY;
  69. open_flags |= O_DIRECT;
  70. if (fixed) {
  71. ret = t_register_buffers(ring, vecs, BUFFERS);
  72. if (ret == T_SETUP_SKIP)
  73. return 0;
  74. if (ret != T_SETUP_OK) {
  75. fprintf(stderr, "buffer reg failed: %d\n", ret);
  76. goto err;
  77. }
  78. }
  79. fd = open(file, open_flags);
  80. if (fd < 0) {
  81. if (errno == EINVAL || errno == EPERM || errno == EACCES)
  82. return 0;
  83. perror("file open");
  84. goto err;
  85. }
  86. if (sqthread) {
  87. ret = io_uring_register_files(ring, &fd, 1);
  88. if (ret) {
  89. fprintf(stderr, "file reg failed: %d\n", ret);
  90. goto err;
  91. }
  92. }
  93. offset = 0;
  94. for (i = 0; i < BUFFERS; i++) {
  95. sqe = io_uring_get_sqe(ring);
  96. if (!sqe) {
  97. fprintf(stderr, "sqe get failed\n");
  98. goto err;
  99. }
  100. offset = BS * (rand() % BUFFERS);
  101. if (write) {
  102. int do_fixed = fixed;
  103. int use_fd = fd;
  104. if (sqthread)
  105. use_fd = 0;
  106. if (fixed && (i & 1))
  107. do_fixed = 0;
  108. if (do_fixed) {
  109. io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
  110. vecs[i].iov_len,
  111. offset, i);
  112. } else {
  113. io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
  114. offset);
  115. }
  116. } else {
  117. int do_fixed = fixed;
  118. int use_fd = fd;
  119. if (sqthread)
  120. use_fd = 0;
  121. if (fixed && (i & 1))
  122. do_fixed = 0;
  123. if (do_fixed) {
  124. io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
  125. vecs[i].iov_len,
  126. offset, i);
  127. } else {
  128. io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
  129. offset);
  130. }
  131. }
  132. if (sqthread)
  133. sqe->flags |= IOSQE_FIXED_FILE;
  134. if (buf_select) {
  135. sqe->flags |= IOSQE_BUFFER_SELECT;
  136. sqe->buf_group = buf_select;
  137. sqe->user_data = i;
  138. }
  139. }
  140. ret = io_uring_submit(ring);
  141. if (ret != BUFFERS) {
  142. ret = io_uring_peek_cqe(ring, &cqe);
  143. if (!ret && cqe->res == -EOPNOTSUPP) {
  144. no_iopoll = 1;
  145. io_uring_cqe_seen(ring, cqe);
  146. goto out;
  147. }
  148. fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
  149. goto err;
  150. }
  151. for (i = 0; i < BUFFERS; i++) {
  152. ret = io_uring_wait_cqe(ring, &cqe);
  153. if (ret) {
  154. fprintf(stderr, "wait_cqe=%d\n", ret);
  155. goto err;
  156. } else if (cqe->res == -EOPNOTSUPP) {
  157. fprintf(stdout, "File/device/fs doesn't support polled IO\n");
  158. no_iopoll = 1;
  159. goto out;
  160. } else if (cqe->res != BS) {
  161. fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, BS);
  162. goto err;
  163. }
  164. io_uring_cqe_seen(ring, cqe);
  165. }
  166. if (fixed) {
  167. ret = io_uring_unregister_buffers(ring);
  168. if (ret) {
  169. fprintf(stderr, "buffer unreg failed: %d\n", ret);
  170. goto err;
  171. }
  172. }
  173. if (sqthread) {
  174. ret = io_uring_unregister_files(ring);
  175. if (ret) {
  176. fprintf(stderr, "file unreg failed: %d\n", ret);
  177. goto err;
  178. }
  179. }
  180. out:
  181. close(fd);
  182. return 0;
  183. err:
  184. if (fd != -1)
  185. close(fd);
  186. return 1;
  187. }
  188. static void sig_alrm(int sig)
  189. {
  190. fprintf(stderr, "Ran out of time for peek test!\n");
  191. exit(T_EXIT_FAIL);
  192. }
  193. /*
  194. * if we are polling, io_uring_cqe_peek() always needs to enter the kernel
  195. */
  196. static int test_io_uring_cqe_peek(const char *file)
  197. {
  198. struct io_uring_cqe *cqe;
  199. struct io_uring ring;
  200. struct sigaction act;
  201. int fd, i, ret = T_EXIT_FAIL;
  202. if (no_iopoll)
  203. return 0;
  204. ret = io_uring_queue_init(64, &ring, IORING_SETUP_IOPOLL);
  205. if (ret) {
  206. fprintf(stderr, "ring create failed: %d\n", ret);
  207. return 1;
  208. }
  209. fd = open(file, O_RDONLY | O_DIRECT);
  210. if (fd < 0) {
  211. if (errno == EINVAL || errno == EPERM || errno == EACCES) {
  212. io_uring_queue_exit(&ring);
  213. return T_EXIT_SKIP;
  214. }
  215. perror("file open");
  216. goto err;
  217. }
  218. for (i = 0; i < BUFFERS; i++) {
  219. struct io_uring_sqe *sqe;
  220. off_t offset = BS * (rand() % BUFFERS);
  221. sqe = io_uring_get_sqe(&ring);
  222. io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
  223. sqe->user_data = 1;
  224. }
  225. /*
  226. * Set alarm for 5 seconds, we should be done way before that
  227. */
  228. memset(&act, 0, sizeof(act));
  229. act.sa_handler = sig_alrm;
  230. sigaction(SIGALRM, &act, NULL);
  231. alarm(5);
  232. ret = io_uring_submit(&ring);
  233. if (ret != BUFFERS) {
  234. fprintf(stderr, "submit=%d\n", ret);
  235. goto err;
  236. }
  237. ret = T_EXIT_PASS;
  238. i = 0;
  239. do {
  240. ret = io_uring_peek_cqe(&ring, &cqe);
  241. if (ret)
  242. continue;
  243. io_uring_cqe_seen(&ring, cqe);
  244. i++;
  245. } while (i < BUFFERS);
  246. err:
  247. if (fd != -1)
  248. close(fd);
  249. io_uring_queue_exit(&ring);
  250. return ret;
  251. }
  252. /*
  253. * if we are polling io_uring_submit needs to always enter the
  254. * kernel to fetch events
  255. */
  256. static int test_io_uring_submit_enters(const char *file)
  257. {
  258. struct io_uring ring;
  259. int fd, i, ret, ring_flags, open_flags;
  260. unsigned head;
  261. struct io_uring_cqe *cqe;
  262. if (no_iopoll)
  263. return 0;
  264. ring_flags = IORING_SETUP_IOPOLL;
  265. ret = io_uring_queue_init(64, &ring, ring_flags);
  266. if (ret) {
  267. fprintf(stderr, "ring create failed: %d\n", ret);
  268. return 1;
  269. }
  270. open_flags = O_WRONLY | O_DIRECT;
  271. fd = open(file, open_flags);
  272. if (fd < 0) {
  273. if (errno == EINVAL || errno == EPERM || errno == EACCES)
  274. return T_EXIT_SKIP;
  275. perror("file open");
  276. goto err;
  277. }
  278. for (i = 0; i < BUFFERS; i++) {
  279. struct io_uring_sqe *sqe;
  280. off_t offset = BS * (rand() % BUFFERS);
  281. sqe = io_uring_get_sqe(&ring);
  282. io_uring_prep_writev(sqe, fd, &vecs[i], 1, offset);
  283. sqe->user_data = 1;
  284. }
  285. /* submit manually to avoid adding IORING_ENTER_GETEVENTS */
  286. ret = __sys_io_uring_enter(ring.ring_fd, __io_uring_flush_sq(&ring), 0,
  287. 0, NULL);
  288. if (ret < 0)
  289. goto err;
  290. for (i = 0; i < 500; i++) {
  291. ret = io_uring_submit(&ring);
  292. if (ret != 0) {
  293. fprintf(stderr, "still had %d sqes to submit, this is unexpected", ret);
  294. goto err;
  295. }
  296. io_uring_for_each_cqe(&ring, head, cqe) {
  297. /* runs after test_io so should not have happened */
  298. if (cqe->res == -EOPNOTSUPP) {
  299. fprintf(stdout, "File/device/fs doesn't support polled IO\n");
  300. goto err;
  301. }
  302. goto ok;
  303. }
  304. usleep(10000);
  305. }
  306. err:
  307. ret = 1;
  308. if (fd != -1)
  309. close(fd);
  310. ok:
  311. io_uring_queue_exit(&ring);
  312. return ret;
  313. }
  314. static int test_io(const char *file, int write, int sqthread, int fixed,
  315. int hybrid, int buf_select, int defer)
  316. {
  317. struct io_uring ring;
  318. int ret, ring_flags = IORING_SETUP_IOPOLL;
  319. if (no_iopoll)
  320. return 0;
  321. if (defer)
  322. ring_flags |= IORING_SETUP_SINGLE_ISSUER |
  323. IORING_SETUP_DEFER_TASKRUN;
  324. if (hybrid)
  325. ring_flags |= IORING_SETUP_HYBRID_IOPOLL;
  326. ret = t_create_ring(64, &ring, ring_flags);
  327. if (ret == T_SETUP_SKIP) {
  328. return 0;
  329. }
  330. if (ret != T_SETUP_OK) {
  331. if (ring_flags & IORING_SETUP_HYBRID_IOPOLL) {
  332. no_hybrid = 1;
  333. return 0;
  334. }
  335. fprintf(stderr, "ring create failed: %d\n", ret);
  336. return 1;
  337. }
  338. ret = __test_io(file, &ring, write, sqthread, fixed, buf_select);
  339. io_uring_queue_exit(&ring);
  340. return ret;
  341. }
  342. static int probe_buf_select(void)
  343. {
  344. struct io_uring_probe *p;
  345. struct io_uring ring;
  346. int ret;
  347. ret = io_uring_queue_init(1, &ring, 0);
  348. if (ret) {
  349. fprintf(stderr, "ring create failed: %d\n", ret);
  350. return 1;
  351. }
  352. p = io_uring_get_probe_ring(&ring);
  353. if (!p || !io_uring_opcode_supported(p, IORING_OP_PROVIDE_BUFFERS)) {
  354. no_buf_select = 1;
  355. fprintf(stdout, "Buffer select not supported, skipping\n");
  356. return 0;
  357. }
  358. io_uring_free_probe(p);
  359. return 0;
  360. }
  361. int main(int argc, char *argv[])
  362. {
  363. int i, ret, nr;
  364. char buf[256];
  365. char *fname;
  366. if (probe_buf_select())
  367. return T_EXIT_FAIL;
  368. if (argc > 1) {
  369. fname = argv[1];
  370. } else {
  371. srand((unsigned)time(NULL));
  372. snprintf(buf, sizeof(buf), ".basic-rw-%u-%u",
  373. (unsigned)rand(), (unsigned)getpid());
  374. fname = buf;
  375. t_create_file(fname, FILE_SIZE);
  376. }
  377. vecs = t_create_buffers(BUFFERS, BS);
  378. nr = 64;
  379. if (no_buf_select)
  380. nr = 16;
  381. else if (!t_probe_defer_taskrun())
  382. nr = 32;
  383. for (i = 0; i < nr; i++) {
  384. int write = (i & 1) != 0;
  385. int sqthread = (i & 2) != 0;
  386. int fixed = (i & 4) != 0;
  387. int hybrid = (i & 8) != 0;
  388. int buf_select = (i & 16) != 0;
  389. int defer = (i & 32) != 0;
  390. ret = test_io(fname, write, sqthread, fixed, hybrid, buf_select, defer);
  391. if (ret) {
  392. fprintf(stderr, "test_io failed %d/%d/%d/%d/%d/%d\n",
  393. write, sqthread, fixed, hybrid, buf_select, defer);
  394. goto err;
  395. }
  396. if (no_iopoll)
  397. break;
  398. }
  399. ret = test_io_uring_submit_enters(fname);
  400. if (ret == T_EXIT_FAIL) {
  401. fprintf(stderr, "test_io_uring_submit_enters failed\n");
  402. goto err;
  403. }
  404. /*
  405. * Keep this last, it exits on failure
  406. */
  407. ret = test_io_uring_cqe_peek(fname);
  408. if (ret == T_EXIT_FAIL) {
  409. fprintf(stderr, "test_io_uring_cqe_peek failed\n");
  410. goto err;
  411. }
  412. if (fname != argv[1])
  413. unlink(fname);
  414. return T_EXIT_PASS;
  415. err:
  416. if (fname != argv[1])
  417. unlink(fname);
  418. return T_EXIT_FAIL;
  419. }