[PATCH] Fix a bunch of bugs
[fio.git] / engines / fio-engine-splice.c
CommitLineData
2866c82d
JA
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
14struct spliceio_data {
15 struct io_u *last_io_u;
16 int pipe[2];
17};
18
7a16dd02
JA
19static int fio_spliceio_sync(struct thread_data fio_unused *td,
20 struct fio_file *f)
2866c82d 21{
53cdc686 22 return fsync(f->fd);
2866c82d
JA
23}
24
25static 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
40static 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 */
54static int fio_splice_read(struct thread_data *td, struct io_u *io_u)
55{
56 struct spliceio_data *sd = td->io_ops->data;
53cdc686 57 struct fio_file *f = io_u->file;
2866c82d
JA
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
53cdc686 71 ret = splice(f->fd, &offset, sd->pipe[1], NULL, this_len, SPLICE_F_MORE);
2866c82d
JA
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 */
98static 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, };
53cdc686 108 struct fio_file *f = io_u->file;
2866c82d
JA
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) {
53cdc686 124 ret2 = splice(sd->pipe[0], NULL, f->fd, &off, ret, 0);
2866c82d
JA
125 if (ret2 < 0)
126 return errno;
127
128 ret -= ret2;
129 }
130 }
131
132 return io_u->buflen;
133}
134
135static 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
159static 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
171static 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
186struct 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};