iopoll.c 9.3 KB

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