|
|
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
utils.c
Abstract:
SCSI class driver routines
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ClassGetDeviceParameter)
#pragma alloc_text(PAGE, ClassScanForSpecial)
#pragma alloc_text(PAGE, ClassSetDeviceParameter)
#endif
// custom string match -- careful!
BOOLEAN ClasspMyStringMatches(IN PCHAR StringToMatch OPTIONAL, IN PCHAR TargetString) { ULONG length; // strlen returns an int, not size_t (!)
PAGED_CODE(); ASSERT(TargetString); // if no match requested, return TRUE
if (StringToMatch == NULL) { return TRUE; } // cache the string length for efficiency
length = strlen(StringToMatch); // ZERO-length strings may only match zero-length strings
if (length == 0) { return (strlen(TargetString) == 0); } // strncmp returns zero if the strings match
return (strncmp(StringToMatch, TargetString, length) == 0); }
VOID ClassGetDeviceParameter( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PWSTR SubkeyName OPTIONAL, IN PWSTR ParameterName, IN OUT PULONG ParameterValue // also default value
) { NTSTATUS status; RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; HANDLE deviceParameterHandle; HANDLE deviceSubkeyHandle; ULONG defaultParameterValue;
PAGED_CODE();
//
// open the given parameter
//
status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &deviceParameterHandle);
if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
UNICODE_STRING subkeyName; OBJECT_ATTRIBUTES objectAttributes = {0};
RtlInitUnicodeString(&subkeyName, SubkeyName); InitializeObjectAttributes(&objectAttributes, &subkeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, deviceParameterHandle, NULL);
status = ZwOpenKey(&deviceSubkeyHandle, KEY_READ, &objectAttributes); if (!NT_SUCCESS(status)) { ZwClose(deviceParameterHandle); }
}
if (NT_SUCCESS(status)) {
defaultParameterValue = *ParameterValue;
queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; queryTable->Name = ParameterName; queryTable->EntryContext = ParameterValue; queryTable->DefaultType = REG_DWORD; queryTable->DefaultData = NULL; queryTable->DefaultLength = 0;
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR)(SubkeyName ? deviceSubkeyHandle : deviceParameterHandle), queryTable, NULL, NULL); if (!NT_SUCCESS(status)) { *ParameterValue = defaultParameterValue; // use default value
}
//
// close what we open
//
if (SubkeyName) { ZwClose(deviceSubkeyHandle); }
ZwClose(deviceParameterHandle); }
if (!NT_SUCCESS(status)) {
//
// Windows 2000 SP3 uses the driver-specific key, so look in there
//
status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &deviceParameterHandle);
if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
UNICODE_STRING subkeyName; OBJECT_ATTRIBUTES objectAttributes = {0};
RtlInitUnicodeString(&subkeyName, SubkeyName); InitializeObjectAttributes(&objectAttributes, &subkeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, deviceParameterHandle, NULL);
status = ZwOpenKey(&deviceSubkeyHandle, KEY_READ, &objectAttributes);
if (!NT_SUCCESS(status)) { ZwClose(deviceParameterHandle); } }
if (NT_SUCCESS(status)) {
defaultParameterValue = *ParameterValue;
queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; queryTable->Name = ParameterName; queryTable->EntryContext = ParameterValue; queryTable->DefaultType = REG_DWORD; queryTable->DefaultData = NULL; queryTable->DefaultLength = 0;
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR)(SubkeyName ? deviceSubkeyHandle : deviceParameterHandle), queryTable, NULL, NULL); if (NT_SUCCESS(status)) {
//
// Migrate the value over to the device-specific key
//
ClassSetDeviceParameter(FdoExtension, SubkeyName, ParameterName, *ParameterValue);
} else {
//
// Use the default value
//
*ParameterValue = defaultParameterValue; }
if (SubkeyName) { ZwClose(deviceSubkeyHandle); }
ZwClose(deviceParameterHandle); } }
return;
} // end ClassGetDeviceParameter()
NTSTATUS ClassSetDeviceParameter( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PWSTR SubkeyName OPTIONAL, IN PWSTR ParameterName, IN ULONG ParameterValue) { NTSTATUS status; HANDLE deviceParameterHandle; HANDLE deviceSubkeyHandle;
PAGED_CODE();
//
// open the given parameter
//
status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ | KEY_WRITE, &deviceParameterHandle);
if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
UNICODE_STRING subkeyName; OBJECT_ATTRIBUTES objectAttributes;
RtlInitUnicodeString(&subkeyName, SubkeyName); InitializeObjectAttributes(&objectAttributes, &subkeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, deviceParameterHandle, NULL);
status = ZwCreateKey(&deviceSubkeyHandle, KEY_READ | KEY_WRITE, &objectAttributes, 0, NULL, 0, NULL); if (!NT_SUCCESS(status)) { ZwClose(deviceParameterHandle); }
}
if (NT_SUCCESS(status)) {
status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE, (PWSTR) (SubkeyName ? deviceSubkeyHandle : deviceParameterHandle), ParameterName, REG_DWORD, &ParameterValue, sizeof(ULONG));
//
// close what we open
//
if (SubkeyName) { ZwClose(deviceSubkeyHandle); }
ZwClose(deviceParameterHandle); }
return status;
} // end ClassSetDeviceParameter()
/*
* ClassScanForSpecial * * This routine was written to simplify scanning for special * hardware based upon id strings. it does not check the registry. */
VOID ClassScanForSpecial( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[], IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function) { PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; PUCHAR vendorId; PUCHAR productId; PUCHAR productRevision; UCHAR nullString[] = ""; ULONG j;
PAGED_CODE(); ASSERT(DeviceList); ASSERT(Function);
deviceDescriptor = FdoExtension->DeviceDescriptor;
if (DeviceList == NULL) { return; } if (Function == NULL) { return; }
//
// SCSI sets offsets to -1, ATAPI sets to 0. check for both.
//
if (deviceDescriptor->VendorIdOffset != 0 && deviceDescriptor->VendorIdOffset != -1) { vendorId = ((PUCHAR)deviceDescriptor); vendorId += deviceDescriptor->VendorIdOffset; } else { vendorId = nullString; } if (deviceDescriptor->ProductIdOffset != 0 && deviceDescriptor->ProductIdOffset != -1) { productId = ((PUCHAR)deviceDescriptor); productId += deviceDescriptor->ProductIdOffset; } else { productId = nullString; } if (deviceDescriptor->ProductRevisionOffset != 0 && deviceDescriptor->ProductRevisionOffset != -1) { productRevision = ((PUCHAR)deviceDescriptor); productRevision += deviceDescriptor->ProductRevisionOffset; } else { productRevision = nullString; }
//
// loop while the device list is valid (not null-filled)
//
for (;(DeviceList->VendorId != NULL || DeviceList->ProductId != NULL || DeviceList->ProductRevision != NULL);DeviceList++) {
if (ClasspMyStringMatches(DeviceList->VendorId, vendorId) && ClasspMyStringMatches(DeviceList->ProductId, productId) && ClasspMyStringMatches(DeviceList->ProductRevision, productRevision) ) {
DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching " "controller Ven: %s Prod: %s Rev: %s\n", vendorId, productId, productRevision));
//
// pass the context to the call back routine and exit
//
(Function)(FdoExtension, DeviceList->Data);
//
// for CHK builds, try to prevent wierd stacks by having a debug
// print here. it's a hack, but i know of no other way to prevent
// the stack from being wrong.
//
DebugPrint((16, "ClasspScanForSpecialByInquiry: " "completed callback\n")); return;
} // else the strings did not match
} // none of the devices matched.
DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n", FdoExtension->DeviceObject)); return;
} // end ClasspScanForSpecialByInquiry()
//
// In order to provide better performance without the need to reboot,
// we need to implement a self-adjusting method to set and clear the
// srb flags based upon current performance.
//
// whenever there is an error, immediately grab the spin lock. the
// MP perf hit here is acceptable, since we're in an error path. this
// is also neccessary because we are guaranteed to be modifying the
// SRB flags here, setting SuccessfulIO to zero, and incrementing the
// actual error count (which is always done within this spinlock).
//
// whenever there is no error, increment a counter. if there have been
// errors on the device, and we've enabled dynamic perf, *and* we've
// just crossed the perf threshhold, then grab the spin lock and
// double check that the threshhold has, indeed been hit(*). then
// decrement the error count, and if it's dropped sufficiently, undo
// some of the safety changes made in the SRB flags due to the errors.
//
// * this works in all cases. even if lots of ios occur after the
// previous guy went in and cleared the successfulio counter, that
// just means that we've hit the threshhold again, and so it's proper
// to run the inner loop again.
//
VOID ClasspPerfIncrementErrorCount( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ) { PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; KIRQL oldIrql; ULONG errors;
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
fdoData->Perf.SuccessfulIO = 0; // implicit interlock
errors = InterlockedIncrement(&FdoExtension->ErrorCount);
if (errors >= CLASS_ERROR_LEVEL_1) {
//
// If the error count has exceeded the error limit, then disable
// any tagged queuing, multiple requests per lu queueing
// and sychronous data transfers.
//
// Clearing the no queue freeze flag prevents the port driver
// from sending multiple requests per logical unit.
//
CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: " "Too many errors; disabling tagged queuing and " "synchronous data tranfers.\n"));
}
if (errors >= CLASS_ERROR_LEVEL_2) {
//
// If a second threshold is reached, disable disconnects.
//
SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: " "Too many errors; disabling disconnects.\n")); }
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); return; }
VOID ClasspPerfIncrementSuccessfulIo( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ) { PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; KIRQL oldIrql; ULONG errors; ULONG succeeded = 0;
//
// don't take a hit from the interlocked op unless we're in
// a degraded state and we've got a threshold to hit.
//
if (FdoExtension->ErrorCount == 0) { return; }
if (fdoData->Perf.ReEnableThreshhold == 0) { return; }
succeeded = InterlockedIncrement(&fdoData->Perf.SuccessfulIO); if (succeeded < fdoData->Perf.ReEnableThreshhold) { return; }
//
// if we hit the threshold, grab the spinlock and verify we've
// actually done so. this allows us to ignore the spinlock 99%
// of the time.
//
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
//
// re-read the value, so we don't run this multiple times
// for a single threshhold being hit. this keeps errorcount
// somewhat useful.
//
succeeded = fdoData->Perf.SuccessfulIO;
if ((FdoExtension->ErrorCount != 0) && (fdoData->Perf.ReEnableThreshhold <= succeeded) ) {
fdoData->Perf.SuccessfulIO = 0; // implicit interlock
ASSERT(FdoExtension->ErrorCount > 0); errors = InterlockedDecrement(&FdoExtension->ErrorCount);
//
// note: do in reverse order of the sets "just in case"
//
if (errors < CLASS_ERROR_LEVEL_2) { if (errors == CLASS_ERROR_LEVEL_2 - 1) { DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: " "Error level 2 no longer required.\n")); } if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_DISABLE_DISCONNECT)) { CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); } }
if (errors < CLASS_ERROR_LEVEL_1) { if (errors == CLASS_ERROR_LEVEL_1 - 1) { DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: " "Error level 1 no longer required.\n")); } if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) { CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); } if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE)) { SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); } if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE)) { SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); } } } // end of threshhold definitely being hit for first time
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); return; }
PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen) { PMDL mdl;
mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL); if (mdl){ try { /*
* We are reading from the device. * Therefore, the device is WRITING to the locked memory. * So we request IoWriteAccess. */ MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
} except(EXCEPTION_EXECUTE_HANDLER) { NTSTATUS status = GetExceptionCode();
DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status)); IoFreeMdl(mdl); mdl = NULL; } } else { DBGWARN(("BuildReadMdl: IoAllocateMdl failed")); }
return mdl; }
VOID FreeDeviceInputMdl(PMDL Mdl) { MmUnlockPages(Mdl); IoFreeMdl(Mdl); }
#if 0
VOID ClasspPerfResetCounters( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ) { PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; KIRQL oldIrql;
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); DebugPrint((ClassDebugError, "ClasspPerfResetCounters: " "Resetting all perf counters.\n")); fdoData->Perf.SuccessfulIO = 0; FdoExtension->ErrorCount = 0;
if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_DISABLE_DISCONNECT)) { CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); } if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) { CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); } if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE)) { SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); } if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE)) { SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); } KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); return; } #endif
|