[CLASSPNP] Import Microsoft SCSI class driver from GitHub

The source code is licensed under MS-PL license, taken from Windows Driver Samples
repository (https://github.com/microsoft/Windows-driver-samples/tree/master/storage/class/classpnp/)
Synched with commit 88541f70c4273ecd30c8c7c72135bc038a00fd88
The driver is written for Windows 8+, so we compile it with ntoskrnl_vista
statically linked and with NTDDI_WIN8 defined

CORE-17129
This commit is contained in:
Victor Perevertkin 2020-08-29 06:06:22 +03:00
parent d9864ca6c5
commit bf1b3cb175
No known key found for this signature in database
GPG key ID: C750B7222E9C7830
24 changed files with 45624 additions and 0 deletions

View file

@ -0,0 +1,51 @@
spec2def(classpnp.sys classpnp.spec ADD_IMPORTLIB)
remove_definitions(-D_WIN32_WINNT=0x502)
list(APPEND SOURCE
autorun.c
class.c
classwmi.c
clntirp.c
create.c
data.c
debug.c
dictlib.c
dispatch.c
guid.c
history.c
lock.c
obsolete.c
power.c
retry.c
srblib.c
utils.c
xferpkt.c)
add_library(classpnp MODULE
${SOURCE}
class.rc
${CMAKE_CURRENT_BINARY_DIR}/classpnp.def)
target_compile_definitions(classpnp PUBLIC
DEBUG_USE_KDPRINT
_WIN32_WINNT=0x602
NTDDI_VERSION=0x06020000) # NTDDI_WIN8
target_compile_definitions(classpnp PRIVATE
CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0
CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100
CLASS_GLOBAL_USE_DELAYED_RETRY=1
CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0
CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512
CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512)
if(GCC)
target_compile_options(classpnp PRIVATE -Wno-unused-but-set-variable -Wno-pointer-to-int-cast -Wno-switch)
endif()
set_module_type(classpnp kernelmodedriver)
target_link_libraries(classpnp ntoskrnl_vista libcntpr ${PSEH_LIB})
add_importlibs(classpnp ntoskrnl hal)
add_cd_file(TARGET classpnp DESTINATION reactos/system32/drivers NO_CAB FOR all)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: class.rc
//
//--------------------------------------------------------------------------
#include <verrsrc.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_FILEDESCRIPTION_STR "SCSI Class System Dll"
#define VER_INTERNALNAME_STR "Classpnp.sys"
#define VER_ORIGINALFILENAME_STR "Classpnp.sys"
#define VER_LANGNEUTRAL
#include "common.ver"

View file

@ -0,0 +1,178 @@
#pragma autorecover
#pragma namespace("\\\\.\\Root\\WMI")
[WMI, guid("F903D6EC-8647-438f-9E42-891F4733EDAF")]
class MSStorageDriver_ScsiRequestBlock {
[read, WmiDataId(1), Description("Length")]
uint16 length;
[read, WmiDataId(2), DisplayInHex, Description("Function")]
uint8 function;
[read, WmiDataId(3), DisplayInHex, Description("SRB Status")]
uint8 srbStatus;
[read, WmiDataId(4), DisplayInHex, Description("SCSI Status")]
uint8 scsiStatus;
[read, WmiDataId(5), DisplayInHex, Description("Path ID")]
uint8 pathID;
[read, WmiDataId(6), DisplayInHex, Description("Target ID")]
uint8 targetID;
[read, WmiDataId(7), DisplayInHex, Description("LUN")]
uint8 lun;
[read, WmiDataId(8), DisplayInHex, Description("Queue Tag")]
uint8 queueTag;
[read, WmiDataId(9), DisplayInHex, Description("Queue Action")]
uint8 queueAction;
[read, WmiDataId(10), DisplayInHex, Description("CDB Length")]
uint8 cdbLength;
[read, WmiDataId(11), DisplayInHex, Description("Sense Info Buffer Length")]
uint8 senseInfoBufferLength;
[read, WmiDataId(12), DisplayInHex, Description("SRB Flags")]
uint32 srbFlags;
[read, WmiDataId(13), DisplayInHex, Description("Data Transfer Length")]
uint32 dataTransferLength;
[read, WmiDataId(14), DisplayInHex, Description("Time Out Value")]
uint32 timeOutValue;
[read, WmiDataId(15), DisplayInHex, Description("Data Buffer Pointer")]
uint64 dataBuffer;
[read, WmiDataId(16), DisplayInHex, Description("Sense Info Buffer Pointer")]
uint64 senseInfoBuffer;
[read, WmiDataId(17), DisplayInHex, Description("Next SRB Pointer")]
uint64 nextSRB;
[read, WmiDataId(18), DisplayInHex, Description("Original Request Pointer")]
uint64 originalRequest;
[read, WmiDataId(19), DisplayInHex, Description("SRB Extension Pointer")]
uint64 srbExtension;
[read, WmiDataId(20), DisplayInHex, Description("Internal Status")]
uint32 internalStatus;
[read, WmiDataId(21), DisplayInHex, Description("Reserved (only available in Win64)")]
uint32 reserved;
[read, WmiDataId(22), DisplayInHex, Description("CDB")]
uint8 cdb[16];
};
[WMI, guid("9065566F-5FD6-4b40-9961-98E3A3DD174E")]
class MSStorageDriver_SenseData {
[read, WmiDataId(1), Description("Error Code")]
uint8 errorCode;
[read, WmiDataId(2), Description("Valid")]
boolean valid;
[read, WmiDataId(3), Description("Segment Number")]
uint8 segmentNumber;
[read, WmiDataId(4), Description("Sense Key")]
uint8 senseKey;
[read, WmiDataId(5), Description("Reserved")]
boolean reserved;
[read, WmiDataId(6), Description("Incorrect Length")]
boolean incorrectLength;
[read, WmiDataId(7), Description("End Of Media")]
boolean endOfMedia;
[read, WmiDataId(8), Description("File Mark")]
boolean fileMark;
[read, WmiDataId(9), Description("Information")]
uint8 information[4];
[read, WmiDataId(10), Description("Additional Sense Length")]
uint8 additionalSenseLength;
[read, WmiDataId(11), Description("Command Specific Information")]
uint8 commandSpecificInformation[4];
[read, WmiDataId(12), Description("Additional Sense Code")]
uint8 additionalSenseCode;
[read, WmiDataId(13), Description("Additional Sense Code Qualifier")]
uint8 additionalSenseCodeQualifier;
[read, WmiDataId(14), Description("Field Replaceable Unit Code")]
uint8 fieldReplaceableUnitCode;
[read, WmiDataId(15), Description("Sense Key Specific")]
uint8 senseKeySpecific[3];
};
[WMI, guid("0C9BF007-50E9-407e-A9DA-0F33800E4B45")]
class MSStorageDriver_ClassErrorLogEntry {
[read, WmiDataId(1), Description("Tick Count")]
uint64 tickCount;
[read, WmiDataId(2), Description("Port Number")]
uint32 portNumber;
[read, WmiDataId(3), Description("Error Paging")]
boolean errorPaging;
[read, WmiDataId(4), Description("Error Retried")]
boolean errorRetried;
[read, WmiDataId(5), Description("Error Unhandled")]
boolean errorUnhandled;
[read, WmiDataId(6), DisplayInHex, Description("Error Reserved")]
uint8 errorReserved;
[read, WmiDataId(7), DisplayInHex, Description("Reserved")]
uint8 reserved[3];
[read, WmiDataId(8), Description("SCSI Request Block")]
MSStorageDriver_ScsiRequestBlock srb;
[read, WmiDataId(9), Description("Sense Data")]
MSStorageDriver_SenseData senseData;
[read, WmiDataId(10), Description("Event Time")]
datetime eventTime;
};
[Dynamic, Provider("WMIProv"),
WMI, Description("MS Storage Class Driver Error Log"),
guid("D5A9A51E-03F9-404d-9722-15F90EB07038"),
locale("MS\\0x409")]
class MSStorageDriver_ClassErrorLog {
[key, read]
string InstanceName;
[read]
boolean Active;
[read,
WmiDataId(1),
Description("Number of Error Log Entries")]
uint32 numEntries;
[read,
WmiDataId(2),
Description("Error Log Array")]
MSStorageDriver_ClassErrorLogEntry logEntries[16];
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,62 @@
@ stdcall ClassInitialize(ptr ptr ptr)
@ stdcall ClassInitializeEx(ptr ptr ptr)
@ stdcall ClassGetDescriptor(ptr ptr ptr)
@ stdcall ClassReadDriveCapacity(ptr)
@ stdcall ClassReleaseQueue(ptr)
@ stdcall ClassAsynchronousCompletion(ptr ptr ptr)
@ stdcall ClassSplitRequest(ptr ptr long)
@ stdcall ClassDeviceControl(ptr ptr)
@ stdcall ClassIoComplete(ptr ptr ptr)
@ stdcall ClassIoCompleteAssociated(ptr ptr ptr)
@ stdcall ClassInterpretSenseInfo(ptr ptr long long long ptr ptr)
@ stdcall ClassSendDeviceIoControlSynchronous(long ptr ptr long long long ptr)
@ stdcall ClassSendIrpSynchronous(ptr ptr)
@ stdcall ClassForwardIrpSynchronous(ptr ptr)
@ stdcall ClassSendSrbSynchronous(ptr ptr ptr long long)
@ stdcall ClassSendSrbAsynchronous(ptr ptr ptr ptr long long)
@ stdcall ClassBuildRequest(ptr ptr)
@ stdcall ClassModeSense(ptr ptr long long)
@ stdcall ClassModeSenseEx(ptr ptr long long long)
@ stdcall ClassModeSelect(ptr ptr long long)
@ stdcall ClassFindModePage(ptr long long long)
@ stdcall ClassClaimDevice(ptr long)
@ stdcall ClassInternalIoControl(ptr ptr)
@ stdcall ClassCreateDeviceObject(ptr ptr ptr long ptr)
@ stdcall ClassRemoveDevice(ptr long)
@ stdcall ClassInitializeSrbLookasideList(ptr long)
@ stdcall ClassDeleteSrbLookasideList(ptr)
@ stdcall ClassQueryTimeOutRegistryValue(ptr)
@ stdcall ClassInvalidateBusRelations(ptr)
@ stdcall ClassMarkChildrenMissing(ptr)
@ stdcall ClassMarkChildMissing(ptr long)
@ varargs ClassDebugPrint(long long)
@ stdcall ClassGetDriverExtension(ptr)
@ stdcall ClassCompleteRequest(ptr ptr long)
@ stdcall ClassReleaseRemoveLock(ptr ptr)
@ stdcall ClassAcquireRemoveLockEx(ptr ptr ptr long)
@ stdcall ClassUpdateInformationInRegistry(ptr ptr long ptr long)
@ stdcall ClassWmiCompleteRequest(ptr ptr long long long)
@ stdcall ClassWmiFireEvent(ptr ptr long long ptr)
@ stdcall ClassGetVpb(ptr)
@ stdcall ClassSetFailurePredictionPoll(ptr long long)
@ stdcall ClassNotifyFailurePredicted(ptr ptr long long long long long long)
@ stdcall ClassInitializeTestUnitPolling(ptr long)
@ stdcall ClassSignalCompletion(ptr ptr ptr)
@ stdcall ClassSendStartUnit(ptr)
@ stdcall ClassSetMediaChangeState(ptr long long)
@ stdcall ClassResetMediaChangeTimer(ptr)
@ stdcall ClassCheckMediaState(ptr)
@ stdcall ClassInitializeMediaChangeDetection(ptr ptr)
@ stdcall ClassCleanupMediaChangeDetection(ptr)
@ stdcall ClassEnableMediaChangeDetection(ptr)
@ stdcall ClassDisableMediaChangeDetection(ptr)
@ stdcall ClassSpinDownPowerHandler(ptr ptr)
@ stdcall ClassStopUnitPowerHandler(ptr ptr)
@ stdcall ClassAcquireChildLock(ptr)
@ stdcall ClassReleaseChildLock(ptr)
@ stdcall ClassScanForSpecial(ptr ptr ptr)
@ stdcall ClassSetDeviceParameter(ptr ptr ptr long)
@ stdcall ClassGetDeviceParameter(ptr ptr ptr ptr)
@ stdcall ClassGetFsContext(ptr ptr)
@ stdcall ClassSendNotification(ptr ptr long ptr)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,770 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
clntirp.c
Abstract:
Client IRP queuing routines for CLASSPNP
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef DEBUG_USE_WPP
#include "clntirp.tmh"
#endif
VOID
ClasspStartIdleTimer(
IN PCLASS_PRIVATE_FDO_DATA FdoData
);
VOID
ClasspStopIdleTimer(
PCLASS_PRIVATE_FDO_DATA FdoData
);
KDEFERRED_ROUTINE ClasspIdleTimerDpc;
VOID
ClasspServiceIdleRequest(
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
BOOLEAN PostToDpc
);
PIRP
ClasspDequeueIdleRequest(
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
);
/*++
EnqueueDeferredClientIrp
Routine Description:
Insert the deferred irp into the list.
Note: we currently do not support Cancel for storage irps.
Arguments:
Fdo - Pointer to the device object
Irp - Pointer to the I/O request packet
Return Value:
None
--*/
VOID
EnqueueDeferredClientIrp(
PDEVICE_OBJECT Fdo,
PIRP Irp
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
KIRQL oldIrql;
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
InsertTailList(&fdoData->DeferredClientIrpList, &Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
}
/*++
DequeueDeferredClientIrp
Routine Description:
Remove the deferred irp from the list.
Arguments:
Fdo - Pointer to the device object
Return Value:
Pointer to removed IRP
--*/
PIRP
DequeueDeferredClientIrp(
PDEVICE_OBJECT Fdo
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
PIRP irp;
//
// The DeferredClientIrpList is almost always empty.
// We don't want to grab the spinlock every time we check it (which is on every xfer completion)
// so check once first before we grab the spinlock.
//
if (IsListEmpty(&fdoData->DeferredClientIrpList)){
irp = NULL;
}
else {
PLIST_ENTRY listEntry;
KIRQL oldIrql;
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
if (IsListEmpty(&fdoData->DeferredClientIrpList)){
listEntry = NULL;
}
else {
listEntry = RemoveHeadList(&fdoData->DeferredClientIrpList);
}
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
if (listEntry == NULL) {
irp = NULL;
}
else {
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
NT_ASSERT(irp->Type == IO_TYPE_IRP);
InitializeListHead(&irp->Tail.Overlay.ListEntry);
}
}
return irp;
}
/*++
ClasspInitializeIdleTimer
Routine Description:
Initialize the idle timer for the given device.
Arguments:
FdoExtension - Pointer to the device extension
Return Value:
None
--*/
VOID
ClasspInitializeIdleTimer(
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
ULONG idleInterval = CLASS_IDLE_INTERVAL;
ULONG idlePrioritySupported = TRUE;
ULONG activeIdleIoMax = 1;
ClassGetDeviceParameter(FdoExtension,
CLASSP_REG_SUBKEY_NAME,
CLASSP_REG_IDLE_PRIORITY_SUPPORTED,
&idlePrioritySupported);
if (idlePrioritySupported == FALSE) {
//
// User has set the registry to disable idle priority for this disk.
// No need to initialize any further.
// Always ensure that none of the other fields used for idle priority
// io are ever used without checking if it is supported.
//
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspInitializeIdleTimer: Idle priority not supported for disk %p\n", FdoExtension));
fdoData->IdlePrioritySupported = FALSE;
fdoData->IdleIoCount = 0;
fdoData->ActiveIoCount = 0;
return;
}
ClassGetDeviceParameter(FdoExtension,
CLASSP_REG_SUBKEY_NAME,
CLASSP_REG_IDLE_INTERVAL_NAME,
&idleInterval);
if ((idleInterval < CLASS_IDLE_INTERVAL_MIN) || (idleInterval > USHORT_MAX)) {
//
// If the interval is too low or too high, reset it to the default value.
//
idleInterval = CLASS_IDLE_INTERVAL;
}
fdoData->IdlePrioritySupported = TRUE;
KeInitializeSpinLock(&fdoData->IdleListLock);
KeInitializeTimer(&fdoData->IdleTimer);
KeInitializeDpc(&fdoData->IdleDpc, ClasspIdleTimerDpc, FdoExtension);
InitializeListHead(&fdoData->IdleIrpList);
fdoData->IdleTimerStarted = FALSE;
fdoData->StarvationDuration = CLASS_STARVATION_INTERVAL;
fdoData->IdleInterval = (USHORT)(idleInterval);
fdoData->IdleIoCount = 0;
fdoData->ActiveIoCount = 0;
fdoData->ActiveIdleIoCount = 0;
ClassGetDeviceParameter(FdoExtension,
CLASSP_REG_SUBKEY_NAME,
CLASSP_REG_IDLE_ACTIVE_MAX,
&activeIdleIoMax);
activeIdleIoMax = max(activeIdleIoMax, 1);
activeIdleIoMax = min(activeIdleIoMax, USHORT_MAX);
fdoData->IdleActiveIoMax = (USHORT)activeIdleIoMax;
return;
}
/*++
ClasspStartIdleTimer
Routine Description:
Start the idle timer if not already running. Reset the
timer counters before starting the timer. Use the IdleInterval
in the private fdo data to setup the timer.
Arguments:
FdoData - Pointer to the private fdo data
Return Value:
None
--*/
VOID
ClasspStartIdleTimer(
IN PCLASS_PRIVATE_FDO_DATA FdoData
)
{
LARGE_INTEGER dueTime;
LONG mstotimer;
LONG timerStarted;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspStartIdleTimer: Start idle timer\n"));
timerStarted = InterlockedCompareExchange(&FdoData->IdleTimerStarted, 1, 0);
if (!timerStarted) {
//
// Reset the anti-starvation start time.
//
FdoData->AntiStarvationStartTime = ClasspGetCurrentTime();
//
// convert milliseconds to a relative 100ns
//
mstotimer = (-10) * 1000;
//
// multiply the period
//
dueTime.QuadPart = Int32x32To64(FdoData->IdleInterval, mstotimer);
KeSetTimerEx(&FdoData->IdleTimer,
dueTime,
FdoData->IdleInterval,
&FdoData->IdleDpc);
}
return;
}
/*++
ClasspStopIdleTimer
Routine Description:
Stop the idle timer if running. Also reset the timer counters.
Arguments:
FdoData - Pointer to the private fdo data
Return Value:
None
--*/
VOID
ClasspStopIdleTimer(
PCLASS_PRIVATE_FDO_DATA FdoData
)
{
LONG timerStarted;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspStopIdleTimer: Stop idle timer\n"));
timerStarted = InterlockedCompareExchange(&FdoData->IdleTimerStarted, 0, 1);
if (timerStarted) {
(VOID)KeCancelTimer(&FdoData->IdleTimer);
}
return;
}
/*++
ClasspGetIdleTime
Routine Description:
This routine returns how long it has been since the last non-idle request
completed by checking the actual time.
Arguments:
FdoData - Pointer to the private fdo data
Return Value:
The idle interval in ms.
--*/
ULONGLONG
ClasspGetIdleTime (
IN PCLASS_PRIVATE_FDO_DATA FdoData,
IN LARGE_INTEGER CurrentTime
)
{
ULONGLONG idleTime;
NTSTATUS status;
//
// Get the time difference between current time and last I/O
// complete time.
//
status = RtlULongLongSub((ULONGLONG)CurrentTime.QuadPart,
(ULONGLONG)FdoData->LastNonIdleIoTime.QuadPart,
&idleTime);
if (NT_SUCCESS(status)) {
//
// Convert the time to milliseconds.
//
idleTime = ClasspTimeDiffToMs(idleTime);
} else {
//
// Failed to get time difference, assume enough time passed.
//
idleTime = FdoData->IdleInterval;
}
return idleTime;
}
/*++
ClasspIdleDurationSufficient
Routine Description:
This routine computes whether enough idle duration has elapsed since the
completion of the last non-idle request.
Arguments:
FdoData - Pointer to the private fdo data
CurrentTimeIn - If CurrentTimeIn is non-NULL
- contents are set to NULL if the time is not updated.
- time is updated otherwise
Return Value:
TRUE if sufficient idle duration has elapsed to issue the next idle request.
--*/
LOGICAL
ClasspIdleDurationSufficient (
IN PCLASS_PRIVATE_FDO_DATA FdoData,
OUT LARGE_INTEGER** CurrentTimeIn
)
{
ULONGLONG idleInterval;
LARGE_INTEGER CurrentTime;
//
// If there are any outstanding non-idle requests, then there has been no
// idle time.
//
if (FdoData->ActiveIoCount > 0) {
if (CurrentTimeIn != NULL) {
*CurrentTimeIn = NULL;
}
return FALSE;
}
//
// Check whether an idle request should be issued now or on the next timer
// expiration.
//
CurrentTime = ClasspGetCurrentTime();
idleInterval = ClasspGetIdleTime(FdoData, CurrentTime);
if (CurrentTimeIn != NULL) {
**CurrentTimeIn = CurrentTime;
}
if (idleInterval >= FdoData->IdleInterval) {
return TRUE;
}
return FALSE;
}
/*++
ClasspIdleTimerDpc
Routine Description:
Timer dpc function. This function will be called once every
IdleInterval. An idle request will be queued if sufficient idle time
has elapsed since the last non-idle request.
Arguments:
Dpc - Pointer to DPC object
Context - Pointer to the fdo device extension
SystemArgument1 - Not used
SystemArgument2 - Not used
Return Value:
None
--*/
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClasspIdleTimerDpc(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Context;
PCLASS_PRIVATE_FDO_DATA fdoData;
ULONGLONG idleTime;
NTSTATUS status;
LARGE_INTEGER currentTime;
LARGE_INTEGER* pCurrentTime;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
if (fdoExtension == NULL) {
NT_ASSERT(fdoExtension != NULL);
return;
}
fdoData = fdoExtension->PrivateFdoData;
if (fdoData->ActiveIoCount <= 0) {
//
// If there are max active idle requests, do not issue another one here.
//
if (fdoData->ActiveIdleIoCount >= fdoData->IdleActiveIoMax) {
return;
}
//
// Check whether enough idle time has passed since the last non-idle
// request has completed.
//
pCurrentTime = &currentTime;
if (ClasspIdleDurationSufficient(fdoData, &pCurrentTime)) {
//
// We are going to issue an idle request so reset the anti-starvation
// timer counter.
// If we are here (Idle duration is sufficient), pCurrentTime is
// expected to be set.
//
NT_ASSERT(pCurrentTime != NULL);
fdoData->AntiStarvationStartTime = *pCurrentTime;
ClasspServiceIdleRequest(fdoExtension, FALSE);
}
return;
}
//
// Get the time difference between current time and last I/O
// complete time.
//
currentTime = ClasspGetCurrentTime();
status = RtlULongLongSub((ULONGLONG)currentTime.QuadPart,
(ULONGLONG)fdoData->AntiStarvationStartTime.QuadPart,
&idleTime);
if (NT_SUCCESS(status)) {
//
// Convert the time to milliseconds.
//
idleTime = ClasspTimeDiffToMs(idleTime);
} else {
//
// Failed to get time difference, assume enough time passed.
//
idleTime = fdoData->StarvationDuration;
}
//
// If the timer is running then there must be at least one idle priority I/O pending
//
if (idleTime >= fdoData->StarvationDuration) {
fdoData->AntiStarvationStartTime = currentTime;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspIdleTimerDpc: Starvation timer. Send one idle request\n"));
ClasspServiceIdleRequest(fdoExtension, FALSE);
}
return;
}
/*++
ClasspEnqueueIdleRequest
Routine Description:
This function will insert the idle request into the list.
If the inserted reqeust is the first request then it will
start the timer.
Arguments:
DeviceObject - Pointer to device object
Irp - Pointer to the idle I/O request packet
Return Value:
NT status code.
--*/
NTSTATUS
ClasspEnqueueIdleRequest(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
KIRQL oldIrql;
BOOLEAN issueRequest = TRUE;
LARGE_INTEGER currentTime;
LARGE_INTEGER* pCurrentTime;
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspEnqueueIdleRequest: Queue idle request %p\n", Irp));
IoMarkIrpPending(Irp);
//
// Reset issueRequest if the idle duration is not sufficient.
//
pCurrentTime = &currentTime;
if (ClasspIdleDurationSufficient(fdoData, &pCurrentTime) == FALSE) {
issueRequest = FALSE;
}
//
// If there are already max active idle requests in the port driver, then
// queue this idle request.
//
if (fdoData->ActiveIdleIoCount >= fdoData->IdleActiveIoMax) {
issueRequest = FALSE;
}
KeAcquireSpinLock(&fdoData->IdleListLock, &oldIrql);
if (IsListEmpty(&fdoData->IdleIrpList)) {
NT_ASSERT(fdoData->IdleIoCount == 0);
}
InsertTailList(&fdoData->IdleIrpList, &Irp->Tail.Overlay.ListEntry);
fdoData->IdleIoCount++;
if (!fdoData->IdleTimerStarted) {
ClasspStartIdleTimer(fdoData);
}
if (fdoData->IdleIoCount != 1) {
issueRequest = FALSE;
}
KeReleaseSpinLock(&fdoData->IdleListLock, oldIrql);
if (issueRequest) {
ClasspServiceIdleRequest(fdoExtension, FALSE);
}
return STATUS_PENDING;
}
/*++
ClasspDequeueIdleRequest
Routine Description:
This function will remove the next idle request from the list.
If there are no requests in the queue, then it will return NULL.
Arguments:
FdoExtension - Pointer to the functional device extension
Return Value:
Pointer to removed IRP
--*/
PIRP
ClasspDequeueIdleRequest(
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
PLIST_ENTRY listEntry = NULL;
PIRP irp = NULL;
KIRQL oldIrql;
KeAcquireSpinLock(&fdoData->IdleListLock, &oldIrql);
if (fdoData->IdleIoCount > 0) {
listEntry = RemoveHeadList(&fdoData->IdleIrpList);
//
// Make sure we actaully removed a request from the list
//
NT_ASSERT(listEntry != &fdoData->IdleIrpList);
//
// Decrement the idle I/O count.
//
fdoData->IdleIoCount--;
//
// Stop the timer on last request
//
if (fdoData->IdleIoCount == 0) {
ClasspStopIdleTimer(fdoData);
}
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
NT_ASSERT(irp->Type == IO_TYPE_IRP);
InitializeListHead(&irp->Tail.Overlay.ListEntry);
}
KeReleaseSpinLock(&fdoData->IdleListLock, oldIrql);
return irp;
}
/*++
ClasspCompleteIdleRequest
Routine Description:
This function will be called every time an idle request is completed.
This will call ClasspServiceIdleRequest to process any other pending idle requests.
Arguments:
FdoExtension - Pointer to the device extension
Return Value:
None
--*/
VOID
ClasspCompleteIdleRequest(
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
//
// Issue the next idle request if there are any left in the queue, there are
// no non-idle requests outstanding, there are less than max idle requests
// outstanding, and it has been long enough since the completion of the last
// non-idle request.
//
if ((fdoData->IdleIoCount > 0) &&
(fdoData->ActiveIdleIoCount < fdoData->IdleActiveIoMax) &&
(fdoData->ActiveIoCount <= 0) &&
(ClasspIdleDurationSufficient(fdoData, NULL))) {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspCompleteIdleRequest: Service next idle reqeusts\n"));
ClasspServiceIdleRequest(FdoExtension, TRUE);
}
return;
}
/*++
ClasspServiceIdleRequest
Routine Description:
Remove the next pending idle request from the queue and process it.
If a request was removed then it will be processed otherwise it will
just return.
Arguments:
FdoExtension - Pointer to the device extension
PostToDpc - Flag to pass to ServiceTransferRequest to indicate if request must be posted to a DPC
Return Value:
None
--*/
VOID
ClasspServiceIdleRequest(
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
BOOLEAN PostToDpc
)
{
PIRP irp;
irp = ClasspDequeueIdleRequest(FdoExtension);
if (irp != NULL) {
ServiceTransferRequest(FdoExtension->DeviceObject, irp, PostToDpc);
}
return;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,222 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 2010
Module Name:
disk.c
Abstract:
SCSI disk class driver
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
/*
* Entry in static list used by debug extension to quickly find all class FDOs.
*/
LIST_ENTRY AllFdosList = {&AllFdosList, &AllFdosList};
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
/*
#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001)
#define FDO_HACK_GESN_IS_BAD (0x00000002)
#define FDO_HACK_NO_SYNC_CACHE (0x00000004)
#define FDO_HACK_NO_RESERVE6 (0x00000008)
#define FDO_HACK_GESN_IGNORE_OPCHANGE (0x00000010)
*/
CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[] = { // Type (HH, slim) + WHQL Date, if known
{ "" , "MITSUMI CD-ROM FX240" , NULL , 0x02 },
{ "" , "MITSUMI CD-ROM FX320" , NULL , 0x02 },
{ "" , "MITSUMI CD-ROM FX322" , NULL , 0x02 },
{ "" , "TEAC DV-28E-A" , "2.0A", 0x02 },
{ "" , "HP CD-Writer cd16h" , "Q000", 0x02 },
{ "" , "_NEC NR-7800A" , "1.33", 0x02 },
{ "" , "COMPAQ CRD-8481B" , NULL , 0x04 },
// The following is a list of device that report too many OpChange/Add events.
// They require ignoring (or not sending) the OpChange flag in the GESN command.
// This list contains vendor ID and product ID as separate strings for USB/1394 interface.
{ "HL-DT-ST", "DVDRAM GMA-4020B" , NULL , 0x10 }, // hh , 2002/04/22
{ "HL-DT-ST", "DVD-RW GCA-4020B" , NULL , 0x10 }, // hh , 2002/05/14
{ "HL-DT-ST", "DVDRAM GSA-4040B" , NULL , 0x10 }, // hh , 2003/05/06
{ "HL-DT-ST", "DVDRAM GMA-4040B" , NULL , 0x10 }, // hh , 2003/07/27
{ "HL-DT-ST", "DVD-RW GWA-4040B" , NULL , 0x10 }, // hh , 2003/11/18
{ "HL-DT-ST", "DVDRAM GSA-4081B" , NULL , 0x10 }, // hh , 2003/11/06
{ "HL-DT-ST", "DVDRAM GSA-4082B" , NULL , 0x10 }, // hh , 2004/01/27
{ "HL-DT-ST", "DVD-RW GWA-4082B" , NULL , 0x10 }, // hh , 2004/03/11
{ "HL-DT-ST", "DVDRAM GSA-4120B" , NULL , 0x10 }, // hh , 2004/05/16
{ "HL-DT-ST", "DVD+RW GRA-4120B" , NULL , 0x10 }, // hh , 2004/04/28
{ "HL-DT-ST", "DVDRAM GSA-4160B" , NULL , 0x10 }, // hh , 2004/08/12
{ "HL-DT-ST", "DVD-RW GWA-4160B" , NULL , 0x10 }, // hh , 2004/08/24
{ "HL-DT-ST", "DVDRAM GSA-4163B" , NULL , 0x10 }, // hh , 2004/11/09
{ "HL-DT-ST", "DVD-RW GWA-4163B" , NULL , 0x10 }, // hh , 2004/12/29
{ "HL-DT-ST", "DVDRAM GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/09
{ "HL-DT-ST", "DVDRAM_GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/28
{ "HL-DT-ST", "DVD-RW GWA-4165B" , NULL , 0x10 }, // hh , 2005/08/23
{ "HL-DT-ST", "DVDRAM GSA-4167B" , NULL , 0x10 }, // hh , 2005/07/01
{ "HL-DT-ST", "DVDRAM GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
{ "HL-DT-ST", "DVDRAM_GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
{ "HL-DT-ST", "DVDRAM GSA-H10L" , NULL , 0x10 }, // hh , 2006/02/27
{ "HL-DT-ST", "DVDRAM_GSA-H10L" , NULL , 0x10 }, // hh , 2006/04/21
{ "HL-DT-ST", "DVDRAM GSA-H10A" , NULL , 0x10 }, // hh , 2006/01/03
{ "HL-DT-ST", "DVDRAM_GSA-H10A" , NULL , 0x10 }, // hh , 2006/05/14
{ "HL-DT-ST", "DVD-RW GSA-H11N" , NULL , 0x10 }, // hh , 2006/04/28
{ "HL-DT-ST", "DVD-RW_GSA-H11N" , NULL , 0x10 }, // hh , 2006/02/22
{ "HL-DT-ST", "DVDRAM GSA-4080N" , NULL , 0x10 }, // slim, 2004/08/08
{ "HL-DT-ST", "DVDRAM GMA-4080N" , NULL , 0x10 }, // slim, 2004/11/09
{ "HL-DT-ST", "DVD-RW GCA-4080N" , NULL , 0x10 }, // slim, 2004/11/22
{ "HL-DT-ST", "DVD-RW GWA-4080N" , NULL , 0x10 }, // slim, 2004/08/17
{ "HL-DT-ST", "DVDRAM GSA-4082N" , NULL , 0x10 }, // slim, 2005/07/12
{ "HL-DT-ST", "DVDRAM_GSA-4082N" , NULL , 0x10 }, // slim, 2005/09/21
{ "HL-DT-ST", "DVDRAM GMA-4082N" , NULL , 0x10 }, // slim, 2005/10/20
{ "HL-DT-ST", "DVD-RW GRA-4082N" , NULL , 0x10 }, // slim, 2006/06/07
{ "HL-DT-ST", "DVD-RW GWA-4082N" , NULL , 0x10 }, // slim, 2005/05/24
{ "HL-DT-ST", "DVDRAM GMA4082Nf" , NULL , 0x10 }, // slim, 2006/02/28
{ "HL-DT-ST", "DVDRAM GMA4082Nj" , NULL , 0x10 }, // slim, 2006/01/26
{ "HL-DT-ST", "DVDRAM GSA-4084N" , NULL , 0x10 }, // slim, 2005/12/21
{ "HL-DT-ST", "DVDRAM GMA-4084N" , NULL , 0x10 }, // slim, 2006/02/15
{ "HP" , "DVD Writer 550s" , NULL , 0x10 }, // slim, 2006/05/08
{ "HL-DT-ST", "DVDRAM GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
{ "HL-DT-ST", "DVDRAM_GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
{ "HL-DT-ST", "DVD+-RW GSA-T11N" , NULL , 0x10 }, // slim, 2006/07/25
{ "HL-DT-ST", "DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/10/12
{ "COMPAQ" , "DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/11/08
{ "HL-DT-ST", "DVD-ROM GDR8161B" , NULL , 0x10 }, // hh , 2002/07/19
{ "HL-DT-ST", "DVD-ROM GDR8162B" , NULL , 0x10 }, // hh , 2003/04/22
{ "HL-DT-ST", "DVD-ROM GDR8163B" , NULL , 0x10 }, // hh , 2004/05/19
{ "HL-DT-ST", "DVD-ROM GDR8164B" , NULL , 0x10 }, // hh , 2005/06/29
{ "HL-DT-ST", "DVD-ROM GDRH10N" , NULL , 0x10 }, // hh , 2006/03/07
{ "HL-DT-ST", "DVD-ROM GDR8081N" , NULL , 0x10 }, // slim, 2001/08/27
{ "HL-DT-ST", "DVD-ROM GDR8082N" , NULL , 0x10 }, // slim, 2003/02/02
{ "HL-DT-ST", "DVD-ROM GDR8083N" , NULL , 0x10 }, // slim, 2003/02/02
{ "HL-DT-ST", "DVD-ROM GDR8085N" , NULL , 0x10 }, // slim, 2005/11/10
{ "HL-DT-ST", "RW/DVD GCC-4080N" , NULL , 0x10 }, // slim, 2001/08/21
{ "HL-DT-ST", "RW/DVD_GCC-4080N" , NULL , 0x10 }, // slim,
{ "HL-DT-ST", "RW/DVD GCC-4160N" , NULL , 0x10 }, // slim, 2002/04/08
{ "HL-DT-ST", "RW/DVD GCC-4240N" , NULL , 0x10 }, // slim, 2002/04/26
{ "HL-DT-ST", "RW/DVD GCC-4241N" , NULL , 0x10 }, // slim, 2003/04/07
{ "HL-DT-ST", "RW/DVD_GCC-4241N" , NULL , 0x10 }, // slim, 2004/03/07
{ "HL-DT-ST", "RW/DVD GCC-4242N" , NULL , 0x10 }, // slim, 2003/12/21
{ "HL-DT-ST", "RW/DVD GCC-4246N" , NULL , 0x10 }, // slim, 2005/05/23
{ "HL-DT-ST", "BD-RE GBW-H10N" , NULL , 0x10 }, // hh , 2006/06/27
{ "HL-DT-ST", "DVDRAM GSA-4083N" , NULL , 0x10 }, // hh , 2006/05/17
{ "HL-DT-ST", "DVD+-RW GWA4083N" , NULL , 0x10 }, // hh , 2006/06/05
{ "PIONEER", "DVD-RW DVR-106D" , NULL , 0x10 }, // hh , ?
{ "ASUS", "DVD-RW DRW-0402P" , NULL , 0x10 }, // hh , ?
//
// This list contains vendor ID and product ID as a single string for ATAPI interface.
//
{ "", "HL-DT-ST DVDRAM GMA-4020B" , NULL , 0x10 }, // hh , 2002/04/22
{ "", "HL-DT-ST DVD-RW GCA-4020B" , NULL , 0x10 }, // hh , 2002/05/14
{ "", "HL-DT-ST DVDRAM GSA-4040B" , NULL , 0x10 }, // hh , 2003/05/06
{ "", "HL-DT-ST DVDRAM GMA-4040B" , NULL , 0x10 }, // hh , 2003/07/27
{ "", "HL-DT-ST DVD-RW GWA-4040B" , NULL , 0x10 }, // hh , 2003/11/18
{ "", "HL-DT-ST DVDRAM GSA-4081B" , NULL , 0x10 }, // hh , 2003/11/06
{ "", "HL-DT-ST DVDRAM GSA-4082B" , NULL , 0x10 }, // hh , 2004/01/27
{ "", "HL-DT-ST DVD-RW GWA-4082B" , NULL , 0x10 }, // hh , 2004/03/11
{ "", "HL-DT-ST DVDRAM GSA-4120B" , NULL , 0x10 }, // hh , 2004/05/16
{ "", "HL-DT-ST DVD+RW GRA-4120B" , NULL , 0x10 }, // hh , 2004/04/28
{ "", "HL-DT-ST DVDRAM GSA-4160B" , NULL , 0x10 }, // hh , 2004/08/12
{ "", "HL-DT-ST DVD-RW GWA-4160B" , NULL , 0x10 }, // hh , 2004/08/24
{ "", "HL-DT-ST DVDRAM GSA-4163B" , NULL , 0x10 }, // hh , 2004/11/09
{ "", "HL-DT-ST DVD-RW GWA-4163B" , NULL , 0x10 }, // hh , 2004/12/29
{ "", "HL-DT-ST DVDRAM GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/09
{ "", "HL-DT-ST DVDRAM_GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/28
{ "", "HL-DT-ST DVD-RW GWA-4165B" , NULL , 0x10 }, // hh , 2005/08/23
{ "", "HL-DT-ST DVDRAM GSA-4167B" , NULL , 0x10 }, // hh , 2005/07/01
{ "", "HL-DT-ST DVDRAM GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
{ "", "HL-DT-ST DVDRAM_GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
{ "", "HL-DT-ST DVDRAM GSA-H10L" , NULL , 0x10 }, // hh , 2006/02/27
{ "", "HL-DT-ST DVDRAM_GSA-H10L" , NULL , 0x10 }, // hh , 2006/04/21
{ "", "HL-DT-ST DVDRAM GSA-H10A" , NULL , 0x10 }, // hh , 2006/01/03
{ "", "HL-DT-ST DVDRAM_GSA-H10A" , NULL , 0x10 }, // hh , 2006/05/14
{ "", "HL-DT-ST DVD-RW GSA-H11N" , NULL , 0x10 }, // hh , 2006/04/28
{ "", "HL-DT-ST DVD-RW_GSA-H11N" , NULL , 0x10 }, // hh , 2006/02/22
{ "", "HL-DT-ST DVDRAM GSA-4080N" , NULL , 0x10 }, // slim, 2004/08/08
{ "", "HL-DT-ST DVDRAM GMA-4080N" , NULL , 0x10 }, // slim, 2004/11/09
{ "", "HL-DT-ST DVD-RW GCA-4080N" , NULL , 0x10 }, // slim, 2004/11/22
{ "", "HL-DT-ST DVD-RW GWA-4080N" , NULL , 0x10 }, // slim, 2004/08/17
{ "", "HL-DT-ST DVDRAM GSA-4082N" , NULL , 0x10 }, // slim, 2005/07/12
{ "", "HL-DT-ST DVDRAM_GSA-4082N" , NULL , 0x10 }, // slim, 2005/09/21
{ "", "HL-DT-ST DVDRAM GMA-4082N" , NULL , 0x10 }, // slim, 2005/10/20
{ "", "HL-DT-ST DVD-RW GRA-4082N" , NULL , 0x10 }, // slim, 2006/06/07
{ "", "HL-DT-ST DVD-RW GWA-4082N" , NULL , 0x10 }, // slim, 2005/05/24
{ "", "HL-DT-ST DVDRAM GMA4082Nf" , NULL , 0x10 }, // slim, 2006/02/28
{ "", "HL-DT-ST DVDRAM GMA4082Nj" , NULL , 0x10 }, // slim, 2006/01/26
{ "", "HL-DT-ST DVDRAM GSA-4084N" , NULL , 0x10 }, // slim, 2005/12/21
{ "", "HL-DT-ST DVDRAM GMA-4084N" , NULL , 0x10 }, // slim, 2006/02/15
{ "", "HP DVD Writer 550s" , NULL , 0x10 }, // slim, 2006/05/08
{ "", "HL-DT-ST DVDRAM GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
{ "", "HL-DT-ST DVDRAM_GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
{ "", "HL-DT-ST DVD+-RW GSA-T11N" , NULL , 0x10 }, // slim, 2006/07/25
{ "", "HL-DT-ST DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/10/12
{ "", "COMPAQ DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/11/08
{ "", "HL-DT-ST DVD-ROM GDR8161B" , NULL , 0x10 }, // hh , 2002/07/19
{ "", "HL-DT-ST DVD-ROM GDR8162B" , NULL , 0x10 }, // hh , 2003/04/22
{ "", "HL-DT-ST DVD-ROM GDR8163B" , NULL , 0x10 }, // hh , 2004/05/19
{ "", "HL-DT-ST DVD-ROM GDR8164B" , NULL , 0x10 }, // hh , 2005/06/29
{ "", "HL-DT-ST DVD-ROM GDRH10N" , NULL , 0x10 }, // hh , 2006/03/07
{ "", "HL-DT-ST DVD-ROM GDR8081N" , NULL , 0x10 }, // slim, 2001/08/27
{ "", "HL-DT-ST DVD-ROM GDR8082N" , NULL , 0x10 }, // slim, 2003/02/02
{ "", "HL-DT-ST DVD-ROM GDR8083N" , NULL , 0x10 }, // slim, 2003/02/02
{ "", "HL-DT-ST DVD-ROM GDR8085N" , NULL , 0x10 }, // slim, 2005/11/10
{ "", "HL-DT-ST RW/DVD GCC-4080N" , NULL , 0x10 }, // slim, 2001/08/21
{ "", "HL-DT-ST RW/DVD_GCC-4080N" , NULL , 0x10 }, // slim,
{ "", "HL-DT-ST RW/DVD GCC-4160N" , NULL , 0x10 }, // slim, 2002/04/08
{ "", "HL-DT-ST RW/DVD GCC-4240N" , NULL , 0x10 }, // slim, 2002/04/26
{ "", "HL-DT-ST RW/DVD GCC-4241N" , NULL , 0x10 }, // slim, 2003/04/07
{ "", "HL-DT-ST RW/DVD_GCC-4241N" , NULL , 0x10 }, // slim, 2004/03/07
{ "", "HL-DT-ST RW/DVD GCC-4242N" , NULL , 0x10 }, // slim, 2003/12/21
{ "", "HL-DT-ST RW/DVD GCC-4246N" , NULL , 0x10 }, // slim, 2005/05/23
{ "", "HL-DT-ST BD-RE GBW-H10N" , NULL , 0x10 }, // hh , 2006/06/27
{ "", "HL-DT-ST DVDRAM GSA-4083N" , NULL , 0x10 }, // hh , 2006/05/17
{ "", "HL-DT-ST DVD+-RW GWA4083N" , NULL , 0x10 }, // hh , 2006/06/05
{ "", "PIONEER DVD-RW DVR-106D" , NULL , 0x10 }, // hh , ?
{ "", "ASUS DVD-RW DRW-0402P" , NULL , 0x10 }, // hh , ?
// Sony sourced some drives from LG also....
{ NULL , NULL , NULL , 0x00 },
};
GUID ClassGuidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
GUID ClassGuidSenseInfo2 = GUID_CLASSPNP_SENSEINFO2;
GUID ClassGuidWorkingSet = GUID_CLASSPNP_WORKING_SET;
GUID ClassGuidSrbSupport = GUID_CLASSPNP_SRB_SUPPORT;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif

View file

@ -0,0 +1,972 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 2010
Module Name:
debug.c
Abstract:
CLASSPNP debug code and data
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef DEBUG_USE_WPP
#include "debug.tmh"
#endif
#if DBG
//
// default to not breaking in for lost irps, five minutes before we even
// bother checking for lost irps, using standard debug print macros, and
// using a 64k debug print buffer
//
#ifndef CLASS_GLOBAL_BREAK_ON_LOST_IRPS
#error "CLASS_GLOBAL_BREAK_ON_LOST_IRPS undefined"
#define CLASS_GLOBAL_BREAK_ON_LOST_IRPS 0
#endif // CLASS_GLOBAL_BREAK_ON_LOST_IRPS
#ifndef CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
#error "CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB undefined"
#define CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB 300
#endif // CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
#ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
#error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT undefined"
#define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT 0
#endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
#ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
#error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE undefined"
#define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE 512
#endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
#ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
#error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS undefined"
#define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS 512
#endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
#ifdef _MSC_VER
#pragma data_seg("NONPAGE")
#endif
CLASSPNP_GLOBALS ClasspnpGlobals;
//
// the low sixteen bits are used to see if the debug level is high enough
// the high sixteen bits are used to singly enable debug levels 1-16
//
LONG ClassDebug = 0x00000000;
BOOLEAN DebugTrapOnWarn = FALSE;
//
// Used to track callers when we receive an access and the disk
// is powered down.
//
ULONG DiskSpinupIndex = 0;
DISK_SPINUP_TRACES DiskSpinupTraces[NUMBER_OF_DISK_SPINUP_TRACES];
VOID ClasspInitializeDebugGlobals()
{
KIRQL irql;
if (InterlockedCompareExchange(&ClasspnpGlobals.Initializing, 1, 0) == 0) {
KeInitializeSpinLock(&ClasspnpGlobals.SpinLock);
KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "CLASSPNP.SYS => Initializing ClasspnpGlobals...\n"));
ClasspnpGlobals.Buffer = NULL;
ClasspnpGlobals.Index = (ULONG)-1;
ClasspnpGlobals.BreakOnLostIrps = CLASS_GLOBAL_BREAK_ON_LOST_IRPS;
ClasspnpGlobals.EachBufferSize = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE;
ClasspnpGlobals.NumberOfBuffers = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS;
ClasspnpGlobals.SecondsToWaitForIrps = CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB;
//
// this should be the last item set
//
ClasspnpGlobals.UseBufferedDebugPrint = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT;
KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);
InterlockedExchange(&ClasspnpGlobals.Initialized, 1);
}
}
/*++////////////////////////////////////////////////////////////////////////////
ClassDebugPrint()
Routine Description:
Debug print for all class drivers, NOOP on FRE versions.
Allows printing to a debug buffer (with auto fallback to kdprint) by
properly setting the Globals in classpnp on CHK versions.
Arguments:
Debug print level, or from 0 to 3 for legacy drivers.
Return Value:
None
--*/
VOID ClassDebugPrint(_In_ CLASS_DEBUG_LEVEL DebugPrintLevel, _In_z_ PCCHAR DebugMessage, ...)
{
va_list ap;
va_start(ap, DebugMessage);
if ((DebugPrintLevel <= (ClassDebug & 0x0000ffff)) ||
((1 << (DebugPrintLevel + 15)) & ClassDebug)) {
if (ClasspnpGlobals.UseBufferedDebugPrint &&
ClasspnpGlobals.Buffer == NULL) {
//
// this double-check prevents always taking
// a spinlock just to ensure we have a buffer
//
KIRQL irql;
KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
if (ClasspnpGlobals.Buffer == NULL) {
SIZE_T bufferSize;
if (NT_SUCCESS(
RtlSIZETMult(ClasspnpGlobals.NumberOfBuffers,
ClasspnpGlobals.EachBufferSize,
&bufferSize))) {
DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
"ClassDebugPrint: Allocating %x bytes for "
"classdebugprint buffer\n", (ULONG)bufferSize);
ClasspnpGlobals.Index = (ULONG)-1;
ClasspnpGlobals.Buffer =
ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, 'bDcS');
DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
"ClassDebugPrint: Allocated buffer at %p\n",
ClasspnpGlobals.Buffer);
if (ClasspnpGlobals.Buffer) {
RtlZeroMemory(ClasspnpGlobals.Buffer, bufferSize);
}
}
}
KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);
}
if (ClasspnpGlobals.UseBufferedDebugPrint &&
ClasspnpGlobals.Buffer != NULL) {
//
// we never free the buffer, so once it exists,
// we can just print to it with immunity
//
ULONG index;
PUCHAR buffer;
NTSTATUS status;
index = InterlockedIncrement((volatile LONG *)&ClasspnpGlobals.Index);
index %= ClasspnpGlobals.NumberOfBuffers;
index *= (ULONG)ClasspnpGlobals.EachBufferSize;
buffer = ClasspnpGlobals.Buffer;
buffer += index;
RtlZeroMemory(buffer, ClasspnpGlobals.EachBufferSize);
status = RtlStringCchVPrintfA((NTSTRSAFE_PSTR)buffer, ClasspnpGlobals.EachBufferSize, DebugMessage, ap);
if (!NT_SUCCESS(status))
{
*buffer = 0; // force-null on failure
}
} else {
//
// either we could not allocate a buffer for debug prints
// or buffered debug prints are disabled
//
vDbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_INFO_LEVEL, DebugMessage, ap);
}
}
va_end(ap);
}
/*
* DbgCheckReturnedPkt
*
* Check a completed TRANSFER_PACKET for all sorts of error conditions
* and warn/trap appropriately.
*/
VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt)
{
PCDB pCdb = ClasspTransferPacketGetCdb(Pkt);
NT_ASSERT(SrbGetOriginalRequest(Pkt->Srb) == Pkt->Irp);
NT_ASSERT(SrbGetDataBuffer(Pkt->Srb) == Pkt->BufPtrCopy);
NT_ASSERT(SrbGetDataTransferLength(Pkt->Srb) <= Pkt->BufLenCopy);
NT_ASSERT(!Pkt->Irp->CancelRoutine);
if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_PENDING){
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "SRB completed with status PENDING in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
(ULONG)Pkt->Srb->SrbStatus,
Pkt->Irp->IoStatus.Status));
}
else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SUCCESS){
/*
* Make sure SRB and IRP status match.
*/
if (!NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
(ULONG)Pkt->Srb->SrbStatus,
Pkt->Irp->IoStatus.Status));
}
if (Pkt->Irp->IoStatus.Information != SrbGetDataTransferLength(Pkt->Srb)){
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "SRB and IRP result transfer lengths don't match in succeeded packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%Ixh).",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
SrbGetDataTransferLength(Pkt->Srb),
Pkt->Irp->IoStatus.Information));
}
}
else {
if (NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
(ULONG)Pkt->Srb->SrbStatus,
Pkt->Irp->IoStatus.Status));
}
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
(ULONG)Pkt->Srb->SrbStatus,
Pkt->Irp->IoStatus.Status,
DBGGETSENSECODESTR(Pkt->Srb),
DBGGETADSENSECODESTR(Pkt->Srb),
DBGGETADSENSEQUALIFIERSTR(Pkt->Srb)));
/*
* If the SRB failed with underrun or overrun, then the actual
* transferred length should be returned in both SRB and IRP.
* (SRB's only have an error status for overrun, so it's overloaded).
*/
if ((SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) &&
(Pkt->Irp->IoStatus.Information != SrbGetDataTransferLength(Pkt->Srb))){
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "SRB and IRP result transfer lengths don't match in failed packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%Ixh).",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
SrbGetDataTransferLength(Pkt->Srb),
Pkt->Irp->IoStatus.Information));
}
}
/*
* If the port driver returned STATUS_INSUFFICIENT_RESOURCES,
* make sure this is also the InternalStatus in the SRB so that we process it correctly.
*/
if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
NT_ASSERT(SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR);
NT_ASSERT(SrbGetSystemStatus(Pkt->Srb) == STATUS_INSUFFICIENT_RESOURCES);
}
/*
* Some miniport drivers have been caught changing the SCSI operation
* code in the SRB. This is absolutely disallowed as it breaks our error handling.
*/
switch (pCdb->CDB10.OperationCode){
case SCSIOP_MEDIUM_REMOVAL:
case SCSIOP_MODE_SENSE:
case SCSIOP_READ_CAPACITY:
case SCSIOP_READ:
case SCSIOP_WRITE:
case SCSIOP_START_STOP_UNIT:
case SCSIOP_READ_CAPACITY16:
case SCSIOP_READ16:
case SCSIOP_WRITE16:
break;
default:
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "Miniport illegally changed Srb.Cdb.OperationCode in packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)",
Pkt,
DBGGETSCSIOPSTR(Pkt->Srb),
DBGGETSRBSTATUSSTR(Pkt->Srb),
(ULONG)Pkt->Srb->SrbStatus,
Pkt->Irp->IoStatus.Status,
DBGGETSENSECODESTR(Pkt->Srb),
DBGGETADSENSECODESTR(Pkt->Srb),
DBGGETADSENSEQUALIFIERSTR(Pkt->Srb)));
break;
}
}
VOID DbgLogSendPacket(TRANSFER_PACKET *Pkt)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
KIRQL oldIrql;
if (Pkt->OriginalIrp){
Pkt->DbgOriginalIrpCopy = *Pkt->OriginalIrp;
if (Pkt->OriginalIrp->MdlAddress){
Pkt->DbgMdlCopy = *Pkt->OriginalIrp->MdlAddress;
}
}
KeQueryTickCount(&Pkt->DbgTimeSent);
Pkt->DbgTimeReturned.QuadPart = 0L;
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
fdoData->DbgPacketLogs[fdoData->DbgPacketLogNextIndex] = *Pkt;
fdoData->DbgPacketLogNextIndex++;
fdoData->DbgPacketLogNextIndex %= DBG_NUM_PACKET_LOG_ENTRIES;
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
}
VOID DbgLogReturnPacket(TRANSFER_PACKET *Pkt)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
KIRQL oldIrql;
KeQueryTickCount(&Pkt->DbgTimeReturned);
#if 0
// ISSUE: there are some problems with this check (e.g. multiproc), so don't include it yet
if (Pkt->OriginalIrp){
/*
* No one should have touched the original irp while the packet was outstanding,
* except for a couple fields that we ourselves update during the transfer
* or that are allowed to change;
* make those couple fields the same and then to a bytewise compare
*/
ULONG lenSame;
Pkt->DbgOriginalIrpCopy.IoStatus.Status = Pkt->OriginalIrp->IoStatus.Status;
Pkt->DbgOriginalIrpCopy.IoStatus.Information = Pkt->OriginalIrp->IoStatus.Information;
Pkt->DbgOriginalIrpCopy.Tail.Overlay.DriverContext[0] = Pkt->OriginalIrp->Tail.Overlay.DriverContext[0];
Pkt->DbgOriginalIrpCopy.ThreadListEntry = Pkt->OriginalIrp->ThreadListEntry;
Pkt->DbgOriginalIrpCopy.Cancel = Pkt->OriginalIrp->Cancel;
lenSame = (ULONG)RtlCompareMemory(Pkt->OriginalIrp, &Pkt->DbgOriginalIrpCopy, sizeof(IRP));
NT_ASSERT(lenSame == sizeof(IRP));
}
#endif
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
fdoData->DbgPacketLogs[fdoData->DbgPacketLogNextIndex] = *Pkt;
fdoData->DbgPacketLogNextIndex++;
fdoData->DbgPacketLogNextIndex %= DBG_NUM_PACKET_LOG_ENTRIES;
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
}
/*++////////////////////////////////////////////////////////////////////////////
DbgSafeInc()
Routine Description:
Safely increments a ULONG. If the increment would result in an overflow,
the value is unchanged.
Arguments:
A pointer to the value to be incremented.
--*/
__inline VOID DbgSafeInc(PULONG pValue)
{
ULONG incrementResult;
if(NT_SUCCESS(RtlULongAdd(*pValue, 1, &incrementResult))) {
*pValue = incrementResult;
} else {
//
// Leave *pValue unchanged (i.e. at ULONG_MAX).
//
}
}
VOID DbgLogFlushInfo(PCLASS_PRIVATE_FDO_DATA FdoData, BOOLEAN IsIO, BOOLEAN IsFUA, BOOLEAN IsFlush)
{
/*
* Reset all FUA/Flush logging fields.
*/
if (FdoData->DbgInitFlushLogging){
FdoData->DbgNumIORequests = 0;
FdoData->DbgNumFUAs = 0;
FdoData->DbgNumFlushes = 0;
FdoData->DbgIOsSinceFUA = 0;
FdoData->DbgIOsSinceFlush = 0;
FdoData->DbgAveIOsToFUA = 0;
FdoData->DbgAveIOsToFlush = 0;
FdoData->DbgMaxIOsToFUA = 0;
FdoData->DbgMaxIOsToFlush = 0;
FdoData->DbgMinIOsToFUA = 0xffffffff;
FdoData->DbgMinIOsToFlush = 0xffffffff;
FdoData->DbgInitFlushLogging = FALSE;
}
//
// Using DbgSafeInc for all increments (instead of ++) guarantees
// that there will be no overflow hence no division by 0. All counters
// are capped at ULONG_MAX.
//
if (IsIO){
DbgSafeInc(&FdoData->DbgNumIORequests);
DbgSafeInc(&FdoData->DbgIOsSinceFlush);
if (IsFUA){
if (FdoData->DbgNumFUAs > 0){
FdoData->DbgMinIOsToFUA = min(FdoData->DbgMinIOsToFUA, FdoData->DbgIOsSinceFUA);
}
DbgSafeInc(&FdoData->DbgNumFUAs);
FdoData->DbgAveIOsToFUA = FdoData->DbgNumIORequests/FdoData->DbgNumFUAs;
FdoData->DbgIOsSinceFUA = 0;
}
else {
DbgSafeInc(&FdoData->DbgIOsSinceFUA);
FdoData->DbgMaxIOsToFUA = max(FdoData->DbgMaxIOsToFUA, FdoData->DbgIOsSinceFUA);
}
FdoData->DbgMaxIOsToFlush = max(FdoData->DbgMaxIOsToFlush, FdoData->DbgIOsSinceFlush);
}
else if (IsFlush){
if (FdoData->DbgNumFlushes > 0){
FdoData->DbgMinIOsToFlush = min(FdoData->DbgMinIOsToFlush, FdoData->DbgIOsSinceFlush);
}
DbgSafeInc(&FdoData->DbgNumFlushes);
FdoData->DbgAveIOsToFlush = FdoData->DbgNumIORequests/FdoData->DbgNumFlushes;
FdoData->DbgIOsSinceFlush = 0;
}
}
/*++////////////////////////////////////////////////////////////////////////////
SnapDiskStartup()
Routine Description:
This function will attempt to record the caller responsible for spinning
up the disk.
Arguments:
NONE.
Return Value:
NONE.
--*/
VOID
SnapDiskStartup(
VOID
)
{
ULONG Index;
PDISK_SPINUP_TRACES Entry;
LARGE_INTEGER SpinUpTime;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4210) // nonstandard extension used : function given file scope
#endif
extern NTSYSAPI USHORT NTAPI RtlCaptureStackBackTrace(
_In_ ULONG FramesToSkip,
_In_ ULONG FramesToCapture,
_Out_writes_to_(FramesToCapture, return) PVOID * BackTrace,
_Out_opt_ PULONG BackTraceHash );
#ifdef _MSC_VER
#pragma warning(pop)
#endif
//
// Grab the current count, then mod it so that it
// becomes an index into the DiskSpinupTraces array.
//
Index = InterlockedIncrement( (volatile LONG *)&DiskSpinupIndex );
Index = Index & (NUMBER_OF_DISK_SPINUP_TRACES - 1);
Entry = &DiskSpinupTraces[Index];
//
// Timestamp the instance.
//
KeQueryTickCount(&SpinUpTime);
SpinUpTime.QuadPart = (SpinUpTime.QuadPart * KeQueryTimeIncrement())/(10000000);
//
// Ask the kernel to read back up our stack by
// DISK_SPINUP_BACKTRACE_LENGTH frames.
//
Entry->TimeStamp.QuadPart = SpinUpTime.QuadPart;
RtlZeroMemory( &Entry->StackTrace[0], DISK_SPINUP_BACKTRACE_LENGTH * sizeof(PVOID) );
RtlCaptureStackBackTrace( 5, // stacks to skip
DISK_SPINUP_BACKTRACE_LENGTH, // buffer size
Entry->StackTrace,
&Index );
}
#else
// We have to keep this in the retail build for legacy.
VOID ClassDebugPrint(_In_ CLASS_DEBUG_LEVEL DebugPrintLevel, _In_z_ PCCHAR DebugMessage, ...)
{
UNREFERENCED_PARAMETER(DebugPrintLevel);
UNREFERENCED_PARAMETER(DebugMessage);
}
#endif
char *DbgGetIoctlStr(ULONG ioctl)
{
char *ioctlStr = "?";
switch (ioctl){
#undef MAKE_CASE
#define MAKE_CASE(ioctlCode) case ioctlCode: ioctlStr = #ioctlCode; break;
MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY)
MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY2)
MAKE_CASE(IOCTL_STORAGE_MEDIA_REMOVAL)
MAKE_CASE(IOCTL_STORAGE_EJECT_MEDIA)
MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA)
MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA2)
MAKE_CASE(IOCTL_STORAGE_RESERVE)
MAKE_CASE(IOCTL_STORAGE_RELEASE)
MAKE_CASE(IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
MAKE_CASE(IOCTL_STORAGE_PERSISTENT_RESERVE_OUT)
MAKE_CASE(IOCTL_STORAGE_FIND_NEW_DEVICES)
MAKE_CASE(IOCTL_STORAGE_EJECTION_CONTROL)
MAKE_CASE(IOCTL_STORAGE_MCN_CONTROL)
MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES)
MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES_EX)
MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER)
MAKE_CASE(IOCTL_STORAGE_GET_HOTPLUG_INFO)
MAKE_CASE(IOCTL_STORAGE_RESET_BUS)
MAKE_CASE(IOCTL_STORAGE_RESET_DEVICE)
MAKE_CASE(IOCTL_STORAGE_GET_DEVICE_NUMBER)
MAKE_CASE(IOCTL_STORAGE_PREDICT_FAILURE)
MAKE_CASE(IOCTL_STORAGE_QUERY_PROPERTY)
MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_BUS)
MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_DEVICE)
}
return ioctlStr;
}
char *DbgGetScsiOpStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
{
PCDB pCdb = SrbGetCdb(Srb);
char *scsiOpStr = "?";
if (pCdb) {
switch (pCdb->CDB6GENERIC.OperationCode){
#undef MAKE_CASE
#define MAKE_CASE(scsiOpCode) case scsiOpCode: scsiOpStr = #scsiOpCode; break;
MAKE_CASE(SCSIOP_TEST_UNIT_READY)
MAKE_CASE(SCSIOP_REWIND) // aka SCSIOP_REZERO_UNIT
MAKE_CASE(SCSIOP_REQUEST_BLOCK_ADDR)
MAKE_CASE(SCSIOP_REQUEST_SENSE)
MAKE_CASE(SCSIOP_FORMAT_UNIT)
MAKE_CASE(SCSIOP_READ_BLOCK_LIMITS)
MAKE_CASE(SCSIOP_INIT_ELEMENT_STATUS) // aka SCSIOP_REASSIGN_BLOCKS
MAKE_CASE(SCSIOP_RECEIVE) // aka SCSIOP_READ6
MAKE_CASE(SCSIOP_SEND) // aka SCSIOP_WRITE6, SCSIOP_PRINT
MAKE_CASE(SCSIOP_SLEW_PRINT) // aka SCSIOP_SEEK6, SCSIOP_TRACK_SELECT
MAKE_CASE(SCSIOP_SEEK_BLOCK)
MAKE_CASE(SCSIOP_PARTITION)
MAKE_CASE(SCSIOP_READ_REVERSE)
MAKE_CASE(SCSIOP_FLUSH_BUFFER) // aka SCSIOP_WRITE_FILEMARKS
MAKE_CASE(SCSIOP_SPACE)
MAKE_CASE(SCSIOP_INQUIRY)
MAKE_CASE(SCSIOP_VERIFY6)
MAKE_CASE(SCSIOP_RECOVER_BUF_DATA)
MAKE_CASE(SCSIOP_MODE_SELECT)
MAKE_CASE(SCSIOP_RESERVE_UNIT)
MAKE_CASE(SCSIOP_RELEASE_UNIT)
MAKE_CASE(SCSIOP_COPY)
MAKE_CASE(SCSIOP_ERASE)
MAKE_CASE(SCSIOP_MODE_SENSE)
MAKE_CASE(SCSIOP_START_STOP_UNIT) // aka SCSIOP_STOP_PRINT, SCSIOP_LOAD_UNLOAD
MAKE_CASE(SCSIOP_RECEIVE_DIAGNOSTIC)
MAKE_CASE(SCSIOP_SEND_DIAGNOSTIC)
MAKE_CASE(SCSIOP_MEDIUM_REMOVAL)
MAKE_CASE(SCSIOP_READ_FORMATTED_CAPACITY)
MAKE_CASE(SCSIOP_READ_CAPACITY)
MAKE_CASE(SCSIOP_READ)
MAKE_CASE(SCSIOP_WRITE)
MAKE_CASE(SCSIOP_SEEK) // aka SCSIOP_LOCATE, SCSIOP_POSITION_TO_ELEMENT
MAKE_CASE(SCSIOP_WRITE_VERIFY)
MAKE_CASE(SCSIOP_VERIFY)
MAKE_CASE(SCSIOP_SEARCH_DATA_HIGH)
MAKE_CASE(SCSIOP_SEARCH_DATA_EQUAL)
MAKE_CASE(SCSIOP_SEARCH_DATA_LOW)
MAKE_CASE(SCSIOP_SET_LIMITS)
MAKE_CASE(SCSIOP_READ_POSITION)
MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE)
MAKE_CASE(SCSIOP_COMPARE)
MAKE_CASE(SCSIOP_COPY_COMPARE)
MAKE_CASE(SCSIOP_WRITE_DATA_BUFF)
MAKE_CASE(SCSIOP_READ_DATA_BUFF)
MAKE_CASE(SCSIOP_CHANGE_DEFINITION)
MAKE_CASE(SCSIOP_READ_SUB_CHANNEL)
MAKE_CASE(SCSIOP_READ_TOC)
MAKE_CASE(SCSIOP_READ_HEADER)
MAKE_CASE(SCSIOP_PLAY_AUDIO)
MAKE_CASE(SCSIOP_GET_CONFIGURATION)
MAKE_CASE(SCSIOP_PLAY_AUDIO_MSF)
MAKE_CASE(SCSIOP_PLAY_TRACK_INDEX)
MAKE_CASE(SCSIOP_PLAY_TRACK_RELATIVE)
MAKE_CASE(SCSIOP_GET_EVENT_STATUS)
MAKE_CASE(SCSIOP_PAUSE_RESUME)
MAKE_CASE(SCSIOP_LOG_SELECT)
MAKE_CASE(SCSIOP_LOG_SENSE)
MAKE_CASE(SCSIOP_STOP_PLAY_SCAN)
MAKE_CASE(SCSIOP_READ_DISK_INFORMATION)
MAKE_CASE(SCSIOP_READ_TRACK_INFORMATION)
MAKE_CASE(SCSIOP_RESERVE_TRACK_RZONE)
MAKE_CASE(SCSIOP_SEND_OPC_INFORMATION)
MAKE_CASE(SCSIOP_MODE_SELECT10)
MAKE_CASE(SCSIOP_MODE_SENSE10)
MAKE_CASE(SCSIOP_CLOSE_TRACK_SESSION)
MAKE_CASE(SCSIOP_READ_BUFFER_CAPACITY)
MAKE_CASE(SCSIOP_SEND_CUE_SHEET)
MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_IN)
MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_OUT)
MAKE_CASE(SCSIOP_REPORT_LUNS)
MAKE_CASE(SCSIOP_BLANK)
MAKE_CASE(SCSIOP_SEND_KEY)
MAKE_CASE(SCSIOP_REPORT_KEY)
MAKE_CASE(SCSIOP_MOVE_MEDIUM)
MAKE_CASE(SCSIOP_LOAD_UNLOAD_SLOT) // aka SCSIOP_EXCHANGE_MEDIUM
MAKE_CASE(SCSIOP_SET_READ_AHEAD)
MAKE_CASE(SCSIOP_READ_DVD_STRUCTURE)
MAKE_CASE(SCSIOP_REQUEST_VOL_ELEMENT)
MAKE_CASE(SCSIOP_SEND_VOLUME_TAG)
MAKE_CASE(SCSIOP_READ_ELEMENT_STATUS)
MAKE_CASE(SCSIOP_READ_CD_MSF)
MAKE_CASE(SCSIOP_SCAN_CD)
MAKE_CASE(SCSIOP_SET_CD_SPEED)
MAKE_CASE(SCSIOP_PLAY_CD)
MAKE_CASE(SCSIOP_MECHANISM_STATUS)
MAKE_CASE(SCSIOP_READ_CD)
MAKE_CASE(SCSIOP_SEND_DVD_STRUCTURE)
MAKE_CASE(SCSIOP_INIT_ELEMENT_RANGE)
MAKE_CASE(SCSIOP_READ16)
MAKE_CASE(SCSIOP_WRITE16)
MAKE_CASE(SCSIOP_VERIFY16)
MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE16)
MAKE_CASE(SCSIOP_READ_CAPACITY16)
}
}
return scsiOpStr;
}
char *DbgGetSrbStatusStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
{
char *srbStatStr = "?";
switch (Srb->SrbStatus){
#undef MAKE_CASE
#define MAKE_CASE(srbStat) \
case srbStat: \
srbStatStr = #srbStat; \
break; \
case srbStat|SRB_STATUS_QUEUE_FROZEN: \
srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN"; \
break; \
case srbStat|SRB_STATUS_AUTOSENSE_VALID: \
srbStatStr = #srbStat "|SRB_STATUS_AUTOSENSE_VALID"; \
break; \
case srbStat|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID: \
srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID"; \
break;
MAKE_CASE(SRB_STATUS_PENDING)
MAKE_CASE(SRB_STATUS_SUCCESS)
MAKE_CASE(SRB_STATUS_ABORTED)
MAKE_CASE(SRB_STATUS_ABORT_FAILED)
MAKE_CASE(SRB_STATUS_ERROR)
MAKE_CASE(SRB_STATUS_BUSY)
MAKE_CASE(SRB_STATUS_INVALID_REQUEST)
MAKE_CASE(SRB_STATUS_INVALID_PATH_ID)
MAKE_CASE(SRB_STATUS_NO_DEVICE)
MAKE_CASE(SRB_STATUS_TIMEOUT)
MAKE_CASE(SRB_STATUS_SELECTION_TIMEOUT)
MAKE_CASE(SRB_STATUS_COMMAND_TIMEOUT)
MAKE_CASE(SRB_STATUS_MESSAGE_REJECTED)
MAKE_CASE(SRB_STATUS_BUS_RESET)
MAKE_CASE(SRB_STATUS_PARITY_ERROR)
MAKE_CASE(SRB_STATUS_REQUEST_SENSE_FAILED)
MAKE_CASE(SRB_STATUS_NO_HBA)
MAKE_CASE(SRB_STATUS_DATA_OVERRUN)
MAKE_CASE(SRB_STATUS_UNEXPECTED_BUS_FREE)
MAKE_CASE(SRB_STATUS_PHASE_SEQUENCE_FAILURE)
MAKE_CASE(SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
MAKE_CASE(SRB_STATUS_REQUEST_FLUSHED)
MAKE_CASE(SRB_STATUS_INVALID_LUN)
MAKE_CASE(SRB_STATUS_INVALID_TARGET_ID)
MAKE_CASE(SRB_STATUS_BAD_FUNCTION)
MAKE_CASE(SRB_STATUS_ERROR_RECOVERY)
MAKE_CASE(SRB_STATUS_NOT_POWERED)
MAKE_CASE(SRB_STATUS_INTERNAL_ERROR)
}
return srbStatStr;
}
char *DbgGetSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
{
char *senseCodeStr = "?";
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
PVOID senseData;
UCHAR senseCode;
BOOLEAN validSense;
senseData = SrbGetSenseInfoBuffer(Srb);
NT_ASSERT(senseData);
validSense = ScsiGetSenseKeyAndCodes(senseData,
SrbGetSenseInfoBufferLength(Srb),
SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
&senseCode,
NULL,
NULL);
if (validSense) {
switch (senseCode){
#undef MAKE_CASE
#define MAKE_CASE(snsCod) case snsCod: senseCodeStr = #snsCod; break;
MAKE_CASE(SCSI_SENSE_NO_SENSE)
MAKE_CASE(SCSI_SENSE_RECOVERED_ERROR)
MAKE_CASE(SCSI_SENSE_NOT_READY)
MAKE_CASE(SCSI_SENSE_MEDIUM_ERROR)
MAKE_CASE(SCSI_SENSE_HARDWARE_ERROR)
MAKE_CASE(SCSI_SENSE_ILLEGAL_REQUEST)
MAKE_CASE(SCSI_SENSE_UNIT_ATTENTION)
MAKE_CASE(SCSI_SENSE_DATA_PROTECT)
MAKE_CASE(SCSI_SENSE_BLANK_CHECK)
MAKE_CASE(SCSI_SENSE_UNIQUE)
MAKE_CASE(SCSI_SENSE_COPY_ABORTED)
MAKE_CASE(SCSI_SENSE_ABORTED_COMMAND)
MAKE_CASE(SCSI_SENSE_EQUAL)
MAKE_CASE(SCSI_SENSE_VOL_OVERFLOW)
MAKE_CASE(SCSI_SENSE_MISCOMPARE)
MAKE_CASE(SCSI_SENSE_RESERVED)
}
}
}
return senseCodeStr;
}
char *DbgGetAdditionalSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
{
char *adSenseCodeStr = "?";
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
PVOID senseData;
UCHAR adSenseCode;
BOOLEAN validSense;
senseData = SrbGetSenseInfoBuffer(Srb);
NT_ASSERT(senseData);
validSense = ScsiGetSenseKeyAndCodes(senseData,
SrbGetSenseInfoBufferLength(Srb),
SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
NULL,
&adSenseCode,
NULL);
if (validSense) {
switch (adSenseCode){
#undef MAKE_CASE
#define MAKE_CASE(adSnsCod) case adSnsCod: adSenseCodeStr = #adSnsCod; break;
MAKE_CASE(SCSI_ADSENSE_NO_SENSE)
MAKE_CASE(SCSI_ADSENSE_LUN_NOT_READY)
MAKE_CASE(SCSI_ADSENSE_TRACK_ERROR)
MAKE_CASE(SCSI_ADSENSE_SEEK_ERROR)
MAKE_CASE(SCSI_ADSENSE_REC_DATA_NOECC)
MAKE_CASE(SCSI_ADSENSE_REC_DATA_ECC)
MAKE_CASE(SCSI_ADSENSE_ILLEGAL_COMMAND)
MAKE_CASE(SCSI_ADSENSE_ILLEGAL_BLOCK)
MAKE_CASE(SCSI_ADSENSE_INVALID_CDB)
MAKE_CASE(SCSI_ADSENSE_INVALID_LUN)
MAKE_CASE(SCSI_ADSENSE_WRITE_PROTECT) // aka SCSI_ADWRITE_PROTECT
MAKE_CASE(SCSI_ADSENSE_MEDIUM_CHANGED)
MAKE_CASE(SCSI_ADSENSE_BUS_RESET)
MAKE_CASE(SCSI_ADSENSE_INVALID_MEDIA)
MAKE_CASE(SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
MAKE_CASE(SCSI_ADSENSE_POSITION_ERROR)
MAKE_CASE(SCSI_ADSENSE_OPERATOR_REQUEST)
MAKE_CASE(SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED)
MAKE_CASE(SCSI_ADSENSE_COPY_PROTECTION_FAILURE)
MAKE_CASE(SCSI_ADSENSE_VENDOR_UNIQUE)
MAKE_CASE(SCSI_ADSENSE_MUSIC_AREA)
MAKE_CASE(SCSI_ADSENSE_DATA_AREA)
MAKE_CASE(SCSI_ADSENSE_VOLUME_OVERFLOW)
}
}
}
return adSenseCodeStr;
}
char *DbgGetAdditionalSenseCodeQualifierStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
{
char *adSenseCodeQualStr = "?";
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
PVOID senseData;
UCHAR adSenseCode;
UCHAR adSenseCodeQual;
BOOLEAN validSense;
senseData = SrbGetSenseInfoBuffer(Srb);
NT_ASSERT(senseData);
validSense = ScsiGetSenseKeyAndCodes(senseData,
SrbGetSenseInfoBufferLength(Srb),
SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
NULL,
&adSenseCode,
&adSenseCodeQual);
if (validSense) {
switch (adSenseCode){
#undef MAKE_CASE
#define MAKE_CASE(adSnsCodQual) case adSnsCodQual: adSenseCodeQualStr = #adSnsCodQual; break;
case SCSI_ADSENSE_LUN_NOT_READY:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_CAUSE_NOT_REPORTABLE)
MAKE_CASE(SCSI_SENSEQ_BECOMING_READY)
MAKE_CASE(SCSI_SENSEQ_INIT_COMMAND_REQUIRED)
MAKE_CASE(SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED)
MAKE_CASE(SCSI_SENSEQ_FORMAT_IN_PROGRESS)
MAKE_CASE(SCSI_SENSEQ_REBUILD_IN_PROGRESS)
MAKE_CASE(SCSI_SENSEQ_RECALCULATION_IN_PROGRESS)
MAKE_CASE(SCSI_SENSEQ_OPERATION_IN_PROGRESS)
MAKE_CASE(SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)
}
break;
case SCSI_ADSENSE_NO_SENSE:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_FILEMARK_DETECTED)
MAKE_CASE(SCSI_SENSEQ_END_OF_MEDIA_DETECTED)
MAKE_CASE(SCSI_SENSEQ_SETMARK_DETECTED)
MAKE_CASE(SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED)
}
break;
case SCSI_ADSENSE_ILLEGAL_BLOCK:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR)
}
break;
case SCSI_ADSENSE_POSITION_ERROR:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_DESTINATION_FULL)
MAKE_CASE(SCSI_SENSEQ_SOURCE_EMPTY)
}
break;
case SCSI_ADSENSE_INVALID_MEDIA:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_MEDIA_INSTALLED)
MAKE_CASE(SCSI_SENSEQ_UNKNOWN_FORMAT)
MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_FORMAT)
MAKE_CASE(SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED)
}
break;
case SCSI_ADSENSE_OPERATOR_REQUEST:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_STATE_CHANGE_INPUT)
MAKE_CASE(SCSI_SENSEQ_MEDIUM_REMOVAL)
MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_ENABLE)
MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_DISABLE)
}
break;
case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
switch (adSenseCodeQual){
MAKE_CASE(SCSI_SENSEQ_AUTHENTICATION_FAILURE)
MAKE_CASE(SCSI_SENSEQ_KEY_NOT_PRESENT)
MAKE_CASE(SCSI_SENSEQ_KEY_NOT_ESTABLISHED)
MAKE_CASE(SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION)
MAKE_CASE(SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT)
MAKE_CASE(SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR)
}
break;
}
}
}
return adSenseCodeQualStr;
}

