file-verify.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: run various reads tests, verifying data
  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 <sys/ioctl.h>
  15. #include <sys/stat.h>
  16. #include <linux/fs.h>
  17. #include "helpers.h"
  18. #include "liburing.h"
  19. #define FSIZE 128*1024*1024
  20. #define CHUNK_SIZE 131072
  21. #define PUNCH_SIZE 32768
  22. /*
  23. * 8 because it fits within the on-stack iov, 16 because it's larger than 8
  24. */
  25. #define MIN_VECS 8
  26. #define MAX_VECS 16
  27. /*
  28. * Can be anything, let's just do something for a bit of parallelism
  29. */
  30. #define READ_BATCH 16
  31. static void verify_buf_sync(void *buf, size_t size, bool registered)
  32. {
  33. #if defined(__hppa__)
  34. if (registered) {
  35. unsigned long off = (unsigned long) buf & 4095;
  36. unsigned long p = (unsigned long) buf & ~4095;
  37. int i;
  38. size += off;
  39. for (i = 0; i < size; i += 32)
  40. asm volatile("fdc 0(%0)" : : "r" (p + i));
  41. }
  42. #endif
  43. }
  44. /*
  45. * Each offset in the file has the offset / sizeof(int) stored for every
  46. * sizeof(int) address.
  47. */
  48. static int verify_buf(void *buf, size_t size, off_t off, bool registered)
  49. {
  50. int i, u_in_buf = size / sizeof(unsigned int);
  51. unsigned int *ptr;
  52. verify_buf_sync(buf, size, registered);
  53. off /= sizeof(unsigned int);
  54. ptr = buf;
  55. for (i = 0; i < u_in_buf; i++) {
  56. if (off != *ptr) {
  57. fprintf(stderr, "Found %u, wanted %llu\n", *ptr,
  58. (unsigned long long) off);
  59. return 1;
  60. }
  61. ptr++;
  62. off++;
  63. }
  64. return 0;
  65. }
  66. static int test_truncate(struct io_uring *ring, const char *fname, int buffered,
  67. int vectored, int provide_buf)
  68. {
  69. struct io_uring_cqe *cqe;
  70. struct io_uring_sqe *sqe;
  71. struct iovec vec;
  72. struct stat sb;
  73. off_t punch_off, off, file_size;
  74. void *buf = NULL;
  75. int u_in_buf, i, ret, fd, first_pass = 1;
  76. unsigned int *ptr;
  77. if (buffered)
  78. fd = open(fname, O_RDWR);
  79. else
  80. fd = open(fname, O_DIRECT | O_RDWR);
  81. if (fd < 0) {
  82. if (!buffered && errno == EINVAL)
  83. return T_EXIT_SKIP;
  84. perror("open");
  85. return 1;
  86. }
  87. if (fstat(fd, &sb) < 0) {
  88. perror("stat");
  89. close(fd);
  90. return 1;
  91. }
  92. if (S_ISREG(sb.st_mode)) {
  93. file_size = sb.st_size;
  94. } else if (S_ISBLK(sb.st_mode)) {
  95. unsigned long long bytes;
  96. if (ioctl(fd, BLKGETSIZE64, &bytes) < 0) {
  97. perror("ioctl");
  98. close(fd);
  99. return 1;
  100. }
  101. file_size = bytes;
  102. } else {
  103. goto out;
  104. }
  105. if (file_size < CHUNK_SIZE)
  106. goto out;
  107. t_posix_memalign(&buf, 4096, CHUNK_SIZE);
  108. off = file_size - (CHUNK_SIZE / 2);
  109. punch_off = off + CHUNK_SIZE / 4;
  110. u_in_buf = CHUNK_SIZE / sizeof(unsigned int);
  111. ptr = buf;
  112. for (i = 0; i < u_in_buf; i++) {
  113. *ptr = i;
  114. ptr++;
  115. }
  116. ret = pwrite(fd, buf, CHUNK_SIZE / 2, off);
  117. if (ret < 0) {
  118. perror("pwrite");
  119. goto err;
  120. } else if (ret != CHUNK_SIZE / 2)
  121. goto out;
  122. again:
  123. /*
  124. * Read in last bit of file so it's known cached, then remove half of that
  125. * last bit so we get a short read that needs retry
  126. */
  127. ret = pread(fd, buf, CHUNK_SIZE / 2, off);
  128. if (ret < 0) {
  129. perror("pread");
  130. goto err;
  131. } else if (ret != CHUNK_SIZE / 2)
  132. goto out;
  133. if (posix_fadvise(fd, punch_off, CHUNK_SIZE / 4, POSIX_FADV_DONTNEED) < 0) {
  134. perror("posix_fadivse");
  135. goto err;
  136. }
  137. if (provide_buf) {
  138. sqe = io_uring_get_sqe(ring);
  139. io_uring_prep_provide_buffers(sqe, buf, CHUNK_SIZE, 1, 0, 0);
  140. ret = io_uring_submit(ring);
  141. if (ret != 1) {
  142. fprintf(stderr, "submit failed %d\n", ret);
  143. goto err;
  144. }
  145. ret = io_uring_wait_cqe(ring, &cqe);
  146. if (ret < 0) {
  147. fprintf(stderr, "wait completion %d\n", ret);
  148. goto err;
  149. }
  150. ret = cqe->res;
  151. io_uring_cqe_seen(ring, cqe);
  152. if (ret) {
  153. fprintf(stderr, "Provide buffer failed %d\n", ret);
  154. goto err;
  155. }
  156. }
  157. sqe = io_uring_get_sqe(ring);
  158. if (!sqe) {
  159. fprintf(stderr, "get sqe failed\n");
  160. goto err;
  161. }
  162. if (vectored) {
  163. assert(!provide_buf);
  164. vec.iov_base = buf;
  165. vec.iov_len = CHUNK_SIZE;
  166. io_uring_prep_readv(sqe, fd, &vec, 1, off);
  167. } else {
  168. if (provide_buf) {
  169. io_uring_prep_read(sqe, fd, NULL, CHUNK_SIZE, off);
  170. sqe->flags |= IOSQE_BUFFER_SELECT;
  171. } else {
  172. io_uring_prep_read(sqe, fd, buf, CHUNK_SIZE, off);
  173. }
  174. }
  175. memset(buf, 0, CHUNK_SIZE);
  176. ret = io_uring_submit(ring);
  177. if (ret != 1) {
  178. fprintf(stderr, "Submit failed %d\n", ret);
  179. goto err;
  180. }
  181. ret = io_uring_wait_cqe(ring, &cqe);
  182. if (ret < 0) {
  183. fprintf(stderr, "wait completion %d\n", ret);
  184. goto err;
  185. }
  186. ret = cqe->res;
  187. io_uring_cqe_seen(ring, cqe);
  188. if (ret != CHUNK_SIZE / 2) {
  189. fprintf(stderr, "Unexpected truncated read %d\n", ret);
  190. goto err;
  191. }
  192. if (verify_buf(buf, CHUNK_SIZE / 2, 0, false))
  193. goto err;
  194. /*
  195. * Repeat, but punch first part instead of last
  196. */
  197. if (first_pass) {
  198. punch_off = file_size - CHUNK_SIZE / 4;
  199. first_pass = 0;
  200. goto again;
  201. }
  202. out:
  203. free(buf);
  204. close(fd);
  205. return 0;
  206. err:
  207. free(buf);
  208. close(fd);
  209. return 1;
  210. }
  211. enum {
  212. PUNCH_NONE,
  213. PUNCH_FRONT,
  214. PUNCH_MIDDLE,
  215. PUNCH_END,
  216. };
  217. /*
  218. * For each chunk in file, DONTNEED a start, end, or middle segment of it.
  219. * We enter here with the file fully cached every time, either freshly
  220. * written or after other reads. This forces (at least) the buffered reads
  221. * to be handled incrementally, exercising that path.
  222. */
  223. static int do_punch(int fd)
  224. {
  225. off_t offset = 0;
  226. int punch_type;
  227. while (offset + CHUNK_SIZE <= FSIZE) {
  228. off_t punch_off;
  229. punch_type = rand() % (PUNCH_END + 1);
  230. switch (punch_type) {
  231. default:
  232. case PUNCH_NONE:
  233. punch_off = -1; /* gcc... */
  234. break;
  235. case PUNCH_FRONT:
  236. punch_off = offset;
  237. break;
  238. case PUNCH_MIDDLE:
  239. punch_off = offset + PUNCH_SIZE;
  240. break;
  241. case PUNCH_END:
  242. punch_off = offset + CHUNK_SIZE - PUNCH_SIZE;
  243. break;
  244. }
  245. offset += CHUNK_SIZE;
  246. if (punch_type == PUNCH_NONE)
  247. continue;
  248. if (posix_fadvise(fd, punch_off, PUNCH_SIZE, POSIX_FADV_DONTNEED) < 0) {
  249. perror("posix_fadivse");
  250. return 1;
  251. }
  252. }
  253. return 0;
  254. }
  255. static int provide_buffers(struct io_uring *ring, void **buf)
  256. {
  257. struct io_uring_cqe *cqe;
  258. struct io_uring_sqe *sqe;
  259. int i, ret;
  260. /* real use case would have one buffer chopped up, but... */
  261. for (i = 0; i < READ_BATCH; i++) {
  262. sqe = io_uring_get_sqe(ring);
  263. io_uring_prep_provide_buffers(sqe, buf[i], CHUNK_SIZE, 1, 0, i);
  264. }
  265. ret = io_uring_submit(ring);
  266. if (ret != READ_BATCH) {
  267. fprintf(stderr, "Submit failed %d\n", ret);
  268. return 1;
  269. }
  270. for (i = 0; i < READ_BATCH; i++) {
  271. ret = io_uring_wait_cqe(ring, &cqe);
  272. if (ret) {
  273. fprintf(stderr, "wait cqe %d\n", ret);
  274. return 1;
  275. }
  276. if (cqe->res < 0) {
  277. fprintf(stderr, "cqe res provide %d\n", cqe->res);
  278. return 1;
  279. }
  280. io_uring_cqe_seen(ring, cqe);
  281. }
  282. return 0;
  283. }
  284. static int test(struct io_uring *ring, const char *fname, int buffered,
  285. int vectored, int small_vecs, int registered, int provide)
  286. {
  287. struct iovec vecs[READ_BATCH][MAX_VECS];
  288. struct io_uring_cqe *cqe;
  289. struct io_uring_sqe *sqe;
  290. void *buf[READ_BATCH];
  291. int ret, fd, flags;
  292. int i, j, nr_vecs;
  293. off_t off, voff;
  294. size_t left;
  295. if (registered) {
  296. assert(!provide);
  297. assert(!vectored && !small_vecs);
  298. }
  299. if (provide) {
  300. assert(!registered);
  301. assert(!vectored && !small_vecs);
  302. }
  303. flags = O_RDONLY;
  304. if (!buffered)
  305. flags |= O_DIRECT;
  306. fd = open(fname, flags);
  307. if (fd < 0) {
  308. if (errno == EINVAL || errno == EPERM || errno == EACCES)
  309. return T_EXIT_SKIP;
  310. perror("open");
  311. return 1;
  312. }
  313. if (do_punch(fd))
  314. return 1;
  315. if (vectored) {
  316. if (small_vecs)
  317. nr_vecs = MIN_VECS;
  318. else
  319. nr_vecs = MAX_VECS;
  320. for (j = 0; j < READ_BATCH; j++) {
  321. for (i = 0; i < nr_vecs; i++) {
  322. void *ptr;
  323. t_posix_memalign(&ptr, 4096, CHUNK_SIZE / nr_vecs);
  324. vecs[j][i].iov_base = ptr;
  325. vecs[j][i].iov_len = CHUNK_SIZE / nr_vecs;
  326. }
  327. }
  328. } else {
  329. for (j = 0; j < READ_BATCH; j++)
  330. t_posix_memalign(&buf[j], 4096, CHUNK_SIZE);
  331. nr_vecs = 0;
  332. }
  333. if (registered) {
  334. struct iovec v[READ_BATCH];
  335. for (i = 0; i < READ_BATCH; i++) {
  336. v[i].iov_base = buf[i];
  337. v[i].iov_len = CHUNK_SIZE;
  338. }
  339. ret = t_register_buffers(ring, v, READ_BATCH);
  340. if (ret) {
  341. if (ret == T_SETUP_SKIP) {
  342. ret = 0;
  343. goto free_bufs;
  344. }
  345. goto err;
  346. }
  347. }
  348. i = 0;
  349. left = FSIZE;
  350. off = 0;
  351. while (left) {
  352. int pending = 0;
  353. if (provide && provide_buffers(ring, buf))
  354. goto err;
  355. for (i = 0; i < READ_BATCH; i++) {
  356. size_t this = left;
  357. if (this > CHUNK_SIZE)
  358. this = CHUNK_SIZE;
  359. sqe = io_uring_get_sqe(ring);
  360. if (!sqe) {
  361. fprintf(stderr, "get sqe failed\n");
  362. goto err;
  363. }
  364. if (vectored) {
  365. io_uring_prep_readv(sqe, fd, vecs[i], nr_vecs, off);
  366. } else {
  367. if (registered) {
  368. io_uring_prep_read_fixed(sqe, fd, buf[i], this, off, i);
  369. } else if (provide) {
  370. io_uring_prep_read(sqe, fd, NULL, this, off);
  371. sqe->flags |= IOSQE_BUFFER_SELECT;
  372. } else {
  373. io_uring_prep_read(sqe, fd, buf[i], this, off);
  374. }
  375. }
  376. sqe->user_data = ((uint64_t)off << 32) | i;
  377. off += this;
  378. left -= this;
  379. pending++;
  380. if (!left)
  381. break;
  382. }
  383. ret = io_uring_submit(ring);
  384. if (ret != pending) {
  385. fprintf(stderr, "sqe submit failed: %d\n", ret);
  386. goto err;
  387. }
  388. for (i = 0; i < pending; i++) {
  389. int index;
  390. ret = io_uring_wait_cqe(ring, &cqe);
  391. if (ret < 0) {
  392. fprintf(stderr, "wait completion %d\n", ret);
  393. goto err;
  394. }
  395. if (cqe->res < 0) {
  396. fprintf(stderr, "bad read %d, read %d\n", cqe->res, i);
  397. goto err;
  398. }
  399. if (cqe->res < CHUNK_SIZE) {
  400. fprintf(stderr, "short read %d, read %d\n", cqe->res, i);
  401. goto err;
  402. }
  403. if (cqe->flags & IORING_CQE_F_BUFFER)
  404. index = cqe->flags >> 16;
  405. else
  406. index = cqe->user_data & 0xffffffff;
  407. voff = cqe->user_data >> 32;
  408. io_uring_cqe_seen(ring, cqe);
  409. if (vectored) {
  410. for (j = 0; j < nr_vecs; j++) {
  411. void *buf = vecs[index][j].iov_base;
  412. size_t len = vecs[index][j].iov_len;
  413. if (verify_buf(buf, len, voff, registered))
  414. goto err;
  415. voff += len;
  416. }
  417. } else {
  418. if (verify_buf(buf[index], CHUNK_SIZE, voff, registered))
  419. goto err;
  420. }
  421. }
  422. }
  423. ret = 0;
  424. done:
  425. if (registered)
  426. io_uring_unregister_buffers(ring);
  427. free_bufs:
  428. if (vectored) {
  429. for (j = 0; j < READ_BATCH; j++)
  430. for (i = 0; i < nr_vecs; i++)
  431. free(vecs[j][i].iov_base);
  432. } else {
  433. for (j = 0; j < READ_BATCH; j++)
  434. free(buf[j]);
  435. }
  436. close(fd);
  437. return ret;
  438. err:
  439. ret = 1;
  440. goto done;
  441. }
  442. static int fill_pattern(const char *fname)
  443. {
  444. size_t left = FSIZE;
  445. unsigned int val, *ptr;
  446. void *buf;
  447. int fd, i;
  448. fd = open(fname, O_WRONLY);
  449. if (fd < 0) {
  450. if (errno == EPERM || errno == EACCES)
  451. return T_EXIT_SKIP;
  452. perror("open");
  453. return 1;
  454. }
  455. val = 0;
  456. buf = t_malloc(4096);
  457. while (left) {
  458. int u_in_buf = 4096 / sizeof(val);
  459. size_t this = left;
  460. if (this > 4096)
  461. this = 4096;
  462. ptr = buf;
  463. for (i = 0; i < u_in_buf; i++) {
  464. *ptr = val;
  465. val++;
  466. ptr++;
  467. }
  468. if (write(fd, buf, 4096) != 4096)
  469. return 1;
  470. left -= 4096;
  471. }
  472. fsync(fd);
  473. close(fd);
  474. free(buf);
  475. return 0;
  476. }
  477. int main(int argc, char *argv[])
  478. {
  479. struct io_uring ring;
  480. const char *fname;
  481. char buf[32];
  482. int ret;
  483. srand(getpid());
  484. if (argc > 1) {
  485. fname = argv[1];
  486. } else {
  487. sprintf(buf, ".file-verify.%d", getpid());
  488. fname = buf;
  489. t_create_file(fname, FSIZE);
  490. }
  491. ret = io_uring_queue_init(READ_BATCH, &ring, 0);
  492. if (ret) {
  493. fprintf(stderr, "ring setup failed: %d\n", ret);
  494. goto err;
  495. }
  496. ret = fill_pattern(fname);
  497. if (ret == T_EXIT_SKIP)
  498. return T_EXIT_SKIP;
  499. else if (ret)
  500. goto err;
  501. ret = test(&ring, fname, 1, 0, 0, 0, 0);
  502. if (ret == T_EXIT_SKIP)
  503. return T_EXIT_SKIP;
  504. if (ret) {
  505. fprintf(stderr, "Buffered novec test failed\n");
  506. goto err;
  507. }
  508. ret = test(&ring, fname, 1, 0, 0, 1, 0);
  509. if (ret == T_EXIT_FAIL) {
  510. fprintf(stderr, "Buffered novec reg test failed\n");
  511. goto err;
  512. }
  513. ret = test(&ring, fname, 1, 0, 0, 0, 1);
  514. if (ret == T_EXIT_FAIL) {
  515. fprintf(stderr, "Buffered novec provide test failed\n");
  516. goto err;
  517. }
  518. ret = test(&ring, fname, 1, 1, 0, 0, 0);
  519. if (ret == T_EXIT_FAIL) {
  520. fprintf(stderr, "Buffered vec test failed\n");
  521. goto err;
  522. }
  523. ret = test(&ring, fname, 1, 1, 1, 0, 0);
  524. if (ret == T_EXIT_FAIL) {
  525. fprintf(stderr, "Buffered small vec test failed\n");
  526. goto err;
  527. }
  528. ret = test(&ring, fname, 0, 0, 0, 0, 0);
  529. if (ret == T_EXIT_FAIL) {
  530. fprintf(stderr, "O_DIRECT novec test failed\n");
  531. goto err;
  532. }
  533. ret = test(&ring, fname, 0, 0, 0, 1, 0);
  534. if (ret == T_EXIT_FAIL) {
  535. fprintf(stderr, "O_DIRECT novec reg test failed\n");
  536. goto err;
  537. }
  538. ret = test(&ring, fname, 0, 0, 0, 0, 1);
  539. if (ret == T_EXIT_FAIL) {
  540. fprintf(stderr, "O_DIRECT novec provide test failed\n");
  541. goto err;
  542. }
  543. ret = test(&ring, fname, 0, 1, 0, 0, 0);
  544. if (ret == T_EXIT_FAIL) {
  545. fprintf(stderr, "O_DIRECT vec test failed\n");
  546. goto err;
  547. }
  548. ret = test(&ring, fname, 0, 1, 1, 0, 0);
  549. if (ret == T_EXIT_FAIL) {
  550. fprintf(stderr, "O_DIRECT small vec test failed\n");
  551. goto err;
  552. }
  553. ret = test_truncate(&ring, fname, 1, 0, 0);
  554. if (ret == T_EXIT_FAIL) {
  555. fprintf(stderr, "Buffered end truncate read failed\n");
  556. goto err;
  557. }
  558. ret = test_truncate(&ring, fname, 1, 1, 0);
  559. if (ret == T_EXIT_FAIL) {
  560. fprintf(stderr, "Buffered end truncate vec read failed\n");
  561. goto err;
  562. }
  563. ret = test_truncate(&ring, fname, 1, 0, 1);
  564. if (ret == T_EXIT_FAIL) {
  565. fprintf(stderr, "Buffered end truncate pbuf read failed\n");
  566. goto err;
  567. }
  568. ret = test_truncate(&ring, fname, 0, 0, 0);
  569. if (ret == T_EXIT_FAIL) {
  570. fprintf(stderr, "O_DIRECT end truncate read failed\n");
  571. goto err;
  572. }
  573. ret = test_truncate(&ring, fname, 0, 1, 0);
  574. if (ret == T_EXIT_FAIL) {
  575. fprintf(stderr, "O_DIRECT end truncate vec read failed\n");
  576. goto err;
  577. }
  578. ret = test_truncate(&ring, fname, 0, 0, 1);
  579. if (ret == T_EXIT_FAIL) {
  580. fprintf(stderr, "O_DIRECT end truncate pbuf read failed\n");
  581. goto err;
  582. }
  583. if (buf == fname)
  584. unlink(fname);
  585. return T_EXIT_PASS;
  586. err:
  587. if (buf == fname)
  588. unlink(fname);
  589. return T_EXIT_FAIL;
  590. }