Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.1-rc3 251 lines 4.8 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Simple test program that demonstrates a file copy through io_uring. This 4 * uses the API exposed by liburing. 5 * 6 * Copyright (C) 2018-2019 Jens Axboe 7 */ 8#include <stdio.h> 9#include <fcntl.h> 10#include <string.h> 11#include <stdlib.h> 12#include <unistd.h> 13#include <assert.h> 14#include <errno.h> 15#include <inttypes.h> 16#include <sys/stat.h> 17#include <sys/ioctl.h> 18 19#include "liburing.h" 20 21#define QD 64 22#define BS (32*1024) 23 24static int infd, outfd; 25 26struct io_data { 27 int read; 28 off_t first_offset, offset; 29 size_t first_len; 30 struct iovec iov; 31}; 32 33static int setup_context(unsigned entries, struct io_uring *ring) 34{ 35 int ret; 36 37 ret = io_uring_queue_init(entries, ring, 0); 38 if (ret < 0) { 39 fprintf(stderr, "queue_init: %s\n", strerror(-ret)); 40 return -1; 41 } 42 43 return 0; 44} 45 46static int get_file_size(int fd, off_t *size) 47{ 48 struct stat st; 49 50 if (fstat(fd, &st) < 0) 51 return -1; 52 if (S_ISREG(st.st_mode)) { 53 *size = st.st_size; 54 return 0; 55 } else if (S_ISBLK(st.st_mode)) { 56 unsigned long long bytes; 57 58 if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) 59 return -1; 60 61 *size = bytes; 62 return 0; 63 } 64 65 return -1; 66} 67 68static void queue_prepped(struct io_uring *ring, struct io_data *data) 69{ 70 struct io_uring_sqe *sqe; 71 72 sqe = io_uring_get_sqe(ring); 73 assert(sqe); 74 75 if (data->read) 76 io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset); 77 else 78 io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset); 79 80 io_uring_sqe_set_data(sqe, data); 81} 82 83static int queue_read(struct io_uring *ring, off_t size, off_t offset) 84{ 85 struct io_uring_sqe *sqe; 86 struct io_data *data; 87 88 sqe = io_uring_get_sqe(ring); 89 if (!sqe) 90 return 1; 91 92 data = malloc(size + sizeof(*data)); 93 data->read = 1; 94 data->offset = data->first_offset = offset; 95 96 data->iov.iov_base = data + 1; 97 data->iov.iov_len = size; 98 data->first_len = size; 99 100 io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); 101 io_uring_sqe_set_data(sqe, data); 102 return 0; 103} 104 105static void queue_write(struct io_uring *ring, struct io_data *data) 106{ 107 data->read = 0; 108 data->offset = data->first_offset; 109 110 data->iov.iov_base = data + 1; 111 data->iov.iov_len = data->first_len; 112 113 queue_prepped(ring, data); 114 io_uring_submit(ring); 115} 116 117static int copy_file(struct io_uring *ring, off_t insize) 118{ 119 unsigned long reads, writes; 120 struct io_uring_cqe *cqe; 121 off_t write_left, offset; 122 int ret; 123 124 write_left = insize; 125 writes = reads = offset = 0; 126 127 while (insize || write_left) { 128 unsigned long had_reads; 129 int got_comp; 130 131 /* 132 * Queue up as many reads as we can 133 */ 134 had_reads = reads; 135 while (insize) { 136 off_t this_size = insize; 137 138 if (reads + writes >= QD) 139 break; 140 if (this_size > BS) 141 this_size = BS; 142 else if (!this_size) 143 break; 144 145 if (queue_read(ring, this_size, offset)) 146 break; 147 148 insize -= this_size; 149 offset += this_size; 150 reads++; 151 } 152 153 if (had_reads != reads) { 154 ret = io_uring_submit(ring); 155 if (ret < 0) { 156 fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret)); 157 break; 158 } 159 } 160 161 /* 162 * Queue is full at this point. Find at least one completion. 163 */ 164 got_comp = 0; 165 while (write_left) { 166 struct io_data *data; 167 168 if (!got_comp) { 169 ret = io_uring_wait_completion(ring, &cqe); 170 got_comp = 1; 171 } else 172 ret = io_uring_get_completion(ring, &cqe); 173 if (ret < 0) { 174 fprintf(stderr, "io_uring_get_completion: %s\n", 175 strerror(-ret)); 176 return 1; 177 } 178 if (!cqe) 179 break; 180 181 data = (struct io_data *) (uintptr_t) cqe->user_data; 182 if (cqe->res < 0) { 183 if (cqe->res == -EAGAIN) { 184 queue_prepped(ring, data); 185 continue; 186 } 187 fprintf(stderr, "cqe failed: %s\n", 188 strerror(-cqe->res)); 189 return 1; 190 } else if ((size_t) cqe->res != data->iov.iov_len) { 191 /* Short read/write, adjust and requeue */ 192 data->iov.iov_base += cqe->res; 193 data->iov.iov_len -= cqe->res; 194 data->offset += cqe->res; 195 queue_prepped(ring, data); 196 continue; 197 } 198 199 /* 200 * All done. if write, nothing else to do. if read, 201 * queue up corresponding write. 202 */ 203 if (data->read) { 204 queue_write(ring, data); 205 write_left -= data->first_len; 206 reads--; 207 writes++; 208 } else { 209 free(data); 210 writes--; 211 } 212 } 213 } 214 215 return 0; 216} 217 218int main(int argc, char *argv[]) 219{ 220 struct io_uring ring; 221 off_t insize; 222 int ret; 223 224 if (argc < 3) { 225 printf("%s: infile outfile\n", argv[0]); 226 return 1; 227 } 228 229 infd = open(argv[1], O_RDONLY); 230 if (infd < 0) { 231 perror("open infile"); 232 return 1; 233 } 234 outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); 235 if (outfd < 0) { 236 perror("open outfile"); 237 return 1; 238 } 239 240 if (setup_context(QD, &ring)) 241 return 1; 242 if (get_file_size(infd, &insize)) 243 return 1; 244 245 ret = copy_file(&ring, insize); 246 247 close(infd); 248 close(outfd); 249 io_uring_queue_exit(&ring); 250 return ret; 251}