View file

@ -0,0 +1,132 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
debug.h
Abstract:
Author:
Environment:
kernel mode only
Notes:
Revision History:
--*/
#define DBGGETIOCTLSTR(_ioctl) DbgGetIoctlStr(_ioctl)
#define DBGGETSCSIOPSTR(_pSrb) DbgGetScsiOpStr(_pSrb)
#define DBGGETSRBSTATUSSTR(_pSrb) DbgGetSrbStatusStr(_pSrb)
#define DBGGETSENSECODESTR(_pSrb) DbgGetSenseCodeStr(_pSrb)
#define DBGGETADSENSECODESTR(_pSrb) DbgGetAdditionalSenseCodeStr(_pSrb)
#define DBGGETADSENSEQUALIFIERSTR(_pSrb) DbgGetAdditionalSenseCodeQualifierStr(_pSrb)
char *DbgGetIoctlStr(ULONG ioctl);
char *DbgGetScsiOpStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
char *DbgGetSrbStatusStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
char *DbgGetSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
char *DbgGetAdditionalSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
char *DbgGetAdditionalSenseCodeQualifierStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
#if DBG
typedef struct _CLASSPNP_GLOBALS {
//
// whether or not to NT_ASSERT for lost irps
//
ULONG BreakOnLostIrps;
ULONG SecondsToWaitForIrps;
//
// use a buffered debug print to help
// catch timing issues that do not
// reproduce with std debugprints enabled
//
ULONG UseBufferedDebugPrint;
ULONG UseDelayedRetry;
//
// the next four are the buffered printing support
// (currently unimplemented) and require the spinlock
// to use
//
ULONG Index; // index into buffer
KSPIN_LOCK SpinLock;
PUCHAR Buffer; // requires spinlock to access
ULONG NumberOfBuffers; // number of buffers available
SIZE_T EachBufferSize; // size of each buffer
//
// interlocked variables to initialize
// this data only once
//
LONG Initializing;
LONG Initialized;
} CLASSPNP_GLOBALS, *PCLASSPNP_GLOBALS;
//
// Define a structure used to capture stack traces when we
// get an access request and the disks are powered off. This
// will help us determine who's causing disk respins.
//
//
// How many stack frames to capture each time?
//
#define DISK_SPINUP_BACKTRACE_LENGTH (0x18)
//
// How many stack traces can we capture before
// out buffer wraps? (needs to be power of 2)
//
#define NUMBER_OF_DISK_SPINUP_TRACES (0x10)
typedef struct _DISK_SPINUP_TRACES {
LARGE_INTEGER TimeStamp; // timestamp of the spinup event.
PVOID StackTrace[DISK_SPINUP_BACKTRACE_LENGTH]; // Holds stack trace
} DISK_SPINUP_TRACES, *PDISK_SPINUP_TRACES;
#define DBGCHECKRETURNEDPKT(_pkt) DbgCheckReturnedPkt(_pkt)
#define DBGLOGSENDPACKET(_pkt) DbgLogSendPacket(_pkt)
#define DBGLOGRETURNPACKET(_pkt) DbgLogReturnPacket(_pkt)
#define DBGLOGFLUSHINFO(_fdoData, _isIO, _isFUA, _isFlush) DbgLogFlushInfo(_fdoData, _isIO, _isFUA, _isFlush)
VOID ClasspInitializeDebugGlobals();
VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt);
VOID DbgLogSendPacket(TRANSFER_PACKET *Pkt);
VOID DbgLogReturnPacket(TRANSFER_PACKET *Pkt);
VOID DbgLogFlushInfo(PCLASS_PRIVATE_FDO_DATA FdoData, BOOLEAN IsIO, BOOLEAN IsFUA, BOOLEAN IsFlush);
VOID SnapDiskStartup(VOID);
extern CLASSPNP_GLOBALS ClasspnpGlobals;
extern LONG ClassDebug;
extern BOOLEAN DebugTrapOnWarn;
#else
#define ClasspInitializeDebugGlobals()
#define SnapDiskStartup()
#define DBGCHECKRETURNEDPKT(_pkt)
#define DBGLOGSENDPACKET(_pkt)
#define DBGLOGRETURNPACKET(_pkt)
#define DBGLOGFLUSHINFO(_fdoData, _isIO, _isFUA, _isFlush)
#endif

