1 /* 2 * Native Solaris async IO engine 3 * 4 */ 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <signal.h> 9 #include <errno.h> 10 11 #include "../fio.h" 12 13 #include <sys/asynch.h> 14 15 struct solarisaio_data { 16 struct io_u **aio_events; 17 unsigned int aio_pending; 18 unsigned int nr; 19 unsigned int max_depth; 20 }; 21 22 static int fio_solarisaio_cancel(struct thread_data fio_unused *td, 23 struct io_u *io_u) 24 { 25 return aiocancel(&io_u->resultp); 26 } 27 28 static int fio_solarisaio_prep(struct thread_data fio_unused *td, 29 struct io_u *io_u) 30 { 31 struct solarisaio_data *sd = td->io_ops->data; 32 33 io_u->resultp.aio_return = AIO_INPROGRESS; 34 io_u->engine_data = sd; 35 return 0; 36 } 37 38 static void wait_for_event(struct timeval *tv) 39 { 40 struct solarisaio_data *sd; 41 struct io_u *io_u; 42 aio_result_t *res; 43 44 res = aiowait(tv); 45 if (res == (aio_result_t *) -1) { 46 int err = errno; 47 48 if (err != EINVAL) { 49 log_err("fio: solarisaio got %d in aiowait\n", err); 50 exit(err); 51 } 52 return; 53 } else if (!res) 54 return; 55 56 io_u = container_of(res, struct io_u, resultp); 57 sd = io_u->engine_data; 58 59 if (io_u->resultp.aio_return >= 0) { 60 io_u->resid = io_u->xfer_buflen - io_u->resultp.aio_return; 61 io_u->error = 0; 62 } else 63 io_u->error = io_u->resultp.aio_errno; 64 65 /* 66 * For SIGIO, we need a write barrier between the two, so that 67 * the ->aio_pending store is seen after the ->aio_events store 68 */ 69 sd->aio_events[sd->aio_pending] = io_u; 70 write_barrier(); 71 sd->aio_pending++; 72 sd->nr--; 73 } 74 75 static int fio_solarisaio_getevents(struct thread_data *td, unsigned int min, 76 unsigned int max, struct timespec *t) 77 { 78 struct solarisaio_data *sd = td->io_ops->data; 79 struct timeval tv; 80 int ret; 81 82 if (!min || !t) { 83 tv.tv_sec = 0; 84 tv.tv_usec = 0; 85 } else { 86 tv.tv_sec = t->tv_sec; 87 tv.tv_usec = t->tv_nsec / 1000; 88 } 89 90 while (sd->aio_pending < min) 91 wait_for_event(&tv); 92 93 /* 94 * should be OK without locking, as int operations should be atomic 95 */ 96 ret = sd->aio_pending; 97 sd->aio_pending -= ret; 98 return ret; 99 } 100 101 static struct io_u *fio_solarisaio_event(struct thread_data *td, int event) 102 { 103 struct solarisaio_data *sd = td->io_ops->data; 104 105 return sd->aio_events[event]; 106 } 107 108 static int fio_solarisaio_queue(struct thread_data fio_unused *td, 109 struct io_u *io_u) 110 { 111 struct solarisaio_data *sd = td->io_ops->data; 112 struct fio_file *f = io_u->file; 113 off_t off; 114 int ret; 115 116 fio_ro_check(td, io_u); 117 118 if (io_u->ddir == DDIR_SYNC) { 119 if (sd->nr) 120 return FIO_Q_BUSY; 121 if (fsync(f->fd) < 0) 122 io_u->error = errno; 123 124 return FIO_Q_COMPLETED; 125 } 126 127 if (io_u->ddir == DDIR_DATASYNC) { 128 if (sd->nr) 129 return FIO_Q_BUSY; 130 if (fdatasync(f->fd) < 0) 131 io_u->error = errno; 132 133 return FIO_Q_COMPLETED; 134 } 135 136 if (sd->nr == sd->max_depth) 137 return FIO_Q_BUSY; 138 139 off = io_u->offset; 140 if (io_u->ddir == DDIR_READ) 141 ret = aioread(f->fd, io_u->xfer_buf, io_u->xfer_buflen, off, 142 SEEK_SET, &io_u->resultp); 143 else 144 ret = aiowrite(f->fd, io_u->xfer_buf, io_u->xfer_buflen, off, 145 SEEK_SET, &io_u->resultp); 146 if (ret) { 147 io_u->error = errno; 148 td_verror(td, io_u->error, "xfer"); 149 return FIO_Q_COMPLETED; 150 } 151 152 sd->nr++; 153 return FIO_Q_QUEUED; 154 } 155 156 static void fio_solarisaio_cleanup(struct thread_data *td) 157 { 158 struct solarisaio_data *sd = td->io_ops->data; 159 160 if (sd) { 161 free(sd->aio_events); 162 free(sd); 163 } 164 } 165 166 /* 167 * Set USE_SIGNAL_COMPLETIONS to use SIGIO as completion events. 168 */ 169 #ifdef USE_SIGNAL_COMPLETIONS 170 static void fio_solarisaio_sigio(int sig) 171 { 172 wait_for_event(NULL); 173 } 174 175 static void fio_solarisaio_init_sigio(void) 176 { 177 struct sigaction act; 178 179 memset(&act, 0, sizeof(act)); 180 act.sa_handler = fio_solarisaio_sigio; 181 act.sa_flags = SA_RESTART; 182 sigaction(SIGIO, &act, NULL); 183 } 184 #endif 185 186 static int fio_solarisaio_init(struct thread_data *td) 187 { 188 struct solarisaio_data *sd = malloc(sizeof(*sd)); 189 unsigned int max_depth; 190 191 max_depth = td->o.iodepth; 192 if (max_depth > MAXASYNCHIO) { 193 max_depth = MAXASYNCHIO; 194 log_info("fio: lower depth to %d due to OS constraints\n", 195 max_depth); 196 } 197 198 memset(sd, 0, sizeof(*sd)); 199 sd->aio_events = malloc(max_depth * sizeof(struct io_u *)); 200 memset(sd->aio_events, 0, max_depth * sizeof(struct io_u *)); 201 sd->max_depth = max_depth; 202 203 #ifdef USE_SIGNAL_COMPLETIONS 204 fio_solarisaio_init_sigio(); 205 #endif 206 207 td->io_ops->data = sd; 208 return 0; 209 } 210 211 static struct ioengine_ops ioengine = { 212 .name = "solarisaio", 213 .version = FIO_IOOPS_VERSION, 214 .init = fio_solarisaio_init, 215 .prep = fio_solarisaio_prep, 216 .queue = fio_solarisaio_queue, 217 .cancel = fio_solarisaio_cancel, 218 .getevents = fio_solarisaio_getevents, 219 .event = fio_solarisaio_event, 220 .cleanup = fio_solarisaio_cleanup, 221 .open_file = generic_open_file, 222 .close_file = generic_close_file, 223 .get_file_size = generic_get_file_size, 224 }; 225 226 static void fio_init fio_solarisaio_register(void) 227 { 228 register_ioengine(&ioengine); 229 } 230 231 static void fio_exit fio_solarisaio_unregister(void) 232 { 233 unregister_ioengine(&ioengine); 234 } 235