file-verify.c 13 KB

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