X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=fio-io.c;h=7b1c1bd3a3360e9feadd4def4eb8c90bd83111aa;hp=46cd11a7a1f4fb25bdb2ecc0d42e274b11233847;hb=592ef98a623407437c1807bc29deaa87726de5b4;hpb=b1ff34038687057f1365d31e3eb3dc2e31ecac28 diff --git a/fio-io.c b/fio-io.c index 46cd11a7..7b1c1bd3 100644 --- a/fio-io.c +++ b/fio-io.c @@ -461,7 +461,7 @@ int fio_mmapio_init(struct thread_data *td) #ifdef FIO_HAVE_SGIO struct sgio_cmd { - char cdb[10]; + unsigned char cdb[10]; int nr; }; @@ -497,7 +497,7 @@ static int fio_sgio_getevents(struct thread_data *td, int min, int max, struct sgio_data *sd = td->io_data; struct pollfd pfd = { .fd = td->fd, .events = POLLIN }; void *buf = malloc(max * sizeof(struct sg_io_hdr)); - int left = max, ret, events, i, r = 0, fl; + int left = max, ret, events, i, r = 0, fl = 0; /* * don't block for !events @@ -544,16 +544,20 @@ static int fio_sgio_getevents(struct thread_data *td, int min, int max, return r; } -static int fio_sgio_doio(struct thread_data *td, struct io_u *io_u, int sync) +static int fio_sgio_ioctl_doio(struct thread_data *td, struct io_u *io_u) { struct sgio_data *sd = td->io_data; struct sg_io_hdr *hdr = &io_u->hdr; - int ret; - if (td->filetype == FIO_TYPE_BD) { - sd->events[0] = io_u; - return ioctl(td->fd, SG_IO, hdr); - } + sd->events[0] = io_u; + + return ioctl(td->fd, SG_IO, hdr); +} + +static int fio_sgio_rw_doio(struct thread_data *td, struct io_u *io_u, int sync) +{ + struct sg_io_hdr *hdr = &io_u->hdr; + int ret; ret = write(td->fd, hdr, sizeof(*hdr)); if (ret < 0) @@ -568,6 +572,14 @@ static int fio_sgio_doio(struct thread_data *td, struct io_u *io_u, int sync) return 0; } +static int fio_sgio_doio(struct thread_data *td, struct io_u *io_u, int sync) +{ + if (td->filetype == FIO_TYPE_BD) + return fio_sgio_ioctl_doio(td, io_u); + + return fio_sgio_rw_doio(td, io_u, sync); +} + static int fio_sgio_sync(struct thread_data *td) { struct sgio_data *sd = td->io_data; @@ -739,3 +751,169 @@ int fio_sgio_init(struct thread_data *td) } #endif /* FIO_HAVE_SGIO */ + +#ifdef FIO_HAVE_SPLICE +struct spliceio_data { + struct io_u *last_io_u; + int pipe[2]; +}; + +static struct io_u *fio_spliceio_event(struct thread_data *td, int event) +{ + struct spliceio_data *sd = td->io_data; + + assert(event == 0); + + return sd->last_io_u; +} + +/* + * For splice reading, we unfortunately cannot (yet) vmsplice the other way. + * So just splice the data from the file into the pipe, and use regular + * read to fill the buffer. Doesn't make a lot of sense, but... + */ +static int fio_splice_read(struct thread_data *td, struct io_u *io_u) +{ + struct spliceio_data *sd = td->io_data; + int ret, ret2, buflen; + off_t offset; + void *p; + + offset = io_u->offset; + buflen = io_u->buflen; + p = io_u->buf; + while (buflen) { + int this_len = buflen; + + if (this_len > SPLICE_DEF_SIZE) + this_len = SPLICE_DEF_SIZE; + + ret = splice(td->fd, &offset, sd->pipe[1], NULL, this_len, SPLICE_F_MORE); + if (ret < 0) { + if (errno == ENODATA || errno == EAGAIN) + continue; + + return errno; + } + + buflen -= ret; + + while (ret) { + ret2 = read(sd->pipe[0], p, ret); + if (ret2 < 0) + return errno; + + ret -= ret2; + p += ret2; + } + } + + return io_u->buflen; +} + +/* + * For splice writing, we can vmsplice our data buffer directly into a + * pipe and then splice that to a file. + */ +static int fio_splice_write(struct thread_data *td, struct io_u *io_u) +{ + struct spliceio_data *sd = td->io_data; + struct iovec iov[1] = { + { + .iov_base = io_u->buf, + .iov_len = io_u->buflen, + } + }; + struct pollfd pfd = { .fd = sd->pipe[1], .events = POLLOUT, }; + off_t off = io_u->offset; + int ret, ret2; + + while (iov[0].iov_len) { + if (poll(&pfd, 1, -1) < 0) + return errno; + + ret = vmsplice(sd->pipe[1], iov, 1, SPLICE_F_NONBLOCK); + if (ret < 0) + return errno; + + iov[0].iov_len -= ret; + iov[0].iov_base += ret; + + while (ret) { + ret2 = splice(sd->pipe[0], NULL, td->fd, &off, ret, 0); + if (ret2 < 0) + return errno; + + ret -= ret2; + } + } + + return io_u->buflen; +} + +static int fio_spliceio_queue(struct thread_data *td, struct io_u *io_u) +{ + struct spliceio_data *sd = td->io_data; + int ret; + + if (io_u->ddir == DDIR_READ) + ret = fio_splice_read(td, io_u); + else + ret = fio_splice_write(td, io_u); + + if ((unsigned int) ret != io_u->buflen) { + if (ret > 0) { + io_u->resid = io_u->buflen - ret; + io_u->error = ENODATA; + } else + io_u->error = errno; + } + + if (!io_u->error) + sd->last_io_u = io_u; + + return io_u->error; +} + +static void fio_spliceio_cleanup(struct thread_data *td) +{ + struct spliceio_data *sd = td->io_data; + + if (sd) { + close(sd->pipe[0]); + close(sd->pipe[1]); + free(sd); + td->io_data = NULL; + } +} + +int fio_spliceio_init(struct thread_data *td) +{ + struct spliceio_data *sd = malloc(sizeof(*sd)); + + td->io_queue = fio_spliceio_queue; + td->io_getevents = fio_syncio_getevents; + td->io_event = fio_spliceio_event; + td->io_cancel = NULL; + td->io_cleanup = fio_spliceio_cleanup; + td->io_sync = fio_io_sync; + + sd->last_io_u = NULL; + if (pipe(sd->pipe) < 0) { + td_verror(td, errno); + free(sd); + return 1; + } + + td->io_data = sd; + return 0; +} + +#else /* FIO_HAVE_SPLICE */ + +int fio_spliceio_init(struct thread_data *td) +{ + return EINVAL; +} + +#endif /* FIO_HAVE_SPLICE */