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