/* * 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; }