home *** CD-ROM | disk | FTP | other *** search
- /*
- $Module DX.C$
-
- Copyright 1990
- By NetFRAME Systems Inc.
- Sunnyvale, California U.S.A.
-
- $Author: Karl S. Johnson $
- $Date: 02 Feb 1990 15:46:42 $
- $Revision: 1.2 $
-
- $Description$
- This is a NetWare Loadable Module to measure disk performance.
- $EndDescription$
-
-
- Revision History
- $Log: H:/386/NLMS/DX/SRC/VCS/DX.C $
- *
- * Rev 1.2 02 Feb 1990 15:46:42 Karl S. Johnson
- * Changed to use PDiskRequest to allow greater than cache block size I/Os.
- *
- * Rev 1.1 01 Feb 1990 14:47:18 Karl S. Johnson
- * Added full random I/O. Added paged display and processor utilization.
- *
- * Rev 1.0 01 Feb 1990 10:03:50 Karl S. Johnson
- * Initial revision.
- */
- #include "procdefs.h"
- #include "dmanage.h"
- #include "random.h"
- #define DX_PRIORITY 50
- #define MAX_DISKS 64
- #define MAX_REQUESTS_PER_DISK 64
- #define MAX_KB_PER_IO 60
- #define DISKS_PER_PAGE 16
- #define STACK_SIZE 2048*16
- #define _UNUSED(x) if (0) if (x)
- extern LONG NumberOfPollingLoops, MaximumNumberOfPollingLoops;
- BYTE *DXRequestArea;
- LONG DXRequestAreaSize;
- struct rstruct *DXRequestFreeListHead;
- struct rstruct *DXRequestFreeListTail;
- LONG DXUpdateInterval;
- LONG DXKBytesPerIO;
- LONG DXMaxIOsPerDrive;
- BYTE DXReadWrite;
- BYTE DXAccessType;
- ScreenStruct *dXerciseScreen;
- int DriveCount;
- BYTE *stack; /* DX Monitor Stack */
- LONG stackSize;
- BYTE *DXStack = NULL; /* DX Exerciser Stack */
- LONG DXActualStackSize;
- LONG dXerciseModuleHandle;
- LONG ServerProcessPriority = 50;
- LONG DXMonitorProcessID = 0;
- LONG DXerciseProcessID = 0;
- int DXWakeRequested = 0;
- char DXMonitorStatus = 'I'; /* Possible Status I=Initializing R=Running P=Pending Stop S=Stopped */
- struct DiskData
- {
- struct ldstruct *LDisk;
- struct plock *Lock;
- int OutstandingIOs;
- LONG Size;
- LONG Sector;
- LONG Random;
- BYTE Name[80];
- } Disk[MAX_DISKS];
- struct PerformanceData
- {
- LONG TotalIOsCompleted;
- LONG CurrentIOsCompleted;
- LONG TotalErrors;
- } Performance[MAX_DISKS];
- struct PerformanceData Aggregate;
- int AggregateOutstandingIOs = 0;
- BYTE CommonBuffer[1024*MAX_KB_PER_IO];
-
- void
- LockAlert( LONG skipParameter, LONG criticality, int DriveIndex );
-
- void
- DXIOCompletion( struct rstruct *currentRequest );
-
- struct rstruct *DXAllocateRequest();
-
- void
- DXReturnRequest( struct rstruct *Request);
-
- void
- MakeDriveDescription( struct pdstruct *driveID, BYTE *buffer);
-
- void
- DXercise();
-
- void
- DXMonitor();
-
- LONG
- StartProcedure(
- LONG moduleHandle,
- ScreenStruct *initializationErrorScreen,
- BYTE *commandLine,
- BYTE *loadDirectoryPath,
- LONG unitializedDataLength,
- LONG fileHandle,
- LONG (*ReadRoutine)(LONG handle, LONG offset, BYTE *buffer, LONG length),
- LONG customDataOffset,
- LONG customDataSize)
- {
- static BYTE dXerciseScreenName[] = "DX Screen";
- BYTE *Token;
- _UNUSED(commandLine);
- _UNUSED(loadDirectoryPath);
- _UNUSED(unitializedDataLength);
- _UNUSED(fileHandle);
- _UNUSED(ReadRoutine);
- _UNUSED(customDataOffset);
- _UNUSED(customDataSize);
-
- for (Token = commandLine; *Token != '\000'; Token++)
- {
- if (*Token == 'D') EnterDebugger();
- }
-
- stack = GetNonMovableMemory(STACK_SIZE, &stackSize);
- if (stack == NULL)
- {
- OutputToScreen(initializationErrorScreen,
- "DX: Unable to get memory for stack\r\n");
- goto Error0;
- }
-
- dXerciseModuleHandle = moduleHandle;
- if (OpenScreen(dXerciseScreenName, &dXerciseScreen) != 0)
- {
- OutputToScreen(initializationErrorScreen,
- "DX: Unable to open DX screen\r\n");
- goto Error1;
- }
-
- /* The variable stackSize contains the amount of memory actually
- allocated for the stack, so use it instead of STACK_SIZE.*/
-
- /* this should be the last thing we do */
- DXMonitorProcessID = CCreateProcess(DX_PRIORITY, DXMonitor,
- stack + stackSize, stackSize, "DXercise");
- while ( DXMonitorStatus == 'I') CRescheduleLast();
- switch ( DXMonitorStatus)
- {
- case 'R':
- break;
- case 'S':
- default:
- OutputToScreen( systemConsoleScreen,
- "Failed to start DX monitor process\r\n" );
- goto Error3;
- }
- return (0);
-
- /* Error Recovery */
- Error3:
- CloseScreen(dXerciseScreen);
- Error1:
- ReturnNonMovableMemory(stack);
- Error0:
- return (-1);
- }
-
- void ExitProcedure(void)
- {
- int i;
- if ( DXMonitorProcessID != 0 ) CDestroyProcess( DXMonitorProcessID );
-
- if ( DXMonitorStatus != 'S' ) /* if not already stopped - stop */
- {
- DXMonitorStatus = 'S';
- while ( AggregateOutstandingIOs != 0 ) CRescheduleLast();
- if ( DXerciseProcessID != 0 ) CDestroyProcess( DXerciseProcessID );
- }
- for ( i = 0; i < DriveCount; i++ ) ReleasePartition( Disk[i].Lock );
- CloseScreen( dXerciseScreen );
- if ( DXStack != NULL ) ReturnNonMovableMemory( DXStack );
- if ( DXRequestArea != NULL ) ReturnNonMovableMemory( DXRequestArea );
-
- ReturnNonMovableMemory( stack );
-
- }
-
- void DXExit(void)
- {
- KillMe((struct LoadDefinitionStructure *)dXerciseModuleHandle);
-
- /* Sleep forever until the exit procedure kills this process */
- for (;;)
- CSleepUntilInterrupt();
- }
-
- void
- DXMonitor()
- {
- struct pdstruct *driveID;
- struct ldstruct *lDisk;
- long int LStatus;
- int DriveIndex;
- LONG MaxPage;
- LONG CurrentPage;
- LONG FirstDrive;
- LONG LastDrive;
- LONG StartTime;
- LONG IntervalStartTime;
- LONG ElapsedSeconds;
- LONG IntervalSeconds;
- LONG Tenths;
- LONG utilization;
- char *AccessString;
- char *IOTypeString;
- struct rstruct *Request;
- LONG UpdateTicks;
- int i;
- BYTE Answer;
- BYTE Dummy;
- BYTE buffer[200];
-
- RandomSectorsCount = sizeof( RandomSectors ) / 4;
- /* Activate our screen */
- Enable();
- ActivateScreen(dXerciseScreen);
-
- /* Tell DXLoad we are Running */
- DXMonitorStatus = 'R';
- /* Ask for type of disk drive access S=Sequential R=Random*/
- InputFromScreen( dXerciseScreen,
- "FSR",
- 2,
- 2,
- buffer,
- 0L,
- TRUE,
- "S",
- "Exerciser access pattern [S=Sequential R=Random]? " );
- DXAccessType = buffer[0];
- switch ( DXAccessType )
- {
- case 'F':
- AccessString = "Fixed";
- break;
- case 'S':
- AccessString = "Sequential";
- break;
- case 'R':
- AccessString = "Random";
- break;
- default:
- AccessString = "UnknownTest";
- }
- /* Ask for Read or Write test */
- InputFromScreen( dXerciseScreen,
- "RW",
- 2,
- 2,
- buffer,
- 0L,
- TRUE,
- "R",
- "Exerciser test type [R=Read W=Write]? " );
- DXReadWrite = buffer[0];
- switch ( DXReadWrite )
- {
- case 'R':
- IOTypeString = "Read";
- break;
- case 'W':
- IOTypeString = "Write";
- break;
- default:
- IOTypeString = "UnknownIO";
- }
-
- /* Ask for block size */
- PromptForUnsignedNumber( dXerciseScreen,
- &DXKBytesPerIO,
- 1L,
- MAX_KB_PER_IO,
- 10L,
- 0L,
- TRUE,
- 8L,
- "Number of kilo bytes (KB) per IO [1-%d]? ",
- MAX_KB_PER_IO);
-
- /* Ask for queue depth */
- PromptForUnsignedNumber( dXerciseScreen,
- &DXMaxIOsPerDrive,
- 1L,
- MAX_REQUESTS_PER_DISK,
- 10L,
- 0L,
- TRUE,
- 3L,
- "Number of concurrent IO to queue per drive [1-%d]? ",
- MAX_REQUESTS_PER_DISK);
-
- /* Ask for update interval */
- PromptForUnsignedNumber( dXerciseScreen,
- &DXUpdateInterval,
- 1L,
- 60L,
- 10L,
- 0L,
- TRUE,
- 5L,
- "Screen update interval in seconds [1-60]? ");
- ConvertSecondsToTicks( DXUpdateInterval, 0L, &UpdateTicks );
-
- /* List all possible disks */
- ScanForNewDrives();
-
- /* Ask for selections or go */
- DriveCount = 0;
- for (driveID = PhysicalDiskList; driveID != NULL; driveID =
- driveID->PPLink)
- {
- for ( i = 0; i < sizeof( Disk[0].Name ); i++ ) Disk[DriveCount].Name[i] = 0;
- MakeDriveDescription( driveID, Disk[DriveCount].Name );
- LStatus = PromptForYesOrNo( dXerciseScreen,
- 0L,
- TRUE,
- "\r\nExercise %s ?",
- Disk[DriveCount].Name );
- if ( LStatus )
- {
- for ( lDisk = driveID->PLogicalLink;
- lDisk != NULL;
- lDisk = lDisk->LLogicalLink )
- if ( lDisk->LOSType == NETWAREOSTYPE )
- break;
- if ( lDisk == NULL )
- {
- OutputToScreen( dXerciseScreen,
- "\r\nDisk does not have a NetWare Partition - not usable\r\n");
- break;
- }
- Disk[DriveCount].Lock = LockSinglePartition(
- lDisk,
- LockAlert,
- DriveCount,
- EXCLUSIVELOCK,
- (BYTE *)"DXercise" );
- if ( Disk[DriveCount].Lock == NULL )
- {
- OutputToScreen( dXerciseScreen,
- "\r\nCannot get EXCLUSIVE LOCK on NetWare partition - not usable\r\n");
- break;
- }
- Disk[DriveCount].LDisk = lDisk;
- Disk[DriveCount].Size = lDisk->LLogicalSize;
- Disk[DriveCount].OutstandingIOs = 0;
- Performance[DriveCount].TotalIOsCompleted = 0;
- Performance[DriveCount].CurrentIOsCompleted = 0;
- Performance[DriveCount].TotalErrors = 0;
- DriveCount++;
- }
- }
- /* Zero aggregate preformance numbers */
-
- Aggregate.TotalIOsCompleted = 0;
- Aggregate.CurrentIOsCompleted = 0;
- Aggregate.TotalErrors = 0;
- AggregateOutstandingIOs = 0;
-
- /* Make sure at least one disk is selected */
-
- if ( DriveCount < 1 )
- {
- OutputToScreen( dXerciseScreen,
- "No Drives selected\r\n" );
- Delay( 91 );
- DXExit();
- }
- /* Allocate and initialize the request list */
- DXRequestAreaSize = DriveCount
- * MAX_REQUESTS_PER_DISK
- * ( sizeof( struct rstruct ) + 4 );
- DXRequestArea = GetNonMovableMemory( DXRequestAreaSize,
- &DXRequestAreaSize );
- if ( DXRequestArea == NULL)
- {
- OutputToScreen ( dXerciseScreen,
- "Insufficient memory to allocate disk requests\r\n");
- Delay( 91 );
- DXExit();
- }
- CSetB( 0, DXRequestArea, DXRequestAreaSize );
- DXRequestFreeListHead = NULL;
- DXRequestFreeListTail = NULL;
- Request = (struct rstruct *)DXRequestArea;
- for ( i = 0; i < DXRequestAreaSize / ( sizeof( struct rstruct ) + 4 ); i++ )
- {
- *(LONG *)Request = 0x55514552; /* Put in the "REQU" block signature */
- Request = (struct rstruct *)((LONG *)Request + 1); /* just before block */
- DXReturnRequest( Request );
- Request++;
- }
-
- /* Start DXercise */
- DXStack = GetNonMovableMemory( STACK_SIZE,
- &DXActualStackSize );
- if ( DXStack == NULL)
- {
- OutputToScreen ( dXerciseScreen,
- "Insufficient memory to start DX subprocess\r\n");
- Delay( 91 );
- DXExit();
- }
- DXerciseProcessID = CCreateProcess( DX_PRIORITY,
- DXercise,
- DXStack + DXActualStackSize,
- DXActualStackSize,
- "DXercise");
- /* Compute pages of displays */
- MaxPage = ( DriveCount / DISKS_PER_PAGE ) + 1;
- CurrentPage = 1;
- /* Clear the Screen and setup the title */
- ClearScreen( dXerciseScreen );
- PositionOutputCursor( dXerciseScreen, 0, 0 );
- OutputToScreen( dXerciseScreen,
- "NetFRAME Disk Test : %s %s %d KB/IO with %d IOs queued Page %d of %d",
- AccessString,
- IOTypeString,
- DXKBytesPerIO,
- DXMaxIOsPerDrive,
- CurrentPage,
- MaxPage );
-
- /* Note the starting time for later computations */
- StartTime = CurrentTime;
-
- while (1)
- {
- /* Sleep for update interval */
- IntervalStartTime = CurrentTime;
- Delay( UpdateTicks );
- /* Test for keyboard key */
- if ( CheckKeyStatus( dXerciseScreen ) )
- {
- /* Get the key and process it */
- GetKey( dXerciseScreen, &Dummy, &Answer, &Dummy, &Dummy, 0L );
- if ( Answer == 'Q' || Answer =='q' )
- {
- PositionOutputCursor( dXerciseScreen, 24, 0 );
- LStatus = PromptForYesOrNo( dXerciseScreen,
- 0L,
- TRUE,
- "Exit Disk Exerciser? " );
- if ( LStatus )
- {
- DXMonitorStatus = 'P';
- }
- }
- else
- {
- if ( Answer > '0' && Answer <= '9' )
- {
- i = Answer - '0';
- if ( i <= MaxPage )
- {
- CurrentPage = i;
- ClearScreen( dXerciseScreen );
- PositionOutputCursor( dXerciseScreen, 0, 0 );
- OutputToScreen( dXerciseScreen,
- "NetFRAME Disk Test : %s %s %d KB/IO with %d IOs queued Page %d of %d",
- AccessString,
- IOTypeString,
- DXKBytesPerIO,
- DXMaxIOsPerDrive,
- CurrentPage,
- MaxPage );
- }
- else RingTheBell();
- }
- else RingTheBell();
- }
- }
- /* Test for requested stop */
- if ( DXMonitorStatus == 'P' )
- {
- while ( AggregateOutstandingIOs != 0 ) CRescheduleLast();
- if ( DXerciseProcessID != 0 )
- {
- CDestroyProcess( DXerciseProcessID );
- DXerciseProcessID = 0;
- }
- DXMonitorStatus = 'S'; /* Say we are stopped */
- ReturnNonMovableMemory( DXStack );
- DXStack = NULL;
- DXExit();
- }
- /* Compute and display the numbers */
- ConvertTicksToSeconds( ( CurrentTime - StartTime ),
- &ElapsedSeconds,
- &Tenths );
- ConvertTicksToSeconds( ( CurrentTime - IntervalStartTime ),
- &IntervalSeconds,
- &Tenths );
-
- PositionOutputCursor( dXerciseScreen, 1, 0 );
- OutputToScreen( dXerciseScreen, "%-28.28s "," Disk" );
- OutputToScreen( dXerciseScreen, "%-9s "," KB/sec." );
- OutputToScreen( dXerciseScreen, "%-9s "," IOs/sec." );
- OutputToScreen( dXerciseScreen, "%-9s ","Ave. KB/s" );
- OutputToScreen( dXerciseScreen, "%-9s ","Total IOs" );
- OutputToScreen( dXerciseScreen, "%-5s\r\n","Error" );
- FirstDrive = ( CurrentPage - 1 ) * DISKS_PER_PAGE;
- /* note - LastDrive is really last drive index (0 based) plus 1) */
- if ( CurrentPage == MaxPage )
- {
- LastDrive = DriveCount;
- }
- else
- {
- LastDrive = CurrentPage * DISKS_PER_PAGE;
- }
- for ( DriveIndex = FirstDrive; DriveIndex < LastDrive; DriveIndex++ )
- {
- OutputToScreen( dXerciseScreen, "%-28.28s ", Disk[DriveIndex].Name );
- OutputToScreen( dXerciseScreen, "%9d ", ( Performance[DriveIndex].CurrentIOsCompleted * DXKBytesPerIO ) / IntervalSeconds );
- OutputToScreen( dXerciseScreen, "%9d ", Performance[DriveIndex].CurrentIOsCompleted / DXUpdateInterval );
- OutputToScreen( dXerciseScreen, "%9d ", ( Performance[DriveIndex].TotalIOsCompleted * DXKBytesPerIO ) / ElapsedSeconds );
- OutputToScreen( dXerciseScreen, "%9d ", Performance[DriveIndex].TotalIOsCompleted );
- OutputToScreen( dXerciseScreen, "%5d ", Performance[DriveIndex].TotalErrors );
- OutputToScreen( dXerciseScreen, "\r\n" );
- }
-
- /* Aggregate numbers */
- OutputToScreen( dXerciseScreen, "\r\n" );
- OutputToScreen( dXerciseScreen, "%-28.28s ", "Aggregate Disk Performance" );
- OutputToScreen( dXerciseScreen, "%9d ", ( Aggregate.CurrentIOsCompleted * DXKBytesPerIO ) / IntervalSeconds );
- OutputToScreen( dXerciseScreen, "%9d ", Aggregate.CurrentIOsCompleted / DXUpdateInterval );
- OutputToScreen( dXerciseScreen, "%9d ", ( Aggregate.TotalIOsCompleted * DXKBytesPerIO ) / ElapsedSeconds );
- OutputToScreen( dXerciseScreen,"%9d ", Aggregate.TotalIOsCompleted );
- OutputToScreen( dXerciseScreen,"%5d ", Aggregate.TotalErrors );
- OutputToScreen( dXerciseScreen, "\r\n" );
-
- /* Utilization */
- utilization = 100 - ((NumberOfPollingLoops * 100 +
- (MaximumNumberOfPollingLoops >> 1)) / MaximumNumberOfPollingLoops);
- OutputToScreen( dXerciseScreen, "Server utilization %6d%%\r\n", utilization);
-
- /* Instructions */
- OutputToScreen( dXerciseScreen, "Q to quit or page number to view\n\r" );
-
- /* Zero current counts */
- for ( DriveIndex = 0; DriveIndex < DriveCount; DriveIndex++ )
- {
- Performance[DriveIndex].CurrentIOsCompleted = 0;
- }
- Aggregate.CurrentIOsCompleted = 0;
-
- }
- }
-
- void
- DXercise()
- {
- struct rstruct *Request;
- int i;
- while (1)
- {
- if ( DXMonitorStatus != 'R' ) /* If not running go to sleep forever */
- /* and wait to be destroyed */
- {
- while (1) CSleepUntilInterrupt();
- }
- for ( i = 0; i < DriveCount; i++ )
- {
- while ( Disk[i].OutstandingIOs < DXMaxIOsPerDrive )
- {
- /* Start another IO */
- Disk[i].OutstandingIOs++;
- switch ( DXAccessType )
- {
- case 'F':
-
- Disk[i].Sector = 0;
- break;
-
- case 'S':
-
- Disk[i].Sector += DXKBytesPerIO * 2;
- if ( Disk[i].Sector + ( DXKBytesPerIO * 2 ) >=
- Disk[i].Size ) Disk[i].Sector = 0;
- break;
-
- case 'R':
-
- Disk[i].Sector = RandomSectors[Disk[i].Random] % ( Disk[i].Size - ( DXKBytesPerIO * 2 ) );
- Disk[i].Random++;
- if ( Disk[i].Random == RandomSectorsCount )
- Disk[i].Random = 0;
- break;
- }
- /* Build a physical I/O request */
- Request = DXAllocateRequest();
- Request->RReserved = (LONG)Disk[i].LDisk;
- Request->RDiskID = Disk[i].LDisk->LPLink;
- Request->RStartingSector = Disk[i].Sector +
- Disk[i].LDisk->LPartitionOffset;
- Request->RMirrorLink = Request;
- Request->RRoutine = DXIOCompletion;
- Request->REBXValue = i;
- Request->RNumberOfSectors = DXKBytesPerIO * 2;
- Request->RBufferAddress = CommonBuffer;
- if ( DXReadWrite == 'W' )
- Request->RFunction = 1;
- else
- Request->RFunction = 0;
- PDiskRequest( Request );
- AggregateOutstandingIOs++;
- }
- }
- DXWakeRequested = 0;
- CSleepUntilInterrupt();
- }
- }
-
- void
- LockAlert( LONG skipParameter, LONG criticality, int DriveIndex )
- {
- _UNUSED( skipParameter );
- _UNUSED( criticality );
- _UNUSED( DriveIndex );
- }
-
- void
- DXIOCompletion( struct rstruct *currentRequest )
- {
- int i;
- i = (int)currentRequest->REBXValue;
- --Disk[i].OutstandingIOs;
- --AggregateOutstandingIOs;
- /* Update global aggregate counts */
- Aggregate.TotalIOsCompleted++;
- Aggregate.CurrentIOsCompleted++;
- /* Update individual disk counts */
- Performance[i].TotalIOsCompleted++;
- Performance[i].CurrentIOsCompleted++;
- /* Count errors */
- if ( currentRequest-> RCompletionCode != 0 )
- {
- Performance[i].TotalErrors++;
- Aggregate.TotalErrors++;
- }
- DXReturnRequest( currentRequest );
- if ( !DXWakeRequested )
- {
- DXWakeRequested = -1;
- CRescheduleFromInterrupt( DXerciseProcessID );
- }
- }
-
- struct rstruct *DXAllocateRequest()
- {
- struct rstruct *Request;
- if ( DXRequestFreeListHead == NULL ) Abend( "DX Out of request nodes");
- Request = DXRequestFreeListHead;
- DXRequestFreeListHead = Request->RLink;
- return ( Request );
- }
-
- void
- DXReturnRequest( struct rstruct *Request)
- {
- if ( DXRequestFreeListHead == NULL )
- {
- DXRequestFreeListHead = Request;
- DXRequestFreeListTail = Request;
- }
- else
- {
- DXRequestFreeListTail->RLink = Request;
- DXRequestFreeListTail = Request;
- }
- DXRequestFreeListTail->RLink = NULL;
- }
-
- void
- MakeDriveDescription(
- struct pdstruct *driveID,
- BYTE *buffer)
- {
- while (*buffer != 0)
- ++buffer;
- sprintf(buffer, (BYTE *)"%15.15s ", driveID->PDeviceName + 1);
- while (*buffer != 0)
- ++buffer;
- sprintf(buffer, (BYTE *)"%d-", driveID->PCardNumber);
- while (*buffer != 0)
- ++buffer;
- sprintf(buffer, (BYTE *)"%d-", driveID->PControllerNumber);
- while (*buffer != 0)
- ++buffer;
- sprintf(buffer, (BYTE *)"%d", driveID->PDriveNumber);
- }
-