Home | History | Annotate | Download | only in disktest
      1 /*
      2 * Disktest
      3 * Copyright (c) International Business Machines Corp., 2001
      4 *
      5 *
      6 * This program is free software; you can redistribute it and/or modify
      7 * it under the terms of the GNU General Public License as published by
      8 * the Free Software Foundation; either version 2 of the License, or
      9 * (at your option) any later version.
     10 *
     11 * This program is distributed in the hope that it will be useful,
     12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 * GNU General Public License for more details.
     15 *
     16 * You should have received a copy of the GNU General Public License
     17 * along with this program; if not, write to the Free Software
     18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     19 *
     20 *  Please send e-mail to yardleyb (at) us.ibm.com if you have
     21 *  questions or comments.
     22 *
     23 *  Project Website:  TBD
     24 *
     25 * $Id: childmain.c,v 1.11 2009/02/26 12:14:53 subrata_modak Exp $
     26 *
     27 */
     28 
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <stdarg.h>
     32 #include <stdint.h>
     33 #ifdef WINDOWS
     34 #include <windows.h>
     35 #include <winioctl.h>
     36 #include <io.h>
     37 #include <process.h>
     38 #include <sys/stat.h>
     39 #include "getopt.h"
     40 #else
     41 #include <pthread.h>
     42 #include <sys/types.h>
     43 #include <unistd.h>
     44 #endif
     45 #include <signal.h>
     46 #include <time.h>
     47 #include <errno.h>
     48 #include <fcntl.h>
     49 #include <string.h>
     50 #include <ctype.h>
     51 
     52 #include "defs.h"
     53 #include "globals.h"
     54 #include "main.h"
     55 #include "sfunc.h"
     56 #include "threading.h"
     57 #include "io.h"
     58 #include "dump.h"
     59 #include "timer.h"
     60 #include "signals.h"
     61 #include "childmain.h"
     62 
     63 /*
     64  * The following three functions are used to mutex LBAs that are in use by another
     65  * thread from any other thread performing an action on that lba.
     66  */
     67 unsigned short action_in_use(const test_env_t * env, const action_t target)
     68 {
     69 	int i = 0;
     70 
     71 	for (i = 0; i < env->action_list_entry; i++) {
     72 		if ((target.lba == env->action_list[i].lba)	/* attempting same transfer start lba */
     73 		    ||((target.lba < env->action_list[i].lba) && (target.lba + target.trsiz - 1) >= env->action_list[i].lba)	/* attempting transfer over an lba in use */
     74 		    ) {
     75 			/*
     76 			 * The lba(s) we want to do IO to are in use by another thread,
     77 			 * but since POSIX allows for multiple readers, we need to compare
     78 			 * our action with the action being executed by the other thread
     79 			 */
     80 			switch (target.oper) {
     81 			case WRITER:	/* if we want to write, we can't */
     82 				return TRUE;
     83 			case READER:	/* if we want to read, and a write is in progress, we can't */
     84 				if (env->action_list[i].oper == WRITER) {
     85 					return TRUE;
     86 				}
     87 				/* otherwise allow multiple readers */
     88 				return FALSE;
     89 			default:
     90 				/* for all other operations, always assume inuse */
     91 				return TRUE;
     92 			}
     93 		}
     94 	}
     95 
     96 	return FALSE;
     97 }
     98 
     99 void add_action(test_env_t * env, const child_args_t * args,
    100 		const action_t target)
    101 {
    102 
    103 	if (env->action_list_entry == args->t_kids) {	/* we should never get here */
    104 		printf
    105 		    ("ATTEMPT TO ADD MORE ENTRIES TO LBA WRITE LIST THEN ALLOWED, CODE BUG!!!\n");
    106 		abort();
    107 	}
    108 
    109 	env->action_list[env->action_list_entry++] = target;
    110 }
    111 
    112 void remove_action(test_env_t * env, const action_t target)
    113 {
    114 	int i = 0;
    115 
    116 	if (env->action_list_entry == 0) {
    117 		/* we should never get here */
    118 		printf
    119 		    ("ATTEMPT TO REMOVE ENTRIES FROM LBA WRITE LIST WHERE NONE EXIST, CODE BUG!!!\n");
    120 		abort();
    121 	}
    122 
    123 	/* look for the removing target */
    124 	while (target.lba != env->action_list[i].lba) {
    125 		if (env->action_list_entry == i++) {
    126 			printf
    127 			    ("INDEX AND CURRENT LIST ENTRY, CODE BUG!!!!!!\n");
    128 			abort();
    129 		}
    130 	}
    131 
    132 	/* move eny other entries down */
    133 	for (; i < env->action_list_entry - 1; i++) {
    134 		env->action_list[i] = env->action_list[i + 1];
    135 	}
    136 
    137 	/* reduce the slot */
    138 	env->action_list_entry--;
    139 }
    140 
    141 void decrement_io_count(const child_args_t * args, test_env_t * env,
    142 			const action_t target)
    143 {
    144 	if (args->flags & CLD_FLG_LBA_SYNC) {
    145 		remove_action(env, target);
    146 	}
    147 	if (target.oper == WRITER) {
    148 		(env->wcount)--;
    149 	} else {
    150 		(env->rcount)--;
    151 	}
    152 }
    153 
    154 /*
    155  * This function will write a special mark to LBA 0 of
    156  * a target, if an error occured on the target.  This
    157  * is so a trigger can be set, i.e. on an analyser.
    158  */
    159 void write_error_mark(fd_t fd, char *data)
    160 {
    161 	OFF_T ActualBytePos = 0;
    162 	long tcnt = 0;
    163 
    164 	ActualBytePos = Seek(fd, 0);
    165 	if (ActualBytePos != 0) {
    166 		/* could not seek to LBA 0 */
    167 		return;
    168 	}
    169 
    170 	memcpy(data, "DISKTEST ERROR OCCURRED",
    171 	       strlen("DISKTEST ERROR OCCURRED"));
    172 	tcnt = Write(fd, data, BLK_SIZE);
    173 }
    174 
    175 /*
    176  * Sets the test state correctly, and updates test flags
    177  * based on user parsed options
    178  */
    179 void update_test_state(child_args_t * args, test_env_t * env,
    180 		       const int this_thread_id, fd_t fd, char *data)
    181 {
    182 	extern unsigned short glb_run;
    183 	extern unsigned long glb_flags;
    184 
    185 	if (args->flags & CLD_FLG_ALLDIE) {
    186 #ifdef _DEBUG
    187 		PDBG4(DBUG, args,
    188 		      "Thread %d: Setting bContinue to FALSE, io error, all die\n",
    189 		      this_thread_id);
    190 #endif
    191 		args->test_state = SET_STS_FAIL(args->test_state);
    192 		env->bContinue = FALSE;
    193 	}
    194 	if (glb_flags & GLB_FLG_KILL) {
    195 #ifdef _DEBUG
    196 		PDBG4(DBUG, args,
    197 		      "Thread %d: Setting bContinue to FALSE, io error, global die\n",
    198 		      this_thread_id);
    199 #endif
    200 		args->test_state = SET_STS_FAIL(args->test_state);
    201 		env->bContinue = FALSE;
    202 		glb_run = 0;
    203 	}
    204 	if ((args->flags & CLD_FLG_W) && (args->flags & CLD_FLG_ERR_MARK)) {
    205 		write_error_mark(fd, data);
    206 	}
    207 }
    208 
    209 #ifdef _DEBUG
    210 #ifdef _DEBUG_PRINTMAP
    211 void print_lba_bitmap(const test_env_t * env)
    212 {
    213 	unsigned char *wbitmap = (unsigned char *)env->shared_mem + BMP_OFFSET;
    214 	int i;
    215 
    216 	for (i = 0; i < (env->bmp_siz - 1); i++) {
    217 		printf("%02x", *(wbitmap + i));
    218 	}
    219 	printf("\n");
    220 }
    221 #endif
    222 #endif
    223 
    224 action_t get_next_action(child_args_t * args, test_env_t * env,
    225 			 const OFF_T mask)
    226 {
    227 
    228 	OFF_T *pVal1 = (OFF_T *) env->shared_mem;
    229 	OFF_T *tmpLBA;
    230 	OFF_T guessLBA;
    231 	unsigned char *wbitmap = (unsigned char *)env->shared_mem + BMP_OFFSET;
    232 
    233 	short blk_written = 0;
    234 	unsigned long i;
    235 	action_t target = { NONE, 0, 0 };
    236 	short direct = 0;
    237 
    238 	/* pick an operation */
    239 	target.oper = env->lastAction.oper;
    240 	if ((args->flags & CLD_FLG_LINEAR) && !(args->flags & CLD_FLG_NTRLVD)) {
    241 		target.oper = TST_OPER(args->test_state);
    242 	} else if ((args->flags & CLD_FLG_RANDOM)
    243 		   && !(args->flags & CLD_FLG_NTRLVD)) {
    244 		if ((((env->wcount) * 100) /
    245 		     (((env->rcount) + 1) + (env->wcount))) >= (args->wperc)) {
    246 			target.oper = READER;
    247 		} else {
    248 			target.oper = WRITER;
    249 		}
    250 #ifdef _DEBUG
    251 		PDBG4(DBUG, args, "W:%.2f%% R:%.2f%%\n",
    252 		      100 * ((double)(env->wcount) /
    253 			     ((double)env->rcount + (double)env->wcount)),
    254 		      100 * ((double)(env->rcount) /
    255 			     ((double)env->wcount + (double)env->rcount)));
    256 #endif
    257 	} else if ((args->flags & CLD_FLG_NTRLVD)
    258 		   && !TST_wFST_TIME(args->test_state)) {
    259 		if ((args->flags & CLD_FLG_R) && (args->flags & CLD_FLG_W)) {
    260 			target.oper =
    261 			    (env->lastAction.oper == WRITER) ? READER : WRITER;
    262 		}
    263 	} else if (target.oper == NONE) {
    264 		/* if still no decision for an operation, do the basics */
    265 		target.oper = (args->flags & CLD_FLG_W) ? WRITER : READER;
    266 	}
    267 
    268 	/* pick a transfer length */
    269 	if (!(args->flags & CLD_FLG_RTRSIZ)) {
    270 		target.trsiz = args->ltrsiz;
    271 	} else {
    272 		if ((args->flags & CLD_FLG_NTRLVD) &&
    273 		    (args->flags & CLD_FLG_W) &&
    274 		    (args->flags & CLD_FLG_R) &&
    275 		    (env->lastAction.trsiz != 0) && (target.oper == READER)) {
    276 			target.trsiz = env->lastAction.trsiz;
    277 		} else {
    278 			do {
    279 				target.trsiz = (rand() & 0xFFF) + args->ltrsiz;
    280 				if ((args->flags & CLD_FLG_SKS)
    281 				    && (((env->wcount) + (env->rcount)) >=
    282 					args->seeks))
    283 					break;
    284 			} while (target.trsiz > args->htrsiz);
    285 		}
    286 	}
    287 
    288 	/* pick an lba */
    289 	if (args->start_blk == args->stop_blk) {	/* diskcache test */
    290 		target.lba = args->start_lba + args->offset;
    291 	} else if (args->flags & CLD_FLG_LINEAR) {
    292 		tmpLBA =
    293 		    (target.oper ==
    294 		     WRITER) ? pVal1 + OFF_WLBA : pVal1 + OFF_RLBA;
    295 		direct = (TST_DIRCTN(args->test_state)) ? 1 : -1;
    296 		if ((target.oper == WRITER) && TST_wFST_TIME(args->test_state)) {
    297 			*(tmpLBA) = args->start_lba + args->offset;
    298 		} else if ((target.oper == READER)
    299 			   && TST_rFST_TIME(args->test_state)) {
    300 			*(tmpLBA) = args->start_lba + args->offset;
    301 		} else if ((TST_DIRCTN(args->test_state))
    302 			   && ((*(tmpLBA) + (target.trsiz - 1)) <=
    303 			       args->stop_lba)) {
    304 		} else if (!(TST_DIRCTN(args->test_state))
    305 			   && (*(tmpLBA) >= (args->start_lba + args->offset))) {
    306 		} else {
    307 			if (args->flags & CLD_FLG_LUNU) {
    308 				*(tmpLBA) = args->start_lba + args->offset;
    309 				if ((args->flags & CLD_FLG_CYC)
    310 				    && (target.oper == WRITER)) {
    311 					target.oper = NONE;
    312 				}
    313 			} else if (args->flags & CLD_FLG_LUND) {
    314 				args->test_state = DIRCT_CNG(args->test_state);
    315 				direct =
    316 				    (TST_DIRCTN(args->test_state)) ? 1 : -1;
    317 				*(tmpLBA) +=
    318 				    (OFF_T) direct *(OFF_T) target.trsiz;
    319 				if ((args->flags & CLD_FLG_CYC) && (direct > 0)) {
    320 					target.oper = NONE;
    321 				}
    322 			}
    323 		}
    324 		target.lba = *(tmpLBA);
    325 	} else if (args->flags & CLD_FLG_RANDOM) {
    326 		if ((args->flags & CLD_FLG_NTRLVD)
    327 		    && (args->flags & CLD_FLG_W)
    328 		    && (args->flags & CLD_FLG_R)
    329 		    && (target.oper == READER)) {
    330 			target.lba = env->lastAction.lba;
    331 		} else {
    332 			do {
    333 				target.lba =
    334 				    (Rand64() & mask) + args->start_lba;
    335 			} while (target.lba > args->stop_lba);
    336 
    337 			guessLBA =
    338 			    ALIGN(target.lba, target.trsiz) + args->offset;
    339 			if (guessLBA > args->stop_lba) {
    340 				target.lba = guessLBA = args->stop_lba;
    341 			}
    342 			if (target.lba != guessLBA) {
    343 				if ((target.lba - guessLBA) <=
    344 				    ((guessLBA + target.trsiz) - target.lba)) {
    345 					target.lba = guessLBA;
    346 				} else if ((guessLBA + target.trsiz) >
    347 					   args->stop_lba) {
    348 					target.lba = guessLBA;
    349 				} else {
    350 					target.lba = guessLBA + target.trsiz;
    351 				}
    352 			}
    353 			if ((target.lba + (target.trsiz - 1)) > args->stop_lba) {
    354 				target.lba -= target.trsiz;
    355 			}
    356 		}
    357 	}
    358 	if ((args->flags & CLD_FLG_LBA_SYNC) && (action_in_use(env, target))) {
    359 		target.oper = RETRY;
    360 	}
    361 
    362 	if (!(args->flags & CLD_FLG_NTRLVD)
    363 	    && !(args->flags & CLD_FLG_RANDOM)
    364 	    && (args->flags & CLD_FLG_W)
    365 	    && (args->flags & CLD_FLG_R)) {
    366 		if (((target.oper == WRITER) ? env->wcount : env->rcount) >=
    367 		    (args->seeks / 2)) {
    368 			target.oper = NONE;
    369 		}
    370 	}
    371 
    372 	/* get out if exceeded one of the following */
    373 	if ((args->flags & CLD_FLG_SKS)
    374 	    && (((env->wcount) + (env->rcount)) >= args->seeks)) {
    375 		target.oper = NONE;
    376 	}
    377 
    378 	/*
    379 	 * check the bitmask to see if we can read,
    380 	 * if the bitmask is set for the block of LBAs,
    381 	 * then we are OK to read
    382 	 *
    383 	 * only matters of error checking or write once
    384 	 */
    385 	blk_written = 1;
    386 	if (args->flags & (CLD_FLG_CMPR | CLD_FLG_WRITE_ONCE)) {
    387 		for (i = 0; i < target.trsiz; i++) {
    388 			if ((*
    389 			     (wbitmap +
    390 			      (((target.lba - args->offset - args->start_lba) +
    391 				i) / 8)) & (0x80 >> (((target.lba -
    392 						       args->offset -
    393 						       args->start_lba) +
    394 						      i) % 8))) == 0) {
    395 				blk_written = 0;
    396 				break;
    397 			}
    398 		}
    399 	}
    400 
    401 	/* get out, nothing to do */
    402 	if ((target.oper == NONE) || (target.oper == RETRY)) ;
    403 	/* get out, read only, or not comparing */
    404 	else if (!(args->flags & CLD_FLG_W)) ;
    405 	/* get out, we are a writer, write once enabled, and block not written */
    406 	else if ((target.oper == WRITER) && (args->flags & CLD_FLG_WRITE_ONCE)
    407 		 && !blk_written) ;
    408 	/* get out, we are a writer and not write once */
    409 	else if ((target.oper == WRITER)
    410 		 && !(args->flags & CLD_FLG_WRITE_ONCE)) ;
    411 	/* get out, we are a reader, and blocks written */
    412 	else if ((target.oper == READER) && blk_written) ;
    413 	else if ((args->flags & CLD_FLG_LINEAR)
    414 		 || ((args->flags & CLD_FLG_NTRLVD)
    415 		     && (args->flags & CLD_FLG_RANDOM))) {
    416 		if (!blk_written) {
    417 			/*
    418 			 * if we are linear and not interleaved and on the read pass
    419 			 * with random transfer sizes, and we hit the limit of the
    420 			 * random write transfer lengths, because blk_written was
    421 			 * false, then we cannot do any more reads unless we start
    422 			 * over at start_lba+offset.
    423 			 */
    424 			if ((args->flags & CLD_FLG_LINEAR) &&
    425 			    !(args->flags & CLD_FLG_NTRLVD) &&
    426 			    (args->flags & CLD_FLG_RTRSIZ) &&
    427 			    (target.oper == READER)) {
    428 				tmpLBA = pVal1 + OFF_RLBA;
    429 				*(tmpLBA) = args->start_lba + args->offset;
    430 				target.lba = *(tmpLBA);
    431 			} else {
    432 				/*
    433 				 * we must retry, as we can't start the read, since the write
    434 				 * has not happened yet.
    435 				 */
    436 				target.oper = RETRY;
    437 			}
    438 		}
    439 	} else if ((target.oper == READER) && (args->flags & CLD_FLG_CMPR)
    440 		   && !blk_written) {
    441 		/* should have been a random reader, but blk not written, and running with compare, so make me a writer */
    442 		target.oper = WRITER;
    443 		args->test_state = SET_OPER_W(args->test_state);
    444 		/* if we switched to a writer, then we have to check action_in_use again */
    445 		if ((args->flags & CLD_FLG_LBA_SYNC)
    446 		    && (action_in_use(env, target))) {
    447 			target.oper = RETRY;
    448 		}
    449 	} else {
    450 		/* should have been a random writer, but blk already written, so make me a reader */
    451 		target.oper = READER;
    452 		args->test_state = SET_OPER_R(args->test_state);
    453 		/* if we switched to a reader, then no need to check action_in_use again */
    454 	}
    455 
    456 #ifdef _DEBUG
    457 #ifdef WINDOWS
    458 	PDBG5(DBUG, args, "%I64d, %I64d, %I64d, %I64d\n", env->wcount,
    459 	      env->rcount, args->seeks, args->stop_lba);
    460 #else
    461 	PDBG5(DBUG, args, "%lld, %lld, %lld, %lld\n", env->wcount, env->rcount,
    462 	      args->seeks, args->stop_lba);
    463 #endif
    464 #endif
    465 
    466 	if (target.oper == WRITER) {
    467 		(env->wcount)++;
    468 		if ((args->flags & CLD_FLG_LUND))
    469 			*(pVal1 + OFF_RLBA) = *(pVal1 + OFF_WLBA);
    470 		*(pVal1 + OFF_WLBA) += (OFF_T) direct *(OFF_T) target.trsiz;
    471 		if (TST_wFST_TIME(args->test_state))
    472 			args->test_state = CLR_wFST_TIME(args->test_state);
    473 		env->lastAction = target;
    474 		if (args->flags & CLD_FLG_LBA_SYNC) {
    475 			add_action(env, args, target);
    476 		}
    477 	}
    478 	if (target.oper == READER) {
    479 		(env->rcount)++;
    480 		*(pVal1 + OFF_RLBA) += (OFF_T) direct *(OFF_T) target.trsiz;
    481 		if (TST_rFST_TIME(args->test_state))
    482 			args->test_state = CLR_rFST_TIME(args->test_state);
    483 		env->lastAction = target;
    484 		if (args->flags & CLD_FLG_LBA_SYNC) {
    485 			add_action(env, args, target);
    486 		}
    487 	}
    488 
    489 	return target;
    490 }
    491 
    492 void miscompare_dump(const child_args_t * args, const char *data,
    493 		     const size_t buf_len, OFF_T tPosition, const size_t offset,
    494 		     mc_func_t oper, const int this_thread_id)
    495 {
    496 	FILE *fpDumpFile;
    497 	char obuff[80];
    498 
    499 	obuff[0] = 0;
    500 	sprintf(obuff, "dump_%d.dat", args->pid);
    501 	fpDumpFile = fopen(obuff, "a");
    502 
    503 	if (oper == EXP) {
    504 		if (fpDumpFile)
    505 			fprintf(fpDumpFile, "\n\n\n");
    506 		if (fpDumpFile)
    507 			fprintf(fpDumpFile, "Execution string: %s\n",
    508 				args->argstr);
    509 		if (fpDumpFile)
    510 			fprintf(fpDumpFile, "Target: %s\n", args->device);
    511 		if (fpDumpFile)
    512 			fprintf(fpDumpFile, DMSTR, this_thread_id, tPosition,
    513 				tPosition);
    514 		if (fpDumpFile)
    515 			fprintf(fpDumpFile, DMOFFSTR, this_thread_id, offset,
    516 				offset);
    517 		pMsg(ERR, args, "EXPECTED:\n");
    518 		if (fpDumpFile)
    519 			fprintf(fpDumpFile, DMFILESTR, "EXPECTED", args->device,
    520 				tPosition, offset);
    521 	} else if (oper == ACT) {
    522 		pMsg(ERR, args, "ACTUAL:\n");
    523 		if (fpDumpFile)
    524 			fprintf(fpDumpFile, DMFILESTR, "ACTUAL", args->device,
    525 				tPosition, offset);
    526 	} else if (oper == REREAD) {
    527 		pMsg(ERR, args, "REREAD ACTUAL:\n");
    528 		if (fpDumpFile)
    529 			fprintf(fpDumpFile, DMFILESTR, "REREAD ACTUAL",
    530 				args->device, tPosition, offset);
    531 	}
    532 
    533 	dump_data(stdout, data, 16, 16, offset, FMT_STR);
    534 	if (fpDumpFile)
    535 		dump_data(fpDumpFile, data, buf_len, 16, 0, FMT_STR);
    536 	if (fpDumpFile)
    537 		fclose(fpDumpFile);
    538 }
    539 
    540 /*
    541  * called after all the checks have been made to verify
    542  * that the io completed successfully.
    543  */
    544 void complete_io(test_env_t * env, const child_args_t * args,
    545 		 const action_t target)
    546 {
    547 	unsigned char *wbitmap = (unsigned char *)env->shared_mem + BMP_OFFSET;
    548 	int i = 0;
    549 
    550 	if (target.oper == WRITER) {
    551 		(env->hbeat_stats.wbytes) += target.trsiz * BLK_SIZE;
    552 		env->hbeat_stats.wcount++;
    553 		for (i = 0; i < target.trsiz; i++) {
    554 			*(wbitmap +
    555 			  (((target.lba - args->offset - args->start_lba) +
    556 			    i) / 8)) |=
    557 		  0x80 >> (((target.lba - args->offset - args->start_lba) + i) %
    558 			   8);
    559 		}
    560 	} else {
    561 		(env->hbeat_stats.rbytes) += target.trsiz * BLK_SIZE;
    562 		env->hbeat_stats.rcount++;
    563 	}
    564 	if (args->flags & CLD_FLG_LBA_SYNC) {
    565 		remove_action(env, target);
    566 	}
    567 }
    568 
    569 /*
    570 * This function is really the main function for a thread
    571 * Once here, this function will act as if it
    572 * were 'main' for that thread.
    573 */
    574 #ifdef WINDOWS
    575 DWORD WINAPI ChildMain(test_ll_t * test)
    576 #else
    577 void *ChildMain(void *vtest)
    578 #endif
    579 {
    580 #ifndef WINDOWS
    581 	test_ll_t *test = (test_ll_t *) vtest;
    582 #endif
    583 
    584 	child_args_t *args = test->args;
    585 	test_env_t *env = test->env;
    586 
    587 	static int thread_id = 0;
    588 	int this_thread_id = thread_id++;
    589 	char *buf1 = NULL, *buffer1 = NULL;	/* 'buf' is the aligned 'buffer' */
    590 	char *buf2 = NULL, *buffer2 = NULL;	/* 'buf' is the aligned 'buffer' */
    591 	unsigned long ulLastError;
    592 	unsigned long delayTime;
    593 
    594 	action_t target = { NONE, 0, 0 };
    595 	unsigned int i;
    596 	OFF_T ActualBytePos = 0, TargetBytePos = 0, mask = 1, delayMask = 1;
    597 	long tcnt = 0;
    598 	int exit_code = 0, rv = 0;
    599 	char filespec[DEV_NAME_LEN];
    600 	fd_t fd;
    601 
    602 	unsigned int retries = 0;
    603 	BOOL is_retry = FALSE;
    604 	lvl_t msg_level = WARN;
    605 	int SET_CHAR = 0;	/* when data buffers are cleared, using memset, use this */
    606 
    607 	extern unsigned long glb_flags;
    608 	extern unsigned short glb_run;
    609 	extern int signal_action;
    610 
    611 #ifdef WINDOWS
    612 	HANDLE MutexMISCOMP;
    613 
    614 	if ((MutexMISCOMP = OpenMutex(SYNCHRONIZE, TRUE, "gbl")) == NULL) {
    615 		pMsg(ERR, args,
    616 		     "Thread %d: Failed to open semaphore, error = %u\n",
    617 		     this_thread_id, GetLastError());
    618 		args->test_state = SET_STS_FAIL(args->test_state);
    619 		TEXIT(GETLASTERROR());
    620 	}
    621 #else
    622 	static pthread_mutex_t MutexMISCOMP = PTHREAD_MUTEX_INITIALIZER;
    623 #endif
    624 
    625 	/*
    626 	 * For some messages, the error level will change, based on if
    627 	 * the test should continue on error, or stop on error.
    628 	 */
    629 	if ((args->flags & CLD_FLG_ALLDIE) || (glb_flags & GLB_FLG_KILL)) {
    630 		msg_level = ERR;
    631 	}
    632 
    633 	target.oper = TST_OPER(args->test_state);
    634 
    635 	strncpy(filespec, args->device, DEV_NAME_LEN);
    636 
    637 	fd = Open(filespec, args->flags);
    638 	if (INVALID_FD(fd)) {
    639 		pMsg(ERR, args, "Thread %d: could not open %s, errno = %u.\n",
    640 		     this_thread_id, args->device, GETLASTERROR());
    641 		args->test_state = SET_STS_FAIL(args->test_state);
    642 		TEXIT((uintptr_t) GETLASTERROR());
    643 	}
    644 
    645 	/* Create aligned memory buffers for sending IO. */
    646 	if ((buffer1 =
    647 	     (char *)ALLOC(((args->htrsiz * BLK_SIZE) + ALIGNSIZE))) == NULL) {
    648 		pMsg(ERR, args,
    649 		     "Thread %d: Memory allocation failure for IO buffer, errno = %u\n",
    650 		     this_thread_id, GETLASTERROR());
    651 		args->test_state = SET_STS_FAIL(args->test_state);
    652 		CLOSE(fd);
    653 		TEXIT((uintptr_t) GETLASTERROR());
    654 	}
    655 	memset(buffer1, SET_CHAR, ((args->htrsiz * BLK_SIZE) + ALIGNSIZE));
    656 	buf1 = (char *)BUFALIGN(buffer1);
    657 
    658 	if ((buffer2 =
    659 	     (char *)ALLOC(((args->htrsiz * BLK_SIZE) + ALIGNSIZE))) == NULL) {
    660 		pMsg(ERR, args,
    661 		     "Thread %d: Memory allocation failure for IO buffer, errno = %u\n",
    662 		     this_thread_id, GETLASTERROR());
    663 		FREE(buffer1);
    664 		args->test_state = SET_STS_FAIL(args->test_state);
    665 		CLOSE(fd);
    666 		TEXIT((uintptr_t) GETLASTERROR());
    667 	}
    668 	memset(buffer2, SET_CHAR, ((args->htrsiz * BLK_SIZE) + ALIGNSIZE));
    669 	buf2 = (char *)BUFALIGN(buffer2);
    670 
    671 	/*  set up lba mask of all 1's with value between vsiz and 2*vsiz */
    672 	while (mask <= (args->stop_lba - args->start_lba)) {
    673 		mask = mask << 1;
    674 	}
    675 	mask -= 1;
    676 
    677 	/*  set up delay mask of all 1's with value between delayTimeMin and 2*delayTimeMax */
    678 	while (delayMask <= (args->delayTimeMax - args->delayTimeMin)) {
    679 		delayMask = delayMask << 1;
    680 	}
    681 	delayMask -= 1;
    682 
    683 	while (env->bContinue) {
    684 		if (!is_retry) {
    685 			retries = args->retries;
    686 #ifdef _DEBUG
    687 			PDBG5(DBUG, args,
    688 			      "Thread %d: lastAction: oper: %d, lba: %lld, trsiz: %ld\n",
    689 			      this_thread_id, target.oper, target.lba,
    690 			      target.trsiz);
    691 #endif
    692 			do {
    693 				if (signal_action & SIGNAL_STOP) {
    694 					break;
    695 				}	/* user request to stop */
    696 				if (glb_run == 0) {
    697 					break;
    698 				}	/* global request to stop */
    699 				LOCK(env->mutexs.MutexACTION);
    700 				target = get_next_action(args, env, mask);
    701 				UNLOCK(env->mutexs.MutexACTION);
    702 				/* this thread has to retry, so give up the reset of my time slice */
    703 				if (target.oper == RETRY) {
    704 					Sleep(0);
    705 				}
    706 			} while ((env->bContinue) && (target.oper == RETRY));	/* we failed to get an action, and were asked to retry */
    707 
    708 #ifdef _DEBUG
    709 			PDBG5(DBUG, args,
    710 			      "Thread %d: nextAction: oper: %d, lba: %lld, trsiz: %ld\n",
    711 			      this_thread_id, target.oper, target.lba,
    712 			      target.trsiz);
    713 #endif
    714 
    715 			/*
    716 			 * Delay delayTime msecs before continuing, for simulated
    717 			 * processing time, requested by user
    718 			 */
    719 
    720 			if (args->delayTimeMin == args->delayTimeMax) {	/* static delay time */
    721 				/* only sleep if delay is greater then zero */
    722 				if (args->delayTimeMin > 0) {
    723 					Sleep(args->delayTimeMin);
    724 				}
    725 			} else {	/* random delay time between min & max */
    726 				do {
    727 					delayTime =
    728 					    (unsigned long)(rand() & delayMask)
    729 					    + args->delayTimeMin;
    730 				} while (delayTime > args->delayTimeMax);
    731 #ifdef _DEBUG
    732 				PDBG3(DBUG, args,
    733 				      "Thread %d: Delay time = %lu\n",
    734 				      this_thread_id, delayTime);
    735 #endif
    736 				Sleep(delayTime);
    737 			}
    738 		}
    739 #ifdef _DEBUG
    740 		if (target.oper == NONE) {	/* nothing left to do */
    741 			PDBG3(DBUG, args,
    742 			      "Thread %d: Setting break, oper is NONE\n",
    743 			      this_thread_id);
    744 		}
    745 #endif
    746 
    747 		if (target.oper == NONE) {
    748 			break;
    749 		}		/* nothing left so stop */
    750 		if (signal_action & SIGNAL_STOP) {
    751 			break;
    752 		}		/* user request to stop */
    753 		if (env->bContinue == FALSE) {
    754 			break;
    755 		}		/* internal request to stop */
    756 		if (glb_run == 0) {
    757 			break;
    758 		}
    759 		/* global request to stop */
    760 		TargetBytePos = (OFF_T) (target.lba * BLK_SIZE);
    761 		ActualBytePos = Seek(fd, TargetBytePos);
    762 		if (ActualBytePos != TargetBytePos) {
    763 			ulLastError = GETLASTERROR();
    764 			pMsg(msg_level, args, SFSTR, this_thread_id,
    765 			     (target.oper ==
    766 			      WRITER) ? (env->wcount) : (env->rcount),
    767 			     target.lba, TargetBytePos, ActualBytePos,
    768 			     ulLastError);
    769 			if (retries-- > 1) {	/* request to retry on error, decrement retry */
    770 				pMsg(INFO, args,
    771 				     "Thread %d: Retry after seek failure, retry count: %u\n",
    772 				     this_thread_id, retries);
    773 				is_retry = TRUE;
    774 				Sleep(args->retry_delay);
    775 			} else {
    776 				exit_code = SEEK_FAILURE;
    777 				is_retry = FALSE;
    778 				LOCK(env->mutexs.MutexACTION);
    779 				update_test_state(args, env, this_thread_id, fd,
    780 						  buf2);
    781 				decrement_io_count(args, env, target);
    782 				UNLOCK(env->mutexs.MutexACTION);
    783 			}
    784 			continue;
    785 		}
    786 
    787 		if (target.oper == WRITER) {
    788 			if (args->flags & CLD_FLG_LPTYPE) {
    789 				fill_buffer(buf2, target.trsiz, &(target.lba),
    790 					    sizeof(OFF_T), CLD_FLG_LPTYPE);
    791 			} else {
    792 				memcpy(buf2, env->data_buffer,
    793 				       target.trsiz * BLK_SIZE);
    794 			}
    795 			if (args->flags & CLD_FLG_MBLK) {
    796 				mark_buffer(buf2, target.trsiz * BLK_SIZE,
    797 					    &(target.lba), args, env);
    798 			}
    799 #ifdef _DEBUG
    800 			setStartTime();
    801 #endif
    802 			if (args->flags & CLD_FLG_IO_SERIAL) {
    803 				LOCK(env->mutexs.MutexIO);
    804 				tcnt = Write(fd, buf2, target.trsiz * BLK_SIZE);
    805 				UNLOCK(env->mutexs.MutexIO);
    806 			} else {
    807 				tcnt = Write(fd, buf2, target.trsiz * BLK_SIZE);
    808 			}
    809 
    810 #ifdef _DEBUG
    811 			setEndTime();
    812 			PDBG5(DBUG, args, "Thread %d: I/O Time: %ld usecs\n",
    813 			      this_thread_id, getTimeDiff());
    814 #endif
    815 			if (args->flags & CLD_FLG_WFSYNC) {
    816 				rv = 0;
    817 				/* if need to sync, then only have one thread do it */
    818 				LOCK(env->mutexs.MutexACTION);
    819 				if (0 ==
    820 				    (env->hbeat_stats.wcount %
    821 				     args->sync_interval)) {
    822 #ifdef _DEBUG
    823 					PDBG3(DBUG, args,
    824 					      "Thread %d: Performing sync, write IO count %llu\n",
    825 					      this_thread_id,
    826 					      env->hbeat_stats.wcount);
    827 #endif
    828 					rv = Sync(fd);
    829 					if (0 != rv) {
    830 						exit_code = GETLASTERROR();
    831 						pMsg(msg_level, args,
    832 						     "Thread %d: fsync error = %d\n",
    833 						     this_thread_id, exit_code);
    834 						is_retry = FALSE;
    835 						update_test_state(args, env,
    836 								  this_thread_id,
    837 								  fd, buf2);
    838 						decrement_io_count(args, env,
    839 								   target);
    840 					}
    841 				}
    842 				UNLOCK(env->mutexs.MutexACTION);
    843 
    844 				if (0 != rv) {	/* sync error, so don't count the write */
    845 					continue;
    846 				}
    847 			}
    848 		}
    849 
    850 		if (target.oper == READER) {
    851 			memset(buf1, SET_CHAR, target.trsiz * BLK_SIZE);
    852 #ifdef _DEBUG
    853 			setStartTime();
    854 #endif
    855 			if (args->flags & CLD_FLG_IO_SERIAL) {
    856 				LOCK(env->mutexs.MutexIO);
    857 				tcnt = Read(fd, buf1, target.trsiz * BLK_SIZE);
    858 				UNLOCK(env->mutexs.MutexIO);
    859 			} else {
    860 				tcnt = Read(fd, buf1, target.trsiz * BLK_SIZE);
    861 			}
    862 #ifdef _DEBUG
    863 			setEndTime();
    864 			PDBG5(DBUG, args, "Thread %d: I/O Time: %ld usecs\n",
    865 			      this_thread_id, getTimeDiff());
    866 #endif
    867 		}
    868 
    869 		if (tcnt != (long)target.trsiz * BLK_SIZE) {
    870 			ulLastError = GETLASTERROR();
    871 			pMsg(msg_level, args, AFSTR, this_thread_id,
    872 			     (target.oper) ? "Read" : "Write",
    873 			     (target.oper) ? (env->rcount) : (env->wcount),
    874 			     target.lba, target.lba, tcnt,
    875 			     target.trsiz * BLK_SIZE, ulLastError);
    876 			if (retries-- > 1) {	/* request to retry on error, decrement retry */
    877 				pMsg(INFO, args,
    878 				     "Thread %d: Retry after transfer failure, retry count: %u\n",
    879 				     this_thread_id, retries);
    880 				is_retry = TRUE;
    881 				Sleep(args->retry_delay);
    882 			} else {
    883 				exit_code = ACCESS_FAILURE;
    884 				is_retry = FALSE;
    885 				LOCK(env->mutexs.MutexACTION);
    886 				update_test_state(args, env, this_thread_id, fd,
    887 						  buf2);
    888 				decrement_io_count(args, env, target);
    889 				UNLOCK(env->mutexs.MutexACTION);
    890 			}
    891 			continue;
    892 		}
    893 
    894 		/* data compare routine.  Act as if we were to write, but just compare */
    895 		if ((target.oper == READER) && (args->flags & CLD_FLG_CMPR)) {
    896 			/* This is very SLOW!!! */
    897 			if ((args->cmp_lng == 0)
    898 			    || (args->cmp_lng > target.trsiz * BLK_SIZE)) {
    899 				args->cmp_lng = target.trsiz * BLK_SIZE;
    900 			}
    901 			if (args->flags & CLD_FLG_LPTYPE) {
    902 				fill_buffer(buf2, target.trsiz, &(target.lba),
    903 					    sizeof(OFF_T), CLD_FLG_LPTYPE);
    904 			} else {
    905 				memcpy(buf2, env->data_buffer,
    906 				       target.trsiz * BLK_SIZE);
    907 			}
    908 			if (args->flags & CLD_FLG_MBLK) {
    909 				mark_buffer(buf2, target.trsiz * BLK_SIZE,
    910 					    &(target.lba), args, env);
    911 			}
    912 			if (memcmp(buf2, buf1, args->cmp_lng) != 0) {
    913 				/* data miscompare, this takes lots of time, but its OK... !!! */
    914 				LOCK(MutexMISCOMP);
    915 				pMsg(ERR, args, DMSTR, this_thread_id,
    916 				     target.lba, target.lba);
    917 				/* find the actual byte that started the miscompare */
    918 				for (i = 0; i < args->htrsiz * BLK_SIZE; i++) {
    919 					if (*(buf2 + i) != *(buf1 + i)) {
    920 						pMsg(ERR, args, DMOFFSTR,
    921 						     this_thread_id, i, i);
    922 						break;
    923 					}
    924 				}
    925 				miscompare_dump(args, buf2,
    926 						args->htrsiz * BLK_SIZE,
    927 						target.lba, i, EXP,
    928 						this_thread_id);
    929 				miscompare_dump(args, buf1,
    930 						args->htrsiz * BLK_SIZE,
    931 						target.lba, i, ACT,
    932 						this_thread_id);
    933 				/* perform a reread of the target, if requested */
    934 				if (args->flags & CLD_FLG_ERR_REREAD) {
    935 					ActualBytePos = Seek(fd, TargetBytePos);
    936 					if (ActualBytePos == TargetBytePos) {
    937 						memset(buf1, SET_CHAR,
    938 						       target.trsiz * BLK_SIZE);
    939 #ifdef _DEBUG
    940 						setStartTime();
    941 #endif
    942 						tcnt =
    943 						    Read(fd, buf1,
    944 							 target.trsiz *
    945 							 BLK_SIZE);
    946 #ifdef _DEBUG
    947 						setEndTime();
    948 						PDBG5(DBUG, args,
    949 						      "Thread %d: ReRead I/O Time: %ld usecs\n",
    950 						      this_thread_id,
    951 						      getTimeDiff());
    952 #endif
    953 						if (tcnt !=
    954 						    (long)target.trsiz *
    955 						    BLK_SIZE) {
    956 							pMsg(ERR, args,
    957 							     "Thread %d: ReRead after data miscompare failed on transfer.\n",
    958 							     this_thread_id);
    959 							pMsg(ERR, args, AFSTR,
    960 							     this_thread_id,
    961 							     "ReRead",
    962 							     (target.
    963 							      oper) ? (env->
    964 								       rcount)
    965 							     : (env->wcount),
    966 							     target.lba,
    967 							     target.lba, tcnt,
    968 							     target.trsiz *
    969 							     BLK_SIZE);
    970 						}
    971 						miscompare_dump(args, buf1,
    972 								args->htrsiz *
    973 								BLK_SIZE,
    974 								target.lba, i,
    975 								REREAD,
    976 								this_thread_id);
    977 					} else {
    978 						pMsg(ERR, args,
    979 						     "Thread %d: ReRead after data miscompare failed on seek.\n",
    980 						     this_thread_id);
    981 						pMsg(ERR, args, SFSTR,
    982 						     this_thread_id,
    983 						     (target.oper ==
    984 						      WRITER) ? (env->
    985 								 wcount)
    986 						     : (env->rcount),
    987 						     target.lba, TargetBytePos,
    988 						     ActualBytePos);
    989 					}
    990 				}
    991 				UNLOCK(MutexMISCOMP);
    992 
    993 				exit_code = DATA_MISCOMPARE;
    994 				is_retry = FALSE;
    995 				LOCK(env->mutexs.MutexACTION);
    996 				update_test_state(args, env, this_thread_id, fd,
    997 						  buf2);
    998 				decrement_io_count(args, env, target);
    999 				UNLOCK(env->mutexs.MutexACTION);
   1000 				continue;
   1001 			}
   1002 		}
   1003 
   1004 		/* update stats, bitmap, and release LBA */
   1005 		LOCK(env->mutexs.MutexACTION);
   1006 		complete_io(env, args, target);
   1007 		UNLOCK(env->mutexs.MutexACTION);
   1008 
   1009 		is_retry = FALSE;
   1010 	}
   1011 
   1012 #ifdef _DEBUG
   1013 #ifdef _DEBUG_PRINTMAP
   1014 	LOCK(env->mutexs.MutexACTION);
   1015 	print_lba_bitmap(env);
   1016 	UNLOCK(env->mutexs.MutexACTION);
   1017 #endif
   1018 #endif
   1019 
   1020 	FREE(buffer1);
   1021 	FREE(buffer2);
   1022 
   1023 	if ((args->flags & CLD_FLG_W) && !(args->flags & CLD_FLG_RAW)) {
   1024 #ifdef _DEBUG
   1025 		PDBG5(DBUG, args, "Thread %d: starting sync\n", this_thread_id);
   1026 #endif
   1027 		if (Sync(fd) < 0) {	/* just sync, should not matter the device type */
   1028 			exit_code = GETLASTERROR();
   1029 			pMsg(ERR, args, "Thread %d: fsync error = %d\n",
   1030 			     this_thread_id, exit_code);
   1031 			args->test_state = SET_STS_FAIL(args->test_state);
   1032 		}
   1033 #ifdef _DEBUG
   1034 		PDBG5(DBUG, args, "Thread %d: finished sync\n", this_thread_id);
   1035 #endif
   1036 	}
   1037 
   1038 	if (CLOSE(fd) < 0) {	/* check return status on close */
   1039 		exit_code = GETLASTERROR();
   1040 		pMsg(ERR, args, "Thread %d: close error = %d\n", this_thread_id,
   1041 		     exit_code);
   1042 		args->test_state = SET_STS_FAIL(args->test_state);
   1043 	}
   1044 
   1045 	TEXIT((uintptr_t) exit_code);
   1046 }
   1047