501 lines
12 KiB
C
501 lines
12 KiB
C
|
|
|||
|
/*++
|
|||
|
|
|||
|
Copyright (C) Microsoft Corporation, 1997 - 1998
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
pmwmicnt.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This file contains the routines to manage and maintain the disk perf
|
|||
|
counters. The counter structure is hidden from the various drivers.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bruce Worthington 26-Oct-1998
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#define RTL_USE_AVL_TABLES 0
|
|||
|
|
|||
|
#include <ntosp.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <ntddvol.h>
|
|||
|
#include <ntdddisk.h>
|
|||
|
#include <wmilib.h>
|
|||
|
#include <partmgr.h>
|
|||
|
|
|||
|
typedef struct _PMWMICOUNTER_CONTEXT
|
|||
|
{
|
|||
|
ULONG EnableCount;
|
|||
|
ULONG Processors;
|
|||
|
ULONG QueueDepth;
|
|||
|
PDISK_PERFORMANCE *DiskCounters;
|
|||
|
LARGE_INTEGER LastIdleClock;
|
|||
|
} PMWMICOUNTER_CONTEXT, *PPMWMICOUNTER_CONTEXT;
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PmWmiCounterEnable(
|
|||
|
IN OUT PPMWMICOUNTER_CONTEXT* CounterContext
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PmWmiCounterDisable(
|
|||
|
IN PPMWMICOUNTER_CONTEXT* CounterContext,
|
|||
|
IN BOOLEAN ForceDisable,
|
|||
|
IN BOOLEAN DeallocateOnZero
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PmWmiCounterIoStart(
|
|||
|
IN PPMWMICOUNTER_CONTEXT CounterContext,
|
|||
|
OUT PLARGE_INTEGER TimeStamp
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PmWmiCounterIoComplete(
|
|||
|
IN PPMWMICOUNTER_CONTEXT CounterContext,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PLARGE_INTEGER TimeStamp
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PmWmiCounterQuery(
|
|||
|
IN PPMWMICOUNTER_CONTEXT CounterContext,
|
|||
|
IN OUT PDISK_PERFORMANCE CounterBuffer,
|
|||
|
IN PWCHAR StorageManagerName,
|
|||
|
IN ULONG StorageDeviceNumber
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, PmWmiCounterEnable)
|
|||
|
#pragma alloc_text(PAGE, PmWmiCounterDisable)
|
|||
|
#pragma alloc_text(PAGE, PmWmiCounterQuery)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PmWmiCounterEnable(
|
|||
|
IN OUT PPMWMICOUNTER_CONTEXT* CounterContext
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will allocate and initialize a PMWMICOUNTER_CONTEXT structure
|
|||
|
for *CounterContext if it is NULL. Otherwise, an enable count is
|
|||
|
incremented.
|
|||
|
Must be called at IRQ <= SYNCH_LEVEL (APC)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CounterContext - Supplies a pointer to the PMWMICOUNTER_CONTEXT pointer
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG buffersize;
|
|||
|
ULONG processors;
|
|||
|
ULONG i = 0;
|
|||
|
PCHAR buffer;
|
|||
|
PPMWMICOUNTER_CONTEXT HoldContext; // Holds context during initialization
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (CounterContext == NULL)
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
|
|||
|
if (*CounterContext != NULL) {
|
|||
|
if ((*CounterContext)->EnableCount == 0) {
|
|||
|
(*CounterContext)->QueueDepth = 0;
|
|||
|
PmWmiGetClock((*CounterContext)->LastIdleClock, NULL);
|
|||
|
}
|
|||
|
InterlockedIncrement(& (*CounterContext)->EnableCount);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
processors = KeNumberProcessors;
|
|||
|
|
|||
|
buffersize= sizeof(PMWMICOUNTER_CONTEXT) +
|
|||
|
((sizeof(PDISK_PERFORMANCE) + sizeof(DISK_PERFORMANCE))
|
|||
|
* processors);
|
|||
|
buffer = (PCHAR) ExAllocatePoolWithTag(NonPagedPool, buffersize,
|
|||
|
PARTMGR_TAG_PARTITION_ENTRY);
|
|||
|
|
|||
|
if (buffer == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(buffer, buffersize);
|
|||
|
HoldContext = (PPMWMICOUNTER_CONTEXT) buffer;
|
|||
|
buffer += sizeof(PMWMICOUNTER_CONTEXT);
|
|||
|
HoldContext->DiskCounters = (PDISK_PERFORMANCE*) buffer;
|
|||
|
buffer += sizeof(PDISK_PERFORMANCE) * processors;
|
|||
|
for (i=0; i<processors; i++) {
|
|||
|
HoldContext->DiskCounters[i] = (PDISK_PERFORMANCE) buffer;
|
|||
|
buffer += sizeof(DISK_PERFORMANCE);
|
|||
|
}
|
|||
|
|
|||
|
HoldContext->EnableCount = 1;
|
|||
|
HoldContext->Processors = processors;
|
|||
|
PmWmiGetClock(HoldContext->LastIdleClock, NULL);
|
|||
|
|
|||
|
*CounterContext = HoldContext;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOLEAN // Return value indicates if counters are still enabled
|
|||
|
PmWmiCounterDisable(
|
|||
|
IN PPMWMICOUNTER_CONTEXT* CounterContext,
|
|||
|
IN BOOLEAN ForceDisable,
|
|||
|
IN BOOLEAN DeallocateOnZero
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine decrements the enable count and, if deallocation is requested,
|
|||
|
frees the *CounterContext PMWMICOUNTER_CONTEXT data structure when the
|
|||
|
enable count reaches zero. The enable count may also be forced to zero,
|
|||
|
if explicitly requested.
|
|||
|
Must be called at IRQ <= SYNCH_LEVEL (APC)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CounterContext - Supplies a pointer to the PMWMICOUNTER_CONTEXT pointer
|
|||
|
|
|||
|
ForceDisable - If TRUE, force enable count to zero (rather than decrement)
|
|||
|
|
|||
|
DeallocateOnZero - If TRUE, deallocate PMWMICOUNTER_CONTEXT when enable
|
|||
|
count reaches zero
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Boolean indicating if the enable count is still non-zero (i.e., counters
|
|||
|
are still enabled!)
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LONG enablecount = 0;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (CounterContext == NULL)
|
|||
|
return FALSE;
|
|||
|
|
|||
|
if (*CounterContext != NULL) {
|
|||
|
if (ForceDisable) {
|
|||
|
InterlockedExchange(& (*CounterContext)->EnableCount, enablecount);
|
|||
|
enablecount = 0;
|
|||
|
} else if ((enablecount =
|
|||
|
InterlockedDecrement(&(*CounterContext)->EnableCount))!=0) {
|
|||
|
if (enablecount > 0) {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
enablecount = InterlockedIncrement(&(*CounterContext)->EnableCount);
|
|||
|
}
|
|||
|
if (!enablecount && DeallocateOnZero) {
|
|||
|
ExFreePool(*CounterContext);
|
|||
|
*CounterContext = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return FALSE; // counters disabled
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
PmWmiCounterIoStart(
|
|||
|
IN PPMWMICOUNTER_CONTEXT CounterContext,
|
|||
|
OUT PLARGE_INTEGER TimeStamp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine increments the queue counter in CounterContext and records
|
|||
|
the current time in TimeStamp. If the queue was empty prior to this call,
|
|||
|
the idle time counter is also accumulated.
|
|||
|
Can be called at IRQ <= DISPATCH_LEVEL
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CounterContext - Supplies a pointer to a PMWMICOUNTER_CONTEXT structure.
|
|||
|
|
|||
|
TimeStamp - Address at which to store the current time
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
void
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG processor = (ULONG) KeGetCurrentProcessorNumber();
|
|||
|
ULONG queueLen;
|
|||
|
|
|||
|
//
|
|||
|
// Increment queue depth counter.
|
|||
|
//
|
|||
|
|
|||
|
queueLen = InterlockedIncrement(&CounterContext->QueueDepth);
|
|||
|
|
|||
|
//
|
|||
|
// Time stamp current request start.
|
|||
|
//
|
|||
|
|
|||
|
PmWmiGetClock((*TimeStamp), NULL);
|
|||
|
|
|||
|
if (queueLen == 1) {
|
|||
|
CounterContext->DiskCounters[processor]->IdleTime.QuadPart +=
|
|||
|
TimeStamp->QuadPart - CounterContext->LastIdleClock.QuadPart;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
PmWmiCounterIoComplete(
|
|||
|
IN PPMWMICOUNTER_CONTEXT CounterContext,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PLARGE_INTEGER TimeStamp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine decrements the queue counter in CounterContext and increments
|
|||
|
the split counter and read or write byte, time, and count counters with
|
|||
|
information from the Irp. If the queue is now empty, the current
|
|||
|
time is stored for future use in accumulating the idle time counter.
|
|||
|
Can be called at IRQ <= DISPATCH_LEVEL
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CounterContext - Supplies a pointer to a PMWMICOUNTER_CONTEXT structure.
|
|||
|
|
|||
|
Irp - relevant IRP
|
|||
|
|
|||
|
TimeStamp - Time of the corresponding PmWmiCounterIoStart call
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
void
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PDISK_PERFORMANCE partitionCounters;
|
|||
|
LARGE_INTEGER timeStampComplete;
|
|||
|
LONG queueLen;
|
|||
|
|
|||
|
partitionCounters
|
|||
|
= CounterContext->DiskCounters[(ULONG)KeGetCurrentProcessorNumber()];
|
|||
|
//
|
|||
|
// Time stamp current request complete.
|
|||
|
//
|
|||
|
|
|||
|
PmWmiGetClock(timeStampComplete, NULL);
|
|||
|
TimeStamp->QuadPart = timeStampComplete.QuadPart - TimeStamp->QuadPart;
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the queue depth counters for the volume. This is
|
|||
|
// done without the spinlock using the Interlocked functions.
|
|||
|
// This is the only
|
|||
|
// legal way to do this.
|
|||
|
//
|
|||
|
|
|||
|
queueLen = InterlockedDecrement(&CounterContext->QueueDepth);
|
|||
|
|
|||
|
if (queueLen < 0) {
|
|||
|
queueLen = InterlockedIncrement(&CounterContext->QueueDepth);
|
|||
|
}
|
|||
|
|
|||
|
if (queueLen == 0) {
|
|||
|
CounterContext->LastIdleClock = timeStampComplete;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update counters protection.
|
|||
|
//
|
|||
|
|
|||
|
if (irpStack->MajorFunction == IRP_MJ_READ) {
|
|||
|
|
|||
|
//
|
|||
|
// Add bytes in this request to bytes read counters.
|
|||
|
//
|
|||
|
|
|||
|
partitionCounters->BytesRead.QuadPart += Irp->IoStatus.Information;
|
|||
|
|
|||
|
//
|
|||
|
// Increment read requests processed counters.
|
|||
|
//
|
|||
|
|
|||
|
partitionCounters->ReadCount++;
|
|||
|
|
|||
|
//
|
|||
|
// Calculate request processing time.
|
|||
|
//
|
|||
|
|
|||
|
partitionCounters->ReadTime.QuadPart += TimeStamp->QuadPart;
|
|||
|
}
|
|||
|
|
|||
|
else {
|
|||
|
|
|||
|
//
|
|||
|
// Add bytes in this request to bytes write counters.
|
|||
|
//
|
|||
|
|
|||
|
partitionCounters->BytesWritten.QuadPart += Irp->IoStatus.Information;
|
|||
|
|
|||
|
//
|
|||
|
// Increment write requests processed counters.
|
|||
|
//
|
|||
|
|
|||
|
partitionCounters->WriteCount++;
|
|||
|
|
|||
|
//
|
|||
|
// Calculate request processing time.
|
|||
|
//
|
|||
|
|
|||
|
partitionCounters->WriteTime.QuadPart += TimeStamp->QuadPart;
|
|||
|
}
|
|||
|
|
|||
|
if (Irp->Flags & IRP_ASSOCIATED_IRP) {
|
|||
|
partitionCounters->SplitCount++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
PmWmiCounterQuery(
|
|||
|
IN PPMWMICOUNTER_CONTEXT CounterContext,
|
|||
|
IN OUT PDISK_PERFORMANCE TotalCounters,
|
|||
|
IN PWCHAR StorageManagerName,
|
|||
|
IN ULONG StorageDeviceNumber
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine combines all of the per-processor counters in CounterContext
|
|||
|
into TotalCounters. The current time is also included.
|
|||
|
Must be called at IRQ <= SYNCH_LEVEL (APC)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
CounterContext - Supplies a pointer to a PMWMICOUNTER_CONTEXT structure.
|
|||
|
|
|||
|
TotalCounters - Pointer to a DISK_PERFORMANCE structure to fill with the
|
|||
|
current counter status
|
|||
|
|
|||
|
StorageManagerName - Supplies an 8-character storage manager unicode string
|
|||
|
|
|||
|
StorageDeviceNumber - Supplies a storage device number (unique within
|
|||
|
the storage manager)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
void
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
LARGE_INTEGER frequency;
|
|||
|
#ifdef USE_PERF_CTR
|
|||
|
LARGE_INTEGER perfctr;
|
|||
|
#endif
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
RtlZeroMemory(TotalCounters, sizeof(DISK_PERFORMANCE));
|
|||
|
|
|||
|
KeQuerySystemTime(&TotalCounters->QueryTime);
|
|||
|
frequency.QuadPart = 0;
|
|||
|
|
|||
|
#ifdef USE_PERF_CTR
|
|||
|
perfctr = KeQueryPerformanceCounter(&frequency);
|
|||
|
#endif
|
|||
|
|
|||
|
TotalCounters->QueueDepth = CounterContext->QueueDepth;
|
|||
|
for (i = 0; i < CounterContext->Processors; i++) {
|
|||
|
PDISK_PERFORMANCE IndividualCounter = CounterContext->DiskCounters[i];
|
|||
|
TotalCounters->BytesRead.QuadPart
|
|||
|
+= IndividualCounter->BytesRead.QuadPart;
|
|||
|
TotalCounters->BytesWritten.QuadPart
|
|||
|
+= IndividualCounter->BytesWritten.QuadPart;
|
|||
|
TotalCounters->ReadCount += IndividualCounter->ReadCount;
|
|||
|
TotalCounters->WriteCount += IndividualCounter->WriteCount;
|
|||
|
TotalCounters->SplitCount += IndividualCounter->SplitCount;
|
|||
|
#ifdef USE_PERF_CTR
|
|||
|
if (frequency.QuadPart > 0) {
|
|||
|
TotalCounters->ReadTime.QuadPart +=
|
|||
|
IndividualCounter->ReadTime.QuadPart * 10000000
|
|||
|
/ frequency.QuadPart;
|
|||
|
TotalCounters->WriteTime.QuadPart +=
|
|||
|
IndividualCounter->WriteTime.QuadPart * 10000000
|
|||
|
/ frequency.QuadPart;
|
|||
|
TotalCounters->IdleTime.QuadPart +=
|
|||
|
IndividualCounter->IdleTime.QuadPart * 10000000
|
|||
|
/ frequency.QuadPart;
|
|||
|
}
|
|||
|
else
|
|||
|
#endif
|
|||
|
{
|
|||
|
TotalCounters->ReadTime.QuadPart
|
|||
|
+= IndividualCounter->ReadTime.QuadPart;
|
|||
|
TotalCounters->WriteTime.QuadPart
|
|||
|
+= IndividualCounter->WriteTime.QuadPart;
|
|||
|
TotalCounters->IdleTime.QuadPart
|
|||
|
+= IndividualCounter->IdleTime.QuadPart;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (TotalCounters->QueueDepth == 0) {
|
|||
|
LARGE_INTEGER difference;
|
|||
|
|
|||
|
difference.QuadPart
|
|||
|
#ifdef USE_PERF_CTR
|
|||
|
= perfctr.QuadPart -
|
|||
|
#else
|
|||
|
= TotalCounters->QueryTime.QuadPart -
|
|||
|
#endif
|
|||
|
CounterContext->LastIdleClock.QuadPart;
|
|||
|
|
|||
|
if (frequency.QuadPart > 0) {
|
|||
|
TotalCounters->IdleTime.QuadPart +=
|
|||
|
#ifdef USE_PERF_CTR
|
|||
|
10000000 * difference.QuadPart / frequency.QuadPart;
|
|||
|
#else
|
|||
|
difference.QuadPart;
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
TotalCounters->StorageDeviceNumber = StorageDeviceNumber;
|
|||
|
RtlCopyMemory(
|
|||
|
&TotalCounters->StorageManagerName[0],
|
|||
|
&StorageManagerName[0],
|
|||
|
sizeof(WCHAR) * 8);
|
|||
|
}
|