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@us.ibm.com if you have
21*  questions or comments.
22*
23*  Project Website:  TBD
24*
25* $Id: main.c,v 1.11 2009/02/26 12:14:53 subrata_modak Exp $
26*
27*/
28#include <stdio.h>
29#ifdef WINDOWS
30#include <windows.h>
31#include <winioctl.h>
32#include <io.h>
33#include <process.h>
34#include <sys/stat.h>
35#include "getopt.h"
36#else
37#include <pthread.h>
38#include <sys/types.h>
39#include <unistd.h>
40#endif
41#include <stdlib.h>
42#include <stdarg.h>
43#include <stdint.h>
44#include <signal.h>
45#include <time.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <string.h>
49#include <ctype.h>
50
51#include "defs.h"
52#include "globals.h"
53#include "main.h"
54#include "usage.h"
55#include "sfunc.h"
56#include "parse.h"
57#include "childmain.h"
58#include "threading.h"
59#include "dump.h"
60#include "timer.h"
61#include "stats.h"
62#include "signals.h"
63
64/* global */
65child_args_t cleanArgs;
66test_env_t cleanEnv;
67char hostname[HOSTNAME_SIZE];	/* global system hostname */
68
69void linear_read_write_test(test_ll_t * test)
70{
71	OFF_T *pVal1 = (OFF_T *) test->env->shared_mem;
72	int i;
73
74	if (test->args->flags & CLD_FLG_W) {
75		test->env->bContinue = TRUE;
76		*(pVal1 + OFF_WLBA) = test->args->start_lba;
77		test->args->test_state = DIRCT_INC(test->args->test_state);
78		test->env->lastAction.oper = WRITER;
79		test->args->test_state = SET_OPER_W(test->args->test_state);
80		test->args->test_state = SET_wFST_TIME(test->args->test_state);
81//              srand(test->args->seed);        /* reseed so we can re create the same random transfers */
82		memset(test->env->action_list, 0,
83		       sizeof(action_t) * test->args->t_kids);
84		test->env->action_list_entry = 0;
85		test->env->wcount = 0;
86		test->env->rcount = 0;
87		if (test->args->flags & CLD_FLG_CYC)
88			if (test->args->cycles == 0) {
89				pMsg(INFO, test->args,
90				     "Starting write pass, cycle %lu\n",
91				     (unsigned long)test->env->pass_count);
92			} else {
93				pMsg(INFO, test->args,
94				     "Starting write pass, cycle %lu of %lu\n",
95				     (unsigned long)test->env->pass_count,
96				     test->args->cycles);
97		} else {
98			pMsg(INFO, test->args, "Starting write pass\n");
99		}
100		CreateTestChild(ChildTimer, test);
101		for (i = 0; i < test->args->t_kids; i++) {
102			CreateTestChild(ChildMain, test);
103		}
104		/* Wait for the writers to finish */
105		cleanUpTestChildren(test);
106	}
107
108	/* If the write test failed don't start the read test */
109	if (!(TST_STS(test->args->test_state))) {
110		return;
111	}
112
113	if (test->args->flags & CLD_FLG_R) {
114		test->env->bContinue = TRUE;
115		*(pVal1 + OFF_RLBA) = test->args->start_lba;
116		test->args->test_state = DIRCT_INC(test->args->test_state);
117		test->env->lastAction.oper = READER;
118		test->args->test_state = SET_OPER_R(test->args->test_state);
119		test->args->test_state = SET_rFST_TIME(test->args->test_state);
120//              srand(test->args->seed);        /* reseed so we can re create the same random transfers */
121		memset(test->env->action_list, 0,
122		       sizeof(action_t) * test->args->t_kids);
123		test->env->action_list_entry = 0;
124		test->env->wcount = 0;
125		test->env->rcount = 0;
126		if (test->args->flags & CLD_FLG_CYC)
127			if (test->args->cycles == 0) {
128				pMsg(INFO, test->args,
129				     "Starting read pass, cycle %lu\n",
130				     (unsigned long)test->env->pass_count);
131			} else {
132				pMsg(INFO, test->args,
133				     "Starting read pass, cycle %lu of %lu\n",
134				     (unsigned long)test->env->pass_count,
135				     test->args->cycles);
136		} else {
137			pMsg(INFO, test->args, "Starting read pass\n");
138		}
139		CreateTestChild(ChildTimer, test);
140		for (i = 0; i < test->args->t_kids; i++) {
141			CreateTestChild(ChildMain, test);
142		}
143		/* Wait for the readers to finish */
144		cleanUpTestChildren(test);
145	}
146}
147
148unsigned long init_data(test_ll_t * test, unsigned char **data_buffer_unaligned)
149{
150	int i;
151	OFF_T *pVal1;
152
153	unsigned long data_buffer_size;
154
155#ifdef WINDOWS
156	if (CreateMutex(NULL, FALSE, "gbl") == NULL) {
157		pMsg(ERR, test->args,
158		     "Failed to create semaphore, error = %u\n",
159		     GetLastError());
160		return (GetLastError());
161	}
162	if ((test->env->mutexs.MutexACTION =
163	     CreateMutex(NULL, FALSE, NULL)) == NULL) {
164		pMsg(ERR, test->args,
165		     "Failed to create semaphore, error = %u\n",
166		     GetLastError());
167		return (GetLastError());
168	}
169	if ((test->env->mutexs.MutexIO =
170	     CreateMutex(NULL, FALSE, NULL)) == NULL) {
171		pMsg(ERR, test->args,
172		     "Failed to create semaphore, error = %u\n",
173		     GetLastError());
174		return (GetLastError());
175	}
176#else
177
178	mutexs_t mutexs =
179	    { PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
180	test->env->mutexs = mutexs;
181
182#endif
183
184	if (test->args->seed == 0)
185		test->args->seed = test->args->pid;
186	srand(test->args->seed);
187
188	/* create bitmap to hold write/read context: each bit is an LBA */
189	/* the stuff before BMP_OFFSET is the data for child/thread shared context */
190	test->env->bmp_siz =
191	    (((((size_t) test->args->vsiz)) / 8) ==
192	     0) ? 1 : ((((size_t) test->args->vsiz)) / 8);
193	if ((test->args->vsiz / 8) != 0)
194		test->env->bmp_siz += 1;	/* account for rounding error */
195
196	/* We use that same data buffer for static data, so alloc here. */
197	data_buffer_size = ((test->args->htrsiz * BLK_SIZE) * 2);
198	if ((*data_buffer_unaligned =
199	     (unsigned char *)ALLOC(data_buffer_size + ALIGNSIZE)) == NULL) {
200		pMsg(ERR, test->args,
201		     "Failed to allocate static data buffer memory.\n");
202		return (-1);
203	}
204	/* create list to hold lbas currently be written */
205	if ((test->env->action_list =
206	     (action_t *) ALLOC(sizeof(action_t) * test->args->t_kids)) ==
207	    NULL) {
208		pMsg(ERR, test->args,
209		     "Failed to allocate static data buffer memory.\n");
210		return (-1);
211	}
212
213	test->env->data_buffer =
214	    (unsigned char *)BUFALIGN(*data_buffer_unaligned);
215
216	if ((test->env->shared_mem =
217	     (void *)ALLOC(test->env->bmp_siz + BMP_OFFSET)) == NULL) {
218		pMsg(ERR, test->args, "Failed to allocate bitmap memory\n");
219		return (-1);
220	}
221
222	memset(test->env->shared_mem, 0, test->env->bmp_siz + BMP_OFFSET);
223	memset(test->env->data_buffer, 0, data_buffer_size);
224	memset(test->env->action_list, 0,
225	       sizeof(action_t) * test->args->t_kids);
226	test->env->action_list_entry = 0;
227
228	pVal1 = (OFF_T *) test->env->shared_mem;
229	*(pVal1 + OFF_WLBA) = test->args->start_lba;
230	*(pVal1 + OFF_RLBA) = test->args->start_lba;
231	test->args->test_state = SET_STS_PASS(test->args->test_state);
232	test->args->test_state = SET_wFST_TIME(test->args->test_state);
233	test->args->test_state = SET_rFST_TIME(test->args->test_state);
234	test->args->test_state = DIRCT_INC(test->args->test_state);
235	if (test->args->flags & CLD_FLG_W) {
236		test->env->lastAction.oper = WRITER;
237		test->args->test_state = SET_OPER_W(test->args->test_state);
238	} else {
239		test->env->lastAction.oper = READER;
240		test->args->test_state = SET_OPER_R(test->args->test_state);
241	}
242
243	/* prefill the data buffer with data for compares and writes */
244	switch (test->args->flags & CLD_FLG_PTYPS) {
245	case CLD_FLG_FPTYPE:
246		for (i = 0; i < sizeof(test->args->pattern); i++) {
247			if ((test->args->
248			     pattern & (((OFF_T) 0xff) <<
249					(((sizeof(test->args->pattern) - 1) -
250					  i) * 8))) != 0)
251				break;
252		}
253		/* special case for pattern = 0 */
254		if (i == sizeof(test->args->pattern))
255			i = 0;
256		fill_buffer(test->env->data_buffer, data_buffer_size,
257			    &test->args->pattern,
258			    sizeof(test->args->pattern) - i, CLD_FLG_FPTYPE);
259		break;
260	case CLD_FLG_RPTYPE:
261		fill_buffer(test->env->data_buffer, data_buffer_size, NULL, 0,
262			    CLD_FLG_RPTYPE);
263		break;
264	case CLD_FLG_CPTYPE:
265		fill_buffer(test->env->data_buffer, data_buffer_size, 0, 0,
266			    CLD_FLG_CPTYPE);
267	case CLD_FLG_LPTYPE:
268		break;
269	default:
270		pMsg(WARN, test->args, "Unknown fill pattern\n");
271		return (-1);
272	}
273
274	return 0;
275}
276
277#ifdef WINDOWS
278DWORD WINAPI threadedMain(test_ll_t * test)
279#else
280void *threadedMain(void *vtest)
281#endif
282{
283#ifndef WINDOWS
284	test_ll_t *test = (test_ll_t *) vtest;
285#endif
286
287	OFF_T *pVal1;
288	unsigned char *data_buffer_unaligned = NULL;
289	unsigned long ulRV;
290	int i;
291	unsigned char *sharedMem;
292
293	extern unsigned short glb_run;
294	extern int signal_action;
295
296	test->args->pid = GETPID();
297
298	init_gbl_data(test->env);
299
300	if (make_assumptions(test->args) < 0) {
301		TEXIT((uintptr_t) GETLASTERROR());
302	}
303	if (check_conclusions(test->args) < 0) {
304		TEXIT((uintptr_t) GETLASTERROR());
305	}
306	if (test->args->flags & CLD_FLG_DUMP) {
307		/*
308		 * All we are doing is dumping filespec data to STDOUT, so
309		 * we will do this here and be done.
310		 */
311		do_dump(test->args);
312		TEXIT((uintptr_t) GETLASTERROR());
313	} else {
314		ulRV = init_data(test, &data_buffer_unaligned);
315		if (ulRV != 0) {
316			TEXIT(ulRV);
317		}
318		pVal1 = (OFF_T *) test->env->shared_mem;
319	}
320
321	pMsg(START, test->args, "Start args: %s\n", test->args->argstr);
322
323	/*
324	 * This loop takes care of passes
325	 */
326	do {
327		test->env->pass_count++;
328		test->env->start_time = time(NULL);
329		if (test->args->flags & CLD_FLG_RPTYPE) {	/* force random data to be different each cycle */
330			fill_buffer(test->env->data_buffer,
331				    ((test->args->htrsiz * BLK_SIZE) * 2), NULL,
332				    0, CLD_FLG_RPTYPE);
333		}
334		sharedMem = test->env->shared_mem;
335		memset(sharedMem + BMP_OFFSET, 0, test->env->bmp_siz);
336		if ((test->args->flags & CLD_FLG_LINEAR)
337		    && !(test->args->flags & CLD_FLG_NTRLVD)) {
338			linear_read_write_test(test);
339		} else {
340			/* we only reset the end time if not running a linear read / write test */
341			test->env->end_time =
342			    test->env->start_time + test->args->run_time;
343			test->env->bContinue = TRUE;
344			*(pVal1 + OFF_WLBA) = test->args->start_lba;
345			test->args->test_state =
346			    DIRCT_INC(test->args->test_state);
347			test->args->test_state =
348			    SET_wFST_TIME(test->args->test_state);
349			test->args->test_state =
350			    SET_rFST_TIME(test->args->test_state);
351			if (test->args->flags & CLD_FLG_W) {
352				test->env->lastAction.oper = WRITER;
353				test->args->test_state =
354				    SET_OPER_W(test->args->test_state);
355			} else {
356				test->env->lastAction.oper = READER;
357				test->args->test_state =
358				    SET_OPER_R(test->args->test_state);
359			}
360			memset(test->env->action_list, 0,
361			       sizeof(action_t) * test->args->t_kids);
362			test->env->action_list_entry = 0;
363			test->env->wcount = 0;
364			test->env->rcount = 0;
365
366			if (test->args->flags & CLD_FLG_CYC)
367				if (test->args->cycles == 0) {
368					pMsg(INFO, test->args,
369					     "Starting pass %lu\n",
370					     (unsigned long)test->env->
371					     pass_count);
372				} else {
373					pMsg(INFO, test->args,
374					     "Starting pass %lu of %lu\n",
375					     (unsigned long)test->env->
376					     pass_count, test->args->cycles);
377			} else {
378				pMsg(INFO, test->args, "Starting pass\n");
379			}
380
381			CreateTestChild(ChildTimer, test);
382			for (i = 0; i < test->args->t_kids; i++) {
383				CreateTestChild(ChildMain, test);
384			}
385			/* Wait for the children to finish */
386			cleanUpTestChildren(test);
387		}
388
389		update_cyc_stats(test->env);
390		if ((test->args->flags & CLD_FLG_CYC)
391		    && (test->args->flags & CLD_FLG_PCYC)) {
392			print_stats(test->args, test->env, CYCLE);
393		}
394		update_gbl_stats(test->env);
395
396		if (signal_action & SIGNAL_STOP) {
397			break;
398		}		/* user request to stop */
399		if ((glb_run == 0)) {
400			break;
401		}
402		/* global request to stop */
403		if (!(test->args->flags & CLD_FLG_CYC)) {
404			break;	/* leave, unless cycle testing */
405		} else {
406			if ((test->args->cycles > 0)
407			    && (test->env->pass_count >= test->args->cycles)) {
408				break;	/* leave, cycle testing complete */
409			}
410		}
411	} while (TST_STS(test->args->test_state));
412	print_stats(test->args, test->env, TOTAL);
413
414	FREE(data_buffer_unaligned);
415	FREE(test->env->shared_mem);
416#ifdef WINDOWS
417	CloseHandle(OpenMutex(SYNCHRONIZE, TRUE, "gbl"));
418	CloseHandle(OpenMutex(SYNCHRONIZE, TRUE, "data"));
419#endif
420
421	if (TST_STS(test->args->test_state)) {
422		if (signal_action & SIGNAL_STOP) {
423			pMsg(END, test->args,
424			     "User Interrupt: Test Done (Passed)\n");
425		} else {
426			pMsg(END, test->args, "Test Done (Passed)\n");
427		}
428	} else {
429		if (signal_action & SIGNAL_STOP) {
430			pMsg(END, test->args,
431			     "User Interrupt: Test Done (Failed)\n");
432		} else {
433			pMsg(END, test->args, "Test Done (Failed)\n");
434		}
435	}
436	TEXIT((uintptr_t) GETLASTERROR());
437}
438
439/*
440 * Creates a new test structure and adds it to the list of
441 * test structures already available.  Allocate all memory
442 * needed by the new test.
443 *
444 * Returns the newly created test structure
445 */
446test_ll_t *getNewTest(test_ll_t * testList)
447{
448	test_ll_t *pNewTest;
449
450	if ((pNewTest = (test_ll_t *) ALLOC(sizeof(test_ll_t))) == NULL) {
451		pMsg(ERR, &cleanArgs,
452		     "%d : Could not allocate memory for new test.\n",
453		     GETLASTERROR());
454		return NULL;
455	}
456
457	memset(pNewTest, 0, sizeof(test_ll_t));
458
459	if ((pNewTest->args =
460	     (child_args_t *) ALLOC(sizeof(child_args_t))) == NULL) {
461		pMsg(ERR, &cleanArgs,
462		     "%d : Could not allocate memory for new test.\n",
463		     GETLASTERROR());
464		FREE(pNewTest);
465		return NULL;
466	}
467	if ((pNewTest->env = (test_env_t *) ALLOC(sizeof(test_env_t))) == NULL) {
468		pMsg(ERR, &cleanArgs,
469		     "%d : Could not allocate memory for new test.\n",
470		     GETLASTERROR());
471		FREE(pNewTest->args);
472		FREE(pNewTest);
473		return NULL;
474	}
475	memcpy(pNewTest->args, &cleanArgs, sizeof(child_args_t));
476	memcpy(pNewTest->env, &cleanEnv, sizeof(test_env_t));
477
478	pNewTest->next = testList;
479	testList = pNewTest;
480	return pNewTest;
481}
482
483test_ll_t *run()
484{
485	test_ll_t *newTest = NULL, *lastTest = NULL;
486
487	if (cleanArgs.flags & CLD_FLG_FSLIST) {
488		char *filespec = cleanArgs.device;
489		char *aFilespec = NULL;
490		FILE *file = NULL;
491
492		if ((aFilespec = (char *)ALLOC(80)) == NULL) {
493			pMsg(ERR, &cleanArgs,
494			     "Could not allocate memory to read file");
495			return newTest;
496		}
497
498		file = fopen(filespec, "r");
499		if (file == NULL) {
500			pMsg(ERR,
501			     &cleanArgs,
502			     "%s is not a regular file, could not be opened for reading, or was not found.",
503			     filespec);
504			FREE(aFilespec);
505
506			return newTest;
507		}
508
509		while (!feof(file)) {
510			memset(aFilespec, 0, 80);
511			fscanf(file, "%79s", aFilespec);
512			if (aFilespec[0] != 0) {	/* if we read something useful */
513				lastTest = newTest;
514				newTest = getNewTest(lastTest);
515				if (newTest != lastTest) {
516					memset(newTest->args->device, 0,
517					       DEV_NAME_LEN);
518					strncpy(newTest->args->device,
519						aFilespec, strlen(aFilespec));
520					createChild(threadedMain, newTest);
521				} else {
522					newTest = lastTest;
523					break;
524				}
525			}
526		}
527
528		fclose(file);
529		FREE(aFilespec);
530	} else {
531		newTest = getNewTest(newTest);
532		if (newTest != NULL) {
533			createChild(threadedMain, newTest);
534		}
535	}
536
537	return newTest;
538}
539
540int main(int argc, char **argv)
541{
542	extern time_t global_start_time;
543	extern unsigned long glb_flags;	/* global flags GLB_FLG_xxx */
544	int i;
545
546#ifdef WINDOWS
547	WORD wVersionRequested;
548	WSADATA wsaData;
549	int err;
550
551	wVersionRequested = MAKEWORD(2, 2);
552
553	err = WSAStartup(wVersionRequested, &wsaData);
554	if (err != 0) {
555		pMsg(WARN, &cleanArgs,
556		     "Windows setup of Winsock failed, can't retrieve host name, continuing");
557	}
558#endif
559
560	setup_sig_mask();
561
562	memset(hostname, 0, HOSTNAME_SIZE);
563	gethostname(hostname, HOSTNAME_SIZE);
564
565	setbuf(stdout, NULL);
566
567	glb_flags = 0;
568	global_start_time = time(NULL);
569
570	strncpy(cleanArgs.device, "No filespec", strlen("No filespec"));
571	cleanArgs.stop_lba = -1;
572	cleanArgs.stop_blk = -1;
573	cleanArgs.ioTimeout = DEFAULT_IO_TIMEOUT;
574	cleanArgs.flags |= CLD_FLG_ALLDIE;
575	cleanArgs.flags |= CLD_FLG_ERR_REREAD;
576	cleanArgs.flags |= CLD_FLG_LBA_SYNC;
577
578	for (i = 1; i < argc - 1; i++) {
579		strncat(cleanArgs.argstr, argv[i],
580			(MAX_ARG_LEN - 1) - strlen(cleanArgs.argstr));
581		strncat(cleanArgs.argstr, " ",
582			(MAX_ARG_LEN - 1) - strlen(cleanArgs.argstr));
583	}
584
585	if (fill_cld_args(argc, argv, &cleanArgs) < 0)
586		exit(1);
587
588	cleanUp(run());
589
590#ifdef WINDOWS
591	WSACleanup();
592#endif
593
594	return 0;
595}
596