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