windows-nt/Source/XPSP1/NT/drivers/storage/mpath/control/pdo.c
2020-09-26 16:20:57 +08:00

1622 lines
46 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}