resize-rings.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: test sq/cq ring resizing
  5. *
  6. */
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <pthread.h>
  14. #include "liburing.h"
  15. #include "helpers.h"
  16. static bool only_defer, no_defer;
  17. #define NVECS 128
  18. #define min(a, b) ((a) < (b) ? (a) : (b))
  19. struct data {
  20. pthread_t thread;
  21. int fd;
  22. int nr_writes;
  23. int failed;
  24. };
  25. static void *thread_fn(void *__data)
  26. {
  27. struct data *d = __data;
  28. char buffer[8];
  29. int to_write = d->nr_writes;
  30. memset(buffer, 0x5a, sizeof(buffer));
  31. usleep(10000);
  32. while (to_write) {
  33. int ret = write(d->fd, buffer, sizeof(buffer));
  34. if (ret < 0) {
  35. perror("write");
  36. d->failed = 1;
  37. break;
  38. } else if (ret != sizeof(buffer)) {
  39. printf("short write %d\n", ret);
  40. }
  41. to_write--;
  42. usleep(5);
  43. }
  44. return NULL;
  45. }
  46. static int test_pipes(struct io_uring *ring, int async)
  47. {
  48. struct io_uring_params p = { };
  49. struct io_uring_sqe *sqe;
  50. struct io_uring_cqe *cqe;
  51. unsigned long ud = 0;
  52. struct data d = { };
  53. int ret, i, fds[2], to_read;
  54. char buffer[8];
  55. void *tret;
  56. p.sq_entries = 128;
  57. p.cq_entries = 128;
  58. ret = io_uring_resize_rings(ring, &p);
  59. if (ret < 0) {
  60. fprintf(stderr, "Failed to resize ring: %d\n", ret);
  61. return T_EXIT_FAIL;
  62. }
  63. if (pipe(fds) < 0) {
  64. perror("pipe");
  65. return T_EXIT_FAIL;
  66. }
  67. /*
  68. * Put NVECS inflight, then resize while waiting. Repeat until
  69. * 'to_read' has been read.
  70. */
  71. d.nr_writes = 4096;
  72. d.fd = fds[1];
  73. p.sq_entries = 64;
  74. p.cq_entries = 256;
  75. pthread_create(&d.thread, NULL, thread_fn, &d);
  76. to_read = d.nr_writes - 128;
  77. while (to_read && !d.failed) {
  78. unsigned long start_ud = -1UL, end_ud;
  79. int to_wait;
  80. to_wait = NVECS;
  81. if (to_wait > to_read)
  82. to_wait = to_read;
  83. for (i = 0; i < to_wait; i++) {
  84. sqe = io_uring_get_sqe(ring);
  85. /* resized smaller */
  86. if (!sqe)
  87. break;
  88. io_uring_prep_read(sqe, fds[0], buffer, sizeof(buffer), 0);
  89. if (async)
  90. sqe->flags |= IOSQE_ASYNC;
  91. if (start_ud == -1UL)
  92. start_ud = ud;
  93. sqe->user_data = ++ud;
  94. to_read--;
  95. }
  96. end_ud = ud;
  97. ret = io_uring_submit(ring);
  98. if (ret != i) {
  99. fprintf(stderr, "submitted; %d\n", ret);
  100. return T_EXIT_FAIL;
  101. }
  102. to_wait = i;
  103. for (i = 0; i < to_wait; i++) {
  104. if (i == 0) {
  105. ret = io_uring_resize_rings(ring, &p);
  106. if (ret < 0) {
  107. if (ret != -EOVERFLOW) {
  108. fprintf(stderr, "resize failed: %d\n", ret);
  109. return T_EXIT_FAIL;
  110. }
  111. }
  112. p.sq_entries = 32;
  113. p.cq_entries = 128;
  114. }
  115. if (d.failed)
  116. break;
  117. ret = io_uring_wait_cqe(ring, &cqe);
  118. if (ret) {
  119. fprintf(stderr, "wait cqe: %d\n", ret);
  120. return T_EXIT_FAIL;
  121. }
  122. if (cqe->res < 0) {
  123. fprintf(stderr, "cqe res %d\n", cqe->res);
  124. return T_EXIT_FAIL;
  125. }
  126. if (cqe->user_data < start_ud ||
  127. cqe->user_data > end_ud) {
  128. fprintf(stderr, "use_data out-of-range: <%lu-%lu>: %lu\n",
  129. start_ud, end_ud, (long) cqe->user_data);
  130. return T_EXIT_FAIL;
  131. }
  132. io_uring_cqe_seen(ring, cqe);
  133. if (!(i % 17)) {
  134. ret = io_uring_resize_rings(ring, &p);
  135. if (ret < 0) {
  136. if (ret == -EOVERFLOW)
  137. continue;
  138. fprintf(stderr, "resize failed: %d\n", ret);
  139. return T_EXIT_FAIL;
  140. }
  141. if (p.sq_entries == 32)
  142. p.sq_entries = 64;
  143. else if (p.sq_entries == 64)
  144. p.sq_entries = 16;
  145. else
  146. p.sq_entries = 32;
  147. if (p.cq_entries == 128)
  148. p.cq_entries = 256;
  149. else
  150. p.cq_entries = 128;
  151. }
  152. }
  153. }
  154. pthread_join(d.thread, &tret);
  155. close(fds[0]);
  156. close(fds[0]);
  157. return 0;
  158. }
  159. static int test_reads(struct io_uring *ring, int fd, int async)
  160. {
  161. struct io_uring_params p = { };
  162. struct io_uring_sqe *sqe;
  163. struct io_uring_cqe *cqe;
  164. struct iovec vecs[NVECS];
  165. unsigned long to_read;
  166. unsigned long ud = 0;
  167. unsigned long offset;
  168. int ret, i;
  169. if (fd == -1)
  170. return T_EXIT_SKIP;
  171. p.sq_entries = 128;
  172. p.cq_entries = 128;
  173. ret = io_uring_resize_rings(ring, &p);
  174. if (ret < 0) {
  175. fprintf(stderr, "Failed to resize ring: %d\n", ret);
  176. return T_EXIT_FAIL;
  177. }
  178. for (i = 0; i < NVECS; i++) {
  179. if (posix_memalign(&vecs[i].iov_base, 4096, 4096))
  180. return T_EXIT_FAIL;
  181. vecs[i].iov_len = 4096;
  182. }
  183. /*
  184. * Put NVECS inflight, then resize while waiting. Repeat until
  185. * 'to_read' has been read.
  186. */
  187. to_read = 64*1024*1024;
  188. p.sq_entries = 64;
  189. p.cq_entries = 256;
  190. offset = 0;
  191. while (to_read) {
  192. unsigned long start_ud = -1UL, end_ud;
  193. int to_wait;
  194. for (i = 0; i < NVECS; i++) {
  195. sqe = io_uring_get_sqe(ring);
  196. /* resized smaller */
  197. if (!sqe)
  198. break;
  199. io_uring_prep_read(sqe, fd, vecs[i].iov_base,
  200. vecs[i].iov_len, offset);
  201. if (async)
  202. sqe->flags |= IOSQE_ASYNC;
  203. offset += 8192;
  204. if (start_ud == -1UL)
  205. start_ud = ud;
  206. sqe->user_data = ++ud;
  207. }
  208. end_ud = ud;
  209. ret = io_uring_submit(ring);
  210. if (ret != i) {
  211. fprintf(stderr, "submitted; %d\n", ret);
  212. return T_EXIT_FAIL;
  213. }
  214. to_wait = i;
  215. for (i = 0; i < to_wait; i++) {
  216. if (i == 0) {
  217. ret = io_uring_resize_rings(ring, &p);
  218. if (ret < 0) {
  219. if (ret != -EOVERFLOW) {
  220. fprintf(stderr, "resize failed: %d\n", ret);
  221. return T_EXIT_FAIL;
  222. }
  223. }
  224. p.sq_entries = 32;
  225. p.cq_entries = 128;
  226. }
  227. ret = io_uring_wait_cqe(ring, &cqe);
  228. if (ret) {
  229. fprintf(stderr, "wait cqe: %d\n", ret);
  230. return T_EXIT_FAIL;
  231. }
  232. if (cqe->res < 0) {
  233. fprintf(stderr, "cqe res %d\n", cqe->res);
  234. return T_EXIT_FAIL;
  235. }
  236. if (cqe->user_data < start_ud ||
  237. cqe->user_data > end_ud) {
  238. fprintf(stderr, "use_data out-of-range: <%lu-%lu>: %lu\n",
  239. start_ud, end_ud, (long) cqe->user_data);
  240. return T_EXIT_FAIL;
  241. }
  242. io_uring_cqe_seen(ring, cqe);
  243. if (to_read)
  244. to_read -= min(to_read, 4096);
  245. if (!(i % 17)) {
  246. ret = io_uring_resize_rings(ring, &p);
  247. if (ret < 0) {
  248. if (ret == -EOVERFLOW)
  249. continue;
  250. fprintf(stderr, "resize failed: %d\n", ret);
  251. return T_EXIT_FAIL;
  252. }
  253. if (p.sq_entries == 32)
  254. p.sq_entries = 64;
  255. else if (p.sq_entries == 64)
  256. p.sq_entries = 16;
  257. else
  258. p.sq_entries = 32;
  259. if (p.cq_entries == 128)
  260. p.cq_entries = 256;
  261. else
  262. p.cq_entries = 128;
  263. }
  264. }
  265. }
  266. return 0;
  267. }
  268. static int test_basic(struct io_uring *ring, int async)
  269. {
  270. struct io_uring_params p = { };
  271. struct io_uring_sqe *sqe;
  272. struct io_uring_cqe *cqe;
  273. int i, ret;
  274. sqe = io_uring_get_sqe(ring);
  275. io_uring_prep_nop(sqe);
  276. if (async)
  277. sqe->flags |= IOSQE_ASYNC;
  278. sqe->user_data = 1;
  279. io_uring_submit(ring);
  280. p.sq_entries = 32;
  281. p.cq_entries = 64;
  282. ret = io_uring_resize_rings(ring, &p);
  283. if (ret == -EINVAL)
  284. return T_EXIT_SKIP;
  285. sqe = io_uring_get_sqe(ring);
  286. io_uring_prep_nop(sqe);
  287. if (async)
  288. sqe->flags |= IOSQE_ASYNC;
  289. sqe->user_data = 2;
  290. io_uring_submit(ring);
  291. for (i = 0; i < 2; i++) {
  292. ret = io_uring_wait_cqe(ring, &cqe);
  293. if (ret) {
  294. fprintf(stderr, "wait cqe %d\n", ret);
  295. return T_EXIT_FAIL;
  296. }
  297. if (cqe->user_data != i + 1) {
  298. fprintf(stderr, "bad user_data %ld\n", (long) cqe->user_data);
  299. return T_EXIT_FAIL;
  300. }
  301. io_uring_cqe_seen(ring, cqe);
  302. }
  303. return T_EXIT_PASS;
  304. }
  305. static int test_all_copy(struct io_uring *ring)
  306. {
  307. struct io_uring_params p = { };
  308. struct io_uring_sqe *sqe;
  309. struct io_uring_cqe *cqe;
  310. unsigned head;
  311. int i, ret;
  312. p.sq_entries = 32;
  313. p.cq_entries = 64;
  314. ret = io_uring_resize_rings(ring, &p);
  315. if (ret) {
  316. fprintf(stderr, "resize failed: %d\n", ret);
  317. return T_EXIT_FAIL;
  318. }
  319. for (i = 0; i < 32; i++) {
  320. sqe = io_uring_get_sqe(ring);
  321. io_uring_prep_nop(sqe);
  322. sqe->user_data = i + 1;
  323. }
  324. io_uring_submit(ring);
  325. memset(&p, 0, sizeof(p));
  326. p.sq_entries = 64;
  327. p.cq_entries = 128;
  328. ret = io_uring_resize_rings(ring, &p);
  329. if (ret) {
  330. fprintf(stderr, "resize failed: %d\n", ret);
  331. return T_EXIT_FAIL;
  332. }
  333. i = 1;
  334. io_uring_for_each_cqe(ring, head, cqe) {
  335. if (cqe->user_data != i) {
  336. fprintf(stderr, "Found cqe at wrong offset\n");
  337. return T_EXIT_FAIL;
  338. }
  339. i++;
  340. }
  341. io_uring_cq_advance(ring, 32);
  342. return T_EXIT_PASS;
  343. }
  344. static int test_overflow(struct io_uring *ring)
  345. {
  346. struct io_uring_params p = { };
  347. struct io_uring_sqe *sqe;
  348. int i, ret;
  349. p.sq_entries = 32;
  350. p.cq_entries = 64;
  351. ret = io_uring_resize_rings(ring, &p);
  352. if (ret) {
  353. fprintf(stderr, "resize failed: %d\n", ret);
  354. return T_EXIT_FAIL;
  355. }
  356. for (i = 0; i < 32; i++) {
  357. sqe = io_uring_get_sqe(ring);
  358. io_uring_prep_nop(sqe);
  359. sqe->user_data = i + 1;
  360. }
  361. io_uring_submit(ring);
  362. /* have 32 CQEs pending, resize to CQ size 32 which should work */
  363. memset(&p, 0, sizeof(p));
  364. p.sq_entries = 32;
  365. p.cq_entries = 32;
  366. ret = io_uring_resize_rings(ring, &p);
  367. if (ret) {
  368. fprintf(stderr, "resize failed: %d\n", ret);
  369. return T_EXIT_FAIL;
  370. }
  371. /* now resize to CQ size 16, which should fail with -EOVERFLOW */
  372. memset(&p, 0, sizeof(p));
  373. p.sq_entries = 8;
  374. p.cq_entries = 16;
  375. ret = io_uring_resize_rings(ring, &p);
  376. if (ret != -EOVERFLOW) {
  377. fprintf(stderr, "Expected overflow, got %d\n", ret);
  378. return T_EXIT_FAIL;
  379. }
  380. io_uring_cq_advance(ring, 32);
  381. return T_EXIT_PASS;
  382. }
  383. static int test_same_resize(int flags)
  384. {
  385. struct io_uring_params p = { };
  386. struct io_uring_sqe *sqe;
  387. struct io_uring_cqe *cqe;
  388. struct io_uring ring;
  389. int i, ret;
  390. ret = io_uring_queue_init(32, &ring, flags);
  391. if (ret)
  392. return T_EXIT_FAIL;
  393. p.sq_entries = 32;
  394. p.cq_entries = 64;
  395. ret = io_uring_resize_rings(&ring, &p);
  396. if (ret) {
  397. fprintf(stderr, "resize failed: %d\n", ret);
  398. return T_EXIT_FAIL;
  399. }
  400. for (i = 0; i < 32; i++) {
  401. sqe = io_uring_get_sqe(&ring);
  402. io_uring_prep_nop(sqe);
  403. sqe->user_data = i + 1;
  404. }
  405. io_uring_submit(&ring);
  406. for (i = 0; i < 32; i++) {
  407. ret = io_uring_wait_cqe(&ring, &cqe);
  408. if (ret) {
  409. fprintf(stderr, "wait_cqe: %d\n", ret);
  410. return T_EXIT_FAIL;
  411. }
  412. if (cqe->user_data != i + 1) {
  413. fprintf(stderr, "Found cqe at wrong offset\n");
  414. return T_EXIT_FAIL;
  415. }
  416. io_uring_cqe_seen(&ring, cqe);
  417. }
  418. io_uring_queue_exit(&ring);
  419. return T_EXIT_PASS;
  420. }
  421. static int mmap_child(struct io_uring *__ring, struct io_uring_params *__p)
  422. {
  423. struct io_uring ring = *__ring;
  424. struct timeval tv;
  425. int ret;
  426. gettimeofday(&tv, NULL);
  427. do {
  428. struct io_uring_params p = *__p;
  429. void *sq_ptr, *cq_ptr;
  430. ret = io_uring_queue_mmap(__ring->ring_fd, &p, &ring);
  431. if (ret)
  432. continue;
  433. sq_ptr = ring.sq.ring_ptr + 2 * sizeof(__u32);
  434. cq_ptr = ring.cq.ring_ptr + 2 * sizeof(__u32);
  435. memset(sq_ptr, 0x5a, ring.sq.ring_sz - 2 * sizeof(__u32));
  436. memset(cq_ptr, 0xa5, ring.cq.ring_sz - 2 * sizeof(__u32));
  437. io_uring_unmap_rings(&ring.sq, &ring.cq);
  438. } while (mtime_since_now(&tv) < 2500);
  439. exit(T_EXIT_PASS);
  440. }
  441. static int test_mmap_race(struct io_uring *ring, struct io_uring_params *__p)
  442. {
  443. unsigned long useless_sum;
  444. int i, w, nr_children;
  445. struct timeval tv;
  446. pid_t pid;
  447. nr_children = sysconf(_SC_NPROCESSORS_ONLN);
  448. if (nr_children < 0)
  449. nr_children = 4;
  450. for (i = 0; i < nr_children; i++) {
  451. pid = fork();
  452. if (!pid) {
  453. mmap_child(ring, __p);
  454. return T_EXIT_PASS;
  455. }
  456. }
  457. useless_sum = 0;
  458. gettimeofday(&tv, NULL);
  459. do {
  460. struct io_uring_params p = { .sq_entries = 32, };
  461. void *ptr;
  462. io_uring_resize_rings(ring, &p);
  463. ptr = memchr(ring->sq.ring_ptr, 0x5a, ring->sq.ring_sz);
  464. if (ptr)
  465. useless_sum += ptr - ring->sq.ring_ptr;
  466. ptr = memchr(ring->cq.ring_ptr, 0xa5, ring->cq.ring_sz);
  467. if (ptr)
  468. useless_sum += ptr - ring->cq.ring_ptr;
  469. p.sq_entries = 128;
  470. io_uring_resize_rings(ring, &p);
  471. } while (mtime_since_now(&tv) < 2500);
  472. for (i = 0; i < nr_children; i++)
  473. wait(&w);
  474. if (useless_sum)
  475. return T_EXIT_PASS;
  476. return T_EXIT_PASS;
  477. }
  478. static int test(int flags, int fd, int async)
  479. {
  480. struct io_uring_params p = {
  481. .flags = flags,
  482. };
  483. struct io_uring ring;
  484. int ret;
  485. if (no_defer)
  486. return T_EXIT_SKIP;
  487. if (!(flags & IORING_SETUP_DEFER_TASKRUN) && only_defer)
  488. return T_EXIT_SKIP;
  489. ret = io_uring_queue_init_params(8, &ring, &p);
  490. if (ret < 0) {
  491. fprintf(stderr, "ring setup failed: %d\n", ret);
  492. return T_EXIT_FAIL;
  493. }
  494. ret = test_basic(&ring, async);
  495. if (ret == T_EXIT_SKIP) {
  496. if (!(flags & IORING_SETUP_DEFER_TASKRUN)) {
  497. io_uring_queue_exit(&ring);
  498. only_defer = true;
  499. } else {
  500. no_defer = true;
  501. }
  502. return T_EXIT_SKIP;
  503. } else if (ret == T_EXIT_FAIL) {
  504. fprintf(stderr, "test_basic %x failed\n", flags);
  505. return T_EXIT_FAIL;
  506. }
  507. ret = test_reads(&ring, fd, async);
  508. if (ret == T_EXIT_FAIL) {
  509. fprintf(stderr, "test_reads %x failed\n", flags);
  510. return T_EXIT_FAIL;
  511. }
  512. ret = test_pipes(&ring, async);
  513. if (ret == T_EXIT_FAIL) {
  514. fprintf(stderr, "test_pipes %x failed\n", flags);
  515. return T_EXIT_FAIL;
  516. }
  517. if (async)
  518. return T_EXIT_PASS;
  519. ret = test_all_copy(&ring);
  520. if (ret == T_EXIT_FAIL) {
  521. fprintf(stderr, "test_all_copy %x failed\n", flags);
  522. return T_EXIT_FAIL;
  523. }
  524. ret = test_overflow(&ring);
  525. if (ret == T_EXIT_FAIL) {
  526. fprintf(stderr, "test_overflow %x failed\n", flags);
  527. return T_EXIT_FAIL;
  528. }
  529. ret = test_same_resize(flags);
  530. if (ret == T_EXIT_FAIL) {
  531. fprintf(stderr, "test_same_resize %x failed\n", flags);
  532. return T_EXIT_FAIL;
  533. }
  534. /* must go at the end, insert more tests above this one */
  535. ret = test_mmap_race(&ring, &p);
  536. if (ret == T_EXIT_FAIL) {
  537. fprintf(stderr, "test_mmap_race %x failed\n", flags);
  538. return T_EXIT_FAIL;
  539. }
  540. io_uring_queue_exit(&ring);
  541. return T_EXIT_PASS;
  542. }
  543. int main(int argc, char *argv[])
  544. {
  545. int ret, fd = -1;
  546. if (argc > 1)
  547. fd = open("/dev/nvme0n1", O_RDONLY | O_DIRECT);
  548. ret = test(0, fd, 0);
  549. if (ret == T_EXIT_SKIP)
  550. goto try_defer;
  551. else if (ret == T_EXIT_FAIL)
  552. return T_EXIT_FAIL;
  553. ret = test(0, fd, 1);
  554. if (ret == T_EXIT_FAIL)
  555. return T_EXIT_FAIL;
  556. ret = test(IORING_SETUP_SQPOLL, fd, 0);
  557. if (ret == T_EXIT_FAIL)
  558. return T_EXIT_FAIL;
  559. ret = test(IORING_SETUP_SQPOLL, fd, 1);
  560. if (ret == T_EXIT_FAIL)
  561. return T_EXIT_FAIL;
  562. try_defer:
  563. ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, fd, 0);
  564. if (ret == T_EXIT_FAIL)
  565. return T_EXIT_FAIL;
  566. ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, fd, 1);
  567. if (ret == T_EXIT_FAIL)
  568. return T_EXIT_FAIL;
  569. return T_EXIT_PASS;
  570. }