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