Home | History | Annotate | Download | only in engines
      1 /*
      2  * windowsaio engine
      3  *
      4  * IO engine using Windows IO Completion Ports.
      5  */
      6 
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <unistd.h>
     10 #include <signal.h>
     11 #include <errno.h>
     12 
     13 #include "../fio.h"
     14 
     15 typedef BOOL (WINAPI *CANCELIOEX)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
     16 
     17 int geterrno_from_win_error (DWORD code, int deferrno);
     18 
     19 struct fio_overlapped {
     20 	OVERLAPPED o;
     21 	struct io_u *io_u;
     22 	BOOL io_complete;
     23 };
     24 
     25 struct windowsaio_data {
     26 	struct io_u **aio_events;
     27 	HANDLE iocp;
     28 	HANDLE iothread;
     29 	HANDLE iocomplete_event;
     30 	BOOL iothread_running;
     31 };
     32 
     33 struct thread_ctx {
     34 	HANDLE iocp;
     35 	struct windowsaio_data *wd;
     36 };
     37 
     38 static BOOL timeout_expired(DWORD start_count, DWORD end_count);
     39 static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min,
     40 				unsigned int max, const struct timespec *t);
     41 static struct io_u *fio_windowsaio_event(struct thread_data *td, int event);
     42 static int fio_windowsaio_queue(struct thread_data *td,
     43 				  struct io_u *io_u);
     44 static void fio_windowsaio_cleanup(struct thread_data *td);
     45 static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter);
     46 static int fio_windowsaio_init(struct thread_data *td);
     47 static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f);
     48 static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f);
     49 
     50 static int fio_windowsaio_init(struct thread_data *td)
     51 {
     52 	struct windowsaio_data *wd;
     53 	int rc = 0;
     54 
     55 	wd = calloc(1, sizeof(struct windowsaio_data));
     56 	if (wd == NULL) {
     57 		 log_err("windowsaio: failed to allocate memory for engine data\n");
     58 		rc = 1;
     59 	}
     60 
     61 	if (!rc) {
     62 		wd->aio_events = malloc(td->o.iodepth * sizeof(struct io_u*));
     63 		if (wd->aio_events == NULL) {
     64 			log_err("windowsaio: failed to allocate memory for aio events list\n");
     65 			rc = 1;
     66 		}
     67 	}
     68 
     69 	if (!rc) {
     70 		/* Create an auto-reset event */
     71 		wd->iocomplete_event = CreateEvent(NULL, FALSE, FALSE, NULL);
     72 		if (wd->iocomplete_event == NULL) {
     73 			log_err("windowsaio: failed to create io complete event handle\n");
     74 			rc = 1;
     75 		}
     76 	}
     77 
     78 	if (rc) {
     79 		if (wd != NULL) {
     80 			if (wd->aio_events != NULL)
     81 				free(wd->aio_events);
     82 
     83 			free(wd);
     84 		}
     85 	}
     86 
     87 	td->io_ops->data = wd;
     88 
     89 	if (!rc) {
     90 		struct thread_ctx *ctx;
     91 		struct windowsaio_data *wd;
     92 		HANDLE hFile;
     93 
     94 		hFile = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
     95 		if (hFile == INVALID_HANDLE_VALUE) {
     96 			log_err("windowsaio: failed to create io completion port\n");
     97 			rc = 1;
     98 		}
     99 
    100 		wd = td->io_ops->data;
    101 		wd->iothread_running = TRUE;
    102 		wd->iocp = hFile;
    103 
    104 		if (!rc)
    105 			ctx = malloc(sizeof(struct thread_ctx));
    106 
    107 		if (!rc && ctx == NULL)
    108 		{
    109 			log_err("windowsaio: failed to allocate memory for thread context structure\n");
    110 			CloseHandle(hFile);
    111 			rc = 1;
    112 		}
    113 
    114 		if (!rc)
    115 		{
    116 			ctx->iocp = hFile;
    117 			ctx->wd = wd;
    118 			wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, NULL);
    119 			if (wd->iothread == NULL)
    120 				log_err("windowsaio: failed to create io completion thread\n");
    121 		}
    122 
    123 		if (rc || wd->iothread == NULL)
    124 			rc = 1;
    125 	}
    126 
    127 	return rc;
    128 }
    129 
    130 static void fio_windowsaio_cleanup(struct thread_data *td)
    131 {
    132 	struct windowsaio_data *wd;
    133 
    134 	wd = td->io_ops->data;
    135 
    136 	if (wd != NULL) {
    137 		wd->iothread_running = FALSE;
    138 		WaitForSingleObject(wd->iothread, INFINITE);
    139 
    140 		CloseHandle(wd->iothread);
    141 		CloseHandle(wd->iocomplete_event);
    142 
    143 		free(wd->aio_events);
    144 		free(wd);
    145 
    146 		td->io_ops->data = NULL;
    147 	}
    148 }
    149 
    150 
    151 static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f)
    152 {
    153 	int rc = 0;
    154 	DWORD flags = FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_OVERLAPPED;
    155 	DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    156 	DWORD openmode = OPEN_ALWAYS;
    157 	DWORD access;
    158 
    159 	dprint(FD_FILE, "fd open %s\n", f->file_name);
    160 
    161 	if (f->filetype == FIO_TYPE_PIPE) {
    162 		log_err("windowsaio: pipes are not supported\n");
    163 		return 1;
    164 	}
    165 
    166 	if (!strcmp(f->file_name, "-")) {
    167 		log_err("windowsaio: can't read/write to stdin/out\n");
    168 		return 1;
    169 	}
    170 
    171 	if (td->o.odirect)
    172 		flags |= FILE_FLAG_NO_BUFFERING;
    173 	if (td->o.sync_io)
    174 		flags |= FILE_FLAG_WRITE_THROUGH;
    175 
    176 	/*
    177 	 * Inform Windows whether we're going to be doing sequential or
    178 	 * random io so it can tune the Cache Manager
    179 	 */
    180 	if (td->o.td_ddir == TD_DDIR_READ  ||
    181 		td->o.td_ddir == TD_DDIR_WRITE)
    182 		flags |= FILE_FLAG_SEQUENTIAL_SCAN;
    183 	else
    184 		flags |= FILE_FLAG_RANDOM_ACCESS;
    185 
    186 	if (!td_write(td) || read_only)
    187 		access = GENERIC_READ;
    188 	else
    189 		access = (GENERIC_READ | GENERIC_WRITE);
    190 
    191 	if (td->o.create_on_open)
    192 		openmode = OPEN_ALWAYS;
    193 	else
    194 		openmode = OPEN_EXISTING;
    195 
    196 	f->hFile = CreateFile(f->file_name, access, sharemode,
    197 		NULL, openmode, flags, NULL);
    198 
    199 	if (f->hFile == INVALID_HANDLE_VALUE) {
    200 		log_err("windowsaio: failed to open file \"%s\"\n", f->file_name);
    201 		rc = 1;
    202 	}
    203 
    204 	/* Only set up the completion port and thread if we're not just
    205 	 * querying the device size */
    206 	if (!rc && td->io_ops->data != NULL) {
    207 		struct windowsaio_data *wd;
    208 
    209 		wd = td->io_ops->data;
    210 
    211 		if (CreateIoCompletionPort(f->hFile, wd->iocp, 0, 0) == NULL) {
    212 			log_err("windowsaio: failed to create io completion port\n");
    213 			rc = 1;
    214 		}
    215 	}
    216 
    217 	return rc;
    218 }
    219 
    220 static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f)
    221 {
    222 	int rc = 0;
    223 
    224 	dprint(FD_FILE, "fd close %s\n", f->file_name);
    225 
    226 	if (f->hFile != INVALID_HANDLE_VALUE) {
    227 		if (!CloseHandle(f->hFile)) {
    228 			log_info("windowsaio: failed to close file handle for \"%s\"\n", f->file_name);
    229 			rc = 1;
    230 		}
    231 	}
    232 
    233 	f->hFile = INVALID_HANDLE_VALUE;
    234 	return rc;
    235 }
    236 
    237 static BOOL timeout_expired(DWORD start_count, DWORD end_count)
    238 {
    239 	BOOL expired = FALSE;
    240 	DWORD current_time;
    241 
    242 	current_time = GetTickCount();
    243 
    244 	if ((end_count > start_count) && current_time >= end_count)
    245 		expired = TRUE;
    246 	else if (current_time < start_count && current_time > end_count)
    247 		expired = TRUE;
    248 
    249 	return expired;
    250 }
    251 
    252 static struct io_u* fio_windowsaio_event(struct thread_data *td, int event)
    253 {
    254 	struct windowsaio_data *wd = td->io_ops->data;
    255 	return wd->aio_events[event];
    256 }
    257 
    258 static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min,
    259 				    unsigned int max,
    260 				    const struct timespec *t)
    261 {
    262 	struct windowsaio_data *wd = td->io_ops->data;
    263 	unsigned int dequeued = 0;
    264 	struct io_u *io_u;
    265 	int i;
    266 	struct fio_overlapped *fov;
    267 	DWORD start_count = 0;
    268 	DWORD end_count = 0;
    269 	DWORD status;
    270 	DWORD mswait = 250;
    271 
    272 	if (t != NULL) {
    273 		mswait = (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
    274 		start_count = GetTickCount();
    275 		end_count = start_count + (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
    276 	}
    277 
    278 	do {
    279 		io_u_qiter(&td->io_u_all, io_u, i) {
    280 			if (!(io_u->flags & IO_U_F_FLIGHT))
    281 				continue;
    282 
    283 			fov = (struct fio_overlapped*)io_u->engine_data;
    284 
    285 			if (fov->io_complete) {
    286 				fov->io_complete = FALSE;
    287 				ResetEvent(fov->o.hEvent);
    288 				wd->aio_events[dequeued] = io_u;
    289 				dequeued++;
    290 			}
    291 
    292 			if (dequeued >= min)
    293 				break;
    294 		}
    295 
    296 		if (dequeued < min) {
    297 			status = WaitForSingleObject(wd->iocomplete_event, mswait);
    298 			if (status != WAIT_OBJECT_0 && dequeued >= min)
    299 				break;
    300 		}
    301 
    302 		if (dequeued >= min || (t != NULL && timeout_expired(start_count, end_count)))
    303 			break;
    304 	} while (1);
    305 
    306 	return dequeued;
    307 }
    308 
    309 static int fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u)
    310 {
    311 	struct fio_overlapped *o = io_u->engine_data;
    312 	LPOVERLAPPED lpOvl = &o->o;
    313 	DWORD iobytes;
    314 	BOOL success = FALSE;
    315 	int rc = FIO_Q_COMPLETED;
    316 
    317 	fio_ro_check(td, io_u);
    318 
    319 	lpOvl->Internal = STATUS_PENDING;
    320 	lpOvl->InternalHigh = 0;
    321 	lpOvl->Offset = io_u->offset & 0xFFFFFFFF;
    322 	lpOvl->OffsetHigh = io_u->offset >> 32;
    323 
    324 	switch (io_u->ddir) {
    325 	case DDIR_WRITE:
    326 		success = WriteFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &iobytes, lpOvl);
    327 		break;
    328 	case DDIR_READ:
    329 		success = ReadFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &iobytes, lpOvl);
    330 		break;
    331 	case DDIR_SYNC:
    332 	case DDIR_DATASYNC:
    333 	case DDIR_SYNC_FILE_RANGE:
    334 		success = FlushFileBuffers(io_u->file->hFile);
    335 		if (!success) {
    336 			log_err("windowsaio: failed to flush file buffers\n");
    337 			io_u->error = win_to_posix_error(GetLastError());
    338 		}
    339 
    340 		return FIO_Q_COMPLETED;
    341 		break;
    342 	case DDIR_TRIM:
    343 		log_err("windowsaio: manual TRIM isn't supported on Windows\n");
    344 		io_u->error = 1;
    345 		io_u->resid = io_u->xfer_buflen;
    346 		return FIO_Q_COMPLETED;
    347 		break;
    348 	default:
    349 		assert(0);
    350 		break;
    351 	}
    352 
    353 	if (success || GetLastError() == ERROR_IO_PENDING)
    354 		rc = FIO_Q_QUEUED;
    355 	else {
    356 		io_u->error = win_to_posix_error(GetLastError());
    357 		io_u->resid = io_u->xfer_buflen;
    358 	}
    359 
    360 	return rc;
    361 }
    362 
    363 /* Runs as a thread and waits for queued IO to complete */
    364 static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter)
    365 {
    366 	OVERLAPPED *ovl;
    367 	struct fio_overlapped *fov;
    368 	struct io_u *io_u;
    369 	struct windowsaio_data *wd;
    370 	struct thread_ctx *ctx;
    371 	ULONG_PTR ulKey = 0;
    372 	DWORD bytes;
    373 
    374 	ctx = (struct thread_ctx*)lpParameter;
    375 	wd = ctx->wd;
    376 
    377 	do {
    378 		if (!GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey, &ovl, 250) && ovl == NULL)
    379 			continue;
    380 
    381 		fov = CONTAINING_RECORD(ovl, struct fio_overlapped, o);
    382 		io_u = fov->io_u;
    383 
    384 		if (ovl->Internal == ERROR_SUCCESS) {
    385 			io_u->resid = io_u->xfer_buflen - ovl->InternalHigh;
    386 			io_u->error = 0;
    387 		} else {
    388 			io_u->resid = io_u->xfer_buflen;
    389 			io_u->error = win_to_posix_error(GetLastError());
    390 		}
    391 
    392 		fov->io_complete = TRUE;
    393 		SetEvent(wd->iocomplete_event);
    394 	} while (ctx->wd->iothread_running);
    395 
    396 	CloseHandle(ctx->iocp);
    397 	free(ctx);
    398 	return 0;
    399 }
    400 
    401 static void fio_windowsaio_io_u_free(struct thread_data *td, struct io_u *io_u)
    402 {
    403 	struct fio_overlapped *o = io_u->engine_data;
    404 
    405 	if (o) {
    406 		CloseHandle(o->o.hEvent);
    407 		io_u->engine_data = NULL;
    408 		free(o);
    409 	}
    410 }
    411 
    412 static int fio_windowsaio_io_u_init(struct thread_data *td, struct io_u *io_u)
    413 {
    414 	struct fio_overlapped *o;
    415 
    416 	o = malloc(sizeof(*o));
    417 	o->io_complete = FALSE;
    418 	o->io_u = io_u;
    419 	o->o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    420 	if (o->o.hEvent == NULL) {
    421 		log_err("windowsaio: failed to create event handle\n");
    422 		free(o);
    423 		return 1;
    424 	}
    425 
    426 	io_u->engine_data = o;
    427 	return 0;
    428 }
    429 
    430 static struct ioengine_ops ioengine = {
    431 	.name		= "windowsaio",
    432 	.version	= FIO_IOOPS_VERSION,
    433 	.init		= fio_windowsaio_init,
    434 	.queue		= fio_windowsaio_queue,
    435 	.getevents	= fio_windowsaio_getevents,
    436 	.event		= fio_windowsaio_event,
    437 	.cleanup	= fio_windowsaio_cleanup,
    438 	.open_file	= fio_windowsaio_open_file,
    439 	.close_file	= fio_windowsaio_close_file,
    440 	.get_file_size	= generic_get_file_size,
    441 	.io_u_init	= fio_windowsaio_io_u_init,
    442 	.io_u_free	= fio_windowsaio_io_u_free,
    443 };
    444 
    445 static void fio_init fio_windowsaio_register(void)
    446 {
    447 	register_ioengine(&ioengine);
    448 }
    449 
    450 static void fio_exit fio_windowsaio_unregister(void)
    451 {
    452 	unregister_ioengine(&ioengine);
    453 }
    454