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, 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, struct timespec *t)
    260 {
    261 	struct windowsaio_data *wd = td->io_ops->data;
    262 	unsigned int dequeued = 0;
    263 	struct io_u *io_u;
    264 	int i;
    265 	struct fio_overlapped *fov;
    266 	DWORD start_count = 0;
    267 	DWORD end_count = 0;
    268 	DWORD status;
    269 	DWORD mswait = 250;
    270 
    271 	if (t != NULL) {
    272 		mswait = (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
    273 		start_count = GetTickCount();
    274 		end_count = start_count + (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
    275 	}
    276 
    277 	do {
    278 		io_u_qiter(&td->io_u_all, io_u, i) {
    279 			if (!(io_u->flags & IO_U_F_FLIGHT))
    280 				continue;
    281 
    282 			fov = (struct fio_overlapped*)io_u->engine_data;
    283 
    284 			if (fov->io_complete) {
    285 				fov->io_complete = FALSE;
    286 				ResetEvent(fov->o.hEvent);
    287 				wd->aio_events[dequeued] = io_u;
    288 				dequeued++;
    289 			}
    290 
    291 			if (dequeued >= min)
    292 				break;
    293 		}
    294 
    295 		if (dequeued < min) {
    296 			status = WaitForSingleObject(wd->iocomplete_event, mswait);
    297 			if (status != WAIT_OBJECT_0 && dequeued >= min)
    298 				break;
    299 		}
    300 
    301 		if (dequeued >= min || (t != NULL && timeout_expired(start_count, end_count)))
    302 			break;
    303 	} while (1);
    304 
    305 	return dequeued;
    306 }
    307 
    308 static int fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u)
    309 {
    310 	struct fio_overlapped *o = io_u->engine_data;
    311 	LPOVERLAPPED lpOvl = &o->o;
    312 	DWORD iobytes;
    313 	BOOL success = FALSE;
    314 	int rc = FIO_Q_COMPLETED;
    315 
    316 	fio_ro_check(td, io_u);
    317 
    318 	lpOvl->Internal = STATUS_PENDING;
    319 	lpOvl->InternalHigh = 0;
    320 	lpOvl->Offset = io_u->offset & 0xFFFFFFFF;
    321 	lpOvl->OffsetHigh = io_u->offset >> 32;
    322 
    323 	switch (io_u->ddir) {
    324 	case DDIR_WRITE:
    325 		success = WriteFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &iobytes, lpOvl);
    326 		break;
    327 	case DDIR_READ:
    328 		success = ReadFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, &iobytes, lpOvl);
    329 		break;
    330 	case DDIR_SYNC:
    331 	case DDIR_DATASYNC:
    332 	case DDIR_SYNC_FILE_RANGE:
    333 		success = FlushFileBuffers(io_u->file->hFile);
    334 		if (!success) {
    335 			log_err("windowsaio: failed to flush file buffers\n");
    336 			io_u->error = win_to_posix_error(GetLastError());
    337 		}
    338 
    339 		return FIO_Q_COMPLETED;
    340 		break;
    341 	case DDIR_TRIM:
    342 		log_err("windowsaio: manual TRIM isn't supported on Windows\n");
    343 		io_u->error = 1;
    344 		io_u->resid = io_u->xfer_buflen;
    345 		return FIO_Q_COMPLETED;
    346 		break;
    347 	default:
    348 		assert(0);
    349 		break;
    350 	}
    351 
    352 	if (success || GetLastError() == ERROR_IO_PENDING)
    353 		rc = FIO_Q_QUEUED;
    354 	else {
    355 		io_u->error = win_to_posix_error(GetLastError());
    356 		io_u->resid = io_u->xfer_buflen;
    357 	}
    358 
    359 	return rc;
    360 }
    361 
    362 /* Runs as a thread and waits for queued IO to complete */
    363 static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter)
    364 {
    365 	OVERLAPPED *ovl;
    366 	struct fio_overlapped *fov;
    367 	struct io_u *io_u;
    368 	struct windowsaio_data *wd;
    369 	struct thread_ctx *ctx;
    370 	ULONG_PTR ulKey = 0;
    371 	DWORD bytes;
    372 
    373 	ctx = (struct thread_ctx*)lpParameter;
    374 	wd = ctx->wd;
    375 
    376 	do {
    377 		if (!GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey, &ovl, 250) && ovl == NULL)
    378 			continue;
    379 
    380 		fov = CONTAINING_RECORD(ovl, struct fio_overlapped, o);
    381 		io_u = fov->io_u;
    382 
    383 		if (ovl->Internal == ERROR_SUCCESS) {
    384 			io_u->resid = io_u->xfer_buflen - ovl->InternalHigh;
    385 			io_u->error = 0;
    386 		} else {
    387 			io_u->resid = io_u->xfer_buflen;
    388 			io_u->error = win_to_posix_error(GetLastError());
    389 		}
    390 
    391 		fov->io_complete = TRUE;
    392 		SetEvent(wd->iocomplete_event);
    393 	} while (ctx->wd->iothread_running);
    394 
    395 	CloseHandle(ctx->iocp);
    396 	free(ctx);
    397 	return 0;
    398 }
    399 
    400 static void fio_windowsaio_io_u_free(struct thread_data *td, struct io_u *io_u)
    401 {
    402 	struct fio_overlapped *o = io_u->engine_data;
    403 
    404 	if (o) {
    405 		CloseHandle(o->o.hEvent);
    406 		io_u->engine_data = NULL;
    407 		free(o);
    408 	}
    409 }
    410 
    411 static int fio_windowsaio_io_u_init(struct thread_data *td, struct io_u *io_u)
    412 {
    413 	struct fio_overlapped *o;
    414 
    415 	o = malloc(sizeof(*o));
    416 	o->io_complete = FALSE;
    417 	o->io_u = io_u;
    418 	o->o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    419 	if (o->o.hEvent == NULL) {
    420 		log_err("windowsaio: failed to create event handle\n");
    421 		free(o);
    422 		return 1;
    423 	}
    424 
    425 	io_u->engine_data = o;
    426 	return 0;
    427 }
    428 
    429 static struct ioengine_ops ioengine = {
    430 	.name		= "windowsaio",
    431 	.version	= FIO_IOOPS_VERSION,
    432 	.init		= fio_windowsaio_init,
    433 	.queue		= fio_windowsaio_queue,
    434 	.getevents	= fio_windowsaio_getevents,
    435 	.event		= fio_windowsaio_event,
    436 	.cleanup	= fio_windowsaio_cleanup,
    437 	.open_file	= fio_windowsaio_open_file,
    438 	.close_file	= fio_windowsaio_close_file,
    439 	.get_file_size	= generic_get_file_size,
    440 	.io_u_init	= fio_windowsaio_io_u_init,
    441 	.io_u_free	= fio_windowsaio_io_u_free,
    442 };
    443 
    444 static void fio_init fio_windowsaio_register(void)
    445 {
    446 	register_ioengine(&ioengine);
    447 }
    448 
    449 static void fio_exit fio_windowsaio_unregister(void)
    450 {
    451 	unregister_ioengine(&ioengine);
    452 }
    453