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 			DWORD threadid;
    117 
    118 			ctx->iocp = hFile;
    119 			ctx->wd = wd;
    120 			wd->iothread = CreateThread(NULL, 0, IoCompletionRoutine, ctx, 0, &threadid);
    121 
    122 			if (wd->iothread != NULL)
    123 				fio_setaffinity(threadid, td->o.cpumask);
    124 			else
    125 				log_err("windowsaio: failed to create io completion thread\n");
    126 		}
    127 
    128 		if (rc || wd->iothread == NULL)
    129 			rc = 1;
    130 	}
    131 
    132 	return rc;
    133 }
    134 
    135 static void fio_windowsaio_cleanup(struct thread_data *td)
    136 {
    137 	struct windowsaio_data *wd;
    138 
    139 	wd = td->io_ops_data;
    140 
    141 	if (wd != NULL) {
    142 		wd->iothread_running = FALSE;
    143 		WaitForSingleObject(wd->iothread, INFINITE);
    144 
    145 		CloseHandle(wd->iothread);
    146 		CloseHandle(wd->iocomplete_event);
    147 
    148 		free(wd->aio_events);
    149 		free(wd);
    150 
    151 		td->io_ops_data = NULL;
    152 	}
    153 }
    154 
    155 
    156 static int fio_windowsaio_open_file(struct thread_data *td, struct fio_file *f)
    157 {
    158 	int rc = 0;
    159 	DWORD flags = FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_OVERLAPPED;
    160 	DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    161 	DWORD openmode = OPEN_ALWAYS;
    162 	DWORD access;
    163 
    164 	dprint(FD_FILE, "fd open %s\n", f->file_name);
    165 
    166 	if (f->filetype == FIO_TYPE_PIPE) {
    167 		log_err("windowsaio: pipes are not supported\n");
    168 		return 1;
    169 	}
    170 
    171 	if (!strcmp(f->file_name, "-")) {
    172 		log_err("windowsaio: can't read/write to stdin/out\n");
    173 		return 1;
    174 	}
    175 
    176 	if (td->o.odirect)
    177 		flags |= FILE_FLAG_NO_BUFFERING;
    178 	if (td->o.sync_io)
    179 		flags |= FILE_FLAG_WRITE_THROUGH;
    180 
    181 	/*
    182 	 * Inform Windows whether we're going to be doing sequential or
    183 	 * random io so it can tune the Cache Manager
    184 	 */
    185 	if (td->o.td_ddir == TD_DDIR_READ  ||
    186 		td->o.td_ddir == TD_DDIR_WRITE)
    187 		flags |= FILE_FLAG_SEQUENTIAL_SCAN;
    188 	else
    189 		flags |= FILE_FLAG_RANDOM_ACCESS;
    190 
    191 	if (!td_write(td) || read_only)
    192 		access = GENERIC_READ;
    193 	else
    194 		access = (GENERIC_READ | GENERIC_WRITE);
    195 
    196 	if (td->o.create_on_open)
    197 		openmode = OPEN_ALWAYS;
    198 	else
    199 		openmode = OPEN_EXISTING;
    200 
    201 	f->hFile = CreateFile(f->file_name, access, sharemode,
    202 		NULL, openmode, flags, NULL);
    203 
    204 	if (f->hFile == INVALID_HANDLE_VALUE) {
    205 		log_err("windowsaio: failed to open file \"%s\"\n", f->file_name);
    206 		rc = 1;
    207 	}
    208 
    209 	/* Only set up the completion port and thread if we're not just
    210 	 * querying the device size */
    211 	if (!rc && td->io_ops_data != NULL) {
    212 		struct windowsaio_data *wd;
    213 
    214 		wd = td->io_ops_data;
    215 
    216 		if (CreateIoCompletionPort(f->hFile, wd->iocp, 0, 0) == NULL) {
    217 			log_err("windowsaio: failed to create io completion port\n");
    218 			rc = 1;
    219 		}
    220 	}
    221 
    222 	return rc;
    223 }
    224 
    225 static int fio_windowsaio_close_file(struct thread_data fio_unused *td, struct fio_file *f)
    226 {
    227 	int rc = 0;
    228 
    229 	dprint(FD_FILE, "fd close %s\n", f->file_name);
    230 
    231 	if (f->hFile != INVALID_HANDLE_VALUE) {
    232 		if (!CloseHandle(f->hFile)) {
    233 			log_info("windowsaio: failed to close file handle for \"%s\"\n", f->file_name);
    234 			rc = 1;
    235 		}
    236 	}
    237 
    238 	f->hFile = INVALID_HANDLE_VALUE;
    239 	return rc;
    240 }
    241 
    242 static BOOL timeout_expired(DWORD start_count, DWORD end_count)
    243 {
    244 	BOOL expired = FALSE;
    245 	DWORD current_time;
    246 
    247 	current_time = GetTickCount();
    248 
    249 	if ((end_count > start_count) && current_time >= end_count)
    250 		expired = TRUE;
    251 	else if (current_time < start_count && current_time > end_count)
    252 		expired = TRUE;
    253 
    254 	return expired;
    255 }
    256 
    257 static struct io_u* fio_windowsaio_event(struct thread_data *td, int event)
    258 {
    259 	struct windowsaio_data *wd = td->io_ops_data;
    260 	return wd->aio_events[event];
    261 }
    262 
    263 static int fio_windowsaio_getevents(struct thread_data *td, unsigned int min,
    264 				    unsigned int max,
    265 				    const struct timespec *t)
    266 {
    267 	struct windowsaio_data *wd = td->io_ops_data;
    268 	unsigned int dequeued = 0;
    269 	struct io_u *io_u;
    270 	int i;
    271 	struct fio_overlapped *fov;
    272 	DWORD start_count = 0;
    273 	DWORD end_count = 0;
    274 	DWORD status;
    275 	DWORD mswait = 250;
    276 
    277 	if (t != NULL) {
    278 		mswait = (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
    279 		start_count = GetTickCount();
    280 		end_count = start_count + (t->tv_sec * 1000) + (t->tv_nsec / 1000000);
    281 	}
    282 
    283 	do {
    284 		io_u_qiter(&td->io_u_all, io_u, i) {
    285 			if (!(io_u->flags & IO_U_F_FLIGHT))
    286 				continue;
    287 
    288 			fov = (struct fio_overlapped*)io_u->engine_data;
    289 
    290 			if (fov->io_complete) {
    291 				fov->io_complete = FALSE;
    292 				wd->aio_events[dequeued] = io_u;
    293 				dequeued++;
    294 			}
    295 
    296 		}
    297 		if (dequeued >= min)
    298 			break;
    299 
    300 		if (dequeued < min) {
    301 			status = WaitForSingleObject(wd->iocomplete_event, mswait);
    302 			if (status != WAIT_OBJECT_0 && dequeued >= min)
    303 				break;
    304 		}
    305 
    306 		if (dequeued >= min || (t != NULL && timeout_expired(start_count, end_count)))
    307 			break;
    308 	} while (1);
    309 
    310 	return dequeued;
    311 }
    312 
    313 static int fio_windowsaio_queue(struct thread_data *td, struct io_u *io_u)
    314 {
    315 	struct fio_overlapped *o = io_u->engine_data;
    316 	LPOVERLAPPED lpOvl = &o->o;
    317 	BOOL success = FALSE;
    318 	int rc = FIO_Q_COMPLETED;
    319 
    320 	fio_ro_check(td, io_u);
    321 
    322 	lpOvl->Internal = 0;
    323 	lpOvl->InternalHigh = 0;
    324 	lpOvl->Offset = io_u->offset & 0xFFFFFFFF;
    325 	lpOvl->OffsetHigh = io_u->offset >> 32;
    326 
    327 	switch (io_u->ddir) {
    328 	case DDIR_WRITE:
    329 		success = WriteFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, NULL, lpOvl);
    330 		break;
    331 	case DDIR_READ:
    332 		success = ReadFile(io_u->file->hFile, io_u->xfer_buf, io_u->xfer_buflen, NULL, lpOvl);
    333 		break;
    334 	case DDIR_SYNC:
    335 	case DDIR_DATASYNC:
    336 	case DDIR_SYNC_FILE_RANGE:
    337 		success = FlushFileBuffers(io_u->file->hFile);
    338 		if (!success) {
    339 			log_err("windowsaio: failed to flush file buffers\n");
    340 			io_u->error = win_to_posix_error(GetLastError());
    341 		}
    342 
    343 		return FIO_Q_COMPLETED;
    344 		break;
    345 	case DDIR_TRIM:
    346 		log_err("windowsaio: manual TRIM isn't supported on Windows\n");
    347 		io_u->error = 1;
    348 		io_u->resid = io_u->xfer_buflen;
    349 		return FIO_Q_COMPLETED;
    350 		break;
    351 	default:
    352 		assert(0);
    353 		break;
    354 	}
    355 
    356 	if (success || GetLastError() == ERROR_IO_PENDING)
    357 		rc = FIO_Q_QUEUED;
    358 	else {
    359 		io_u->error = win_to_posix_error(GetLastError());
    360 		io_u->resid = io_u->xfer_buflen;
    361 	}
    362 
    363 	return rc;
    364 }
    365 
    366 /* Runs as a thread and waits for queued IO to complete */
    367 static DWORD WINAPI IoCompletionRoutine(LPVOID lpParameter)
    368 {
    369 	OVERLAPPED *ovl;
    370 	struct fio_overlapped *fov;
    371 	struct io_u *io_u;
    372 	struct windowsaio_data *wd;
    373 	struct thread_ctx *ctx;
    374 	ULONG_PTR ulKey = 0;
    375 	DWORD bytes;
    376 
    377 	ctx = (struct thread_ctx*)lpParameter;
    378 	wd = ctx->wd;
    379 
    380 	do {
    381 		if (!GetQueuedCompletionStatus(ctx->iocp, &bytes, &ulKey, &ovl, 250) && ovl == NULL)
    382 			continue;
    383 
    384 		fov = CONTAINING_RECORD(ovl, struct fio_overlapped, o);
    385 		io_u = fov->io_u;
    386 
    387 		if (ovl->Internal == ERROR_SUCCESS) {
    388 			io_u->resid = io_u->xfer_buflen - ovl->InternalHigh;
    389 			io_u->error = 0;
    390 		} else {
    391 			io_u->resid = io_u->xfer_buflen;
    392 			io_u->error = win_to_posix_error(GetLastError());
    393 		}
    394 
    395 		fov->io_complete = TRUE;
    396 		SetEvent(wd->iocomplete_event);
    397 	} while (ctx->wd->iothread_running);
    398 
    399 	CloseHandle(ctx->iocp);
    400 	free(ctx);
    401 	return 0;
    402 }
    403 
    404 static void fio_windowsaio_io_u_free(struct thread_data *td, struct io_u *io_u)
    405 {
    406 	struct fio_overlapped *o = io_u->engine_data;
    407 
    408 	if (o) {
    409 		io_u->engine_data = NULL;
    410 		free(o);
    411 	}
    412 }
    413 
    414 static int fio_windowsaio_io_u_init(struct thread_data *td, struct io_u *io_u)
    415 {
    416 	struct fio_overlapped *o;
    417 
    418 	o = malloc(sizeof(*o));
    419 	o->io_complete = FALSE;
    420 	o->io_u = io_u;
    421 	o->o.hEvent = NULL;
    422 	io_u->engine_data = o;
    423 	return 0;
    424 }
    425 
    426 static struct ioengine_ops ioengine = {
    427 	.name		= "windowsaio",
    428 	.version	= FIO_IOOPS_VERSION,
    429 	.init		= fio_windowsaio_init,
    430 	.queue		= fio_windowsaio_queue,
    431 	.getevents	= fio_windowsaio_getevents,
    432 	.event		= fio_windowsaio_event,
    433 	.cleanup	= fio_windowsaio_cleanup,
    434 	.open_file	= fio_windowsaio_open_file,
    435 	.close_file	= fio_windowsaio_close_file,
    436 	.get_file_size	= generic_get_file_size,
    437 	.io_u_init	= fio_windowsaio_io_u_init,
    438 	.io_u_free	= fio_windowsaio_io_u_free,
    439 };
    440 
    441 static void fio_init fio_windowsaio_register(void)
    442 {
    443 	register_ioengine(&ioengine);
    444 }
    445 
    446 static void fio_exit fio_windowsaio_unregister(void)
    447 {
    448 	unregister_ioengine(&ioengine);
    449 }
    450