View file

@ -0,0 +1,231 @@
/*++
Copyright (C) Microsoft Corporation, 1990 - 1999
Module Name:
dictlib.c
Abstract:
Support library for maintaining a dictionary list (list of objects
referenced by a key value).
Environment:
kernel mode only
Notes:
This module generates a static library
Revision History:
--*/
#include <ntddk.h>
#include <classpnp.h>
#ifdef __REACTOS__
#undef MdlMappingNoExecute
#define MdlMappingNoExecute 0
#define NonPagedPoolNx NonPagedPool
#define NonPagedPoolNxCacheAligned NonPagedPoolCacheAligned
#undef POOL_NX_ALLOCATION
#define POOL_NX_ALLOCATION 0
#endif
#define DICTIONARY_SIGNATURE 'tciD'
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4200) // nonstandard extension used : zero-sized array in struct/union
#endif
struct _DICTIONARY_HEADER {
PDICTIONARY_HEADER Next;
ULONGLONG Key;
UCHAR Data[0];
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
struct _DICTIONARY_HEADER;
typedef struct _DICTIONARY_HEADER DICTIONARY_HEADER, *PDICTIONARY_HEADER;
VOID
InitializeDictionary(
IN PDICTIONARY Dictionary
)
{
RtlZeroMemory(Dictionary, sizeof(DICTIONARY));
Dictionary->Signature = DICTIONARY_SIGNATURE;
KeInitializeSpinLock(&Dictionary->SpinLock);
return;
}
BOOLEAN
TestDictionarySignature(
IN PDICTIONARY Dictionary
)
{
return Dictionary->Signature == DICTIONARY_SIGNATURE;
}
NTSTATUS
AllocateDictionaryEntry(
IN PDICTIONARY Dictionary,
IN ULONGLONG Key,
_In_range_(0, sizeof(FILE_OBJECT_EXTENSION)) IN ULONG Size,
IN ULONG Tag,
OUT PVOID *Entry
)
{
PDICTIONARY_HEADER header;
KIRQL oldIrql;
PDICTIONARY_HEADER *entry;
NTSTATUS status = STATUS_SUCCESS;
*Entry = NULL;
header = ExAllocatePoolWithTag(NonPagedPoolNx,
Size + sizeof(DICTIONARY_HEADER),
Tag);
if(header == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(header, sizeof(DICTIONARY_HEADER) + Size);
header->Key = Key;
//
// Find the correct location for this entry in the dictionary.
//
KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
TRY {
entry = &(Dictionary->List);
while(*entry != NULL) {
if((*entry)->Key == Key) {
//
// Dictionary must have unique keys.
//
status = STATUS_OBJECT_NAME_COLLISION;
LEAVE;
} else if ((*entry)->Key < Key) {
//
// We will go ahead and insert the key in here.
//
break;
} else {
entry = &((*entry)->Next);
}
}
//
// If we make it here then we will go ahead and do the insertion.
//
header->Next = *entry;
*entry = header;
} FINALLY {
KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
if(!NT_SUCCESS(status)) {
FREE_POOL(header);
} else {
*Entry = (PVOID) header->Data;
}
}
return status;
}
PVOID
GetDictionaryEntry(
IN PDICTIONARY Dictionary,
IN ULONGLONG Key
)
{
PDICTIONARY_HEADER entry;
PVOID data;
KIRQL oldIrql;
data = NULL;
KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
entry = Dictionary->List;
while (entry != NULL) {
if (entry->Key == Key) {
data = entry->Data;
break;
} else {
entry = entry->Next;
}
}
KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
return data;
}
VOID
FreeDictionaryEntry(
IN PDICTIONARY Dictionary,
IN PVOID Entry
)
{
PDICTIONARY_HEADER header;
PDICTIONARY_HEADER *entry;
KIRQL oldIrql;
BOOLEAN found;
found = FALSE;
header = CONTAINING_RECORD(Entry, DICTIONARY_HEADER, Data);
KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
entry = &(Dictionary->List);
while(*entry != NULL) {
if(*entry == header) {
*entry = header->Next;
found = TRUE;
break;
} else {
entry = &(*entry)->Next;
}
}
KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
//
// calling this w/an invalid pointer invalidates the dictionary system,
// so NT_ASSERT() that we never try to Free something not in the list
//
NT_ASSERT(found);
if (found) {
FREE_POOL(header);
}
return;
}

View file

@ -0,0 +1,133 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 2005
Module Name:
dispatch.c
Abstract:
Code to support multiple dispatch tables.
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ClassInitializeDispatchTables)
#endif
DRIVER_DISPATCH ClassDispatchUnimplemented;
//
// Routines start
//
VOID
ClassInitializeDispatchTables(
PCLASS_DRIVER_EXTENSION DriverExtension
)
{
ULONG idx;
PAGED_CODE();
//
// Initialize the standard device dispatch table
//
for (idx = 0; idx <= IRP_MJ_MAXIMUM_FUNCTION; idx++) {
DriverExtension->DeviceMajorFunctionTable[idx] = ClassDispatchUnimplemented;
}
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_CREATE] = ClassCreateClose;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_CLOSE] = ClassCreateClose;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_READ] = ClassReadWrite;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_WRITE] = ClassReadWrite;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_SCSI] = ClassInternalIoControl;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_PNP] = ClassDispatchPnp;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_POWER] = ClassDispatchPower;
DriverExtension->DeviceMajorFunctionTable[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
return;
}
NTSTATUS
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassGlobalDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
// Code Analysis cannot analyze the code paths specific to clients.
_Analysis_assume_(FALSE);
return (commonExtension->DispatchTable[irpStack->MajorFunction])(DeviceObject, Irp);
}
NTSTATUS
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassDispatchUnimplemented(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function is the default dispatch routine. Its
responsibility is simply to set the status in the packet to indicate
that the operation requested is invalid for this device type, and then
complete the packet.
Arguments:
DeviceObject - Specifies the device object for which this request is
bound. Ignored by this routine.
Irp - Specifies the address of the I/O Request Packet (IRP) for this
request.
Return Value:
The final status is always STATUS_INVALID_DEVICE_REQUEST.
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
//
// Simply store the appropriate status, complete the request, and return
// the same status stored in the packet.
//
if ((IoGetCurrentIrpStackLocation(Irp))->MajorFunction == IRP_MJ_POWER) {
PoStartNextPowerIrp(Irp);
}
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_INVALID_DEVICE_REQUEST;
}

