Allow more flexibility in zone start and span
[fio.git] / engines / sync.c
... / ...
CommitLineData
1/*
2 * sync/psync engine
3 *
4 * IO engine that does regular read(2)/write(2) with lseek(2) to transfer
5 * data and IO engine that does regular pread(2)/pwrite(2) to transfer data.
6 *
7 */
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <sys/uio.h>
12#include <errno.h>
13
14#include "../fio.h"
15#include "../optgroup.h"
16#include "../lib/rand.h"
17
18/*
19 * Sync engine uses engine_data to store last offset
20 */
21#define LAST_POS(f) ((f)->engine_pos)
22
23struct syncio_data {
24 struct iovec *iovecs;
25 struct io_u **io_us;
26 unsigned int queued;
27 unsigned int events;
28 unsigned long queued_bytes;
29
30 unsigned long long last_offset;
31 struct fio_file *last_file;
32 enum fio_ddir last_ddir;
33
34 struct frand_state rand_state;
35};
36
37#ifdef FIO_HAVE_PWRITEV2
38struct psyncv2_options {
39 void *pad;
40 unsigned int hipri;
41 unsigned int hipri_percentage;
42 unsigned int uncached;
43};
44
45static struct fio_option options[] = {
46 {
47 .name = "hipri",
48 .lname = "RWF_HIPRI",
49 .type = FIO_OPT_STR_SET,
50 .off1 = offsetof(struct psyncv2_options, hipri),
51 .help = "Set RWF_HIPRI for pwritev2/preadv2",
52 .category = FIO_OPT_C_ENGINE,
53 .group = FIO_OPT_G_INVALID,
54 },
55 {
56 .name = "hipri_percentage",
57 .lname = "RWF_HIPRI_PERCENTAGE",
58 .type = FIO_OPT_INT,
59 .off1 = offsetof(struct psyncv2_options, hipri_percentage),
60 .minval = 0,
61 .maxval = 100,
62 .def = "100",
63 .help = "Probabilistically set RWF_HIPRI for pwritev2/preadv2",
64 .category = FIO_OPT_C_ENGINE,
65 .group = FIO_OPT_G_INVALID,
66 },
67 {
68 .name = "uncached",
69 .lname = "Uncached",
70 .type = FIO_OPT_INT,
71 .off1 = offsetof(struct psyncv2_options, uncached),
72 .help = "Use RWF_UNCACHED for buffered read/writes",
73 .category = FIO_OPT_C_ENGINE,
74 .group = FIO_OPT_G_INVALID,
75 },
76 {
77 .name = NULL,
78 },
79};
80#endif
81
82static int fio_syncio_prep(struct thread_data *td, struct io_u *io_u)
83{
84 struct fio_file *f = io_u->file;
85
86 if (!ddir_rw(io_u->ddir))
87 return 0;
88
89 if (LAST_POS(f) != -1ULL && LAST_POS(f) == io_u->offset)
90 return 0;
91
92 if (lseek(f->fd, io_u->offset, SEEK_SET) == -1) {
93 td_verror(td, errno, "lseek");
94 return 1;
95 }
96
97 return 0;
98}
99
100static int fio_io_end(struct thread_data *td, struct io_u *io_u, int ret)
101{
102 if (io_u->file && ret >= 0 && ddir_rw(io_u->ddir))
103 LAST_POS(io_u->file) = io_u->offset + ret;
104
105 if (ret != (int) io_u->xfer_buflen) {
106 if (ret >= 0) {
107 io_u->resid = io_u->xfer_buflen - ret;
108 io_u->error = 0;
109 return FIO_Q_COMPLETED;
110 } else
111 io_u->error = errno;
112 }
113
114 if (io_u->error) {
115 io_u_log_error(td, io_u);
116 td_verror(td, io_u->error, "xfer");
117 }
118
119 return FIO_Q_COMPLETED;
120}
121
122#ifdef CONFIG_PWRITEV
123static enum fio_q_status fio_pvsyncio_queue(struct thread_data *td,
124 struct io_u *io_u)
125{
126 struct syncio_data *sd = td->io_ops_data;
127 struct iovec *iov = &sd->iovecs[0];
128 struct fio_file *f = io_u->file;
129 int ret;
130
131 fio_ro_check(td, io_u);
132
133 iov->iov_base = io_u->xfer_buf;
134 iov->iov_len = io_u->xfer_buflen;
135
136 if (io_u->ddir == DDIR_READ)
137 ret = preadv(f->fd, iov, 1, io_u->offset);
138 else if (io_u->ddir == DDIR_WRITE)
139 ret = pwritev(f->fd, iov, 1, io_u->offset);
140 else if (io_u->ddir == DDIR_TRIM) {
141 do_io_u_trim(td, io_u);
142 return FIO_Q_COMPLETED;
143 } else
144 ret = do_io_u_sync(td, io_u);
145
146 return fio_io_end(td, io_u, ret);
147}
148#endif
149
150#ifdef FIO_HAVE_PWRITEV2
151static enum fio_q_status fio_pvsyncio2_queue(struct thread_data *td,
152 struct io_u *io_u)
153{
154 struct syncio_data *sd = td->io_ops_data;
155 struct psyncv2_options *o = td->eo;
156 struct iovec *iov = &sd->iovecs[0];
157 struct fio_file *f = io_u->file;
158 int ret, flags = 0;
159
160 fio_ro_check(td, io_u);
161
162 if (o->hipri &&
163 (rand_between(&sd->rand_state, 1, 100) <= o->hipri_percentage))
164 flags |= RWF_HIPRI;
165 if (!td->o.odirect && o->uncached)
166 flags |= RWF_UNCACHED;
167
168 iov->iov_base = io_u->xfer_buf;
169 iov->iov_len = io_u->xfer_buflen;
170
171 if (io_u->ddir == DDIR_READ)
172 ret = preadv2(f->fd, iov, 1, io_u->offset, flags);
173 else if (io_u->ddir == DDIR_WRITE)
174 ret = pwritev2(f->fd, iov, 1, io_u->offset, flags);
175 else if (io_u->ddir == DDIR_TRIM) {
176 do_io_u_trim(td, io_u);
177 return FIO_Q_COMPLETED;
178 } else
179 ret = do_io_u_sync(td, io_u);
180
181 return fio_io_end(td, io_u, ret);
182}
183#endif
184
185static enum fio_q_status fio_psyncio_queue(struct thread_data *td,
186 struct io_u *io_u)
187{
188 struct fio_file *f = io_u->file;
189 int ret;
190
191 fio_ro_check(td, io_u);
192
193 if (io_u->ddir == DDIR_READ)
194 ret = pread(f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset);
195 else if (io_u->ddir == DDIR_WRITE)
196 ret = pwrite(f->fd, io_u->xfer_buf, io_u->xfer_buflen, io_u->offset);
197 else if (io_u->ddir == DDIR_TRIM) {
198 do_io_u_trim(td, io_u);
199 return FIO_Q_COMPLETED;
200 } else
201 ret = do_io_u_sync(td, io_u);
202
203 return fio_io_end(td, io_u, ret);
204}
205
206static enum fio_q_status fio_syncio_queue(struct thread_data *td,
207 struct io_u *io_u)
208{
209 struct fio_file *f = io_u->file;
210 int ret;
211
212 fio_ro_check(td, io_u);
213
214 if (io_u->ddir == DDIR_READ)
215 ret = read(f->fd, io_u->xfer_buf, io_u->xfer_buflen);
216 else if (io_u->ddir == DDIR_WRITE)
217 ret = write(f->fd, io_u->xfer_buf, io_u->xfer_buflen);
218 else if (io_u->ddir == DDIR_TRIM) {
219 do_io_u_trim(td, io_u);
220 return FIO_Q_COMPLETED;
221 } else
222 ret = do_io_u_sync(td, io_u);
223
224 return fio_io_end(td, io_u, ret);
225}
226
227static int fio_vsyncio_getevents(struct thread_data *td, unsigned int min,
228 unsigned int max,
229 const struct timespec fio_unused *t)
230{
231 struct syncio_data *sd = td->io_ops_data;
232 int ret;
233
234 if (min) {
235 ret = sd->events;
236 sd->events = 0;
237 } else
238 ret = 0;
239
240 dprint(FD_IO, "vsyncio_getevents: min=%d,max=%d: %d\n", min, max, ret);
241 return ret;
242}
243
244static struct io_u *fio_vsyncio_event(struct thread_data *td, int event)
245{
246 struct syncio_data *sd = td->io_ops_data;
247
248 return sd->io_us[event];
249}
250
251static int fio_vsyncio_append(struct thread_data *td, struct io_u *io_u)
252{
253 struct syncio_data *sd = td->io_ops_data;
254
255 if (ddir_sync(io_u->ddir))
256 return 0;
257
258 if (io_u->offset == sd->last_offset && io_u->file == sd->last_file &&
259 io_u->ddir == sd->last_ddir)
260 return 1;
261
262 return 0;
263}
264
265static void fio_vsyncio_set_iov(struct syncio_data *sd, struct io_u *io_u,
266 int idx)
267{
268 sd->io_us[idx] = io_u;
269 sd->iovecs[idx].iov_base = io_u->xfer_buf;
270 sd->iovecs[idx].iov_len = io_u->xfer_buflen;
271 sd->last_offset = io_u->offset + io_u->xfer_buflen;
272 sd->last_file = io_u->file;
273 sd->last_ddir = io_u->ddir;
274 sd->queued_bytes += io_u->xfer_buflen;
275 sd->queued++;
276}
277
278static enum fio_q_status fio_vsyncio_queue(struct thread_data *td,
279 struct io_u *io_u)
280{
281 struct syncio_data *sd = td->io_ops_data;
282
283 fio_ro_check(td, io_u);
284
285 if (!fio_vsyncio_append(td, io_u)) {
286 dprint(FD_IO, "vsyncio_queue: no append (%d)\n", sd->queued);
287 /*
288 * If we can't append and have stuff queued, tell fio to
289 * commit those first and then retry this io
290 */
291 if (sd->queued)
292 return FIO_Q_BUSY;
293 if (ddir_sync(io_u->ddir)) {
294 int ret = do_io_u_sync(td, io_u);
295
296 return fio_io_end(td, io_u, ret);
297 }
298
299 sd->queued = 0;
300 sd->queued_bytes = 0;
301 fio_vsyncio_set_iov(sd, io_u, 0);
302 } else {
303 if (sd->queued == td->o.iodepth) {
304 dprint(FD_IO, "vsyncio_queue: max depth %d\n", sd->queued);
305 return FIO_Q_BUSY;
306 }
307
308 dprint(FD_IO, "vsyncio_queue: append\n");
309 fio_vsyncio_set_iov(sd, io_u, sd->queued);
310 }
311
312 dprint(FD_IO, "vsyncio_queue: depth now %d\n", sd->queued);
313 return FIO_Q_QUEUED;
314}
315
316/*
317 * Check that we transferred all bytes, or saw an error, etc
318 */
319static int fio_vsyncio_end(struct thread_data *td, ssize_t bytes)
320{
321 struct syncio_data *sd = td->io_ops_data;
322 struct io_u *io_u;
323 unsigned int i;
324 int err;
325
326 /*
327 * transferred everything, perfect
328 */
329 if (bytes == sd->queued_bytes)
330 return 0;
331
332 err = errno;
333 for (i = 0; i < sd->queued; i++) {
334 io_u = sd->io_us[i];
335
336 if (bytes == -1) {
337 io_u->error = err;
338 } else {
339 unsigned int this_io;
340
341 this_io = bytes;
342 if (this_io > io_u->xfer_buflen)
343 this_io = io_u->xfer_buflen;
344
345 io_u->resid = io_u->xfer_buflen - this_io;
346 io_u->error = 0;
347 bytes -= this_io;
348 }
349 }
350
351 if (bytes == -1) {
352 td_verror(td, err, "xfer vsync");
353 return -err;
354 }
355
356 return 0;
357}
358
359static int fio_vsyncio_commit(struct thread_data *td)
360{
361 struct syncio_data *sd = td->io_ops_data;
362 struct fio_file *f;
363 ssize_t ret;
364
365 if (!sd->queued)
366 return 0;
367
368 io_u_mark_submit(td, sd->queued);
369 f = sd->last_file;
370
371 if (lseek(f->fd, sd->io_us[0]->offset, SEEK_SET) == -1) {
372 int err = -errno;
373
374 td_verror(td, errno, "lseek");
375 return err;
376 }
377
378 if (sd->last_ddir == DDIR_READ)
379 ret = readv(f->fd, sd->iovecs, sd->queued);
380 else
381 ret = writev(f->fd, sd->iovecs, sd->queued);
382
383 dprint(FD_IO, "vsyncio_commit: %d\n", (int) ret);
384 sd->events = sd->queued;
385 sd->queued = 0;
386 return fio_vsyncio_end(td, ret);
387}
388
389static int fio_vsyncio_init(struct thread_data *td)
390{
391 struct syncio_data *sd;
392
393 sd = malloc(sizeof(*sd));
394 memset(sd, 0, sizeof(*sd));
395 sd->last_offset = -1ULL;
396 sd->iovecs = malloc(td->o.iodepth * sizeof(struct iovec));
397 sd->io_us = malloc(td->o.iodepth * sizeof(struct io_u *));
398 init_rand(&sd->rand_state, 0);
399
400 td->io_ops_data = sd;
401 return 0;
402}
403
404static void fio_vsyncio_cleanup(struct thread_data *td)
405{
406 struct syncio_data *sd = td->io_ops_data;
407
408 if (sd) {
409 free(sd->iovecs);
410 free(sd->io_us);
411 free(sd);
412 }
413}
414
415static struct ioengine_ops ioengine_rw = {
416 .name = "sync",
417 .version = FIO_IOOPS_VERSION,
418 .prep = fio_syncio_prep,
419 .queue = fio_syncio_queue,
420 .open_file = generic_open_file,
421 .close_file = generic_close_file,
422 .get_file_size = generic_get_file_size,
423 .flags = FIO_SYNCIO,
424};
425
426static struct ioengine_ops ioengine_prw = {
427 .name = "psync",
428 .version = FIO_IOOPS_VERSION,
429 .queue = fio_psyncio_queue,
430 .open_file = generic_open_file,
431 .close_file = generic_close_file,
432 .get_file_size = generic_get_file_size,
433 .flags = FIO_SYNCIO,
434};
435
436static struct ioengine_ops ioengine_vrw = {
437 .name = "vsync",
438 .version = FIO_IOOPS_VERSION,
439 .init = fio_vsyncio_init,
440 .cleanup = fio_vsyncio_cleanup,
441 .queue = fio_vsyncio_queue,
442 .commit = fio_vsyncio_commit,
443 .event = fio_vsyncio_event,
444 .getevents = fio_vsyncio_getevents,
445 .open_file = generic_open_file,
446 .close_file = generic_close_file,
447 .get_file_size = generic_get_file_size,
448 .flags = FIO_SYNCIO,
449};
450
451#ifdef CONFIG_PWRITEV
452static struct ioengine_ops ioengine_pvrw = {
453 .name = "pvsync",
454 .version = FIO_IOOPS_VERSION,
455 .init = fio_vsyncio_init,
456 .cleanup = fio_vsyncio_cleanup,
457 .queue = fio_pvsyncio_queue,
458 .open_file = generic_open_file,
459 .close_file = generic_close_file,
460 .get_file_size = generic_get_file_size,
461 .flags = FIO_SYNCIO,
462};
463#endif
464
465#ifdef FIO_HAVE_PWRITEV2
466static struct ioengine_ops ioengine_pvrw2 = {
467 .name = "pvsync2",
468 .version = FIO_IOOPS_VERSION,
469 .init = fio_vsyncio_init,
470 .cleanup = fio_vsyncio_cleanup,
471 .queue = fio_pvsyncio2_queue,
472 .open_file = generic_open_file,
473 .close_file = generic_close_file,
474 .get_file_size = generic_get_file_size,
475 .flags = FIO_SYNCIO,
476 .options = options,
477 .option_struct_size = sizeof(struct psyncv2_options),
478};
479#endif
480
481static void fio_init fio_syncio_register(void)
482{
483 register_ioengine(&ioengine_rw);
484 register_ioengine(&ioengine_prw);
485 register_ioengine(&ioengine_vrw);
486#ifdef CONFIG_PWRITEV
487 register_ioengine(&ioengine_pvrw);
488#endif
489#ifdef FIO_HAVE_PWRITEV2
490 register_ioengine(&ioengine_pvrw2);
491#endif
492}
493
494static void fio_exit fio_syncio_unregister(void)
495{
496 unregister_ioengine(&ioengine_rw);
497 unregister_ioengine(&ioengine_prw);
498 unregister_ioengine(&ioengine_vrw);
499#ifdef CONFIG_PWRITEV
500 unregister_ioengine(&ioengine_pvrw);
501#endif
502#ifdef FIO_HAVE_PWRITEV2
503 unregister_ioengine(&ioengine_pvrw2);
504#endif
505}