blob: 753e967842ffe08bfe7dc8e192c6b0978313cb81 [file] [log] [blame]
/*
* 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.9 2009/02/26 11:43:51 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 long 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);
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, (char *)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;
}