View file

@ -0,0 +1,8 @@
/* DO NOT USE THE PRECOMPILED HEADER FOR THIS FILE! */
#include <ntdef.h>
#include <initguid.h>
#include <ioevent.h>
#include <ntifs.h>
/* NO CODE HERE, THIS IS JUST REQUIRED FOR THE GUID DEFINITIONS */

View file

@ -0,0 +1,146 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
history.c
Abstract:
Packet history routines for CLASSPNP
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef DEBUG_USE_WPP
#include "history.tmh"
#endif
//#ifdef ALLOC_PRAGMA
// #pragma alloc_text(PAGE, InitializeTransferPackets)
//#endif
VOID HistoryInitializeRetryLogs(_Out_ PSRB_HISTORY History, ULONG HistoryCount) {
ULONG tmpSize = HistoryCount * sizeof(SRB_HISTORY_ITEM);
tmpSize += sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
RtlZeroMemory(History, tmpSize);
History->TotalHistoryCount = HistoryCount;
return;
}
VOID HistoryLogSendPacket(TRANSFER_PACKET * Pkt) {
PSRB_HISTORY history;
PSRB_HISTORY_ITEM item;
NT_ASSERT( Pkt->RetryHistory != NULL );
history = Pkt->RetryHistory;
// sending a packet implies a new history unit is to be used.
NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
// if already all used up, request class driver to remove at least one history unit
if (history->UsedHistoryCount == history->TotalHistoryCount )
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
NT_ASSERT( fdoData->InterpretSenseInfo != NULL );
NT_ASSERT( fdoData->InterpretSenseInfo->Compress != NULL );
fdoData->InterpretSenseInfo->Compress( fdoExtension->DeviceObject, history );
NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
}
// thus, since we are about to increment the count, it must now be less...
NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
// increment the number of history units in use
history->UsedHistoryCount++;
// determine index to use
item = &( history->History[ history->UsedHistoryCount-1 ] );
// zero out the history item
RtlZeroMemory(item, sizeof(SRB_HISTORY_ITEM));
// Query the tick count and store in the history
KeQueryTickCount(&item->TickCountSent);
return;
}
VOID HistoryLogReturnedPacket(TRANSFER_PACKET *Pkt) {
PSRB_HISTORY history;
PSRB_HISTORY_ITEM item;
UCHAR senseSize;
PVOID senseInfoBuffer;
UCHAR senseInfoBufferLength;
SENSE_DATA convertedSenseBuffer = {0};
BOOLEAN validSense = TRUE;
NT_ASSERT( Pkt->RetryHistory != NULL );
history = Pkt->RetryHistory;
NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
item = &( history->History[ history->UsedHistoryCount-1 ] );
// Query the tick count and store in the history
KeQueryTickCount(&item->TickCountCompleted);
// Copy the SRB Status...
item->SrbStatus = Pkt->Srb->SrbStatus;
//
// Process sense data
//
senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
if (IsDescriptorSenseDataFormat(senseInfoBuffer)) {
validSense = ScsiConvertToFixedSenseFormat(senseInfoBuffer,
senseInfoBufferLength,
(PVOID)&convertedSenseBuffer,
sizeof(convertedSenseBuffer));
if (validSense) {
senseInfoBuffer = (PVOID)&convertedSenseBuffer;
senseInfoBufferLength = sizeof(convertedSenseBuffer);
}
}
RtlZeroMemory(&(item->NormalizedSenseData), sizeof(item->NormalizedSenseData));
if (validSense) {
// Determine the amount of valid sense data
if (!ScsiGetTotalSenseByteCountIndicated(senseInfoBuffer,
senseInfoBufferLength,
&senseSize)) {
senseSize = senseInfoBufferLength;
}
// Normalize the sense data copy in the history
senseSize = min(senseSize, sizeof(item->NormalizedSenseData));
RtlCopyMemory(&(item->NormalizedSenseData),
senseInfoBuffer,
senseSize
);
}
return;
}

