blob: 0bf64adb8c65912ac03ba73987bc8567c8903663 [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: sfunc.c,v 1.7 2009/02/26 11:43:51 subrata_modak Exp $
*
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#ifdef WINDOWS
#include <winsock2.h>
#include <process.h>
#include <windows.h>
#include <winbase.h>
#include <winioctl.h>
#else
#ifdef AIX
#include <sys/ioctl.h>
#include <sys/devinfo.h>
#endif
#include <unistd.h>
#include <ctype.h>
#endif
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifdef LINUX
#include <endian.h>
#endif
#include "main.h"
#include "sfunc.h"
#include "defs.h"
#include "globals.h"
#include "io.h"
#include "threading.h"
/*
* Generates a random 32bit number.
*/
long Rand32(void)
{
/*
* based on the fact that rand returns
* 0 - 0x7FFF
*/
long myRandomNumber = 0;
myRandomNumber = ((long) (rand() & 0x7FFF)) << 16;
myRandomNumber |= ((long) (rand() & 0x7FFF)) << 1;
myRandomNumber |= ((long) (rand() & 0x1));
return(myRandomNumber);
}
/*
* Generates a random 64bit number.
*/
OFF_T Rand64(void)
{
OFF_T myRandomNumber = 0;
myRandomNumber = ((OFF_T) (rand() & 0x7FFF)) << 48;
myRandomNumber |= ((OFF_T) (rand() & 0x7FFF)) << 33;
myRandomNumber |= ((OFF_T) (rand() & 0x7FFF)) << 18;
myRandomNumber |= ((OFF_T) (rand() & 0x7FFF)) << 3;
myRandomNumber |= ((OFF_T) (rand() & 0x7));
return(myRandomNumber);
}
/*
* could not find a function that represented a conversion
* between a long long and a string.
*/
OFF_T my_strtofft(const char *pStr)
{
OFF_T value = 0;
int bOct = 0, bHex = 0;
int neg = 0;
for(;;pStr++) {
switch(*pStr) {
case '0':
bOct = 1;
continue;
case 'x':
if(bOct) bHex = 1;
continue;
case ' ':
case '\t':
continue;
case '-':
neg = 1;
/*FALLTHROUGH*/
case '+':
pStr++;
}
break;
}
if((!bOct) && (!bHex)) {
while (*pStr >= '0' && *pStr <= '9') {
value = (value * 10) + (*pStr++ - '0');
}
} else if(bHex) {
while ((*pStr >= '0' && *pStr <= '9') ||
(*pStr >= 'A' && *pStr <= 'F') ||
(*pStr >= 'a' && *pStr <= 'f')) {
if(*pStr >= '0' && *pStr <= '9')
value = (value << 4) + (*pStr++ - '0');
else if(*pStr >= 'A' && *pStr <= 'F')
value = (value << 4) + 10 + (*pStr++ - 'A');
else if(*pStr >= 'a' && *pStr <= 'f')
value = (value << 4) + 10 + (*pStr++ - 'a');
}
} else if(bOct) {
while (*pStr >= '0' && *pStr <= '7') {
value = (value * 8) + (*pStr++ - '0');
}
}
return (neg ? -value : value);
}
/*
* prints messages to stdout. with added formating
*/
int pMsg(lvl_t level, const child_args_t *args, char *Msg,...)
{
#define FORMAT "| %s | %s | %d | %s | %s | %s"
#define TIME_FORMAT "%04d/%02d/%02d-%02d:%02d:%02d"
#define TIME_FMT_LEN 20
va_list l;
int rv = 0;
size_t len = 0;
char *cpTheMsg;
char levelStr[10];
struct tm struct_time;
struct tm *pstruct_time;
char time_str[TIME_FMT_LEN];
time_t my_time;
extern unsigned long glb_flags;
#ifndef WINDOWS
static pthread_mutex_t mTime = PTHREAD_MUTEX_INITIALIZER;
#endif
#ifndef WINDOWS
LOCK(mTime);
#endif
time(&my_time);
pstruct_time = localtime(&my_time);
if(pstruct_time != NULL)
memcpy(&struct_time, pstruct_time, sizeof(struct tm));
#ifndef WINDOWS
UNLOCK(mTime);
#endif
if((glb_flags & GLB_FLG_QUIET) && (level == INFO))
return 0;
va_start(l, Msg);
if(glb_flags & GLB_FLG_SUPRESS) {
rv = vprintf(Msg,l);
va_end(l);
return rv;
}
switch(level) {
case START:
strcpy(levelStr, "START");
break;
case END:
strcpy(levelStr, "END ");
break;
case STAT:
strcpy(levelStr, "STAT ");
break;
case INFO:
strcpy(levelStr, "INFO ");
break;
case DBUG:
strcpy(levelStr, "DEBUG");
break;
case WARN:
strcpy(levelStr, "WARN ");
break;
case ERR:
strcpy(levelStr, "ERROR");
break;
}
sprintf(time_str, TIME_FORMAT, struct_time.tm_year+1900,
struct_time.tm_mon+1,
struct_time.tm_mday,
struct_time.tm_hour,
struct_time.tm_min,
struct_time.tm_sec
);
len += strlen(FORMAT);
len += strlen(time_str);
len += strlen(levelStr);
len += sizeof(pid_t)*8 + 1;
len += strlen(VER_STR);
len += strlen(args->device);
len += strlen(Msg);
if((cpTheMsg = (char *)ALLOC(len)) == NULL) {
printf("Can't print formatted message, printing message raw.\n");
rv = vprintf(Msg,l);
va_end(l);
return rv;
}
memset(cpTheMsg, 0, len);
sprintf(cpTheMsg, FORMAT, time_str, levelStr, args->pid, VER_STR, args->device, Msg);
rv = vprintf(cpTheMsg,l);
FREE(cpTheMsg);
va_end(l);
return rv;
}
OFF_T getByteOrderedData(const OFF_T data)
{
OFF_T off_tpat = 0;
#ifdef WINDOWS
unsigned char *ucharpattern;
size_t i = 0;
ucharpattern = (unsigned char *) &data;
for(i=0;i<sizeof(OFF_T);i++) {
off_tpat |= (((OFF_T)(ucharpattern[i])) << sizeof(OFF_T)*((sizeof(OFF_T)-1)-i));
}
#endif
#ifdef AIX
off_tpat = data;
#endif
#ifdef LINUX
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned char *ucharpattern;
size_t i = 0;
ucharpattern = (unsigned char *) &data;
for(i=0;i<sizeof(OFF_T);i++) {
off_tpat |= (((OFF_T)(ucharpattern[i])) << sizeof(OFF_T)*((sizeof(OFF_T)-1)-i));
}
#else
off_tpat = data;
#endif
#endif
return off_tpat;
}
void mark_buffer(void *buf, const size_t buf_len, void *lba, const child_args_t *args, const test_env_t *env)
{
OFF_T *plocal_lba = lba;
OFF_T local_lba = *plocal_lba;
OFF_T *off_tbuf = buf;
OFF_T off_tpat = 0, off_tpat2 = 0, off_tpat3 = 0, off_tpat4 = 0;
OFF_T pass_count = env->pass_count;
OFF_T start_time = (OFF_T)env->start_time;
unsigned char * ucharBuf = (unsigned char *)buf;
size_t i = 0;
extern char hostname[];
off_tpat2 = getByteOrderedData(pass_count);
if(args->flags & CLD_FLG_ALT_MARK) {
off_tpat3 = getByteOrderedData(args->alt_mark);
} else {
off_tpat3 = getByteOrderedData(start_time);
}
off_tpat4 = getByteOrderedData(args->seed);
for(i=0;i<buf_len;i=i+BLK_SIZE) {
if(args->flags & CLD_FLG_MRK_LBA) {
/* fill first 8 bytes with lba number */
off_tpat = getByteOrderedData(local_lba);
*(off_tbuf+(i/sizeof(OFF_T))) = off_tpat;
}
if(args->flags & CLD_FLG_MRK_PASS) {
/* fill second 8 bytes with pass_count */
*(off_tbuf+(i/sizeof(OFF_T))+1) = off_tpat2;
}
if(args->flags & CLD_FLG_MRK_TIME) {
/* fill third 8 bytes with start_time */
*(off_tbuf+(i/sizeof(OFF_T))+2) = off_tpat3;
}
if(args->flags & CLD_FLG_MRK_SEED) {
/* fill fourth 8 bytes with seed data */
*(off_tbuf+(i/sizeof(OFF_T))+3) = off_tpat4;
}
if(args->flags & CLD_FLG_MRK_HOST) {
/* now add the hostname to the mark data */
memcpy(ucharBuf+32+i, hostname, HOSTNAME_SIZE);
}
if(args->flags & CLD_FLG_MRK_TARGET) {
/* now add the target to the mark data */
memcpy(ucharBuf+32+HOSTNAME_SIZE+i, args->device, strlen(args->device));
}
local_lba++;
}
}
/*
* function fill_buffer
* This function fills the passed buffer with data based on the pattern and patten type.
* for pattern types of counting the pattern does not matter. For lba pattern type, the
* pattern will be the address of the lba.
*/
void fill_buffer(void *buf, size_t len, void *pattern, size_t pattern_len, const unsigned int pattern_type)
{
size_t i, j;
unsigned char *ucharbuf = buf;
OFF_T *off_tbuf = buf;
unsigned char *ucharpattern = pattern;
OFF_T *poff_tpattern = pattern;
OFF_T off_tpat, off_tpat2;
switch (pattern_type) { /* the pattern type should only be one of the following */
case CLD_FLG_CPTYPE :
/* Will fill buffer with counting pattern 0x00 thru 0xff */
for(i=0;i<len;i++)
ucharbuf[i] = (unsigned char) (i & 0xff);
break;
case CLD_FLG_FPTYPE :
/* arrange data to go on the wire correctly */
off_tpat = 0;
for(j=0;j<(sizeof(OFF_T)/pattern_len);j++)
for(i=0;i<pattern_len;++i)
#ifdef WINDOWS
off_tpat |= (((OFF_T)(ucharpattern[i])) << 8*(7-((j*pattern_len)+i)));
#endif
#ifdef AIX
off_tpat |= (((OFF_T)(ucharpattern[(8-pattern_len)+i])) << 8*(7-((j*pattern_len)+i)));
#endif
#ifdef LINUX
#if __BYTE_ORDER == __LITTLE_ENDIAN
off_tpat |= (((OFF_T)(ucharpattern[i])) << 8*(7-((j*pattern_len)+i)));
#else
off_tpat |= (((OFF_T)(ucharpattern[(8-pattern_len)+i])) << 8*(7-((j*pattern_len)+i)));
#endif
#endif
/* fill buffer with fixed pattern */
for(i=0;i<len/8;i++)
*(off_tbuf+i) = off_tpat;
break;
case CLD_FLG_LPTYPE :
off_tpat2 = *poff_tpattern;
for(j=0;j<len;j++) {
/* arrange data to go on the wire correctly */
ucharpattern = (unsigned char *) &off_tpat2;
off_tpat = 0;
for(i=0;i<pattern_len;i++)
#ifdef WINDOWS
off_tpat |= (((OFF_T)(ucharpattern[i])) << 8*(7-i));
#endif
#ifdef AIX
off_tpat |= (((OFF_T)(ucharpattern[(8-pattern_len)+i])) << 8*(7-i));
#endif
#ifdef LINUX
#if __BYTE_ORDER == __LITTLE_ENDIAN
off_tpat |= (((OFF_T)(ucharpattern[i])) << 8*(7-i));
#else
off_tpat |= (((OFF_T)(ucharpattern[(8-pattern_len)+i])) << 8*(7-i));
#endif
#endif
/* fill buffer with lba number */
for(i=0;i<BLK_SIZE/8;i++) {
*(off_tbuf+i+(j*(BLK_SIZE/8))) = off_tpat;
}
off_tpat2++;
}
break;
case CLD_FLG_RPTYPE :
/* Will fill buffer with a random pattern.
* Unfortunatly, every LBA, 512 bytes of data will be
* the same random data set, this is due to the LBA
* boundary requirement of disktest. This should be fixed
* at some point...
*/
for(i=0;i<BLK_SIZE/sizeof(OFF_T);i++)
*(off_tbuf+i) = Rand64();
for(i=BLK_SIZE;i<len;i+=BLK_SIZE)
memcpy((ucharbuf+i), ucharbuf, BLK_SIZE);
break;
default :
printf("Unknown fill pattern\n");
exit(1);
}
}
void normalize_percs(child_args_t *args)
{
int i, j;
if((args->flags & CLD_FLG_R) && !(args->flags & CLD_FLG_W)) {
if((args->flags & CLD_FLG_DUTY) && (args->rperc < 100)) {
pMsg(WARN, args, "Read specified w/o write, ignoring -D, forcing read only...\n");
}
args->rperc = 100;
args->wperc = 0;
} else if((args->flags & CLD_FLG_W) && !(args->flags & CLD_FLG_R)) {
if((args->flags & CLD_FLG_DUTY) && (args->wperc < 100)) {
pMsg(WARN, args, "Write specified w/o read, ignoring -D, forcing write only...\n");
}
args->rperc = 0;
args->wperc = 100;
} else { /* must be reading and writing */
if (args->rperc == 0 && args->wperc == 0) {
args->rperc = 50;
args->wperc = 50;
} else if(args->rperc == 0) {
args->rperc = 100 - args->wperc;
} else if (args->wperc == 0) {
args->wperc = 100 - args->rperc;
}
}
if (args->rperc + args->wperc != 100) {
pMsg(INFO, args, "Balancing percentage between reads and writes\n");
if((args->flags & CLD_FLG_R) && (args->flags & CLD_FLG_W)) {
i = 100 - (args->rperc + args->wperc);
j = i / 2;
args->wperc += j;
args->rperc += (i - j);
}
}
}
#ifndef WINDOWS
char *strupr(char *String) {
unsigned int i;
for(i=0;i<strlen(String);i++) {
*(String+i) = toupper(*(String+i));
}
return(String);
}
char *strlwr(char *String) {
unsigned int i;
for(i=0;i<strlen(String);i++) {
*(String+i) = tolower(*(String+i));
}
return(String);
}
#endif
OFF_T get_file_size(char *device) {
OFF_T size = 0;
fd_t fd;
#ifdef WINDOWS
SetLastError(0);
fd = CreateFile(device,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
#else
fd = open(device, 0);
#endif
if(INVALID_FD(fd)) {
return size;
}
size = SeekEnd(fd);
size /= BLK_SIZE;
CLOSE(fd);
return size;
}
OFF_T get_vsiz(const char *device)
{
#ifdef PPC
unsigned long size = 0;
#else
OFF_T size = 0;
#endif
#ifdef WINDOWS
HANDLE hFileHandle;
BOOL bRV;
DWORD dwLength;
GET_LENGTH_INFORMATION myLengthInfo;
DISK_GEOMETRY DiskGeom;
hFileHandle = CreateFile(device,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if(hFileHandle == INVALID_HANDLE_VALUE) {
return(GetLastError());
}
SetLastError(0);
bRV = DeviceIoControl(hFileHandle,
IOCTL_DISK_GET_LENGTH_INFO,
NULL,
0,
&myLengthInfo,
sizeof(GET_LENGTH_INFORMATION),
&dwLength,
NULL);
if(bRV) {
size = myLengthInfo.Length.QuadPart;
size /= BLK_SIZE; /* return requires BLOCK */
} else {
bRV = DeviceIoControl(hFileHandle,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
&DiskGeom,
sizeof(DISK_GEOMETRY),
&dwLength,
NULL);
if(bRV) {
size = (OFF_T) DiskGeom.Cylinders.QuadPart;
size *= (OFF_T) DiskGeom.TracksPerCylinder;
size *= (OFF_T) DiskGeom.SectorsPerTrack;
} else {
size = 0;
}
}
CloseHandle(hFileHandle);
#else
int fd = 0;
#if AIX
struct devinfo *my_devinfo = NULL;
unsigned long ulSizeTmp;
#endif
if((fd = open(device, 0)) < 0) {
return 0;
}
#if AIX
my_devinfo = (struct devinfo*) ALLOC(sizeof(struct devinfo));
if(my_devinfo != NULL) {
memset(my_devinfo, 0, sizeof(struct devinfo));
if(ioctl(fd, IOCINFO, my_devinfo) == -1) size = -1;
else {
if(my_devinfo->flags & DF_LGDSK) {
ulSizeTmp = (unsigned long) my_devinfo->un.scdk64.hi_numblks;
size |= ((((OFF_T)ulSizeTmp) << 32) & 0xFFFFFFFF00000000ll);
ulSizeTmp = (unsigned long) my_devinfo->un.scdk64.lo_numblks;
size |= (((OFF_T) ulSizeTmp) & 0x00000000FFFFFFFFll);
} else {
ulSizeTmp = (unsigned long) my_devinfo->un.scdk.numblks;
size |= (((OFF_T) ulSizeTmp) & 0x00000000FFFFFFFFll);
}
}
FREE(my_devinfo);
}
#else
if(ioctl(fd, BLKGETSIZE, &size) == -1) size = -1;
#endif
close(fd);
#endif
#ifdef PPC
return((OFF_T)size);
#else
return(size);
#endif
}
#ifndef WINDOWS
void Sleep(unsigned int msecs)
{
usleep(msecs*1000);
}
#endif
fmt_time_t format_time(time_t seconds)
{
fmt_time_t time_struct;
time_struct.days = seconds/86400;
time_struct.hours = (seconds%86400)/3600;
time_struct.minutes = (seconds%3600)/60;
time_struct.seconds = seconds%60;
return time_struct;
}