|
13 | 13 | #include "../fio.h" |
14 | 14 | #include "../optgroup.h" |
15 | 15 |
|
| 16 | +enum { |
| 17 | + FIO_POSIXAIO_SUSPEND, |
| 18 | + FIO_POSIXAIO_WAITCOMPLETE, |
| 19 | +}; |
| 20 | + |
16 | 21 | struct posixaio_data { |
17 | 22 | struct io_u **aio_events; |
18 | 23 | unsigned int queued; |
| 24 | + int (*getevents)(struct thread_data *, unsigned int, unsigned int, const struct timespec *); |
19 | 25 | }; |
20 | 26 |
|
21 | 27 | struct posixaio_options { |
22 | 28 | void *pad; |
23 | 29 | unsigned int respect_iodepth_batch_complete_max; |
| 30 | + unsigned int wait; |
24 | 31 | }; |
25 | 32 |
|
26 | 33 | static struct fio_option options[] = { |
27 | 34 | { |
28 | 35 | .name = "posixaio_respect_iodepth_batch_complete_max", |
29 | | - .lname = "Respect iodepth_batch_complete_max", |
| 36 | + .lname = "Respect iodepth_batch_complete_max for wait=aio_suspend", |
30 | 37 | .type = FIO_OPT_BOOL, |
31 | 38 | .off1 = offsetof(struct posixaio_options, respect_iodepth_batch_complete_max), |
32 | | - .help = "Whether to cap batch completion", |
| 39 | + .help = "Whether to cap batch completion for wait=aio_suspend", |
33 | 40 | .def = "0", |
34 | 41 | .category = FIO_OPT_C_ENGINE, |
35 | 42 | .group = FIO_OPT_G_POSIXAIO, |
36 | 43 | }, |
| 44 | + { |
| 45 | + .name = "posixaio_wait", |
| 46 | + .lname = "POSIX AIO wait mechanism", |
| 47 | + .type = FIO_OPT_STR, |
| 48 | + .off1 = offsetof(struct posixaio_options, wait), |
| 49 | + .help = "Select mechanism for waiting for I/O completion", |
| 50 | + .def = "aio_suspend", |
| 51 | + .posval = { |
| 52 | + { .ival = "aio_suspend", |
| 53 | + .oval = FIO_POSIXAIO_SUSPEND, |
| 54 | + .help = "Use aio_suspend()", |
| 55 | + }, |
| 56 | +#ifdef CONFIG_HAVE_AIO_WAITCOMPLETE |
| 57 | + { .ival = "aio_waitcomplete", |
| 58 | + .oval = FIO_POSIXAIO_WAITCOMPLETE, |
| 59 | + .help = "Use aio_waitcomplete()", |
| 60 | + }, |
| 61 | +#endif |
| 62 | + }, |
| 63 | + .category = FIO_OPT_C_ENGINE, |
| 64 | + .group = FIO_OPT_G_POSIXAIO, |
| 65 | + }, |
37 | 66 | { |
38 | 67 | .name = NULL, |
39 | 68 | }, |
@@ -77,10 +106,65 @@ static int fio_posixaio_prep(struct thread_data fio_unused *td, |
77 | 106 | return 0; |
78 | 107 | } |
79 | 108 |
|
| 109 | +#ifdef CONFIG_HAVE_AIO_WAITCOMPLETE |
| 110 | + |
| 111 | +static int fio_posixaio_getevents_waitcomplete(struct thread_data *td, |
| 112 | + unsigned int min, |
| 113 | + unsigned int max, |
| 114 | + const struct timespec *t) |
| 115 | +{ |
| 116 | + struct posixaio_data *pd = td->io_ops_data; |
| 117 | + struct aiocb *aiocb; |
| 118 | + struct io_u *io_u; |
| 119 | + ssize_t retval; |
| 120 | + unsigned int events = 0; |
| 121 | + struct timespec zero_timeout = {0}; |
| 122 | + struct timespec *timeout; |
| 123 | + |
| 124 | + do |
| 125 | + { |
| 126 | + if (events < min) { |
| 127 | + /* Wait until the minimum is satisfied. */ |
| 128 | + timeout = (struct timespec *)t; |
| 129 | + } else { |
| 130 | + /* Consume as many more as we can without waiting. */ |
| 131 | + timeout = &zero_timeout; |
| 132 | + } |
| 133 | + |
| 134 | + retval = aio_waitcomplete(&aiocb, timeout); |
| 135 | + if (retval < 0) { |
| 136 | + if (errno == EINTR) |
| 137 | + continue; |
| 138 | + if (errno == EAGAIN) |
| 139 | + break; |
| 140 | + td_verror(td, errno, "aio_waitcomplete"); |
| 141 | + break; |
| 142 | + } |
| 143 | + |
| 144 | + io_u = container_of(aiocb, struct io_u, aiocb); |
| 145 | + pd->queued--; |
| 146 | + pd->aio_events[events++] = io_u; |
| 147 | + |
| 148 | + if (retval >= 0) |
| 149 | + io_u->resid = io_u->xfer_buflen - retval; |
| 150 | + else if (errno == ECANCELED) |
| 151 | + io_u->resid = io_u->xfer_buflen; |
| 152 | + else |
| 153 | + io_u->error = errno; |
| 154 | + |
| 155 | + } while (events < max && pd->queued > 0); |
| 156 | + |
| 157 | + return events; |
| 158 | +} |
| 159 | + |
| 160 | +#endif |
| 161 | + |
80 | 162 | #define SUSPEND_ENTRIES 8 |
81 | 163 |
|
82 | | -static int fio_posixaio_getevents(struct thread_data *td, unsigned int min, |
83 | | - unsigned int max, const struct timespec *t) |
| 164 | +static int fio_posixaio_getevents_suspend(struct thread_data *td, |
| 165 | + unsigned int min, |
| 166 | + unsigned int max, |
| 167 | + const struct timespec *t) |
84 | 168 | { |
85 | 169 | struct posixaio_data *pd = td->io_ops_data; |
86 | 170 | struct posixaio_options *o = td->eo; |
@@ -152,6 +236,16 @@ static int fio_posixaio_getevents(struct thread_data *td, unsigned int min, |
152 | 236 | goto restart; |
153 | 237 | } |
154 | 238 |
|
| 239 | +static int fio_posixaio_getevents(struct thread_data *td, |
| 240 | + unsigned int min, |
| 241 | + unsigned int max, |
| 242 | + const struct timespec *t) |
| 243 | +{ |
| 244 | + struct posixaio_data *pd = td->io_ops_data; |
| 245 | + |
| 246 | + return pd->getevents(td, min, max, t); |
| 247 | +} |
| 248 | + |
155 | 249 | static struct io_u *fio_posixaio_event(struct thread_data *td, int event) |
156 | 250 | { |
157 | 251 | struct posixaio_data *pd = td->io_ops_data; |
@@ -223,13 +317,29 @@ static void fio_posixaio_cleanup(struct thread_data *td) |
223 | 317 |
|
224 | 318 | static int fio_posixaio_init(struct thread_data *td) |
225 | 319 | { |
| 320 | + struct posixaio_options *o = td->eo; |
226 | 321 | struct posixaio_data *pd = malloc(sizeof(*pd)); |
227 | 322 |
|
228 | 323 | memset(pd, 0, sizeof(*pd)); |
229 | 324 | pd->aio_events = malloc(td->o.iodepth * sizeof(struct io_u *)); |
230 | 325 | memset(pd->aio_events, 0, td->o.iodepth * sizeof(struct io_u *)); |
231 | 326 |
|
| 327 | + switch (o->wait) { |
| 328 | + case FIO_POSIXAIO_SUSPEND: |
| 329 | + pd->getevents = fio_posixaio_getevents_suspend; |
| 330 | + break; |
| 331 | +#ifdef CONFIG_HAVE_AIO_WAITCOMPLETE |
| 332 | + case FIO_POSIXAIO_WAITCOMPLETE: |
| 333 | + pd->getevents = fio_posixaio_getevents_waitcomplete; |
| 334 | + break; |
| 335 | +#endif |
| 336 | + default: |
| 337 | + free(pd); |
| 338 | + return -1; |
| 339 | + } |
| 340 | + |
232 | 341 | td->io_ops_data = pd; |
| 342 | + |
233 | 343 | return 0; |
234 | 344 | } |
235 | 345 |
|
|
0 commit comments