View file

@ -0,0 +1,560 @@
/*++
Copyright (C) Microsoft Corporation, 1990 - 1998
Module Name:
lock.c
Abstract:
This is the NT SCSI port driver.
Environment:
kernel mode only
Notes:
This module is a driver dll for scsi miniports.
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef DEBUG_USE_WPP
#include "lock.tmh"
#endif
LONG LockHighWatermark = 0;
LONG LockLowWatermark = 0;
LONG MaxLockedMinutes = 5;
//
// Structure used for tracking remove lock allocations in checked builds
//
typedef struct _REMOVE_TRACKING_BLOCK {
PVOID Tag;
LARGE_INTEGER TimeLocked;
PCSTR File;
ULONG Line;
} REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
/*++////////////////////////////////////////////////////////////////////////////
Classpnp RemoveLockRundown
RemoveLockRundown is a cacheaware rundown protection for the classpnp device object. While this
rundown protection is held successfully, the caller can assume that no pending pnp REMOVE
requests will be completed.
The RemoveLockRundown is a replacement of the original RemoveLock to improve the scalability.
For backward compatibility, we still keep the RemoveLock field in the device common extension structure.
However, the old RemoveLock is only being used in the DBG build.
The usage of the RemoveLockRundown is slightly different from the normal rundown protection usage.
The RemoveLockRundown is acquired via ClassAcquireRemoveLockEx() function
and released via ClassReleaseRemoveLock() function. Usually, we bail out when the acquisition
of rundown protection fails (calls to ExAcquireRundownProtectionCacheAware returns FALSE) and
will not release the rundown protection in acquisition failure. For the RemoveLockRundown,
the caller will always call ClassAcquireRemoveLockEx() and ClassReleaseRemoveLock() in a pair no
matter the return value of ClassAcquireRemoveLockEx(). Therefore, a thread may still call
ClassReleaseRemoveLock() even the previous acquisition RemoveLockRundown protection failed.
To deal with the previous acquisition failure case, we introduced a new field RemoveLockFailAcquire
as a counter for rundown acquisition failures. In the ClassReleaseRemoveLock() function, we only
release the rundown protection when this counter is decremented to zero. Since the change of RemoveLockFailAcquire
and release rundown protection is not protected by a lock as an atomic operation, we use a while loop over
InterlockedCompareExchange operation to make sure when we release the rundown protection, this counter is
actually zero.
--*/
/*++////////////////////////////////////////////////////////////////////////////
ClassAcquireRemoveLockEx()
Routine Description:
This routine is called to acquire the remove lock on the device object.
While the lock is held, the caller can assume that no pending pnp REMOVE
requests will be completed.
The lock should be acquired immediately upon entering a dispatch routine.
It should also be acquired before creating any new reference to the
device object if there's a chance of releasing the reference before the
new one is done.
This routine will return TRUE if the lock was successfully acquired or
FALSE if it cannot be because the device object has already been removed.
Arguments:
DeviceObject - the device object to lock
Tag - Used for tracking lock allocation and release. If an irp is
specified when acquiring the lock then the same Tag must be
used to release the lock before the Tag is completed.
Return Value:
The value of the IsRemoved flag in the device extension. If this is
non-zero then the device object has received a Remove irp and non-cleanup
IRP's should fail.
If the value is REMOVE_COMPLETE, the caller should not even release the
lock.
--*/
ULONG
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassAcquireRemoveLockEx(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PVOID Tag,
_In_ PCSTR File,
_In_ ULONG Line
)
// This function implements the acquisition of Tag
#ifdef _MSC_VER
#pragma warning(suppress:28104)
#endif
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
BOOLEAN rundownAcquired;
PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
//
// Grab the remove lock
//
#if DBG
LONG lockValue;
lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassAcquireRemoveLock: "
"Acquired for Object %p & irp %p - count is %d\n",
DeviceObject, Tag, lockValue));
NT_ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
(lockValue > 0));
NT_ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
((LockHighWatermark == 0) ||
(lockValue != LockHighWatermark)));
if (commonExtension->IsRemoved != REMOVE_COMPLETE) {
PRTL_GENERIC_TABLE removeTrackingList = NULL;
REMOVE_TRACKING_BLOCK trackingBlock;
PREMOVE_TRACKING_BLOCK insertedTrackingBlock = NULL;
BOOLEAN newElement = FALSE;
KIRQL oldIrql;
trackingBlock.Tag = Tag;
trackingBlock.File = File;
trackingBlock.Line = Line;
KeQueryTickCount((&trackingBlock.TimeLocked));
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
&oldIrql);
removeTrackingList = commonExtension->RemoveTrackingList;
if (removeTrackingList != NULL) {
insertedTrackingBlock = RtlInsertElementGenericTable(removeTrackingList,
&trackingBlock,
sizeof(REMOVE_TRACKING_BLOCK),
&newElement);
}
if (insertedTrackingBlock != NULL) {
if (!newElement) {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
"already tracking Tag %p\n", Tag));
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
"acquired in file %s on line %d\n",
insertedTrackingBlock->File, insertedTrackingBlock->Line));
// NT_ASSERT(FALSE);
}
} else {
commonExtension->RemoveTrackingUntrackedCount++;
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
"Cannot track Tag %p - currently %d untracked requsts\n",
Tag, commonExtension->RemoveTrackingUntrackedCount));
}
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
}
#else
UNREFERENCED_PARAMETER(Tag);
UNREFERENCED_PARAMETER(File);
UNREFERENCED_PARAMETER(Line);
#endif
removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
rundownAcquired = ExAcquireRundownProtectionCacheAware(removeLockRundown);
if (!rundownAcquired) {
InterlockedIncrement((volatile LONG*) &(commonExtension->PrivateCommonData->RemoveLockFailAcquire));
TracePrint((TRACE_LEVEL_VERBOSE,
TRACE_FLAG_LOCK,
"ClassAcquireRemoveLockEx: RemoveLockRundown acquisition failed"
"RemoveLockFailAcquire = %d\n",
commonExtension->PrivateCommonData->RemoveLockFailAcquire));
}
return (commonExtension->IsRemoved);
}
/*++////////////////////////////////////////////////////////////////////////////
ClassReleaseRemoveLock()
Routine Description:
This routine is called to release the remove lock on the device object. It
must be called when finished using a previously locked reference to the
device object. If an Tag was specified when acquiring the lock then the
same Tag must be specified when releasing the lock.
When the lock count reduces to zero, this routine will signal the waiting
remove Tag to delete the device object. As a result the DeviceObject
pointer should not be used again once the lock has been released.
Arguments:
DeviceObject - the device object to lock
Tag - The irp (if any) specified when acquiring the lock. This is used
for lock tracking purposes
Return Value:
none
--*/
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassReleaseRemoveLock(
_In_ PDEVICE_OBJECT DeviceObject,
_In_opt_ PIRP Tag
)
// This function implements the release of Tag
#ifdef _MSC_VER
#pragma warning(suppress:28103)
#endif
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
LONG lockValue;
LONG oldValue;
PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
#if DBG
PRTL_GENERIC_TABLE removeTrackingList = NULL;
REMOVE_TRACKING_BLOCK searchDataBlock;
BOOLEAN found = FALSE;
BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
KIRQL oldIrql;
if (isRemoved) {
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
InterlockedDecrement(&(commonExtension->RemoveLock));
return;
}
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
&oldIrql);
removeTrackingList = commonExtension->RemoveTrackingList;
if (removeTrackingList != NULL) {
searchDataBlock.Tag = Tag;
found = RtlDeleteElementGenericTable(removeTrackingList, &searchDataBlock);
}
if (!found) {
if(commonExtension->RemoveTrackingUntrackedCount == 0) {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
"Couldn't find Tag %p in the lock tracking list\n", Tag));
//
// This might happen if the device is being removed and the tracking list
// has already been freed. Don't assert if that is the case.
//
NT_ASSERT((removeTrackingList == NULL) && (commonExtension->IsRemoved != NO_REMOVE));
} else {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
"Couldn't find Tag %p in the lock tracking list - "
"may be one of the %d untracked requests still outstanding\n",
Tag, commonExtension->RemoveTrackingUntrackedCount));
commonExtension->RemoveTrackingUntrackedCount--;
NT_ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
}
}
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
oldIrql);
lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
"Released for Object %p & irp %p - count is %d\n",
DeviceObject, Tag, lockValue));
NT_ASSERT(lockValue >= 0);
NT_ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
if (lockValue == 0) {
NT_ASSERT(commonExtension->IsRemoved);
//
// The device needs to be removed. Signal the remove event
// that it's safe to go ahead.
//
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
"Release for object %p & irp %p caused lock to go to zero\n",
DeviceObject, Tag));
}
#else
UNREFERENCED_PARAMETER(Tag);
#endif
//
// Decrement the RemoveLockFailAcquire by 1 when RemoveLockFailAcquire is non-zero.
// Release the RemoveLockRundown only when RemoveLockFailAcquire is zero.
//
oldValue = 1;
lockValue = commonExtension->PrivateCommonData->RemoveLockFailAcquire;
while (lockValue != 0) {
oldValue =
InterlockedCompareExchange((volatile LONG *) &commonExtension->PrivateCommonData->RemoveLockFailAcquire,
lockValue - 1,
lockValue);
if (oldValue == lockValue) {
break;
}
lockValue = oldValue;
}
if (lockValue == 0) {
removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
ExReleaseRundownProtectionCacheAware(removeLockRundown);
}
return;
}
/*++////////////////////////////////////////////////////////////////////////////
ClassCompleteRequest()
Routine Description:
This routine is a wrapper around (and should be used instead of)
IoCompleteRequest. It is used primarily for debugging purposes.
The routine will assert if the Irp being completed is still holding
the release lock.
Arguments:
DeviceObject - the device object that was handling this request
Irp - the irp to be completed by IoCompleteRequest
PriorityBoost - the priority boost to pass to IoCompleteRequest
Return Value:
none
--*/
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassCompleteRequest(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp,
_In_ CCHAR PriorityBoost
)
{
#if DBG
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PRTL_GENERIC_TABLE removeTrackingList = NULL;
REMOVE_TRACKING_BLOCK searchDataBlock;
PREMOVE_TRACKING_BLOCK foundTrackingBlock;
KIRQL oldIrql;
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
removeTrackingList = commonExtension->RemoveTrackingList;
if (removeTrackingList != NULL)
{
searchDataBlock.Tag = Irp;
foundTrackingBlock = RtlLookupElementGenericTable(removeTrackingList, &searchDataBlock);
if(foundTrackingBlock != NULL) {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
"Irp %p completed while still holding the remove lock\n", Irp));
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
"Lock acquired in file %s on line %d\n",
foundTrackingBlock->File, foundTrackingBlock->Line));
NT_ASSERT(FALSE);
}
}
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
#endif
UNREFERENCED_PARAMETER(DeviceObject);
IoCompleteRequest(Irp, PriorityBoost);
return;
} // end ClassCompleteRequest()
RTL_GENERIC_COMPARE_RESULTS
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
RemoveTrackingCompareRoutine(
PRTL_GENERIC_TABLE Table,
PVOID FirstStruct,
PVOID SecondStruct
)
{
PVOID tag1, tag2;
UNREFERENCED_PARAMETER(Table);
tag1 = ((PREMOVE_TRACKING_BLOCK)FirstStruct)->Tag;
tag2 = ((PREMOVE_TRACKING_BLOCK)SecondStruct)->Tag;
if (tag1 < tag2)
{
return GenericLessThan;
}
else if (tag1 > tag2)
{
return GenericGreaterThan;
}
return GenericEqual;
}
PVOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
RemoveTrackingAllocateRoutine(
PRTL_GENERIC_TABLE Table,
CLONG ByteSize
)
{
UNREFERENCED_PARAMETER(Table);
return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, CLASS_TAG_LOCK_TRACKING);
}
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
RemoveTrackingFreeRoutine(
PRTL_GENERIC_TABLE Table,
PVOID Buffer
)
{
UNREFERENCED_PARAMETER(Table);
FREE_POOL(Buffer);
}
VOID
ClasspInitializeRemoveTracking(
_In_ PDEVICE_OBJECT DeviceObject
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
#if DBG
KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
commonExtension->RemoveTrackingList = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(RTL_GENERIC_TABLE), CLASS_TAG_LOCK_TRACKING);
if (commonExtension->RemoveTrackingList != NULL)
{
RtlInitializeGenericTable(commonExtension->RemoveTrackingList,
RemoveTrackingCompareRoutine,
RemoveTrackingAllocateRoutine,
RemoveTrackingFreeRoutine,
NULL);
}
#else
UNREFERENCED_PARAMETER(DeviceObject);
commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
commonExtension->RemoveTrackingList = NULL;
#endif
}
VOID
ClasspUninitializeRemoveTracking(
_In_ PDEVICE_OBJECT DeviceObject
)
{
#if DBG
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PRTL_GENERIC_TABLE removeTrackingList = commonExtension->RemoveTrackingList;
ASSERTMSG("Removing the device while still holding remove locks",
commonExtension->RemoveTrackingUntrackedCount == 0 &&
removeTrackingList != NULL ? RtlNumberGenericTableElements(removeTrackingList) == 0 : TRUE);
if (removeTrackingList != NULL)
{
KIRQL oldIrql;
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
FREE_POOL(removeTrackingList);
commonExtension->RemoveTrackingList = NULL;
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
}
#else
UNREFERENCED_PARAMETER(DeviceObject);
#endif
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,798 @@
/*++
Copyright (C) Microsoft Corporation, 1991 - 2010
Module Name:
retry.c
Abstract:
Packet retry routines for CLASSPNP
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#ifdef DEBUG_USE_WPP
#include "retry.tmh"
#endif
/*
* InterpretTransferPacketError
*
* Interpret the SRB error into a meaningful IRP status.
* ClassInterpretSenseInfo also may modify the SRB for the retry.
*
* Return TRUE iff packet should be retried.
*/
BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
ULONG timesAlreadyRetried;
BOOLEAN shouldRetry = FALSE;
PCDB pCdb = ClasspTransferPacketGetCdb(Pkt);
/*
* Interpret the error using the returned sense info first.
*/
Pkt->RetryIn100nsUnits = 0;
/*
* Pre-calculate the number of times the IO has already been
* retried, so that all InterpretSenseInfo routines get the right value.
*/
if (ClasspTransferPacketGetNumberOfRetriesDone(Pkt, pCdb, &timesAlreadyRetried) == FALSE)
{
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
}
if (fdoData->InterpretSenseInfo != NULL) {
SCSI_REQUEST_BLOCK tempSrb = { 0 };
PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Pkt->Srb;
// SAL annotation and ClassInitializeEx() both validate this
NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
//
// If class driver does not support extended SRB and this is
// an extended SRB, convert to legacy SRB and pass to class
// driver.
//
if ((Pkt->Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Pkt->Srb);
srbPtr = &tempSrb;
}
shouldRetry = fdoData->InterpretSenseInfo->Interpret(Pkt->Fdo,
Pkt->OriginalIrp,
srbPtr,
IRP_MJ_SCSI,
0,
timesAlreadyRetried,
Pkt->RetryHistory,
&Pkt->Irp->IoStatus.Status,
&Pkt->RetryIn100nsUnits);
} else {
//
// In this case, fdoData->InterpretSenseInfo == NULL so we must do our
// own error code and sense info processing.
//
PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
BOOLEAN validSense = FALSE;
UCHAR senseKey = 0;
UCHAR additionalSenseCode = 0;
UCHAR additionalSenseQual = 0;
NT_ASSERT(senseInfoBuffer);
validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
senseInfoBufferLength,
SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
&senseKey,
&additionalSenseCode,
&additionalSenseQual);
if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL) {
ULONG retryIntervalSeconds = 0;
/*
* This is an Ejection Control SRB. Interpret its sense info specially.
*/
shouldRetry = ClassInterpretSenseInfo(
Pkt->Fdo,
(PSCSI_REQUEST_BLOCK)Pkt->Srb,
IRP_MJ_SCSI,
0,
timesAlreadyRetried,
&Pkt->Irp->IoStatus.Status,
&retryIntervalSeconds);
if (shouldRetry) {
/*
* If the device is not ready, wait at least 2 seconds before retrying.
*/
BOOLEAN setRetryIntervalSeconds = FALSE;
if (validSense) {
if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
(additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
setRetryIntervalSeconds = TRUE;
}
}
if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
setRetryIntervalSeconds = TRUE;
}
if (setRetryIntervalSeconds) {
retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
}
}
if (shouldRetry)
{
Pkt->RetryIn100nsUnits = retryIntervalSeconds;
Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
}
}
else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
(pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)) {
ULONG retryIntervalSeconds = 0;
/*
* This is an Mode Sense SRB. Interpret its sense info specially.
*/
shouldRetry = ClassInterpretSenseInfo(
Pkt->Fdo,
(PSCSI_REQUEST_BLOCK)Pkt->Srb,
IRP_MJ_SCSI,
0,
timesAlreadyRetried,
&Pkt->Irp->IoStatus.Status,
&retryIntervalSeconds);
if (shouldRetry) {
/*
* If the device is not ready, wait at least 2 seconds before retrying.
*/
BOOLEAN setRetryIntervalSeconds = FALSE;
if (validSense) {
if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
(additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
setRetryIntervalSeconds = TRUE;
}
}
if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
setRetryIntervalSeconds = TRUE;
}
if (setRetryIntervalSeconds) {
retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
}
}
/*
* Some special cases for mode sense.
*/
if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
shouldRetry = TRUE;
}
else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
/*
* This is a HACK.
* Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
* underrun (i.e. success, and the buffer is longer than needed).
* So treat this as a success.
* When the caller of this function sees that the status was changed to success,
* it will add the transferred length to the original irp.
*/
Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
shouldRetry = FALSE;
}
if (shouldRetry)
{
Pkt->RetryIn100nsUnits = retryIntervalSeconds;
Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
}
}
else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
(pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) {
ULONG retryIntervalSeconds = 0;
/*
* This is a Drive Capacity SRB. Interpret its sense info specially.
*/
shouldRetry = ClassInterpretSenseInfo(
Pkt->Fdo,
(PSCSI_REQUEST_BLOCK)Pkt->Srb,
IRP_MJ_SCSI,
0,
timesAlreadyRetried,
&Pkt->Irp->IoStatus.Status,
&retryIntervalSeconds);
if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
shouldRetry = TRUE;
}
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
else if (ClasspSrbTimeOutStatus(Pkt->Srb)) {
Pkt->TimedOut = TRUE;
if (shouldRetry) {
//
// For requests that have timed-out we may only perform a limited
// number of retries. This is typically less than the general
// number of retries allowed.
//
if (Pkt->NumIoTimeoutRetries == 0) {
shouldRetry = FALSE;
} else {
Pkt->NumIoTimeoutRetries--;
//
// We expect to be able to retry if there are some general retries remaining.
//
}
}
}
#endif
if (shouldRetry)
{
Pkt->RetryIn100nsUnits = retryIntervalSeconds;
Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
}
}
else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {
ULONG retryIntervalSeconds = 0;
/*
* This is a Read/Write Data packet.
*/
PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
shouldRetry = ClassInterpretSenseInfo(Pkt->Fdo,
(PSCSI_REQUEST_BLOCK)Pkt->Srb,
origCurrentSp->MajorFunction,
0,
timesAlreadyRetried,
&Pkt->Irp->IoStatus.Status,
&retryIntervalSeconds);
/*
* Deal with some special cases.
*/
if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
/*
* We are in extreme low-memory stress.
* We will retry in smaller chunks.
*/
shouldRetry = TRUE;
}
else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
(Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
/*
* We are still verifying a (possibly) reloaded disk/cdrom.
* So retry the request.
*/
Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
shouldRetry = TRUE;
}
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
else if (ClasspSrbTimeOutStatus(Pkt->Srb)) {
Pkt->TimedOut = TRUE;
if (shouldRetry) {
//
// For requests that have timed-out we may only perform a limited
// number of retries. This is typically less than the general
// number of retries allowed.
//
if (Pkt->NumIoTimeoutRetries == 0) {
shouldRetry = FALSE;
} else {
Pkt->NumIoTimeoutRetries--;
//
// We expect to be able to retry if there are some general retries remaining.
//
}
}
}
#endif
if (shouldRetry)
{
Pkt->RetryIn100nsUnits = retryIntervalSeconds;
Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
}
} else if (ClasspIsOffloadDataTransferCommand(pCdb)) {
ULONG retryIntervalSeconds = 0;
Pkt->TransferCount = 0;
shouldRetry = ClassInterpretSenseInfo(
Pkt->Fdo,
(PSCSI_REQUEST_BLOCK)Pkt->Srb,
IRP_MJ_SCSI,
0,
timesAlreadyRetried,
&Pkt->Irp->IoStatus.Status,
&retryIntervalSeconds);
if (shouldRetry) {
Pkt->RetryIn100nsUnits = retryIntervalSeconds;
Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
} else {
if (ClasspIsTokenOperation(pCdb)) {
BOOLEAN isInformationValid = FALSE;
ULONGLONG information = 0;
if (validSense) {
//
// If this is a data underrun condition (i.e. target truncated the offload data transfer),
// the SenseData's Information field may have the TransferCount.
//
if ((senseKey == SCSI_SENSE_COPY_ABORTED || senseKey == SCSI_SENSE_ABORTED_COMMAND) &&
(additionalSenseCode == SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR && additionalSenseQual == SCSI_SENSEQ_DATA_UNDERRUN)) {
//
// Sense data in Descriptor format
//
if (IsDescriptorSenseDataFormat(senseInfoBuffer)) {
PVOID startBuffer = NULL;
UCHAR startBufferLength = 0;
if (ScsiGetSenseDescriptor(senseInfoBuffer,
SrbGetSenseInfoBufferLength(Pkt->Srb),
&startBuffer,
&startBufferLength)) {
UCHAR outType;
PVOID outBuffer = NULL;
UCHAR outBufferLength = 0;
UCHAR typeList[1] = { SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION };
if (ScsiGetNextSenseDescriptorByType(startBuffer,
startBufferLength,
typeList,
ARRAYSIZE(typeList),
&outType,
&outBuffer,
&outBufferLength)) {
if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
isInformationValid = TRUE;
}
} else {
//
// ScsiGetNextDescriptorByType should only return a type that is specified by us.
//
NT_ASSERT(FALSE);
}
}
}
} else {
//
// Sense data in Fixed format
//
REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseInfoBuffer)->Information));
isInformationValid = TRUE;
}
if (isInformationValid) {
Pkt->TransferCount = information;
}
}
}
}
}
}
else {
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
}
}
return shouldRetry;
}
/*
* RetryTransferPacket
*
* Retry sending a TRANSFER_PACKET.
*
* Return TRUE iff the packet is complete.
* (if so the status in pkt->irp is the final status).
*/
BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
{
BOOLEAN packetDone;
BOOLEAN scaleDown = FALSE;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
PCDB pCdb = SrbGetCdb(Pkt->Srb);
#if !defined(__REACTOS__) && NTDDI_VERSION >= NTDDI_WINBLUE
if(ClasspIsThinProvisioningError((PSCSI_REQUEST_BLOCK)Pkt->Srb) &&
(pCdb != NULL) && IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {
if(Pkt->NumThinProvisioningRetries >= NUM_THIN_PROVISIONING_RETRIES) {
//We've already retried this the maximum times. Bail out.
return TRUE;
}
Pkt->NumThinProvisioningRetries++;
}
else {
NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory);
Pkt->NumRetries--;
}
#else
NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory);
Pkt->NumRetries--;
#endif
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(Pkt->Srb)));
if (!fdoData->DisableThrottling) {
//
// If this is the last retry, then turn off disconnect, sync transfer,
// and tagged queuing. On all other retries, leave the original settings.
// Do not apply this for thin provisioning soft threshold errors, since
// they should succeed as soon as they're retried on the right IT nexus.
//
if ((Pkt->NumRetries == 0) && !ClasspIsThinProvisioningError((PSCSI_REQUEST_BLOCK)Pkt->Srb)) {
scaleDown = TRUE;
}
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
//
// If this request previously timed-out and there are no more retries left
// for timed-out requests, then we should also apply the scale down.
//
if (Pkt->TimedOut && Pkt->NumIoTimeoutRetries == 0) {
scaleDown = TRUE;
}
#endif
}
if (scaleDown) {
/*
* Tone down performance on the retry.
* This increases the chance for success on the retry.
* We've seen instances of drives that fail consistently but then start working
* once this scale-down is applied.
*/
SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_DISCONNECT);
SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
SrbClearSrbFlags(Pkt->Srb, SRB_FLAGS_QUEUE_ACTION_ENABLE);
SrbSetRequestTag(Pkt->Srb, SP_UNTAGGED);
}
if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
UCHAR cdbOpcode = 0;
BOOLEAN isReadWrite = FALSE;
if (pCdb) {
cdbOpcode = pCdb->CDB10.OperationCode;
isReadWrite = IS_SCSIOP_READWRITE(cdbOpcode);
}
if ((Pkt->DriverUsesStartIO) &&
( (cdbOpcode == SCSIOP_WRITE6 ) ||
(cdbOpcode == SCSIOP_WRITE ) ||
(cdbOpcode == SCSIOP_WRITE12) ||
(cdbOpcode == SCSIOP_WRITE16) )) {
/* don't retry writes in super-low-memory conditions if the
* driver must serialize against StartIO. This is because
* some write methods used in such drivers cannot accept
* random-sized writes. (i.e CD-RW in packet writing mode)
* Reads, however, are always safe to split up.
*/
SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH1);
packetDone = TRUE;
}
else if (Pkt->InLowMemRetry || !isReadWrite){
/*
* This should never happen under normal circumstances.
* The memory manager guarantees that at least four pages will
* be available to allow forward progress in the port driver.
* So a one-page transfer should never fail with insufficient resources.
*
* However, it is possible to get in here with virtual storage
* or thin provisioned storage for example.
* A single sector write can trigger an allocation request and
* presently a forward progress guarantee is not provided.
* VHD also may have some limitations in forward progress guarantee.
* And USB too might also fall into this category.
*/
SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH2);
packetDone = TRUE;
}
else {
/*
* We are in low-memory stress.
* Start the low-memory retry state machine, which tries to
* resend the packet in little one-page chunks.
*/
SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH3);
InitLowMemRetry(Pkt,
Pkt->BufPtrCopy,
Pkt->BufLenCopy,
Pkt->TargetLocationCopy);
StepLowMemRetry(Pkt);
packetDone = FALSE;
}
}
else {
/*
* Retry the packet by simply resending it after a delay.
* Put the packet back in the pending queue and
* schedule a timer to retry the transfer.
*
* Do not call SetupReadWriteTransferPacket again because:
* (1) The minidriver may have set some bits
* in the SRB that it needs again and
* (2) doing so would reset numRetries.
*
* BECAUSE we do not call SetupReadWriteTransferPacket again,
* we have to reset a couple fields in the SRB that
* some miniports overwrite when they fail an SRB.
*/
SrbSetDataBuffer(Pkt->Srb, Pkt->BufPtrCopy);
SrbSetDataTransferLength(Pkt->Srb, Pkt->BufLenCopy);
TransferPacketQueueRetryDpc(Pkt);
packetDone = FALSE;
}
return packetDone;
}
VOID TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt)
{
KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
if (Pkt->RetryIn100nsUnits == 0){
KeInsertQueueDpc(&Pkt->RetryTimerDPC, NULL, NULL);
}
else {
LARGE_INTEGER timerPeriod;
NT_ASSERT(Pkt->RetryIn100nsUnits < 100 * 1000 * 1000 * 10); // sanity check -- 100 seconds is normally too long
timerPeriod.QuadPart = -(Pkt->RetryIn100nsUnits);
KeInitializeTimer(&Pkt->RetryTimer);
KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
}
}
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
TransferPacketRetryTimerDpc( IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
PTRANSFER_PACKET pkt;
PDEVICE_OBJECT fdo;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
_Analysis_assume_(DeferredContext != NULL);
pkt = (PTRANSFER_PACKET)DeferredContext;
fdo = pkt->Fdo;
fdoExtension = fdo->DeviceExtension;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
/*
* Sometimes the port driver can allocates a new 'sense' buffer
* to report transfer errors, e.g. when the default sense buffer
* is too small. If so, it is up to us to free it.
* Now that we're done using the sense info, free it if appropriate.
* Then clear the sense buffer so it doesn't pollute future errors returned in this packet.
*/
if (PORT_ALLOCATED_SENSE_EX(fdoExtension, pkt->Srb)) {
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt));
FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, pkt->Srb);
SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData);
SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData));
}
else {
NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData);
NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData));
}
RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData));
SubmitTransferPacket(pkt);
}
VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
{
NT_ASSERT(Len > 0);
NT_ASSERT(!Pkt->InLowMemRetry);
if (Pkt->DriverUsesStartIO)
{
/*
* special case: StartIO-based writing must stay serialized for performance
* and proper operations (i.e. sequential writing mode). If need more than
* one transfer to perform this operation, and it's a StartIO-based driver
* (such as CDROM), then just use a single packet and use the retry logic
* that's already built-in to the packet engine. Note that low-mem retry
* cannot be used directly because some write methods do not work if the
* writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
*/
Pkt->InLowMemRetry = FALSE;
}
else
{
Pkt->InLowMemRetry = TRUE;
}
Pkt->LowMemRetry_remainingBufPtr = BufPtr;
Pkt->LowMemRetry_remainingBufLen = Len;
Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
}
/*
* StepLowMemRetry
*
* During extreme low-memory stress, this function retries
* a packet in small one-page chunks, sent serially.
*
* Returns TRUE iff the packet is done.
*/
BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
{
BOOLEAN packetDone;
if (Pkt->LowMemRetry_remainingBufLen == 0){
packetDone = TRUE;
}
else {
ULONG thisChunkLen;
if (Pkt->DriverUsesStartIO)
{
/*
* Need the fdoData for the HwMaxXferLen
*/
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
/*
* Need the adapterDesc to limit transfers based on byte count
*/
PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
ULONG hwMaxXferLen;
/*
* special case: StartIO-based writing must stay serialized for performance
* and proper operations (i.e. sequential writing mode). If need more than
* one transfer to perform this operation, and it's a StartIO-based driver
* (such as CDROM), then just use a single packet and use the retry logic
* that's already built-in to the packet engine. Note that low-mem retry
* cannot be used directly because some write methods do not work if the
* writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
*/
NT_ASSERT(!Pkt->InLowMemRetry);
/*
* We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
* If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
* Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
* transfer aligned 64K requests in one piece.
* Also note: make sure adding PAGE_SIZE back in doesn't wrap to zero.
*/
if (((ULONG_PTR)(Pkt->LowMemRetry_remainingBufPtr) & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
hwMaxXferLen = fdoData->HwMaxXferLen;
}
else {
NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
}
thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, hwMaxXferLen);
}
else {
/*
* Make sure the little chunk we send is <= a page length
* AND that it does not cross any page boundaries.
*/
ULONG bytesToNextPageBoundary;
bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
NT_ASSERT(Pkt->InLowMemRetry);
}
/*
* Set up the transfer packet for the new little chunk.
* This will reset numRetries so that we retry each chunk as required.
*/
SetupReadWriteTransferPacket(Pkt,
Pkt->LowMemRetry_remainingBufPtr,
thisChunkLen,
Pkt->LowMemRetry_nextChunkTargetLocation,
Pkt->OriginalIrp);
Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
//
// When running in low-memory stress, always use a partial MDL.
// This allows lower drivers to potentially map a smaller buffer.
//
Pkt->UsePartialMdl = TRUE;
TransferPacketQueueRetryDpc(Pkt);
packetDone = FALSE;
}
return packetDone;
}

