1622 lines
46 KiB
C
1622 lines
46 KiB
C
|
#include "mpio.h"
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MPIOTimer(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION)Context;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PFLTR_ENTRY fltrEntry;
|
|||
|
ULONG tickDiff;
|
|||
|
ULONG numberDevices;
|
|||
|
PMPIO_REQUEST_INFO requestInfo;
|
|||
|
PREAL_DEV_INFO deviceInfo;
|
|||
|
PDEVICE_OBJECT currentAdapter;
|
|||
|
ULONG i;
|
|||
|
ULONG j;
|
|||
|
PDEVICE_OBJECT controlObject = diskExtension->ControlObject;
|
|||
|
PDEVICE_EXTENSION devExt = controlObject->DeviceExtension;
|
|||
|
PCONTROL_EXTENSION controlExtension = devExt->TypeExtension;
|
|||
|
PLIST_ENTRY listEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Inc current tickcount.
|
|||
|
//
|
|||
|
diskExtension->TickCount++;
|
|||
|
|
|||
|
if ((deviceExtension->State != MPIO_STATE_IN_FO) &&
|
|||
|
(deviceExtension->State != MPIO_STATE_WAIT1)) {
|
|||
|
|
|||
|
//
|
|||
|
// Check for any pending work items to start.
|
|||
|
//
|
|||
|
if (diskExtension->PendingItems) {
|
|||
|
PLIST_ENTRY listEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Yank the packet from the pending list...
|
|||
|
//
|
|||
|
listEntry = ExInterlockedRemoveHeadList(&diskExtension->PendingWorkList,
|
|||
|
&diskExtension->WorkListLock);
|
|||
|
|
|||
|
if (listEntry) {
|
|||
|
|
|||
|
//
|
|||
|
// ...and jam it on the work list.
|
|||
|
//
|
|||
|
ExInterlockedInsertTailList(&diskExtension->WorkList,
|
|||
|
listEntry,
|
|||
|
&diskExtension->WorkListLock);
|
|||
|
|
|||
|
InterlockedDecrement(&diskExtension->PendingItems);
|
|||
|
|
|||
|
//
|
|||
|
// Signal the thread to initiate the F.O.
|
|||
|
//
|
|||
|
KeSetEvent(&diskExtension->ThreadEvent,
|
|||
|
8,
|
|||
|
FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIOTimer: PendingItems set, but entry == NULL\n"));
|
|||
|
ASSERT(FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
MPIOFindMatchingDevice(
|
|||
|
IN PDEVICE_OBJECT MPDiskObject,
|
|||
|
IN PADP_DEVICE_INFO DeviceInfo,
|
|||
|
IN PDSM_ENTRY DsmEntry,
|
|||
|
IN PVOID DsmId
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = MPDiskObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// Hand off each real pdo and the new one to the DSM to see
|
|||
|
// if they are really the same device (accessed via different paths)
|
|||
|
//
|
|||
|
for (i = 0; i < diskExtension->TargetInfoCount; i++) {
|
|||
|
|
|||
|
if (DsmEntry->CompareDevices(DsmEntry->DsmContext,
|
|||
|
diskExtension->TargetInfo[i].DsmID,
|
|||
|
DsmId)) {
|
|||
|
//
|
|||
|
// Have a match.
|
|||
|
//
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOCreateDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PDEVICE_OBJECT FilterObject,
|
|||
|
IN PDEVICE_OBJECT PortObject,
|
|||
|
IN PADP_DEVICE_INFO DeviceInfo,
|
|||
|
IN PDSM_ENTRY DsmEntry,
|
|||
|
IN PVOID DsmId,
|
|||
|
IN OUT PDEVICE_OBJECT *NewDeviceObject
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension;
|
|||
|
PDEVICE_OBJECT diskObject;
|
|||
|
PDEVICE_EXTENSION newExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension;
|
|||
|
UNICODE_STRING unicodeDeviceName;
|
|||
|
PUNICODE_STRING regString;
|
|||
|
PREAL_DEV_INFO targetInfo;
|
|||
|
PMPIO_THREAD_CONTEXT threadContext;
|
|||
|
WCHAR deviceName[30];
|
|||
|
NTSTATUS status;
|
|||
|
ULONG i;
|
|||
|
ULONG numberDevices = controlExtension->NumberDevices;
|
|||
|
|
|||
|
//
|
|||
|
// Build the new name.
|
|||
|
//
|
|||
|
swprintf(deviceName,
|
|||
|
L"\\Device\\MPathDisk%0d",
|
|||
|
numberDevices);
|
|||
|
|
|||
|
RtlInitUnicodeString(&unicodeDeviceName, deviceName);
|
|||
|
|
|||
|
//
|
|||
|
// Create the device object.
|
|||
|
//
|
|||
|
status = IoCreateDevice(deviceExtension->DriverObject,
|
|||
|
sizeof(DEVICE_EXTENSION),
|
|||
|
&unicodeDeviceName,
|
|||
|
FILE_DEVICE_MASS_STORAGE,
|
|||
|
FILE_DEVICE_SECURE_OPEN,
|
|||
|
FALSE,
|
|||
|
&diskObject);
|
|||
|
|
|||
|
if (status == STATUS_SUCCESS) {
|
|||
|
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIOCreateDevice: New PDO %x\n",
|
|||
|
diskObject));
|
|||
|
|
|||
|
//
|
|||
|
// TODO, break this up into some helper functions that
|
|||
|
// can be used by both this routine and UpdateDevice
|
|||
|
//
|
|||
|
//
|
|||
|
// Setup the various devObj stuff to be like the real PDO.
|
|||
|
//
|
|||
|
diskObject->StackSize = DeviceInfo->DeviceObject->StackSize + 1;
|
|||
|
diskObject->Flags = DeviceInfo->DeviceObject->Flags;
|
|||
|
diskObject->Flags |= DO_DEVICE_INITIALIZING;
|
|||
|
diskObject->AlignmentRequirement = DeviceInfo->DeviceObject->AlignmentRequirement;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the type extension.
|
|||
|
//
|
|||
|
diskExtension = ExAllocatePool(NonPagedPool, sizeof(MPDISK_EXTENSION));
|
|||
|
RtlZeroMemory(diskExtension, sizeof(MPDISK_EXTENSION));
|
|||
|
|
|||
|
//
|
|||
|
// Allocate storage for the device descriptor.
|
|||
|
//
|
|||
|
diskExtension->DeviceDescriptor = ExAllocatePool(NonPagedPool,
|
|||
|
DeviceInfo->DeviceDescriptor->Size);
|
|||
|
//
|
|||
|
// Set-up the device extension.
|
|||
|
//
|
|||
|
newExtension = diskObject->DeviceExtension;
|
|||
|
newExtension->TypeExtension = diskExtension;
|
|||
|
|
|||
|
newExtension->DeviceObject = diskObject;
|
|||
|
newExtension->Pdo = diskObject;
|
|||
|
newExtension->LowerDevice = DeviceObject;
|
|||
|
newExtension->DriverObject = deviceExtension->DriverObject;
|
|||
|
newExtension->Type = MPIO_MPDISK;
|
|||
|
|
|||
|
//
|
|||
|
// Since there is only one path, set the original state to DEGRADED, as we
|
|||
|
// can't fail-over yet.
|
|||
|
//
|
|||
|
newExtension->State = MPIO_STATE_NORMAL;
|
|||
|
newExtension->LastState = MPIO_STATE_NORMAL;
|
|||
|
newExtension->CompletionState = MPIO_STATE_NORMAL;
|
|||
|
diskExtension->CheckState = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the Lookaside List of context structs.
|
|||
|
//
|
|||
|
ExInitializeNPagedLookasideList(&newExtension->ContextList,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
sizeof(MPIO_CONTEXT),
|
|||
|
'oCPM',
|
|||
|
0);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up emergency buffers
|
|||
|
//
|
|||
|
KeInitializeSpinLock(&newExtension->EmergencySpinLock);
|
|||
|
|
|||
|
for (i = 0; i < MAX_EMERGENCY_CONTEXT; i++) {
|
|||
|
newExtension->EmergencyContext[i] = ExAllocatePool(NonPagedPool,
|
|||
|
sizeof(MPIO_CONTEXT));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the reg. path from the control object.
|
|||
|
//
|
|||
|
regString = &deviceExtension->RegistryPath;
|
|||
|
newExtension->RegistryPath.Buffer = ExAllocatePool(NonPagedPool,
|
|||
|
regString->MaximumLength);
|
|||
|
newExtension->RegistryPath.MaximumLength = regString->MaximumLength;
|
|||
|
RtlCopyUnicodeString(&newExtension->RegistryPath,
|
|||
|
regString);
|
|||
|
//
|
|||
|
// Save off the important DSM Info.
|
|||
|
//
|
|||
|
RtlCopyMemory(&diskExtension->DsmInfo,
|
|||
|
DsmEntry,
|
|||
|
sizeof(DSM_ENTRY));
|
|||
|
|
|||
|
//
|
|||
|
// Prepare this D.O. to handle WMI requests.
|
|||
|
//
|
|||
|
MPIOSetupWmi(diskObject);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the new DO's type extension.
|
|||
|
//
|
|||
|
diskExtension->ControlObject = DeviceObject;
|
|||
|
diskExtension->DeviceOrdinal = numberDevices;
|
|||
|
|
|||
|
//
|
|||
|
// Copy the device descriptor. Used for PnP ID stuff.
|
|||
|
//
|
|||
|
RtlCopyMemory(diskExtension->DeviceDescriptor,
|
|||
|
DeviceInfo->DeviceDescriptor,
|
|||
|
DeviceInfo->DeviceDescriptor->Size);
|
|||
|
//
|
|||
|
// Prepare all of the queues.
|
|||
|
//
|
|||
|
MPIOInitQueue(&diskExtension->ResubmitQueue, 1);
|
|||
|
MPIOInitQueue(&diskExtension->FailOverQueue, 2);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the thread stuff.
|
|||
|
//
|
|||
|
KeInitializeSpinLock(&diskExtension->SpinLock);
|
|||
|
KeInitializeSpinLock(&diskExtension->WorkListLock);
|
|||
|
InitializeListHead(&diskExtension->WorkList);
|
|||
|
InitializeListHead(&diskExtension->PendingWorkList);
|
|||
|
KeInitializeEvent(&diskExtension->ThreadEvent,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
|
|||
|
threadContext = ExAllocatePool(NonPagedPool, sizeof(MPIO_THREAD_CONTEXT));
|
|||
|
|
|||
|
threadContext->DeviceObject = diskObject;
|
|||
|
threadContext->Event = &diskExtension->ThreadEvent;
|
|||
|
|
|||
|
status = PsCreateSystemThread(&diskExtension->Handle,
|
|||
|
(ACCESS_MASK)0,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
MPIORecoveryThread,
|
|||
|
threadContext);
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
diskExtension->Handle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
status = IoInitializeTimer(diskObject,
|
|||
|
MPIOTimer,
|
|||
|
newExtension);
|
|||
|
|
|||
|
//
|
|||
|
// Add in the info for the first target device.
|
|||
|
// Note that the DevFilter and PathId fields are
|
|||
|
// set to the Port PDO and Adapter Filter.
|
|||
|
// When the DevFilter registers, these get copied over
|
|||
|
// to the correct values and DSMInit flag is set.
|
|||
|
//
|
|||
|
targetInfo = diskExtension->TargetInfo;
|
|||
|
targetInfo->AdapterFilter = FilterObject;
|
|||
|
targetInfo->PathId = FilterObject;
|
|||
|
targetInfo->PortPdo = DeviceInfo->DeviceObject;
|
|||
|
targetInfo->PortFdo = PortObject;
|
|||
|
targetInfo->DsmID = DsmId;
|
|||
|
targetInfo->DevFilter = DeviceInfo->DeviceObject;
|
|||
|
targetInfo->DSMInit = FALSE;
|
|||
|
|
|||
|
diskExtension->DsmIdList.IdList[0] = DsmId;
|
|||
|
diskExtension->DsmIdList.Count = 1;
|
|||
|
diskExtension->TargetInfoCount = 1;
|
|||
|
diskExtension->MaxPaths = 1;
|
|||
|
diskExtension->HasName = FALSE;
|
|||
|
diskExtension->PdoName.Buffer = ExAllocatePool(NonPagedPool, PDO_NAME_LENGTH);
|
|||
|
RtlZeroMemory(diskExtension->PdoName.Buffer, PDO_NAME_LENGTH);
|
|||
|
diskExtension->PdoName.MaximumLength = PDO_NAME_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that this slot is taken.
|
|||
|
//
|
|||
|
diskExtension->DeviceMap |= 1;
|
|||
|
|
|||
|
*NewDeviceObject = diskObject;
|
|||
|
} else {
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIOCreateDevice: Error creating new PDO %x\n",
|
|||
|
status));
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOUpdateDevice(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PDEVICE_OBJECT AdapterFilter,
|
|||
|
IN PDEVICE_OBJECT PortObject,
|
|||
|
IN PADP_DEVICE_INFO DeviceInfo,
|
|||
|
IN PVOID DsmId
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine updates the PDO extension to include
|
|||
|
a newly arrived device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PREAL_DEV_INFO devInfo;
|
|||
|
ULONG allocatedMap = diskExtension->DeviceMap;
|
|||
|
ULONG i;
|
|||
|
ULONG j;
|
|||
|
ULONG currentState;
|
|||
|
ULONG newState;
|
|||
|
WCHAR componentName[64];
|
|||
|
|
|||
|
//
|
|||
|
// Find a free spot in the array.
|
|||
|
//
|
|||
|
for (i = 0; i < MAX_NUMBER_PATHS; i++) {
|
|||
|
if (!(allocatedMap & (1 << i))) {
|
|||
|
diskExtension->DeviceMap |= (1 << i);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that one more path is available.
|
|||
|
//
|
|||
|
diskExtension->DsmIdList.IdList[diskExtension->DsmIdList.Count] = DsmId;
|
|||
|
diskExtension->DsmIdList.Count++;
|
|||
|
diskExtension->TargetInfoCount++;
|
|||
|
|
|||
|
ASSERT(diskExtension->DsmIdList.Count <= MAX_NUMBER_PATHS);
|
|||
|
|
|||
|
//
|
|||
|
// See if we are transitioning from degraded to normal.
|
|||
|
//
|
|||
|
currentState = deviceExtension->State;
|
|||
|
|
|||
|
if ((diskExtension->TargetInfoCount == diskExtension->MaxPaths) &&
|
|||
|
(currentState == MPIO_STATE_DEGRADED)) {
|
|||
|
|
|||
|
//
|
|||
|
// This indicates that a path that went missing, has returned.
|
|||
|
//
|
|||
|
diskExtension->PathBackOnLine = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Update MaxPaths, if necessary. It's the count of the most
|
|||
|
// paths we have seen.
|
|||
|
//
|
|||
|
if (diskExtension->TargetInfoCount > diskExtension->MaxPaths) {
|
|||
|
diskExtension->MaxPaths++;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Determine the state info.
|
|||
|
//
|
|||
|
newState = MPIOHandleStateTransition(DeviceObject);
|
|||
|
|
|||
|
if ((currentState == MPIO_STATE_DEGRADED) && (newState == MPIO_STATE_NORMAL)) {
|
|||
|
|
|||
|
//
|
|||
|
// Did make that tranistion. Fire an event to notify any listeners.
|
|||
|
//
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIOUpdateDevice: Moving to NORMAL (%x)\n",
|
|||
|
DeviceObject));
|
|||
|
|
|||
|
swprintf(componentName, L"MPIO Disk(%02d)", diskExtension->DeviceOrdinal);
|
|||
|
MPIOFireEvent(DeviceObject,
|
|||
|
componentName,
|
|||
|
L"Moved to STATE_NORMAL",
|
|||
|
MPIO_INFORMATION);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the structure pertaining to the device being updated.
|
|||
|
//
|
|||
|
devInfo = &diskExtension->TargetInfo[i];
|
|||
|
RtlZeroMemory(devInfo, sizeof(REAL_DEV_INFO));
|
|||
|
|
|||
|
//
|
|||
|
// Add the FilterObject as being the PathID for the new device.
|
|||
|
//
|
|||
|
devInfo->AdapterFilter = AdapterFilter;
|
|||
|
devInfo->PathId = AdapterFilter;
|
|||
|
|
|||
|
//
|
|||
|
// Point the DevFilter entry to the Port PDO for now.
|
|||
|
// It is set when the device filter registers.
|
|||
|
//
|
|||
|
devInfo->DevFilter = DeviceInfo->DeviceObject;
|
|||
|
|
|||
|
//
|
|||
|
// Add the Scsiport PDO.
|
|||
|
//
|
|||
|
devInfo->PortPdo = DeviceInfo->DeviceObject;
|
|||
|
devInfo->PortFdo = PortObject;
|
|||
|
|
|||
|
for (j = 0; j < diskExtension->TargetInfoCount; j++) {
|
|||
|
if (diskExtension->TargetInfo[j].DsmID == DsmId) {
|
|||
|
MPDebugPrint((0,
|
|||
|
"UpdateDevice: Matching DSM IDs\n"));
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Add the DSM Id that corresponds to the Scsiport PDO.
|
|||
|
//
|
|||
|
devInfo->DsmID = DsmId;
|
|||
|
devInfo->DSMInit = FALSE;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoPowerNotification(
|
|||
|
IN PDEVICE_OBJECT MPDiskObject,
|
|||
|
IN PDEVICE_OBJECT FilterObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoPnPNotification(
|
|||
|
IN PDEVICE_OBJECT MPDiskObject,
|
|||
|
IN PDEVICE_OBJECT FilterObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIODsmFinalInit(
|
|||
|
IN PMPDISK_EXTENSION DiskExtension,
|
|||
|
IN PDEVICE_OBJECT DevFilter,
|
|||
|
IN PREAL_DEV_INFO TargetInfo
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles giving the dsm the remaining info. that it
|
|||
|
needs (PathId, it's own DSMID, and target object matchup). Also,
|
|||
|
determines whether the path is active.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DiskExtension - The type extension for the pseudo-disk
|
|||
|
DevFilter - MPDev's object - the target object for I/O
|
|||
|
TargetInfo - Collection of DSM related info.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDSM_ENTRY dsm = &DiskExtension->DsmInfo;
|
|||
|
ULONGLONG controllerId;
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN active;
|
|||
|
|
|||
|
//
|
|||
|
// Need to give info about this to the DSM
|
|||
|
//
|
|||
|
TargetInfo->PathId = TargetInfo->AdapterFilter;
|
|||
|
status = dsm->SetDeviceInfo(dsm->DsmContext,
|
|||
|
DevFilter,
|
|||
|
TargetInfo->DsmID,
|
|||
|
&TargetInfo->PathId);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// LOG
|
|||
|
// NOTE: This is really a fatal error, as there is no mapping
|
|||
|
// in the DSM for Path, DsmID, and the target D.O.
|
|||
|
// Figure out how to go into limp-home mode.
|
|||
|
//
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If the DSM updated "pathID" need to encapsulate this and
|
|||
|
// the AdapterFilter. Be sure and use
|
|||
|
//
|
|||
|
if (TargetInfo->PathId != TargetInfo->AdapterFilter) {
|
|||
|
MPDebugPrint((2,
|
|||
|
"MPIODsmFinalInit: DSM updated PathID (%x) -> (%x)\n",
|
|||
|
TargetInfo->AdapterFilter,
|
|||
|
TargetInfo->PathId));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Have everything needed now, so create a path entry.
|
|||
|
//
|
|||
|
status = MPIOCreatePathEntry(DiskExtension->ControlObject,
|
|||
|
TargetInfo->AdapterFilter,
|
|||
|
TargetInfo->PortFdo,
|
|||
|
TargetInfo->PathId);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// See if there is already an ID for this path.
|
|||
|
// This routine will create it, if not.
|
|||
|
//
|
|||
|
TargetInfo->Identifier = MPIOCreateUID(DiskExtension->ControlObject,
|
|||
|
TargetInfo->PathId);
|
|||
|
if (TargetInfo->Identifier != 0) {
|
|||
|
TargetInfo->PathUIDValue = TRUE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIODsmFinalInit: Couldn't create the path entry (%x)\n",
|
|||
|
status));
|
|||
|
//
|
|||
|
// LOG
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Verify the path before going on.
|
|||
|
//
|
|||
|
status = dsm->PathVerify(dsm->DsmContext,
|
|||
|
TargetInfo->DsmID,
|
|||
|
TargetInfo->PathId);
|
|||
|
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIODsmFinalInit: PathVerify failed. Dsm (%x). TargetInfo (%x)\n",
|
|||
|
dsm,
|
|||
|
TargetInfo));
|
|||
|
DbgBreakPoint();
|
|||
|
|
|||
|
//
|
|||
|
// Can't use this. Tear down this entry.
|
|||
|
//
|
|||
|
//TODO
|
|||
|
//
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
//
|
|||
|
// Determine if this path is active.
|
|||
|
//
|
|||
|
active = dsm->IsPathActive(dsm->DsmContext,
|
|||
|
TargetInfo->PathId);
|
|||
|
if (active) {
|
|||
|
|
|||
|
//
|
|||
|
// Indicate this.
|
|||
|
//
|
|||
|
TargetInfo->PathActive = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the controller info for the DSM ID on this device.
|
|||
|
//
|
|||
|
controllerId = MPIOBuildControllerInfo(DiskExtension->ControlObject,
|
|||
|
dsm,
|
|||
|
TargetInfo->DsmID);
|
|||
|
|
|||
|
TargetInfo->ControllerId = controllerId;
|
|||
|
|
|||
|
MPDebugPrint((1,
|
|||
|
"DsmFinalInit: TargetInfo (%x) now fully init'ed\n",
|
|||
|
TargetInfo));
|
|||
|
|
|||
|
TargetInfo->DSMInit = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoRegistration(
|
|||
|
IN PDEVICE_OBJECT MPDiskObject,
|
|||
|
IN PDEVICE_OBJECT FilterObject,
|
|||
|
IN PDEVICE_OBJECT LowerDevice,
|
|||
|
IN OUT PMPIO_PDO_INFO PdoInformation
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine handles setting up communication and passing of info.
|
|||
|
between the device filter and the mpdisk.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MPDiskObject - Pseudo-disk that contains the real device.
|
|||
|
FilterObject - MPDev's object
|
|||
|
LowerDevice - The real pdo.
|
|||
|
PdoInformation - Entry points for the filter to call.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = MPDiskObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
ULONG i;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// Ensure that the passed in MPDiskObject and LowerDevice
|
|||
|
// are correct.
|
|||
|
// The DsmIdList is the list of scisport pdo's. This is just
|
|||
|
// to ensure that the device id'ed by LowerDevice is in the list
|
|||
|
// and to get it's index for use below.
|
|||
|
//
|
|||
|
for (i = 0; i < diskExtension->TargetInfoCount; i++) {
|
|||
|
if (diskExtension->TargetInfo[i].PortPdo == LowerDevice) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (i == diskExtension->TargetInfoCount) {
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Capture the dev filter's object. This is the target for
|
|||
|
// I/O's.
|
|||
|
//
|
|||
|
diskExtension->TargetInfo[i].DevFilter = FilterObject;
|
|||
|
|
|||
|
//
|
|||
|
// Now indicate that we are ready.
|
|||
|
//
|
|||
|
MPDiskObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|||
|
|
|||
|
//
|
|||
|
// Finish setting up the DSM.
|
|||
|
//
|
|||
|
status = MPIODsmFinalInit(diskExtension,
|
|||
|
FilterObject,
|
|||
|
&diskExtension->TargetInfo[i]);
|
|||
|
|
|||
|
ASSERT(status == STATUS_SUCCESS);
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the routines that the dev filter can use for
|
|||
|
// power and pnp.
|
|||
|
//
|
|||
|
PdoInformation->DevicePowerNotify = MPIOPdoPowerNotification;
|
|||
|
PdoInformation->DevicePnPNotify = MPIOPdoPnPNotification;
|
|||
|
PdoInformation->PdoObject = MPDiskObject;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoDeviceControl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
|||
|
PDEVICE_OBJECT targetObject;
|
|||
|
|
|||
|
//
|
|||
|
// These should just be IOCTL_STORAGE_XXX and IOCTL_SCSI_XXX
|
|||
|
//
|
|||
|
if ((controlCode == IOCTL_STORAGE_QUERY_PROPERTY) ||
|
|||
|
(controlCode == IOCTL_SCSI_GET_ADDRESS) ||
|
|||
|
(controlCode == IOCTL_SCSI_GET_ADDRESS) ||
|
|||
|
(controlCode == IOCTL_SCSI_PASS_THROUGH) ||
|
|||
|
(controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
|
|||
|
(controlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) ||
|
|||
|
(controlCode == IOCTL_DISK_GET_MEDIA_TYPES)) {
|
|||
|
|
|||
|
} else {
|
|||
|
MPDebugPrint((0,
|
|||
|
"PdoDeviceControl: Unhandled IOCTL Code (%x)\n",
|
|||
|
controlCode));
|
|||
|
}
|
|||
|
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the completion routine.
|
|||
|
// TODO: Yank this and setting up of context into single routine.
|
|||
|
//
|
|||
|
IoSetCompletionRoutine(Irp,
|
|||
|
MPPdoGlobalCompletion,
|
|||
|
context,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Categorization of these should be unneccesary.
|
|||
|
// NOTE: Verify this assertion, and also ensure that the only requests
|
|||
|
// coming here are the ones noted above.
|
|||
|
//
|
|||
|
// Send these to the first device in the multi-path group.
|
|||
|
//
|
|||
|
targetObject = diskExtension->TargetInfo[0].DevFilter;
|
|||
|
|
|||
|
//
|
|||
|
// Save the real pdo in the context.
|
|||
|
//
|
|||
|
context->TargetInfo = (diskExtension->TargetInfo);
|
|||
|
|
|||
|
MPDebugPrint((3,
|
|||
|
"MPIOPdoDeviceControl: Context (%x) TargetInfo (%x)\n",
|
|||
|
context,
|
|||
|
context->TargetInfo));
|
|||
|
|
|||
|
//
|
|||
|
// Set-up our context info.
|
|||
|
// NOTE: Validate whether the DSM will need the opportunity to set it's
|
|||
|
// completion.
|
|||
|
//
|
|||
|
context->DsmCompletion.DsmCompletionRoutine = NULL;
|
|||
|
context->DsmCompletion.DsmContext = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that a request is outstanding to this device.
|
|||
|
//
|
|||
|
InterlockedIncrement(&diskExtension->TargetInfo[0].Requests);
|
|||
|
|
|||
|
//
|
|||
|
// Send the request to the device filter.
|
|||
|
//
|
|||
|
IoCallDriver(targetObject, Irp);
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOReadWrite(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
PREAL_DEV_INFO targetInfo = NULL;
|
|||
|
PDSM_ENTRY dsm;
|
|||
|
NTSTATUS status;
|
|||
|
PVOID pathId;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the completion routine.
|
|||
|
//
|
|||
|
IoSetCompletionRoutine(Irp,
|
|||
|
MPPdoGlobalCompletion,
|
|||
|
context,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
|
|||
|
dsm = &diskExtension->DsmInfo;
|
|||
|
targetInfo = diskExtension->TargetInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Ensure that the DSM has been fully init'ed.
|
|||
|
//
|
|||
|
if (targetInfo->DSMInit) {
|
|||
|
|
|||
|
//
|
|||
|
// Call into the DSM to find the path to which the request should be sent.
|
|||
|
//
|
|||
|
pathId = dsm->GetPath(dsm->DsmContext,
|
|||
|
srb,
|
|||
|
&diskExtension->DsmIdList,
|
|||
|
diskExtension->CurrentPath,
|
|||
|
&status);
|
|||
|
|
|||
|
if (pathId == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// LOG
|
|||
|
// This is fatal. Figure out what to do.
|
|||
|
// Attempt a fail-over, though this probably won't fix anything (all the paths
|
|||
|
// are dead according to the DSM. TODO
|
|||
|
//
|
|||
|
// BUGBUG need to CompleteRequest with an error to cause a fail-over.
|
|||
|
// Will first have to setup Context.
|
|||
|
// and remove the following
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, 8);
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Determine which target info the path is in.
|
|||
|
//
|
|||
|
targetInfo = MPIOGetTargetInfo(diskExtension,
|
|||
|
pathId,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
pathId = targetInfo->AdapterFilter;
|
|||
|
|
|||
|
//
|
|||
|
// Determine which target info the path is in.
|
|||
|
//
|
|||
|
targetInfo = MPIOGetTargetInfo(diskExtension,
|
|||
|
NULL,
|
|||
|
pathId);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this path isn't marked active, this indicates a fail-over.
|
|||
|
//
|
|||
|
if (targetInfo->PathActive == FALSE) {
|
|||
|
|
|||
|
//
|
|||
|
// LOG - Run the F.O. Handler.
|
|||
|
//
|
|||
|
}
|
|||
|
if (targetInfo->PathFailed) {
|
|||
|
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIOReadWrite: Got back a failed path. (%x)\n",
|
|||
|
targetInfo));
|
|||
|
|
|||
|
ASSERT(targetInfo->PathFailed == FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate the new path that is active.
|
|||
|
//
|
|||
|
diskExtension->CurrentPath = targetInfo->PathId;
|
|||
|
|
|||
|
//
|
|||
|
// Save the real pdo in the context.
|
|||
|
//
|
|||
|
context->TargetInfo = targetInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Get a copy of the original srb.
|
|||
|
//
|
|||
|
RtlCopyMemory(&context->Srb,
|
|||
|
srb,
|
|||
|
sizeof(SCSI_REQUEST_BLOCK));
|
|||
|
|
|||
|
if (targetInfo->DSMInit) {
|
|||
|
//
|
|||
|
// Now have determined the correct path, allow the DSM
|
|||
|
// to set-up it's context and completion.
|
|||
|
//
|
|||
|
dsm->SetCompletion(dsm->DsmContext,
|
|||
|
targetInfo->DsmID,
|
|||
|
Irp,
|
|||
|
srb,
|
|||
|
&context->DsmCompletion);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that a request is outstanding to this device.
|
|||
|
//
|
|||
|
InterlockedIncrement(&targetInfo->Requests);
|
|||
|
|
|||
|
//
|
|||
|
// Call the device Filter with the request.
|
|||
|
//
|
|||
|
IoCallDriver(targetInfo->DevFilter, Irp);
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoHandleRequest(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
|
|||
|
PREAL_DEV_INFO targetInfo;
|
|||
|
PDSM_ENTRY dsm;
|
|||
|
NTSTATUS status;
|
|||
|
KEVENT event;
|
|||
|
UCHAR opCode;
|
|||
|
PVOID pathId;
|
|||
|
ULONG action;
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
//
|
|||
|
// Categorize the I/O
|
|||
|
//
|
|||
|
opCode = srb->Cdb[0];
|
|||
|
|
|||
|
if ((opCode == SCSIOP_READ) ||
|
|||
|
(opCode == SCSIOP_WRITE) ||
|
|||
|
(opCode == SCSIOP_VERIFY)) {
|
|||
|
|
|||
|
//
|
|||
|
// Call the Read/Write Handler.
|
|||
|
//
|
|||
|
return MPIOReadWrite(DeviceObject,
|
|||
|
Irp);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The request is something other than a read/write request.
|
|||
|
// Get the DSM information.
|
|||
|
//
|
|||
|
dsm = &diskExtension->DsmInfo;
|
|||
|
targetInfo = diskExtension->TargetInfo;
|
|||
|
|
|||
|
if (targetInfo->DSMInit == FALSE) {
|
|||
|
|
|||
|
//
|
|||
|
// Not fully initialized yet.
|
|||
|
//
|
|||
|
context->DsmCompletion.DsmCompletionRoutine = NULL;
|
|||
|
context->DsmCompletion.DsmContext = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate the current path.
|
|||
|
//
|
|||
|
diskExtension->CurrentPath = targetInfo->PathId;
|
|||
|
|
|||
|
//
|
|||
|
// Save the real pdo in the context.
|
|||
|
//
|
|||
|
context->TargetInfo = targetInfo;
|
|||
|
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the completion routine.
|
|||
|
//
|
|||
|
IoSetCompletionRoutine(Irp,
|
|||
|
MPPdoGlobalCompletion,
|
|||
|
context,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Get a copy of the original srb.
|
|||
|
//
|
|||
|
RtlCopyMemory(&context->Srb,
|
|||
|
srb,
|
|||
|
sizeof(SCSI_REQUEST_BLOCK));
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that a request is outstanding to this device.
|
|||
|
//
|
|||
|
InterlockedIncrement(&targetInfo->Requests);
|
|||
|
|
|||
|
//
|
|||
|
// Issue request to pathId specified.
|
|||
|
//
|
|||
|
return IoCallDriver(targetInfo->DevFilter, Irp);
|
|||
|
}
|
|||
|
|
|||
|
action = dsm->CategorizeRequest(dsm->DsmContext,
|
|||
|
&diskExtension->DsmIdList,
|
|||
|
Irp,
|
|||
|
srb,
|
|||
|
diskExtension->CurrentPath,
|
|||
|
&pathId,
|
|||
|
&status);
|
|||
|
|
|||
|
if (action == DSM_PATH_SET) {
|
|||
|
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Set-up the completion routine.
|
|||
|
//
|
|||
|
IoSetCompletionRoutine(Irp,
|
|||
|
MPPdoGlobalCompletion,
|
|||
|
context,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// DSM has set the path to which the request should be sent.
|
|||
|
//
|
|||
|
// Get the target info corresponding to the pathId returned
|
|||
|
// by the DSM.
|
|||
|
//
|
|||
|
targetInfo = MPIOGetTargetInfo(diskExtension,
|
|||
|
pathId,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (targetInfo->PathFailed) {
|
|||
|
|
|||
|
MPDebugPrint((0,
|
|||
|
"MPIOHandleRequest: Got back a failed path. (%x)\n",
|
|||
|
targetInfo));
|
|||
|
|
|||
|
ASSERT(targetInfo->PathFailed == FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Save the real pdo in the context.
|
|||
|
//
|
|||
|
context->TargetInfo = targetInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Get a copy of the original srb.
|
|||
|
//
|
|||
|
RtlCopyMemory(&context->Srb,
|
|||
|
srb,
|
|||
|
sizeof(SCSI_REQUEST_BLOCK));
|
|||
|
|
|||
|
MPDebugPrint((2,
|
|||
|
"Categorize: Context (%x). TargetInfo (%x)\n",
|
|||
|
context,
|
|||
|
context->TargetInfo));
|
|||
|
|
|||
|
//
|
|||
|
// Allow the DSM to set-up completion info.
|
|||
|
// The DSM has the responsibility for the allocation and free
|
|||
|
// of it's own context.
|
|||
|
//
|
|||
|
dsm->SetCompletion(dsm->DsmContext,
|
|||
|
targetInfo->DsmID,
|
|||
|
Irp,
|
|||
|
srb,
|
|||
|
&context->DsmCompletion);
|
|||
|
|
|||
|
//
|
|||
|
// Indicate the current path.
|
|||
|
//
|
|||
|
diskExtension->CurrentPath = targetInfo->PathId;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that a request is outstanding to this device.
|
|||
|
//
|
|||
|
InterlockedIncrement(&targetInfo->Requests);
|
|||
|
|
|||
|
//
|
|||
|
// Issue request to pathId specified.
|
|||
|
//
|
|||
|
status = IoCallDriver(targetInfo->DevFilter, Irp);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// DSM wants to handle it internally.
|
|||
|
//
|
|||
|
// Init the event. We wait below, and the DSM
|
|||
|
// sets it when it's finished handling the request.
|
|||
|
//
|
|||
|
KeInitializeEvent(&event,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
|
|||
|
if (action == DSM_BROADCAST) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Give it to the DSM's Broadcast routine. The request
|
|||
|
// will be sent down all paths, and possibly be modified
|
|||
|
// from the original OpCode.
|
|||
|
//
|
|||
|
status = dsm->BroadcastSrb(dsm->DsmContext,
|
|||
|
&diskExtension->DsmIdList,
|
|||
|
Irp,
|
|||
|
srb,
|
|||
|
&event);
|
|||
|
|
|||
|
} else if (action == DSM_WILL_HANDLE) {
|
|||
|
|
|||
|
//
|
|||
|
// Hand the request off to the DSM.
|
|||
|
//
|
|||
|
status = dsm->SrbDeviceControl(dsm->DsmContext,
|
|||
|
&diskExtension->DsmIdList,
|
|||
|
Irp,
|
|||
|
srb,
|
|||
|
&event);
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// It's broken (DSM_ERROR).
|
|||
|
//
|
|||
|
// TODO handle this
|
|||
|
// Call InterpretError to see if it's a fail-over.
|
|||
|
// Set-up a bogus assert for the time being, until
|
|||
|
// this case is actually handled.
|
|||
|
//
|
|||
|
// Status is set by the DSM. Go ahead and propogate that
|
|||
|
// back.
|
|||
|
//
|
|||
|
ASSERT(action == DSM_BROADCAST);
|
|||
|
}
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
KeWaitForSingleObject(&event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
status = Irp->IoStatus.Status;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Ensure that the DSM is returning status
|
|||
|
// correctly.
|
|||
|
//
|
|||
|
ASSERT(status == Irp->IoStatus.Status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The calling routine expects these to be completed here.
|
|||
|
//
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
InterlockedDecrement(&diskExtension->OutstandingRequests);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOExecuteNone(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
|
|||
|
NTSTATUS status;
|
|||
|
PREAL_DEV_INFO targetInfo = diskExtension->TargetInfo;
|
|||
|
|
|||
|
//
|
|||
|
// If the path UID isn't set, try again.
|
|||
|
//
|
|||
|
if (targetInfo->PathUIDValue == FALSE) {
|
|||
|
|
|||
|
targetInfo->Identifier = MPIOCreateUID(diskExtension->ControlObject,
|
|||
|
targetInfo->PathId);
|
|||
|
if (targetInfo->Identifier != 0) {
|
|||
|
targetInfo->PathUIDValue = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// Set-up NULL context info as none of these requires
|
|||
|
// a special completion routine.
|
|||
|
//
|
|||
|
context->DsmCompletion.DsmCompletionRoutine = NULL;
|
|||
|
context->DsmCompletion.DsmContext = NULL;
|
|||
|
|
|||
|
switch (srb->Function) {
|
|||
|
|
|||
|
//
|
|||
|
// If it's a Claim ore Release, handle it here.
|
|||
|
// TODO: Lock these accesses.
|
|||
|
//
|
|||
|
case SRB_FUNCTION_CLAIM_DEVICE:
|
|||
|
|
|||
|
//
|
|||
|
// Determine if a claim has already been made.
|
|||
|
//
|
|||
|
if (diskExtension->IsClaimed) {
|
|||
|
|
|||
|
//
|
|||
|
// Already been claimed. Return that the device
|
|||
|
// is busy.
|
|||
|
//
|
|||
|
srb->SrbStatus = SRB_STATUS_BUSY;
|
|||
|
status = STATUS_DEVICE_BUSY;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that the claim has been made
|
|||
|
// and return this deviceObject.
|
|||
|
//
|
|||
|
diskExtension->IsClaimed = TRUE;
|
|||
|
srb->DataBuffer = DeviceObject;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case SRB_FUNCTION_RELEASE_DEVICE:
|
|||
|
|
|||
|
//
|
|||
|
// This is always successful.
|
|||
|
//
|
|||
|
srb->SrbStatus = SRB_STATUS_SUCCESS;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
default:
|
|||
|
|
|||
|
//
|
|||
|
// These aren't handled - check to
|
|||
|
// see whether any do end up here.
|
|||
|
// LOG the error.
|
|||
|
//
|
|||
|
srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
|
|||
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
|
|||
|
MPDebugPrint((1,
|
|||
|
"MPIOInternalDevControl: Unhandled Srb Function (%x)\n",
|
|||
|
srb->Function));
|
|||
|
DbgBreakPoint();
|
|||
|
break;
|
|||
|
}
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoInternalDeviceControl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN completeRequest = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Determine the what the request is.
|
|||
|
// The only requests handled here are EXECUTE_NONE
|
|||
|
//
|
|||
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
case IOCTL_SCSI_EXECUTE_NONE:
|
|||
|
|
|||
|
//
|
|||
|
// Handle all of the EXECUTE_NONE requests here.
|
|||
|
// This routine won't complete the request, so
|
|||
|
// do it below.
|
|||
|
//
|
|||
|
status = MPIOExecuteNone(DeviceObject,
|
|||
|
Irp);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
//
|
|||
|
// This routine will categorise the I/O and execute
|
|||
|
// the appropriate handler.
|
|||
|
//
|
|||
|
status = MPIOPdoHandleRequest(DeviceObject,
|
|||
|
Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Don't complete these
|
|||
|
//
|
|||
|
completeRequest = FALSE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (completeRequest) {
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
InterlockedDecrement(&diskExtension->OutstandingRequests);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoUnhandled(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
|
|||
|
//
|
|||
|
// See what IRP_MJ the request is.
|
|||
|
//
|
|||
|
MPDebugPrint((1,
|
|||
|
"MPIOPdoUnhandled: Major %x, Minor %x\n",
|
|||
|
irpStack->MajorFunction,
|
|||
|
irpStack->MinorFunction));
|
|||
|
|
|||
|
context->DsmCompletion.DsmCompletionRoutine = NULL;
|
|||
|
context->DsmCompletion.DsmContext = NULL;
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
InterlockedDecrement(&diskExtension->OutstandingRequests);
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoPnp(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
context->DsmCompletion.DsmCompletionRoutine = NULL;
|
|||
|
context->DsmCompletion.DsmContext = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Determine the MinorFunction and call the appropriate
|
|||
|
// helper function.
|
|||
|
//
|
|||
|
switch (irpStack->MinorFunction) {
|
|||
|
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
|||
|
|
|||
|
//
|
|||
|
// Call the Qdr handler.
|
|||
|
//
|
|||
|
status = MPIOPdoQdr(DeviceObject, Irp);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_ID:
|
|||
|
|
|||
|
//
|
|||
|
// Call the QueryId routine.
|
|||
|
//
|
|||
|
status = MPIOPdoQueryId(DeviceObject, Irp);
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_DEVICE_USAGE_NOTIFICATION:
|
|||
|
|
|||
|
//
|
|||
|
// Adjust the various counters depending upon the
|
|||
|
// Type of Usage Notification.
|
|||
|
//
|
|||
|
// Invalidate the device state
|
|||
|
//
|
|||
|
// complete the request.
|
|||
|
//
|
|||
|
//status = MPIODeviceUsage(DeviceObject,
|
|||
|
// Irp);
|
|||
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_DEVICE_TEXT:
|
|||
|
|
|||
|
//
|
|||
|
// Get the device text or location.
|
|||
|
//
|
|||
|
status = MPIOQueryDeviceText(DeviceObject, Irp);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_CAPABILITIES: {
|
|||
|
PDEVICE_CAPABILITIES capabilities;
|
|||
|
|
|||
|
capabilities = irpStack->Parameters.DeviceCapabilities.Capabilities;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate no function driver is really necessary, suppress PnP Pop-ups
|
|||
|
// and the MPDisk's Id
|
|||
|
//
|
|||
|
capabilities->RawDeviceOK = 1;
|
|||
|
capabilities->SilentInstall = 1;
|
|||
|
capabilities->Address = diskExtension->DeviceOrdinal;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case IRP_MN_QUERY_PNP_DEVICE_STATE: {
|
|||
|
PPNP_DEVICE_STATE deviceState = (PPNP_DEVICE_STATE) &(Irp->IoStatus.Information);
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// TODO: This depends on whether this is in the hibernate/page path.
|
|||
|
// Implement the check, and implement the IRP_MN_DEVICE_USAGE_NOTIFICATION
|
|||
|
//
|
|||
|
*deviceState |= PNP_DEVICE_NOT_DISABLEABLE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case IRP_MN_QUERY_RESOURCES:
|
|||
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
|
|||
|
|
|||
|
//
|
|||
|
// Don't need resources and don't have a boot config.
|
|||
|
// Complete it successfully with a NULL buffer.
|
|||
|
//
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = (ULONG_PTR)NULL;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_STOP_DEVICE:
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = (ULONG_PTR) NULL;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_START_DEVICE:
|
|||
|
|
|||
|
//
|
|||
|
// Register as a WMI provider.
|
|||
|
//
|
|||
|
IoWMIRegistrationControl(DeviceObject,
|
|||
|
WMIREG_ACTION_REGISTER);
|
|||
|
|
|||
|
//
|
|||
|
// Start the per-disk timer.
|
|||
|
//
|
|||
|
IoStartTimer(DeviceObject);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
|||
|
MPDebugPrint((1,
|
|||
|
"MPIOPdoPnp: QueryRemove on (%x)\n",
|
|||
|
DeviceObject));
|
|||
|
status = STATUS_DEVICE_BUSY;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
|
|||
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
|||
|
case IRP_MN_CANCEL_STOP_DEVICE:
|
|||
|
case IRP_MN_QUERY_STOP_DEVICE:
|
|||
|
case IRP_MN_SURPRISE_REMOVAL:
|
|||
|
case IRP_MN_REMOVE_DEVICE:
|
|||
|
|
|||
|
//
|
|||
|
// TODO
|
|||
|
//
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Send these down
|
|||
|
//
|
|||
|
case IRP_MN_READ_CONFIG:
|
|||
|
case IRP_MN_WRITE_CONFIG:
|
|||
|
case IRP_MN_EJECT:
|
|||
|
case IRP_MN_SET_LOCK:
|
|||
|
case IRP_MN_QUERY_BUS_INFORMATION:
|
|||
|
|
|||
|
InterlockedDecrement(&diskExtension->OutstandingRequests);
|
|||
|
return MPIOForwardRequest(DeviceObject, Irp);
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// The following requests are completed without status
|
|||
|
// being updated.
|
|||
|
//
|
|||
|
case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
|
|||
|
status = Irp->IoStatus.Status;
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MN_QUERY_INTERFACE:
|
|||
|
MPDebugPrint((2,
|
|||
|
"MPIOPdoPnP: Query Interface\n"));
|
|||
|
default:
|
|||
|
MPDebugPrint((2,
|
|||
|
"MPIOPdoPnP: Not handled - (%x)\n",
|
|||
|
irpStack->MinorFunction));
|
|||
|
DbgBreakPoint();
|
|||
|
status = Irp->IoStatus.Status;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
InterlockedDecrement(&diskExtension->OutstandingRequests);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoPowerCompletion(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
|
|||
|
if (Irp->PendingReturned) {
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
}
|
|||
|
|
|||
|
PoStartNextPowerIrp(Irp);
|
|||
|
InterlockedDecrement(&diskExtension->OutstandingRequests);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
MPIOPdoPower(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4;
|
|||
|
|
|||
|
MPDebugPrint((2,
|
|||
|
"MPIOPdoPower: Got a %x power irp\n",
|
|||
|
irpStack->MinorFunction));
|
|||
|
|
|||
|
context->DsmCompletion.DsmCompletionRoutine = NULL;
|
|||
|
context->DsmCompletion.DsmContext = NULL;
|
|||
|
|
|||
|
IoSetCompletionRoutine(Irp,
|
|||
|
MPIOPdoPowerCompletion,
|
|||
|
context,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
PoCallDriver(deviceExtension->LowerDevice, Irp);
|
|||
|
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|