/*
* 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.8 2009/02/26 12:02:23 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));
else
memset(&struct_time, 0, 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;
}