View file

@ -0,0 +1,376 @@
/*++
Copyright (C) Microsoft Corporation 2010
Module Name:
srblib.c
Abstract:
Header for SRB utility functions
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
PVOID
DefaultStorageRequestBlockAllocateRoutine(
_In_ CLONG ByteSize
)
/*++
Routine Description:
Default allocation routine.
Arguments:
ByteSize - SRB size in bytes.
Return Value:
Pointer to the SRB buffer. NULL if SRB buffer could not be allocated.
--*/
{
return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, '+brs');
}
NTSTATUS
pInitializeStorageRequestBlock(
_Inout_bytecount_(ByteSize) PSTORAGE_REQUEST_BLOCK Srb,
_In_ USHORT AddressType,
_In_ ULONG ByteSize,
_In_ ULONG NumSrbExData,
_In_ va_list ap
)
/*++
Routine Description:
Initialize a STORAGE_REQUEST_BLOCK.
Arguments:
Srb - Pointer to STORAGE_REQUEST_BLOCK to initialize.
AddressType - Storage address type.
ByteSize - STORAGE_REQUEST_BLOCK size in bytes.
NumSrbExData - Number of SRB extended data.
ap - Variable argument list matching the SRB extended data in the
STORAGE_REQUEST_BLOCK.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PSTOR_ADDRESS address;
PSRBEX_DATA srbExData;
ULONG offset;
ULONG length = (ULONG)-1;
SRBEXDATATYPE type;
ULONG srbExDataLength = (ULONG)-1;
ULONG varLength;
ULONG i;
if (ByteSize < sizeof(STORAGE_REQUEST_BLOCK)) {
return STATUS_BUFFER_OVERFLOW;
}
RtlZeroMemory(Srb, ByteSize);
Srb->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
Srb->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
Srb->Signature = SRB_SIGNATURE;
Srb->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
Srb->SrbLength = ByteSize;
Srb->NumSrbExData = NumSrbExData;
offset = sizeof(STORAGE_REQUEST_BLOCK);
if (NumSrbExData > 0) {
offset += ((NumSrbExData - 1) * sizeof(ULONG));
// Ensure offset is pointer type aligned
if (offset % sizeof(PVOID)) {
offset += (sizeof(PVOID) - (offset % sizeof(PVOID)));
}
}
Srb->AddressOffset = offset;
if (AddressType == STORAGE_ADDRESS_TYPE_BTL8)
{
if ((ByteSize < offset) ||
(ByteSize < (offset + sizeof(STOR_ADDR_BTL8)))) {
return STATUS_BUFFER_OVERFLOW;
}
address = (PSTOR_ADDRESS)((PUCHAR)Srb + offset);
address->Type = STOR_ADDRESS_TYPE_BTL8;
address->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
offset += sizeof(STOR_ADDR_BTL8);
} else
{
status = STATUS_INVALID_PARAMETER;
}
for (i = 0; i < NumSrbExData && status == STATUS_SUCCESS; i++)
{
if (ByteSize <= offset) {
status = STATUS_BUFFER_OVERFLOW;
break;
}
srbExData = (PSRBEX_DATA)((PUCHAR)Srb + offset);
Srb->SrbExDataOffset[i] = offset;
type = va_arg(ap, SRBEXDATATYPE);
switch (type)
{
case SrbExDataTypeBidirectional:
length = sizeof(SRBEX_DATA_BIDIRECTIONAL);
srbExDataLength = SRBEX_DATA_BIDIRECTIONAL_LENGTH;
break;
case SrbExDataTypeScsiCdb16:
length = sizeof(SRBEX_DATA_SCSI_CDB16);
srbExDataLength = SRBEX_DATA_SCSI_CDB16_LENGTH;
break;
case SrbExDataTypeScsiCdb32:
length = sizeof(SRBEX_DATA_SCSI_CDB32);
srbExDataLength = SRBEX_DATA_SCSI_CDB32_LENGTH;
break;
case SrbExDataTypeScsiCdbVar:
varLength = va_arg(ap, ULONG);
length = sizeof(SRBEX_DATA_SCSI_CDB_VAR) + varLength;
srbExDataLength = SRBEX_DATA_SCSI_CDB_VAR_LENGTH_MIN + varLength;
break;
case SrbExDataTypeWmi:
length = sizeof(SRBEX_DATA_WMI);
srbExDataLength = SRBEX_DATA_WMI_LENGTH;
break;
case SrbExDataTypePower:
length = sizeof(SRBEX_DATA_POWER);
srbExDataLength = SRBEX_DATA_POWER_LENGTH;
break;
case SrbExDataTypePnP:
length = sizeof(SRBEX_DATA_PNP);
srbExDataLength = SRBEX_DATA_PNP_LENGTH;
break;
case SrbExDataTypeIoInfo:
length = sizeof(SRBEX_DATA_IO_INFO);
srbExDataLength = SRBEX_DATA_IO_INFO_LENGTH;
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
if (status == STATUS_SUCCESS)
{
NT_ASSERT(length != (ULONG)-1);
if (ByteSize < (offset + length)) {
status = STATUS_BUFFER_OVERFLOW;
break;
}
NT_ASSERT(srbExDataLength != (ULONG)-1);
srbExData->Type = type;
srbExData->Length = srbExDataLength;
offset += length;
}
}
return status;
}
NTSTATUS
InitializeStorageRequestBlock(
_Inout_bytecount_(ByteSize) PSTORAGE_REQUEST_BLOCK Srb,
_In_ USHORT AddressType,
_In_ ULONG ByteSize,
_In_ ULONG NumSrbExData,
...
)
/*++
Routine Description:
Initialize an extended SRB.
Arguments:
Srb - Pointer to SRB buffer to initialize.
AddressType - Storage address type.
ByteSize - STORAGE_REQUEST_BLOCK size in bytes.
NumSrbExData - Number of SRB extended data.
... - Variable argument list matching the SRB extended data in the
STORAGE_REQUEST_BLOCK.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status;
va_list ap;
va_start(ap, NumSrbExData);
status = pInitializeStorageRequestBlock(Srb, AddressType, ByteSize, NumSrbExData, ap);
va_end(ap);
return status;
}
NTSTATUS
CreateStorageRequestBlock(
_Inout_ PSTORAGE_REQUEST_BLOCK *Srb,
_In_ USHORT AddressType,
_In_opt_ PSRB_ALLOCATE_ROUTINE AllocateRoutine,
_Inout_opt_ ULONG *ByteSize,
_In_ ULONG NumSrbExData,
...
)
/*++
Routine Description:
Create an extended SRB.
Arguments:
Srb - Pointer to buffer to store SRB pointer.
AddressType - Storage address type.
AllocateRoutine - Buffer allocation function (optional).
ByteSize - Pointer to ULONG to store size of SRB in bytes (optional).
NumSrbExData - Number of SRB extended data.
... - Variable argument list matching the SRB extended data in the
STORAGE_REQUEST_BLOCK.
Return Value:
NTSTATUS
--*/
{
ULONG sizeNeeded = 0;
va_list ap;
ULONG i;
NTSTATUS status = STATUS_SUCCESS;
// Ensure SrbExData offsets are pointer type aligned
sizeNeeded = sizeof(STORAGE_REQUEST_BLOCK);
if (NumSrbExData > 0) {
sizeNeeded += ((NumSrbExData - 1) * sizeof(ULONG));
if (sizeNeeded % sizeof(PVOID)) {
sizeNeeded += (sizeof(PVOID) - (sizeNeeded % sizeof(PVOID)));
}
}
if (AddressType == STORAGE_ADDRESS_TYPE_BTL8)
{
sizeNeeded += sizeof(STOR_ADDR_BTL8);
} else
{
status = STATUS_INVALID_PARAMETER;
}
va_start(ap, NumSrbExData);
for (i = 0; i < NumSrbExData && status == STATUS_SUCCESS; i++)
{
switch (va_arg(ap, SRBEXDATATYPE))
{
case SrbExDataTypeBidirectional:
sizeNeeded += sizeof(SRBEX_DATA_BIDIRECTIONAL);
break;
case SrbExDataTypeScsiCdb16:
sizeNeeded += sizeof(SRBEX_DATA_SCSI_CDB16);
break;
case SrbExDataTypeScsiCdb32:
sizeNeeded += sizeof(SRBEX_DATA_SCSI_CDB32);
break;
case SrbExDataTypeScsiCdbVar:
sizeNeeded += sizeof(SRBEX_DATA_SCSI_CDB_VAR) + va_arg(ap, ULONG);
break;
case SrbExDataTypeWmi:
sizeNeeded += sizeof(SRBEX_DATA_WMI);
break;
case SrbExDataTypePower:
sizeNeeded += sizeof(SRBEX_DATA_POWER);
break;
case SrbExDataTypePnP:
sizeNeeded += sizeof(SRBEX_DATA_PNP);
break;
case SrbExDataTypeIoInfo:
sizeNeeded += sizeof(SRBEX_DATA_IO_INFO);
break;
default:
status = STATUS_INVALID_PARAMETER;
break;
}
}
va_end(ap);
if (status == STATUS_SUCCESS)
{
if (AllocateRoutine)
{
*Srb = AllocateRoutine(sizeNeeded);
if (*Srb == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (ByteSize != NULL)
{
*ByteSize = sizeNeeded;
}
if (*Srb)
{
va_start(ap, NumSrbExData);
#ifdef _MSC_VER
#pragma prefast(suppress:26015, "pInitializeStorageRequestBlock will set the SrbLength field")
#endif
status = pInitializeStorageRequestBlock(*Srb, AddressType, sizeNeeded, NumSrbExData, ap);
va_end(ap);
}
}
return status;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff