18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Simple test program that demonstrates a file copy through io_uring. This 48c2ecf20Sopenharmony_ci * uses the API exposed by liburing. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 Jens Axboe 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <stdio.h> 98c2ecf20Sopenharmony_ci#include <fcntl.h> 108c2ecf20Sopenharmony_ci#include <string.h> 118c2ecf20Sopenharmony_ci#include <stdlib.h> 128c2ecf20Sopenharmony_ci#include <unistd.h> 138c2ecf20Sopenharmony_ci#include <assert.h> 148c2ecf20Sopenharmony_ci#include <errno.h> 158c2ecf20Sopenharmony_ci#include <inttypes.h> 168c2ecf20Sopenharmony_ci#include <sys/types.h> 178c2ecf20Sopenharmony_ci#include <sys/stat.h> 188c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "liburing.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define QD 64 238c2ecf20Sopenharmony_ci#define BS (32*1024) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int infd, outfd; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct io_data { 288c2ecf20Sopenharmony_ci int read; 298c2ecf20Sopenharmony_ci off_t first_offset, offset; 308c2ecf20Sopenharmony_ci size_t first_len; 318c2ecf20Sopenharmony_ci struct iovec iov; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int setup_context(unsigned entries, struct io_uring *ring) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci int ret; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci ret = io_uring_queue_init(entries, ring, 0); 398c2ecf20Sopenharmony_ci if (ret < 0) { 408c2ecf20Sopenharmony_ci fprintf(stderr, "queue_init: %s\n", strerror(-ret)); 418c2ecf20Sopenharmony_ci return -1; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int get_file_size(int fd, off_t *size) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct stat st; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (fstat(fd, &st) < 0) 528c2ecf20Sopenharmony_ci return -1; 538c2ecf20Sopenharmony_ci if (S_ISREG(st.st_mode)) { 548c2ecf20Sopenharmony_ci *size = st.st_size; 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci } else if (S_ISBLK(st.st_mode)) { 578c2ecf20Sopenharmony_ci unsigned long long bytes; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) 608c2ecf20Sopenharmony_ci return -1; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci *size = bytes; 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return -1; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void queue_prepped(struct io_uring *ring, struct io_data *data) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct io_uring_sqe *sqe; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci sqe = io_uring_get_sqe(ring); 748c2ecf20Sopenharmony_ci assert(sqe); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (data->read) 778c2ecf20Sopenharmony_ci io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset); 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci io_uring_sqe_set_data(sqe, data); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int queue_read(struct io_uring *ring, off_t size, off_t offset) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct io_uring_sqe *sqe; 878c2ecf20Sopenharmony_ci struct io_data *data; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci data = malloc(size + sizeof(*data)); 908c2ecf20Sopenharmony_ci if (!data) 918c2ecf20Sopenharmony_ci return 1; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci sqe = io_uring_get_sqe(ring); 948c2ecf20Sopenharmony_ci if (!sqe) { 958c2ecf20Sopenharmony_ci free(data); 968c2ecf20Sopenharmony_ci return 1; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci data->read = 1; 1008c2ecf20Sopenharmony_ci data->offset = data->first_offset = offset; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci data->iov.iov_base = data + 1; 1038c2ecf20Sopenharmony_ci data->iov.iov_len = size; 1048c2ecf20Sopenharmony_ci data->first_len = size; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); 1078c2ecf20Sopenharmony_ci io_uring_sqe_set_data(sqe, data); 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void queue_write(struct io_uring *ring, struct io_data *data) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci data->read = 0; 1148c2ecf20Sopenharmony_ci data->offset = data->first_offset; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci data->iov.iov_base = data + 1; 1178c2ecf20Sopenharmony_ci data->iov.iov_len = data->first_len; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci queue_prepped(ring, data); 1208c2ecf20Sopenharmony_ci io_uring_submit(ring); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int copy_file(struct io_uring *ring, off_t insize) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned long reads, writes; 1268c2ecf20Sopenharmony_ci struct io_uring_cqe *cqe; 1278c2ecf20Sopenharmony_ci off_t write_left, offset; 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci write_left = insize; 1318c2ecf20Sopenharmony_ci writes = reads = offset = 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci while (insize || write_left) { 1348c2ecf20Sopenharmony_ci unsigned long had_reads; 1358c2ecf20Sopenharmony_ci int got_comp; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * Queue up as many reads as we can 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci had_reads = reads; 1418c2ecf20Sopenharmony_ci while (insize) { 1428c2ecf20Sopenharmony_ci off_t this_size = insize; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (reads + writes >= QD) 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci if (this_size > BS) 1478c2ecf20Sopenharmony_ci this_size = BS; 1488c2ecf20Sopenharmony_ci else if (!this_size) 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (queue_read(ring, this_size, offset)) 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci insize -= this_size; 1558c2ecf20Sopenharmony_ci offset += this_size; 1568c2ecf20Sopenharmony_ci reads++; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (had_reads != reads) { 1608c2ecf20Sopenharmony_ci ret = io_uring_submit(ring); 1618c2ecf20Sopenharmony_ci if (ret < 0) { 1628c2ecf20Sopenharmony_ci fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret)); 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * Queue is full at this point. Find at least one completion. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci got_comp = 0; 1718c2ecf20Sopenharmony_ci while (write_left) { 1728c2ecf20Sopenharmony_ci struct io_data *data; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!got_comp) { 1758c2ecf20Sopenharmony_ci ret = io_uring_wait_cqe(ring, &cqe); 1768c2ecf20Sopenharmony_ci got_comp = 1; 1778c2ecf20Sopenharmony_ci } else 1788c2ecf20Sopenharmony_ci ret = io_uring_peek_cqe(ring, &cqe); 1798c2ecf20Sopenharmony_ci if (ret < 0) { 1808c2ecf20Sopenharmony_ci fprintf(stderr, "io_uring_peek_cqe: %s\n", 1818c2ecf20Sopenharmony_ci strerror(-ret)); 1828c2ecf20Sopenharmony_ci return 1; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci if (!cqe) 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci data = io_uring_cqe_get_data(cqe); 1888c2ecf20Sopenharmony_ci if (cqe->res < 0) { 1898c2ecf20Sopenharmony_ci if (cqe->res == -EAGAIN) { 1908c2ecf20Sopenharmony_ci queue_prepped(ring, data); 1918c2ecf20Sopenharmony_ci io_uring_cqe_seen(ring, cqe); 1928c2ecf20Sopenharmony_ci continue; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci fprintf(stderr, "cqe failed: %s\n", 1958c2ecf20Sopenharmony_ci strerror(-cqe->res)); 1968c2ecf20Sopenharmony_ci return 1; 1978c2ecf20Sopenharmony_ci } else if ((size_t) cqe->res != data->iov.iov_len) { 1988c2ecf20Sopenharmony_ci /* Short read/write, adjust and requeue */ 1998c2ecf20Sopenharmony_ci data->iov.iov_base += cqe->res; 2008c2ecf20Sopenharmony_ci data->iov.iov_len -= cqe->res; 2018c2ecf20Sopenharmony_ci data->offset += cqe->res; 2028c2ecf20Sopenharmony_ci queue_prepped(ring, data); 2038c2ecf20Sopenharmony_ci io_uring_cqe_seen(ring, cqe); 2048c2ecf20Sopenharmony_ci continue; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * All done. if write, nothing else to do. if read, 2098c2ecf20Sopenharmony_ci * queue up corresponding write. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci if (data->read) { 2128c2ecf20Sopenharmony_ci queue_write(ring, data); 2138c2ecf20Sopenharmony_ci write_left -= data->first_len; 2148c2ecf20Sopenharmony_ci reads--; 2158c2ecf20Sopenharmony_ci writes++; 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci free(data); 2188c2ecf20Sopenharmony_ci writes--; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci io_uring_cqe_seen(ring, cqe); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciint main(int argc, char *argv[]) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct io_uring ring; 2308c2ecf20Sopenharmony_ci off_t insize; 2318c2ecf20Sopenharmony_ci int ret; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (argc < 3) { 2348c2ecf20Sopenharmony_ci printf("%s: infile outfile\n", argv[0]); 2358c2ecf20Sopenharmony_ci return 1; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci infd = open(argv[1], O_RDONLY); 2398c2ecf20Sopenharmony_ci if (infd < 0) { 2408c2ecf20Sopenharmony_ci perror("open infile"); 2418c2ecf20Sopenharmony_ci return 1; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); 2448c2ecf20Sopenharmony_ci if (outfd < 0) { 2458c2ecf20Sopenharmony_ci perror("open outfile"); 2468c2ecf20Sopenharmony_ci return 1; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (setup_context(QD, &ring)) 2508c2ecf20Sopenharmony_ci return 1; 2518c2ecf20Sopenharmony_ci if (get_file_size(infd, &insize)) 2528c2ecf20Sopenharmony_ci return 1; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = copy_file(&ring, insize); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci close(infd); 2578c2ecf20Sopenharmony_ci close(outfd); 2588c2ecf20Sopenharmony_ci io_uring_queue_exit(&ring); 2598c2ecf20Sopenharmony_ci return ret; 2608c2ecf20Sopenharmony_ci} 261