/* * Disktest * Copyright (c) International Business Machines Corp., 2001 * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Please send e-mail to yardleyb@us.ibm.com if you have * questions or comments. * * Project Website: TBD * * $Id: main.c,v 1.11 2009/02/26 12:14:53 subrata_modak Exp $ * */ #include <stdio.h> #ifdef WINDOWS #include <windows.h> #include <winioctl.h> #include <io.h> #include <process.h> #include <sys/stat.h> #include "getopt.h" #else #include <pthread.h> #include <sys/types.h> #include <unistd.h> #endif #include <stdlib.h> #include <stdarg.h> #include <stdint.h> #include <signal.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <ctype.h> #include "defs.h" #include "globals.h" #include "main.h" #include "usage.h" #include "sfunc.h" #include "parse.h" #include "childmain.h" #include "threading.h" #include "dump.h" #include "timer.h" #include "stats.h" #include "signals.h" /* global */ child_args_t cleanArgs; test_env_t cleanEnv; char hostname[HOSTNAME_SIZE]; /* global system hostname */ void linear_read_write_test(test_ll_t * test) { OFF_T *pVal1 = (OFF_T *) test->env->shared_mem; int i; if (test->args->flags & CLD_FLG_W) { test->env->bContinue = TRUE; *(pVal1 + OFF_WLBA) = test->args->start_lba; test->args->test_state = DIRCT_INC(test->args->test_state); test->env->lastAction.oper = WRITER; test->args->test_state = SET_OPER_W(test->args->test_state); test->args->test_state = SET_wFST_TIME(test->args->test_state); // srand(test->args->seed); /* reseed so we can re create the same random transfers */ memset(test->env->action_list, 0, sizeof(action_t) * test->args->t_kids); test->env->action_list_entry = 0; test->env->wcount = 0; test->env->rcount = 0; if (test->args->flags & CLD_FLG_CYC) if (test->args->cycles == 0) { pMsg(INFO, test->args, "Starting write pass, cycle %lu\n", (unsigned long)test->env->pass_count); } else { pMsg(INFO, test->args, "Starting write pass, cycle %lu of %lu\n", (unsigned long)test->env->pass_count, test->args->cycles); } else { pMsg(INFO, test->args, "Starting write pass\n"); } CreateTestChild(ChildTimer, test); for (i = 0; i < test->args->t_kids; i++) { CreateTestChild(ChildMain, test); } /* Wait for the writers to finish */ cleanUpTestChildren(test); } /* If the write test failed don't start the read test */ if (!(TST_STS(test->args->test_state))) { return; } if (test->args->flags & CLD_FLG_R) { test->env->bContinue = TRUE; *(pVal1 + OFF_RLBA) = test->args->start_lba; test->args->test_state = DIRCT_INC(test->args->test_state); test->env->lastAction.oper = READER; test->args->test_state = SET_OPER_R(test->args->test_state); test->args->test_state = SET_rFST_TIME(test->args->test_state); // srand(test->args->seed); /* reseed so we can re create the same random transfers */ memset(test->env->action_list, 0, sizeof(action_t) * test->args->t_kids); test->env->action_list_entry = 0; test->env->wcount = 0; test->env->rcount = 0; if (test->args->flags & CLD_FLG_CYC) if (test->args->cycles == 0) { pMsg(INFO, test->args, "Starting read pass, cycle %lu\n", (unsigned long)test->env->pass_count); } else { pMsg(INFO, test->args, "Starting read pass, cycle %lu of %lu\n", (unsigned long)test->env->pass_count, test->args->cycles); } else { pMsg(INFO, test->args, "Starting read pass\n"); } CreateTestChild(ChildTimer, test); for (i = 0; i < test->args->t_kids; i++) { CreateTestChild(ChildMain, test); } /* Wait for the readers to finish */ cleanUpTestChildren(test); } } unsigned long init_data(test_ll_t * test, unsigned char **data_buffer_unaligned) { int i; OFF_T *pVal1; unsigned long data_buffer_size; #ifdef WINDOWS if (CreateMutex(NULL, FALSE, "gbl") == NULL) { pMsg(ERR, test->args, "Failed to create semaphore, error = %u\n", GetLastError()); return (GetLastError()); } if ((test->env->mutexs.MutexACTION = CreateMutex(NULL, FALSE, NULL)) == NULL) { pMsg(ERR, test->args, "Failed to create semaphore, error = %u\n", GetLastError()); return (GetLastError()); } if ((test->env->mutexs.MutexIO = CreateMutex(NULL, FALSE, NULL)) == NULL) { pMsg(ERR, test->args, "Failed to create semaphore, error = %u\n", GetLastError()); return (GetLastError()); } #else mutexs_t mutexs = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }; test->env->mutexs = mutexs; #endif if (test->args->seed == 0) test->args->seed = test->args->pid; srand(test->args->seed); /* create bitmap to hold write/read context: each bit is an LBA */ /* the stuff before BMP_OFFSET is the data for child/thread shared context */ test->env->bmp_siz = (((((size_t) test->args->vsiz)) / 8) == 0) ? 1 : ((((size_t) test->args->vsiz)) / 8); if ((test->args->vsiz / 8) != 0) test->env->bmp_siz += 1; /* account for rounding error */ /* We use that same data buffer for static data, so alloc here. */ data_buffer_size = ((test->args->htrsiz * BLK_SIZE) * 2); if ((*data_buffer_unaligned = (unsigned char *)ALLOC(data_buffer_size + ALIGNSIZE)) == NULL) { pMsg(ERR, test->args, "Failed to allocate static data buffer memory.\n"); return (-1); } /* create list to hold lbas currently be written */ if ((test->env->action_list = (action_t *) ALLOC(sizeof(action_t) * test->args->t_kids)) == NULL) { pMsg(ERR, test->args, "Failed to allocate static data buffer memory.\n"); return (-1); } test->env->data_buffer = (unsigned char *)BUFALIGN(*data_buffer_unaligned); if ((test->env->shared_mem = (void *)ALLOC(test->env->bmp_siz + BMP_OFFSET)) == NULL) { pMsg(ERR, test->args, "Failed to allocate bitmap memory\n"); return (-1); } memset(test->env->shared_mem, 0, test->env->bmp_siz + BMP_OFFSET); memset(test->env->data_buffer, 0, data_buffer_size); memset(test->env->action_list, 0, sizeof(action_t) * test->args->t_kids); test->env->action_list_entry = 0; pVal1 = (OFF_T *) test->env->shared_mem; *(pVal1 + OFF_WLBA) = test->args->start_lba; *(pVal1 + OFF_RLBA) = test->args->start_lba; test->args->test_state = SET_STS_PASS(test->args->test_state); test->args->test_state = SET_wFST_TIME(test->args->test_state); test->args->test_state = SET_rFST_TIME(test->args->test_state); test->args->test_state = DIRCT_INC(test->args->test_state); if (test->args->flags & CLD_FLG_W) { test->env->lastAction.oper = WRITER; test->args->test_state = SET_OPER_W(test->args->test_state); } else { test->env->lastAction.oper = READER; test->args->test_state = SET_OPER_R(test->args->test_state); } /* prefill the data buffer with data for compares and writes */ switch (test->args->flags & CLD_FLG_PTYPS) { case CLD_FLG_FPTYPE: for (i = 0; i < sizeof(test->args->pattern); i++) { if ((test->args-> pattern & (((OFF_T) 0xff) << (((sizeof(test->args->pattern) - 1) - i) * 8))) != 0) break; } /* special case for pattern = 0 */ if (i == sizeof(test->args->pattern)) i = 0; fill_buffer(test->env->data_buffer, data_buffer_size, &test->args->pattern, sizeof(test->args->pattern) - i, CLD_FLG_FPTYPE); break; case CLD_FLG_RPTYPE: fill_buffer(test->env->data_buffer, data_buffer_size, NULL, 0, CLD_FLG_RPTYPE); break; case CLD_FLG_CPTYPE: fill_buffer(test->env->data_buffer, data_buffer_size, 0, 0, CLD_FLG_CPTYPE); case CLD_FLG_LPTYPE: break; default: pMsg(WARN, test->args, "Unknown fill pattern\n"); return (-1); } return 0; } #ifdef WINDOWS DWORD WINAPI threadedMain(test_ll_t * test) #else void *threadedMain(void *vtest) #endif { #ifndef WINDOWS test_ll_t *test = (test_ll_t *) vtest; #endif OFF_T *pVal1; unsigned char *data_buffer_unaligned = NULL; unsigned long ulRV; int i; unsigned char *sharedMem; extern unsigned short glb_run; extern int signal_action; test->args->pid = GETPID(); init_gbl_data(test->env); if (make_assumptions(test->args) < 0) { TEXIT((uintptr_t) GETLASTERROR()); } if (check_conclusions(test->args) < 0) { TEXIT((uintptr_t) GETLASTERROR()); } if (test->args->flags & CLD_FLG_DUMP) { /* * All we are doing is dumping filespec data to STDOUT, so * we will do this here and be done. */ do_dump(test->args); TEXIT((uintptr_t) GETLASTERROR()); } else { ulRV = init_data(test, &data_buffer_unaligned); if (ulRV != 0) { TEXIT(ulRV); } pVal1 = (OFF_T *) test->env->shared_mem; } pMsg(START, test->args, "Start args: %s\n", test->args->argstr); /* * This loop takes care of passes */ do { test->env->pass_count++; test->env->start_time = time(NULL); if (test->args->flags & CLD_FLG_RPTYPE) { /* force random data to be different each cycle */ fill_buffer(test->env->data_buffer, ((test->args->htrsiz * BLK_SIZE) * 2), NULL, 0, CLD_FLG_RPTYPE); } sharedMem = test->env->shared_mem; memset(sharedMem + BMP_OFFSET, 0, test->env->bmp_siz); if ((test->args->flags & CLD_FLG_LINEAR) && !(test->args->flags & CLD_FLG_NTRLVD)) { linear_read_write_test(test); } else { /* we only reset the end time if not running a linear read / write test */ test->env->end_time = test->env->start_time + test->args->run_time; test->env->bContinue = TRUE; *(pVal1 + OFF_WLBA) = test->args->start_lba; test->args->test_state = DIRCT_INC(test->args->test_state); test->args->test_state = SET_wFST_TIME(test->args->test_state); test->args->test_state = SET_rFST_TIME(test->args->test_state); if (test->args->flags & CLD_FLG_W) { test->env->lastAction.oper = WRITER; test->args->test_state = SET_OPER_W(test->args->test_state); } else { test->env->lastAction.oper = READER; test->args->test_state = SET_OPER_R(test->args->test_state); } memset(test->env->action_list, 0, sizeof(action_t) * test->args->t_kids); test->env->action_list_entry = 0; test->env->wcount = 0; test->env->rcount = 0; if (test->args->flags & CLD_FLG_CYC) if (test->args->cycles == 0) { pMsg(INFO, test->args, "Starting pass %lu\n", (unsigned long)test->env-> pass_count); } else { pMsg(INFO, test->args, "Starting pass %lu of %lu\n", (unsigned long)test->env-> pass_count, test->args->cycles); } else { pMsg(INFO, test->args, "Starting pass\n"); } CreateTestChild(ChildTimer, test); for (i = 0; i < test->args->t_kids; i++) { CreateTestChild(ChildMain, test); } /* Wait for the children to finish */ cleanUpTestChildren(test); } update_cyc_stats(test->env); if ((test->args->flags & CLD_FLG_CYC) && (test->args->flags & CLD_FLG_PCYC)) { print_stats(test->args, test->env, CYCLE); } update_gbl_stats(test->env); if (signal_action & SIGNAL_STOP) { break; } /* user request to stop */ if ((glb_run == 0)) { break; } /* global request to stop */ if (!(test->args->flags & CLD_FLG_CYC)) { break; /* leave, unless cycle testing */ } else { if ((test->args->cycles > 0) && (test->env->pass_count >= test->args->cycles)) { break; /* leave, cycle testing complete */ } } } while (TST_STS(test->args->test_state)); print_stats(test->args, test->env, TOTAL); FREE(data_buffer_unaligned); FREE(test->env->shared_mem); #ifdef WINDOWS CloseHandle(OpenMutex(SYNCHRONIZE, TRUE, "gbl")); CloseHandle(OpenMutex(SYNCHRONIZE, TRUE, "data")); #endif if (TST_STS(test->args->test_state)) { if (signal_action & SIGNAL_STOP) { pMsg(END, test->args, "User Interrupt: Test Done (Passed)\n"); } else { pMsg(END, test->args, "Test Done (Passed)\n"); } } else { if (signal_action & SIGNAL_STOP) { pMsg(END, test->args, "User Interrupt: Test Done (Failed)\n"); } else { pMsg(END, test->args, "Test Done (Failed)\n"); } } TEXIT((uintptr_t) GETLASTERROR()); } /* * Creates a new test structure and adds it to the list of * test structures already available. Allocate all memory * needed by the new test. * * Returns the newly created test structure */ test_ll_t *getNewTest(test_ll_t * testList) { test_ll_t *pNewTest; if ((pNewTest = (test_ll_t *) ALLOC(sizeof(test_ll_t))) == NULL) { pMsg(ERR, &cleanArgs, "%d : Could not allocate memory for new test.\n", GETLASTERROR()); return NULL; } memset(pNewTest, 0, sizeof(test_ll_t)); if ((pNewTest->args = (child_args_t *) ALLOC(sizeof(child_args_t))) == NULL) { pMsg(ERR, &cleanArgs, "%d : Could not allocate memory for new test.\n", GETLASTERROR()); FREE(pNewTest); return NULL; } if ((pNewTest->env = (test_env_t *) ALLOC(sizeof(test_env_t))) == NULL) { pMsg(ERR, &cleanArgs, "%d : Could not allocate memory for new test.\n", GETLASTERROR()); FREE(pNewTest->args); FREE(pNewTest); return NULL; } memcpy(pNewTest->args, &cleanArgs, sizeof(child_args_t)); memcpy(pNewTest->env, &cleanEnv, sizeof(test_env_t)); pNewTest->next = testList; testList = pNewTest; return pNewTest; } test_ll_t *run() { test_ll_t *newTest = NULL, *lastTest = NULL; if (cleanArgs.flags & CLD_FLG_FSLIST) { char *filespec = cleanArgs.device; char *aFilespec = NULL; FILE *file = NULL; if ((aFilespec = (char *)ALLOC(80)) == NULL) { pMsg(ERR, &cleanArgs, "Could not allocate memory to read file"); return newTest; } file = fopen(filespec, "r"); if (file == NULL) { pMsg(ERR, &cleanArgs, "%s is not a regular file, could not be opened for reading, or was not found.", filespec); FREE(aFilespec); return newTest; } while (!feof(file)) { memset(aFilespec, 0, 80); fscanf(file, "%79s", aFilespec); if (aFilespec[0] != 0) { /* if we read something useful */ lastTest = newTest; newTest = getNewTest(lastTest); if (newTest != lastTest) { memset(newTest->args->device, 0, DEV_NAME_LEN); strncpy(newTest->args->device, aFilespec, strlen(aFilespec)); createChild(threadedMain, newTest); } else { newTest = lastTest; break; } } } fclose(file); FREE(aFilespec); } else { newTest = getNewTest(newTest); if (newTest != NULL) { createChild(threadedMain, newTest); } } return newTest; } int main(int argc, char **argv) { extern time_t global_start_time; extern unsigned long glb_flags; /* global flags GLB_FLG_xxx */ int i; #ifdef WINDOWS WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { pMsg(WARN, &cleanArgs, "Windows setup of Winsock failed, can't retrieve host name, continuing"); } #endif setup_sig_mask(); memset(hostname, 0, HOSTNAME_SIZE); gethostname(hostname, HOSTNAME_SIZE); setbuf(stdout, NULL); glb_flags = 0; global_start_time = time(NULL); strncpy(cleanArgs.device, "No filespec", strlen("No filespec")); cleanArgs.stop_lba = -1; cleanArgs.stop_blk = -1; cleanArgs.ioTimeout = DEFAULT_IO_TIMEOUT; cleanArgs.flags |= CLD_FLG_ALLDIE; cleanArgs.flags |= CLD_FLG_ERR_REREAD; cleanArgs.flags |= CLD_FLG_LBA_SYNC; for (i = 1; i < argc - 1; i++) { strncat(cleanArgs.argstr, argv[i], (MAX_ARG_LEN - 1) - strlen(cleanArgs.argstr)); strncat(cleanArgs.argstr, " ", (MAX_ARG_LEN - 1) - strlen(cleanArgs.argstr)); } if (fill_cld_args(argc, argv, &cleanArgs) < 0) exit(1); cleanUp(run()); #ifdef WINDOWS WSACleanup(); #endif return 0; }