submit-link-fail.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: tests linked requests failing during submission
  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 <assert.h>
  13. #include "liburing.h"
  14. #define DRAIN_USER_DATA 42
  15. static int test_underprep_fail(bool hardlink, bool drain, bool link_last,
  16. int link_size, int fail_idx)
  17. {
  18. const int invalid_fd = 42;
  19. int link_flags = IOSQE_IO_LINK;
  20. int total_submit = link_size;
  21. struct io_uring ring;
  22. struct io_uring_sqe *sqe;
  23. struct io_uring_cqe *cqe;
  24. char buffer[1] = { };
  25. int i, ret, fds[2];
  26. if (drain)
  27. link_flags |= IOSQE_IO_DRAIN;
  28. if (hardlink)
  29. link_flags |= IOSQE_IO_HARDLINK;
  30. assert(fail_idx < link_size);
  31. assert(link_size < 40);
  32. /* create a new ring as it leaves it dirty */
  33. ret = io_uring_queue_init(8, &ring, 0);
  34. if (ret) {
  35. printf("ring setup failed\n");
  36. return -1;
  37. }
  38. if (pipe(fds)) {
  39. perror("pipe");
  40. return -1;
  41. }
  42. if (drain) {
  43. /* clog drain, so following reqs sent to draining */
  44. sqe = io_uring_get_sqe(&ring);
  45. io_uring_prep_read(sqe, fds[0], buffer, sizeof(buffer), 0);
  46. sqe->user_data = DRAIN_USER_DATA;
  47. sqe->flags |= IOSQE_IO_DRAIN;
  48. total_submit++;
  49. }
  50. for (i = 0; i < link_size; i++) {
  51. sqe = io_uring_get_sqe(&ring);
  52. if (i == fail_idx) {
  53. io_uring_prep_read(sqe, invalid_fd, buffer, 1, 0);
  54. sqe->ioprio = (short) -1;
  55. } else {
  56. io_uring_prep_nop(sqe);
  57. }
  58. if (i != link_size - 1 || !link_last)
  59. sqe->flags |= link_flags;
  60. sqe->user_data = i;
  61. }
  62. ret = io_uring_submit(&ring);
  63. if (ret != total_submit) {
  64. /* Old behaviour, failed early and under-submitted */
  65. if (ret == fail_idx + 1 + drain)
  66. goto out;
  67. fprintf(stderr, "submit failed: %d\n", ret);
  68. return -1;
  69. }
  70. if (drain) {
  71. /* unclog drain */
  72. ret = write(fds[1], buffer, sizeof(buffer));
  73. if (ret < 0) {
  74. perror("write");
  75. return 1;
  76. }
  77. }
  78. for (i = 0; i < total_submit; i++) {
  79. ret = io_uring_wait_cqe(&ring, &cqe);
  80. if (ret) {
  81. fprintf(stderr, "wait_cqe=%d\n", ret);
  82. return 1;
  83. }
  84. ret = cqe->res;
  85. if (cqe->user_data == DRAIN_USER_DATA) {
  86. if (ret != 1) {
  87. fprintf(stderr, "drain failed %d\n", ret);
  88. return 1;
  89. }
  90. } else if (cqe->user_data == fail_idx) {
  91. if (ret == 0 || ret == -ECANCELED) {
  92. fprintf(stderr, "half-prep req unexpected return %d\n", ret);
  93. return 1;
  94. }
  95. } else {
  96. if (ret != -ECANCELED) {
  97. fprintf(stderr, "cancel failed %d, ud %d\n", ret, (int)cqe->user_data);
  98. return 1;
  99. }
  100. }
  101. io_uring_cqe_seen(&ring, cqe);
  102. }
  103. out:
  104. close(fds[0]);
  105. close(fds[1]);
  106. io_uring_queue_exit(&ring);
  107. return 0;
  108. }
  109. int main(int argc, char *argv[])
  110. {
  111. int ret, link_size, fail_idx, i;
  112. if (argc > 1)
  113. return 0;
  114. /*
  115. * hardlink, size=3, fail_idx=1, drain=false -- kernel fault
  116. * link, size=3, fail_idx=0, drain=true -- kernel fault
  117. * link, size=3, fail_idx=1, drain=true -- invalid cqe->res
  118. */
  119. for (link_size = 0; link_size < 3; link_size++) {
  120. for (fail_idx = 0; fail_idx < link_size; fail_idx++) {
  121. for (i = 0; i < 8; i++) {
  122. bool hardlink = (i & 1) != 0;
  123. bool drain = (i & 2) != 0;
  124. bool link_last = (i & 4) != 0;
  125. ret = test_underprep_fail(hardlink, drain, link_last,
  126. link_size, fail_idx);
  127. if (!ret)
  128. continue;
  129. fprintf(stderr, "failed %d, hard %d, drain %d,"
  130. "link_last %d, size %d, idx %d\n",
  131. ret, hardlink, drain, link_last,
  132. link_size, fail_idx);
  133. return 1;
  134. }
  135. }
  136. }
  137. return 0;
  138. }