Rate must always calculate bytes done
[fio.git] / engines / 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
12 #include "../fio.h"
13 #include "../os.h"
14
15 #ifdef FIO_HAVE_SPLICE
16
17 struct 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  */
26 static 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  */
70 static 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
107 static 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
133 static 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
145 static 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
159 static 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         .flags          = FIO_SYNCIO,
166 };
167
168 #else /* FIO_HAVE_SPLICE */
169
170 /*
171  * When we have a proper configure system in place, we simply wont build
172  * and install this io engine. For now install a crippled version that
173  * just complains and fails to load.
174  */
175 static int fio_spliceio_init(struct thread_data fio_unused *td)
176 {
177         fprintf(stderr, "fio: splice not available\n");
178         return 1;
179 }
180
181 static struct ioengine_ops ioengine = {
182         .name           = "splice",
183         .version        = FIO_IOOPS_VERSION,
184         .init           = fio_spliceio_init,
185 };
186
187 #endif
188
189 static void fio_init fio_spliceio_register(void)
190 {
191         register_ioengine(&ioengine);
192 }
193
194 static void fio_exit fio_spliceio_unregister(void)
195 {
196         unregister_ioengine(&ioengine);
197 }