splice.c revision b5af82930ccfd7dda6a1b11794efb452eb76d8dc
1/* 2 * splice io engine 3 * 4 */ 5#include <stdio.h> 6#include <stdlib.h> 7#include <unistd.h> 8#include <errno.h> 9#include <assert.h> 10#include <sys/poll.h> 11 12#include "../fio.h" 13#include "../os.h" 14 15#ifdef FIO_HAVE_SPLICE 16 17struct spliceio_data { 18 int pipe[2]; 19}; 20 21/* 22 * For splice reading, we unfortunately cannot (yet) vmsplice the other way. 23 * So just splice the data from the file into the pipe, and use regular 24 * read to fill the buffer. Doesn't make a lot of sense, but... 25 */ 26static int fio_splice_read(struct thread_data *td, struct io_u *io_u) 27{ 28 struct spliceio_data *sd = td->io_ops->data; 29 struct fio_file *f = io_u->file; 30 int ret, ret2, buflen; 31 off_t offset; 32 void *p; 33 34 offset = io_u->offset; 35 buflen = io_u->xfer_buflen; 36 p = io_u->xfer_buf; 37 while (buflen) { 38 int this_len = buflen; 39 40 if (this_len > SPLICE_DEF_SIZE) 41 this_len = SPLICE_DEF_SIZE; 42 43 ret = splice(f->fd, &offset, sd->pipe[1], NULL, this_len, SPLICE_F_MORE); 44 if (ret < 0) { 45 if (errno == ENODATA || errno == EAGAIN) 46 continue; 47 48 return errno; 49 } 50 51 buflen -= ret; 52 53 while (ret) { 54 ret2 = read(sd->pipe[0], p, ret); 55 if (ret2 < 0) 56 return errno; 57 58 ret -= ret2; 59 p += ret2; 60 } 61 } 62 63 return io_u->xfer_buflen; 64} 65 66/* 67 * For splice writing, we can vmsplice our data buffer directly into a 68 * pipe and then splice that to a file. 69 */ 70static int fio_splice_write(struct thread_data *td, struct io_u *io_u) 71{ 72 struct spliceio_data *sd = td->io_ops->data; 73 struct iovec iov[1] = { 74 { 75 .iov_base = io_u->xfer_buf, 76 .iov_len = io_u->xfer_buflen, 77 } 78 }; 79 struct pollfd pfd = { .fd = sd->pipe[1], .events = POLLOUT, }; 80 struct fio_file *f = io_u->file; 81 off_t off = io_u->offset; 82 int ret, ret2; 83 84 while (iov[0].iov_len) { 85 if (poll(&pfd, 1, -1) < 0) 86 return errno; 87 88 ret = vmsplice(sd->pipe[1], iov, 1, SPLICE_F_NONBLOCK); 89 if (ret < 0) 90 return errno; 91 92 iov[0].iov_len -= ret; 93 iov[0].iov_base += ret; 94 95 while (ret) { 96 ret2 = splice(sd->pipe[0], NULL, f->fd, &off, ret, 0); 97 if (ret2 < 0) 98 return errno; 99 100 ret -= ret2; 101 } 102 } 103 104 return io_u->xfer_buflen; 105} 106 107static int fio_spliceio_queue(struct thread_data *td, struct io_u *io_u) 108{ 109 int ret; 110 111 if (io_u->ddir == DDIR_READ) 112 ret = fio_splice_read(td, io_u); 113 else if (io_u->ddir == DDIR_WRITE) 114 ret = fio_splice_write(td, io_u); 115 else 116 ret = fsync(io_u->file->fd); 117 118 if (ret != (int) io_u->xfer_buflen) { 119 if (ret >= 0) { 120 io_u->resid = io_u->xfer_buflen - ret; 121 io_u->error = 0; 122 return FIO_Q_COMPLETED; 123 } else 124 io_u->error = errno; 125 } 126 127 if (io_u->error) 128 td_verror(td, io_u->error, "xfer"); 129 130 return FIO_Q_COMPLETED; 131} 132 133static void fio_spliceio_cleanup(struct thread_data *td) 134{ 135 struct spliceio_data *sd = td->io_ops->data; 136 137 if (sd) { 138 close(sd->pipe[0]); 139 close(sd->pipe[1]); 140 free(sd); 141 td->io_ops->data = NULL; 142 } 143} 144 145static int fio_spliceio_init(struct thread_data *td) 146{ 147 struct spliceio_data *sd = malloc(sizeof(*sd)); 148 149 if (pipe(sd->pipe) < 0) { 150 td_verror(td, errno, "pipe"); 151 free(sd); 152 return 1; 153 } 154 155 td->io_ops->data = sd; 156 return 0; 157} 158 159static struct ioengine_ops ioengine = { 160 .name = "splice", 161 .version = FIO_IOOPS_VERSION, 162 .init = fio_spliceio_init, 163 .queue = fio_spliceio_queue, 164 .cleanup = fio_spliceio_cleanup, 165 .open_file = generic_open_file, 166 .close_file = generic_close_file, 167 .flags = FIO_SYNCIO, 168}; 169 170#else /* FIO_HAVE_SPLICE */ 171 172/* 173 * When we have a proper configure system in place, we simply wont build 174 * and install this io engine. For now install a crippled version that 175 * just complains and fails to load. 176 */ 177static int fio_spliceio_init(struct thread_data fio_unused *td) 178{ 179 fprintf(stderr, "fio: splice not available\n"); 180 return 1; 181} 182 183static struct ioengine_ops ioengine = { 184 .name = "splice", 185 .version = FIO_IOOPS_VERSION, 186 .init = fio_spliceio_init, 187}; 188 189#endif 190 191static void fio_init fio_spliceio_register(void) 192{ 193 register_ioengine(&ioengine); 194} 195 196static void fio_exit fio_spliceio_unregister(void) 197{ 198 unregister_ioengine(&ioengine); 199} 200