fdinfo.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: basic read/write tests with buffered, O_DIRECT, and SQPOLL
  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. #define FILE_SIZE (256 * 1024)
  19. #define BS 8192
  20. #define BUFFERS (FILE_SIZE / BS)
  21. static struct iovec *vecs;
  22. static int no_read;
  23. static int warned;
  24. static void fdinfo_read(struct io_uring *ring)
  25. {
  26. char fd_name[128];
  27. char *buf;
  28. int fd;
  29. buf = malloc(4096);
  30. sprintf(fd_name, "/proc/self/fdinfo/%d", ring->ring_fd);
  31. fd = open(fd_name, O_RDONLY);
  32. if (fd < 0) {
  33. perror("open");
  34. return;
  35. }
  36. do {
  37. int ret = read(fd, buf, 4096);
  38. if (ret < 0) {
  39. perror("fdinfo read");
  40. break;
  41. } else if (ret == 4096) {
  42. continue;
  43. }
  44. break;
  45. } while (1);
  46. close(fd);
  47. free(buf);
  48. }
  49. static int __test_io(const char *file, struct io_uring *ring, int write,
  50. int buffered, int sqthread, int fixed, int nonvec,
  51. int buf_select, int seq, int exp_len)
  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. #ifdef VERBOSE
  59. fprintf(stdout, "%s: start %d/%d/%d/%d/%d: ", __FUNCTION__, write,
  60. buffered, sqthread,
  61. fixed, nonvec);
  62. #endif
  63. if (write)
  64. open_flags = O_WRONLY;
  65. else
  66. open_flags = O_RDONLY;
  67. if (!buffered)
  68. open_flags |= O_DIRECT;
  69. if (fixed) {
  70. ret = t_register_buffers(ring, vecs, BUFFERS);
  71. if (ret == T_SETUP_SKIP)
  72. return T_EXIT_SKIP;
  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. if (errno == EPERM || errno == EACCES)
  83. return T_EXIT_SKIP;
  84. perror("file open");
  85. goto err;
  86. }
  87. if (sqthread) {
  88. ret = io_uring_register_files(ring, &fd, 1);
  89. if (ret) {
  90. fprintf(stderr, "file reg failed: %d\n", ret);
  91. goto err;
  92. }
  93. }
  94. offset = 0;
  95. for (i = 0; i < BUFFERS; i++) {
  96. sqe = io_uring_get_sqe(ring);
  97. if (!sqe) {
  98. fprintf(stderr, "sqe get failed\n");
  99. goto err;
  100. }
  101. if (!seq)
  102. offset = BS * (rand() % BUFFERS);
  103. if (write) {
  104. int do_fixed = fixed;
  105. int use_fd = fd;
  106. if (sqthread)
  107. use_fd = 0;
  108. if (fixed && (i & 1))
  109. do_fixed = 0;
  110. if (do_fixed) {
  111. io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
  112. vecs[i].iov_len,
  113. offset, i);
  114. } else if (nonvec) {
  115. io_uring_prep_write(sqe, use_fd, vecs[i].iov_base,
  116. vecs[i].iov_len, offset);
  117. } else {
  118. io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
  119. offset);
  120. }
  121. } else {
  122. int do_fixed = fixed;
  123. int use_fd = fd;
  124. if (sqthread)
  125. use_fd = 0;
  126. if (fixed && (i & 1))
  127. do_fixed = 0;
  128. if (do_fixed) {
  129. io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
  130. vecs[i].iov_len,
  131. offset, i);
  132. } else if (nonvec) {
  133. io_uring_prep_read(sqe, use_fd, vecs[i].iov_base,
  134. vecs[i].iov_len, offset);
  135. } else {
  136. io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
  137. offset);
  138. }
  139. }
  140. sqe->user_data = i;
  141. if (sqthread)
  142. sqe->flags |= IOSQE_FIXED_FILE;
  143. if (buf_select) {
  144. if (nonvec)
  145. sqe->addr = 0;
  146. sqe->flags |= IOSQE_BUFFER_SELECT;
  147. sqe->buf_group = buf_select;
  148. }
  149. if (seq)
  150. offset += BS;
  151. }
  152. fdinfo_read(ring);
  153. ret = io_uring_submit(ring);
  154. if (ret != BUFFERS) {
  155. fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
  156. goto err;
  157. }
  158. for (i = 0; i < 10; i++) {
  159. fdinfo_read(ring);
  160. usleep(2);
  161. }
  162. for (i = 0; i < BUFFERS; i++) {
  163. ret = io_uring_wait_cqe(ring, &cqe);
  164. if (ret) {
  165. fprintf(stderr, "wait_cqe=%d\n", ret);
  166. goto err;
  167. }
  168. if (cqe->res == -EINVAL && nonvec) {
  169. if (!warned) {
  170. fprintf(stdout, "Non-vectored IO not "
  171. "supported, skipping\n");
  172. warned = 1;
  173. no_read = 1;
  174. }
  175. } else if (exp_len == -1) {
  176. int iov_len = vecs[cqe->user_data].iov_len;
  177. if (cqe->res != iov_len) {
  178. fprintf(stderr, "cqe res %d, wanted %d\n",
  179. cqe->res, iov_len);
  180. goto err;
  181. }
  182. } else if (cqe->res != exp_len) {
  183. fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, exp_len);
  184. goto err;
  185. }
  186. if (buf_select && exp_len == BS) {
  187. int bid = cqe->flags >> 16;
  188. unsigned char *ptr = vecs[bid].iov_base;
  189. int j;
  190. for (j = 0; j < BS; j++) {
  191. if (ptr[j] == cqe->user_data)
  192. continue;
  193. fprintf(stderr, "Data mismatch! bid=%d, "
  194. "wanted=%d, got=%d\n", bid,
  195. (int)cqe->user_data, ptr[j]);
  196. return 1;
  197. }
  198. }
  199. io_uring_cqe_seen(ring, cqe);
  200. }
  201. if (fixed) {
  202. ret = io_uring_unregister_buffers(ring);
  203. if (ret) {
  204. fprintf(stderr, "buffer unreg failed: %d\n", ret);
  205. goto err;
  206. }
  207. }
  208. if (sqthread) {
  209. ret = io_uring_unregister_files(ring);
  210. if (ret) {
  211. fprintf(stderr, "file unreg failed: %d\n", ret);
  212. goto err;
  213. }
  214. }
  215. close(fd);
  216. #ifdef VERBOSE
  217. fprintf(stdout, "PASS\n");
  218. #endif
  219. return 0;
  220. err:
  221. #ifdef VERBOSE
  222. fprintf(stderr, "FAILED\n");
  223. #endif
  224. if (fd != -1)
  225. close(fd);
  226. return 1;
  227. }
  228. static int test_io(const char *file, int write, int buffered, int sqthread,
  229. int fixed, int nonvec, int exp_len)
  230. {
  231. struct io_uring ring;
  232. int ret, ring_flags = 0;
  233. if (sqthread)
  234. ring_flags = IORING_SETUP_SQPOLL;
  235. ret = t_create_ring(64, &ring, ring_flags);
  236. if (ret == T_SETUP_SKIP)
  237. return 0;
  238. if (ret != T_SETUP_OK) {
  239. fprintf(stderr, "ring create failed: %d\n", ret);
  240. return 1;
  241. }
  242. ret = __test_io(file, &ring, write, buffered, sqthread, fixed, nonvec,
  243. 0, 0, exp_len);
  244. io_uring_queue_exit(&ring);
  245. return ret;
  246. }
  247. static int has_nonvec_read(void)
  248. {
  249. struct io_uring_probe *p;
  250. struct io_uring ring;
  251. int ret;
  252. ret = io_uring_queue_init(1, &ring, 0);
  253. if (ret) {
  254. fprintf(stderr, "queue init failed: %d\n", ret);
  255. exit(ret);
  256. }
  257. p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
  258. ret = io_uring_register_probe(&ring, p, 256);
  259. /* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
  260. if (ret == -EINVAL) {
  261. out:
  262. io_uring_queue_exit(&ring);
  263. free(p);
  264. return 0;
  265. } else if (ret) {
  266. fprintf(stderr, "register_probe: %d\n", ret);
  267. goto out;
  268. }
  269. if (p->ops_len <= IORING_OP_READ)
  270. goto out;
  271. if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
  272. goto out;
  273. io_uring_queue_exit(&ring);
  274. free(p);
  275. return 1;
  276. }
  277. static int test_eventfd_read(int flags)
  278. {
  279. struct io_uring ring;
  280. int fd, ret;
  281. eventfd_t event;
  282. struct io_uring_sqe *sqe;
  283. struct io_uring_cqe *cqe;
  284. if (no_read)
  285. return 0;
  286. ret = t_create_ring(64, &ring, flags);
  287. if (ret == T_SETUP_SKIP)
  288. return 0;
  289. if (ret != T_SETUP_OK) {
  290. if (ret == -EINVAL)
  291. return 0;
  292. fprintf(stderr, "ring create failed: %d\n", ret);
  293. return 1;
  294. }
  295. fd = eventfd(1, 0);
  296. if (fd < 0) {
  297. perror("eventfd");
  298. return 1;
  299. }
  300. sqe = io_uring_get_sqe(&ring);
  301. io_uring_prep_read(sqe, fd, &event, sizeof(eventfd_t), 0);
  302. ret = io_uring_submit(&ring);
  303. if (ret != 1) {
  304. fprintf(stderr, "submitted %d\n", ret);
  305. return 1;
  306. }
  307. fdinfo_read(&ring);
  308. eventfd_write(fd, 1);
  309. ret = io_uring_wait_cqe(&ring, &cqe);
  310. if (ret) {
  311. fprintf(stderr, "wait_cqe=%d\n", ret);
  312. return 1;
  313. }
  314. if (cqe->res == -EINVAL) {
  315. fprintf(stdout, "eventfd IO not supported, skipping\n");
  316. } else if (cqe->res != sizeof(eventfd_t)) {
  317. fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res,
  318. (int) sizeof(eventfd_t));
  319. return 1;
  320. }
  321. io_uring_cqe_seen(&ring, cqe);
  322. return 0;
  323. }
  324. int main(int argc, char *argv[])
  325. {
  326. int i, ret, nr;
  327. char buf[256];
  328. char *fname;
  329. if (argc > 1) {
  330. fname = argv[1];
  331. } else {
  332. srand((unsigned)time(NULL));
  333. snprintf(buf, sizeof(buf), ".basic-rw-%u-%u",
  334. (unsigned)rand(), (unsigned)getpid());
  335. fname = buf;
  336. t_create_file(fname, FILE_SIZE);
  337. }
  338. signal(SIGXFSZ, SIG_IGN);
  339. vecs = t_create_buffers(BUFFERS, BS);
  340. /* if we don't have nonvec read, skip testing that */
  341. nr = has_nonvec_read() ? 32 : 16;
  342. for (i = 0; i < nr; i++) {
  343. int write = (i & 1) != 0;
  344. int buffered = (i & 2) != 0;
  345. int sqthread = (i & 4) != 0;
  346. int fixed = (i & 8) != 0;
  347. int nonvec = (i & 16) != 0;
  348. ret = test_io(fname, write, buffered, sqthread, fixed, nonvec,
  349. BS);
  350. if (ret == T_EXIT_SKIP)
  351. continue;
  352. if (ret) {
  353. fprintf(stderr, "test_io failed %d/%d/%d/%d/%d\n",
  354. write, buffered, sqthread, fixed, nonvec);
  355. goto err;
  356. }
  357. }
  358. ret = test_eventfd_read(0);
  359. if (ret) {
  360. fprintf(stderr, "eventfd read 0 failed\n");
  361. goto err;
  362. }
  363. ret = test_eventfd_read(IORING_SETUP_DEFER_TASKRUN|IORING_SETUP_SINGLE_ISSUER);
  364. if (ret) {
  365. fprintf(stderr, "eventfd read defer failed\n");
  366. goto err;
  367. }
  368. ret = test_eventfd_read(IORING_SETUP_SQPOLL);
  369. if (ret) {
  370. fprintf(stderr, "eventfd read sqpoll failed\n");
  371. goto err;
  372. }
  373. if (fname != argv[1])
  374. unlink(fname);
  375. return 0;
  376. err:
  377. if (fname != argv[1])
  378. unlink(fname);
  379. return 1;
  380. }