/* * Native Solaris async IO engine * */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include "../fio.h" #include <sys/asynch.h> struct solarisaio_data { struct io_u **aio_events; unsigned int aio_pending; unsigned int nr; unsigned int max_depth; }; static int fio_solarisaio_cancel(struct thread_data fio_unused *td, struct io_u *io_u) { return aiocancel(&io_u->resultp); } static int fio_solarisaio_prep(struct thread_data fio_unused *td, struct io_u *io_u) { struct solarisaio_data *sd = td->io_ops->data; io_u->resultp.aio_return = AIO_INPROGRESS; io_u->engine_data = sd; return 0; } static void wait_for_event(struct timeval *tv) { struct solarisaio_data *sd; struct io_u *io_u; aio_result_t *res; res = aiowait(tv); if (res == (aio_result_t *) -1) { int err = errno; if (err != EINVAL) { log_err("fio: solarisaio got %d in aiowait\n", err); exit(err); } return; } else if (!res) return; io_u = container_of(res, struct io_u, resultp); sd = io_u->engine_data; if (io_u->resultp.aio_return >= 0) { io_u->resid = io_u->xfer_buflen - io_u->resultp.aio_return; io_u->error = 0; } else io_u->error = io_u->resultp.aio_errno; /* * For SIGIO, we need a write barrier between the two, so that * the ->aio_pending store is seen after the ->aio_events store */ sd->aio_events[sd->aio_pending] = io_u; write_barrier(); sd->aio_pending++; sd->nr--; } static int fio_solarisaio_getevents(struct thread_data *td, unsigned int min, unsigned int max, struct timespec *t) { struct solarisaio_data *sd = td->io_ops->data; struct timeval tv; int ret; if (!min || !t) { tv.tv_sec = 0; tv.tv_usec = 0; } else { tv.tv_sec = t->tv_sec; tv.tv_usec = t->tv_nsec / 1000; } while (sd->aio_pending < min) wait_for_event(&tv); /* * should be OK without locking, as int operations should be atomic */ ret = sd->aio_pending; sd->aio_pending -= ret; return ret; } static struct io_u *fio_solarisaio_event(struct thread_data *td, int event) { struct solarisaio_data *sd = td->io_ops->data; return sd->aio_events[event]; } static int fio_solarisaio_queue(struct thread_data fio_unused *td, struct io_u *io_u) { struct solarisaio_data *sd = td->io_ops->data; struct fio_file *f = io_u->file; off_t off; int ret; fio_ro_check(td, io_u); if (io_u->ddir == DDIR_SYNC) { if (sd->nr) return FIO_Q_BUSY; if (fsync(f->fd) < 0) io_u->error = errno; return FIO_Q_COMPLETED; } if (io_u->ddir == DDIR_DATASYNC) { if (sd->nr) return FIO_Q_BUSY; if (fdatasync(f->fd) < 0) io_u->error = errno; return FIO_Q_COMPLETED; } if (sd->nr == sd->max_depth) return FIO_Q_BUSY; off = io_u->offset; if (io_u->ddir == DDIR_READ) ret = aioread(f->fd, io_u->xfer_buf, io_u->xfer_buflen, off, SEEK_SET, &io_u->resultp); else ret = aiowrite(f->fd, io_u->xfer_buf, io_u->xfer_buflen, off, SEEK_SET, &io_u->resultp); if (ret) { io_u->error = errno; td_verror(td, io_u->error, "xfer"); return FIO_Q_COMPLETED; } sd->nr++; return FIO_Q_QUEUED; } static void fio_solarisaio_cleanup(struct thread_data *td) { struct solarisaio_data *sd = td->io_ops->data; if (sd) { free(sd->aio_events); free(sd); } } /* * Set USE_SIGNAL_COMPLETIONS to use SIGIO as completion events. */ #ifdef USE_SIGNAL_COMPLETIONS static void fio_solarisaio_sigio(int sig) { wait_for_event(NULL); } static void fio_solarisaio_init_sigio(void) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = fio_solarisaio_sigio; act.sa_flags = SA_RESTART; sigaction(SIGIO, &act, NULL); } #endif static int fio_solarisaio_init(struct thread_data *td) { struct solarisaio_data *sd = malloc(sizeof(*sd)); unsigned int max_depth; max_depth = td->o.iodepth; if (max_depth > MAXASYNCHIO) { max_depth = MAXASYNCHIO; log_info("fio: lower depth to %d due to OS constraints\n", max_depth); } memset(sd, 0, sizeof(*sd)); sd->aio_events = malloc(max_depth * sizeof(struct io_u *)); memset(sd->aio_events, 0, max_depth * sizeof(struct io_u *)); sd->max_depth = max_depth; #ifdef USE_SIGNAL_COMPLETIONS fio_solarisaio_init_sigio(); #endif td->io_ops->data = sd; return 0; } static struct ioengine_ops ioengine = { .name = "solarisaio", .version = FIO_IOOPS_VERSION, .init = fio_solarisaio_init, .prep = fio_solarisaio_prep, .queue = fio_solarisaio_queue, .cancel = fio_solarisaio_cancel, .getevents = fio_solarisaio_getevents, .event = fio_solarisaio_event, .cleanup = fio_solarisaio_cleanup, .open_file = generic_open_file, .close_file = generic_close_file, .get_file_size = generic_get_file_size, }; static void fio_init fio_solarisaio_register(void) { register_ioengine(&ioengine); } static void fio_exit fio_solarisaio_unregister(void) { unregister_ioengine(&ioengine); }