fpos.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: test io_uring fpos handling
  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 <assert.h>
  14. #include "helpers.h"
  15. #include "liburing.h"
  16. #define FILE_SIZE 5000
  17. #define QUEUE_SIZE 2048
  18. static void create_file(const char *file, size_t size)
  19. {
  20. ssize_t ret;
  21. char *buf;
  22. size_t idx;
  23. int fd;
  24. buf = t_malloc(size);
  25. for (idx = 0; idx < size; ++idx) {
  26. /* write 0 or 1 */
  27. buf[idx] = (unsigned char)(idx & 0x01);
  28. }
  29. fd = open(file, O_WRONLY | O_CREAT, 0644);
  30. assert(fd >= 0);
  31. ret = write(fd, buf, size);
  32. fsync(fd);
  33. close(fd);
  34. free(buf);
  35. assert(ret == size);
  36. }
  37. static int test_read(struct io_uring *ring, bool async, int blocksize)
  38. {
  39. int ret, fd, i;
  40. bool done = false;
  41. struct io_uring_sqe *sqe;
  42. struct io_uring_cqe *cqe;
  43. loff_t current, expected = 0;
  44. int count_ok;
  45. int count_0 = 0, count_1 = 0;
  46. unsigned char buff[QUEUE_SIZE * blocksize];
  47. unsigned char reordered[QUEUE_SIZE * blocksize];
  48. memset(buff, 0, QUEUE_SIZE * blocksize);
  49. memset(reordered, 0, QUEUE_SIZE * blocksize);
  50. create_file(".test_fpos_read", FILE_SIZE);
  51. fd = open(".test_fpos_read", O_RDONLY);
  52. unlink(".test_fpos_read");
  53. assert(fd >= 0);
  54. while (!done) {
  55. for (i = 0; i < QUEUE_SIZE; ++i) {
  56. sqe = io_uring_get_sqe(ring);
  57. if (!sqe) {
  58. fprintf(stderr, "no sqe\n");
  59. return -1;
  60. }
  61. io_uring_prep_read(sqe, fd,
  62. buff + i * blocksize,
  63. blocksize, -1);
  64. sqe->user_data = i;
  65. if (async)
  66. sqe->flags |= IOSQE_ASYNC;
  67. if (i != QUEUE_SIZE - 1)
  68. sqe->flags |= IOSQE_IO_LINK;
  69. }
  70. ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
  71. if (ret != QUEUE_SIZE) {
  72. fprintf(stderr, "submit failed: %d\n", ret);
  73. return 1;
  74. }
  75. count_ok = 0;
  76. for (i = 0; i < QUEUE_SIZE; ++i) {
  77. int res;
  78. ret = io_uring_peek_cqe(ring, &cqe);
  79. if (ret) {
  80. fprintf(stderr, "peek failed: %d\n", ret);
  81. return ret;
  82. }
  83. assert(cqe->user_data < QUEUE_SIZE);
  84. memcpy(reordered + count_ok,
  85. buff + cqe->user_data * blocksize, blocksize);
  86. res = cqe->res;
  87. io_uring_cqe_seen(ring, cqe);
  88. if (res == 0) {
  89. done = true;
  90. } else if (res == -ECANCELED) {
  91. /* canceled, probably ok */
  92. } else if (res < 0 || res > blocksize) {
  93. fprintf(stderr, "bad read: %d\n", res);
  94. return -1;
  95. } else {
  96. expected += res;
  97. count_ok += res;
  98. }
  99. }
  100. ret = 0;
  101. for (i = 0; i < count_ok; i++) {
  102. if (reordered[i] == 1) {
  103. count_1++;
  104. } else if (reordered[i] == 0) {
  105. count_0++;
  106. } else {
  107. fprintf(stderr, "odd read %d\n",
  108. (int)reordered[i]);
  109. ret = -1;
  110. break;
  111. }
  112. }
  113. if (labs(count_1 - count_0) > 1) {
  114. fprintf(stderr, "inconsistent reads, got 0s:%d 1s:%d\n",
  115. count_0, count_1);
  116. ret = -1;
  117. }
  118. current = lseek(fd, 0, SEEK_CUR);
  119. if (current != expected) {
  120. fprintf(stderr, "f_pos incorrect, expected %ld have %ld\n",
  121. (long) expected, (long) current);
  122. ret = -1;
  123. }
  124. if (ret)
  125. return ret;
  126. }
  127. return 0;
  128. }
  129. static int test_write(struct io_uring *ring, bool async, int blocksize)
  130. {
  131. int ret, fd, i;
  132. struct io_uring_sqe *sqe;
  133. struct io_uring_cqe *cqe;
  134. bool fail = false;
  135. loff_t current;
  136. char data[blocksize+1];
  137. char readbuff[QUEUE_SIZE*blocksize+1];
  138. fd = open(".test_fpos_write", O_RDWR | O_CREAT, 0644);
  139. unlink(".test_fpos_write");
  140. assert(fd >= 0);
  141. for (i = 0; i < blocksize; i++)
  142. data[i] = 'A' + i;
  143. data[blocksize] = '\0';
  144. for (i = 0; i < QUEUE_SIZE; ++i) {
  145. sqe = io_uring_get_sqe(ring);
  146. if (!sqe) {
  147. fprintf(stderr, "no sqe\n");
  148. return -1;
  149. }
  150. io_uring_prep_write(sqe, fd, data + (i % blocksize), 1, -1);
  151. sqe->user_data = 1;
  152. if (async)
  153. sqe->flags |= IOSQE_ASYNC;
  154. if (i != QUEUE_SIZE - 1)
  155. sqe->flags |= IOSQE_IO_LINK;
  156. }
  157. ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
  158. if (ret != QUEUE_SIZE) {
  159. fprintf(stderr, "submit failed: %d\n", ret);
  160. return 1;
  161. }
  162. for (i = 0; i < QUEUE_SIZE; ++i) {
  163. int res;
  164. ret = io_uring_peek_cqe(ring, &cqe);
  165. res = cqe->res;
  166. if (ret) {
  167. fprintf(stderr, "peek failed: %d\n", ret);
  168. return ret;
  169. }
  170. io_uring_cqe_seen(ring, cqe);
  171. if (!fail && res != 1) {
  172. fprintf(stderr, "bad result %d\n", res);
  173. fail = true;
  174. }
  175. }
  176. current = lseek(fd, 0, SEEK_CUR);
  177. if (current != QUEUE_SIZE) {
  178. fprintf(stderr, "f_pos incorrect, expected %ld have %d\n",
  179. (long) current, QUEUE_SIZE);
  180. fail = true;
  181. }
  182. current = lseek(fd, 0, SEEK_SET);
  183. if (current != 0) {
  184. perror("seek to start");
  185. return -1;
  186. }
  187. ret = read(fd, readbuff, QUEUE_SIZE);
  188. if (ret != QUEUE_SIZE) {
  189. fprintf(stderr, "did not write enough: %d\n", ret);
  190. return -1;
  191. }
  192. i = 0;
  193. while (i < QUEUE_SIZE - blocksize) {
  194. if (strncmp(readbuff + i, data, blocksize)) {
  195. char bad[QUEUE_SIZE+1];
  196. memcpy(bad, readbuff + i, blocksize);
  197. bad[blocksize] = '\0';
  198. fprintf(stderr, "unexpected data %s\n", bad);
  199. fail = true;
  200. }
  201. i += blocksize;
  202. }
  203. return fail ? -1 : 0;
  204. }
  205. int main(int argc, char *argv[])
  206. {
  207. struct io_uring ring;
  208. int ret;
  209. if (argc > 1)
  210. return T_EXIT_SKIP;
  211. ret = io_uring_queue_init(QUEUE_SIZE, &ring, 0);
  212. if (ret) {
  213. fprintf(stderr, "ring setup failed\n");
  214. return T_EXIT_FAIL;
  215. }
  216. for (int test = 0; test < 8; test++) {
  217. int async = test & 0x01;
  218. int write = test & 0x02;
  219. int blocksize = test & 0x04 ? 1 : 7;
  220. ret = write
  221. ? test_write(&ring, !!async, blocksize)
  222. : test_read(&ring, !!async, blocksize);
  223. if (ret) {
  224. fprintf(stderr, "failed %s async=%d blocksize=%d\n",
  225. write ? "write" : "read",
  226. async, blocksize);
  227. return -1;
  228. }
  229. }
  230. return T_EXIT_PASS;
  231. }