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

1535 lines
42 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.

/*++
Copyright (C) 1999-2000 Microsoft Corporation
Module Name:
mpath.c
Abstract:
The main multi-path module. Provides fault tolerance across adapter/path failures.
Author:
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include <ntddk.h>
#include "stdarg.h"
#include "stdio.h"
#include "mpio.h"
//
// Entry point decl's.
//
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
MPIOGlobalDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
MPIOAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
);
NTSTATUS
MPIOCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
MPIOClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
MPIOWmiDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
MPIOUnload(
IN PDRIVER_OBJECT DriverObject
);
typedef
NTSTATUS
(*PMPIO_DISPATCH) (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PMPIO_CONTEXT Context
);
// PMPIO_DISPATCH PdoDispatch[IRP_MJ_MAXIMUM_FUNCTION];
PDRIVER_DISPATCH PdoDispatch[IRP_MJ_MAXIMUM_FUNCTION];
PDRIVER_DISPATCH FdoDispatch[IRP_MJ_MAXIMUM_FUNCTION];
NTSTATUS
MPIOPdoDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG State
);
BOOLEAN DoASSERT = TRUE;
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is called when the driver loads loads.
Arguments:
DriverObject - Supplies the driver object.
RegistryPath - Supplies the registry path.
Return Value:
NTSTATUS
--*/
{
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
NTSTATUS status;
PDEVICE_OBJECT pdo;
ULONG i;
MPDebugPrint((0,
"MPIO: Driver Entry\n"));
if (DontLoad) {
return STATUS_NO_SUCH_DEVICE;
}
//
// Register the entry points for all IRP_MJ's.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = MPIOCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MPIOClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MPIOGlobalDispatch;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MPIOGlobalDispatch;
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = MPIOGlobalDispatch;
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = MPIOGlobalDispatch;
DriverObject->MajorFunction[IRP_MJ_PNP] = MPIOGlobalDispatch;
DriverObject->MajorFunction[IRP_MJ_POWER] = MPIOGlobalDispatch;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = MPIOWmiDispatch;
//
// Register the Unload.
//
DriverObject->DriverUnload = MPIOUnload;
ObReferenceObject(DriverObject);
pdo = NULL;
//
// Notify PnP that this 'device' was found.
// Indicate no resources, no bus location, and lie that
// resources were reported (as we need none).
//
status = IoReportDetectedDevice(DriverObject,
InterfaceTypeUndefined,
-1,
-1,
NULL,
NULL,
TRUE,
&pdo);
if (!NT_SUCCESS(status)) {
MPDebugPrint((0,
"Multipath DriverEntry: IoReportDetectedDevice returned (%x)\n",
status));
ObDereferenceObject(DriverObject);
return status;
}
//
// Create the fdo. Not really an AddDevice routine
// but name it as such.
//
status = MPIOAddDevice(DriverObject,
pdo);
if (!NT_SUCCESS(status)) {
MPDebugPrint((0,
"MPIO DriverEntry: AddDevice failed (%x)\n",
status));
ObDereferenceObject(DriverObject);
return status;
}
//
// Get deviceObject (fdo) and deviceExtension.
//
deviceObject = IoGetAttachedDeviceReference(pdo);
ObDereferenceObject(deviceObject);
deviceExtension = deviceObject->DeviceExtension;
//
// Setup the device extension.
//
deviceExtension->Pdo = pdo;
deviceExtension->RegistryPath.Buffer = ExAllocatePool(NonPagedPool,
RegistryPath->MaximumLength);
deviceExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
RtlCopyUnicodeString(&deviceExtension->RegistryPath,
RegistryPath);
//
// Setup the Pdo's dispatch routines.
//
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
PdoDispatch[i] = MPIOPdoUnhandled;
}
PdoDispatch[IRP_MJ_DEVICE_CONTROL] = MPIOPdoDeviceControl;
PdoDispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MPIOPdoInternalDeviceControl;
PdoDispatch[IRP_MJ_PNP] = MPIOPdoPnp;
PdoDispatch[IRP_MJ_POWER] = MPIOPdoPower;
MPathDebug = 1;
return status;
}
NTSTATUS
MPIOAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This routine creates and initializes a new FDO for the corresponding
PDO.
Arguments:
DriverObject - Supplies the driver object for mpath control.
PhysicalDeviceObject - Supplies the physical device object.
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension;
PCONTROL_EXTENSION controlExtension;
UNICODE_STRING deviceName;
UNICODE_STRING dosName;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
RtlInitUnicodeString(&deviceName, DD_MULTIPATH_CONTROL_DEVICE_NAME);
//
// Create the fdo.
//
status = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
&deviceName,
FILE_DEVICE_CONTROLLER,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
controlExtension = ExAllocatePool(NonPagedPool, sizeof(CONTROL_EXTENSION));
if (controlExtension == NULL) {
IoDeleteDevice(deviceObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(controlExtension, sizeof(CONTROL_EXTENSION));
//
// Create the symbolic link.
//
RtlInitUnicodeString(&dosName, DD_MULTIPATH_CONTROL_DOS_NAME);
IoCreateSymbolicLink( &dosName, &deviceName);
//
// Save off devObj and build the device extension.
//
deviceExtension = deviceObject->DeviceExtension;
deviceExtension->Type = MPIO_CONTROL;
deviceExtension->DeviceObject = deviceObject;
deviceExtension->DriverObject = DriverObject;
//
// Attach to the pdo.
//
deviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(deviceObject,
PhysicalDeviceObject);
if (deviceExtension->LowerDevice == NULL) {
IoDeleteSymbolicLink(&dosName);
IoDeleteDevice(deviceObject);
return STATUS_NO_SUCH_DEVICE;
}
deviceExtension->Pdo = PhysicalDeviceObject;
deviceExtension->TypeExtension = controlExtension;
//
// Initialize the 'control' portion of the device extension.
//
KeInitializeSpinLock(&controlExtension->SpinLock);
InitializeListHead(&controlExtension->FilterList);
InitializeListHead(&controlExtension->DeviceList);
InitializeListHead(&controlExtension->DsmList);
InitializeListHead(&controlExtension->IdList);
InitializeListHead(&controlExtension->ControllerList);
InitializeListHead(&controlExtension->FailPacketList);
//
// Set-up the WMILIB Context.
//
MPIOSetupWmi(deviceObject);
//
// Register as a WMI provider.
//
IoWMIRegistrationControl(deviceObject,
WMIREG_ACTION_REGISTER);
//
// Indicate that this device is ready.
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return status;
}
NTSTATUS
MPIOCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch for IRP_MJ_CREATE.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the IO request block.
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
MPIOClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch for IRP_MJ_CLOSE
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the IO request block.
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
MPIOUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine unloads.
Arguments:
DriverObject - Supplies the driver object.
Return Value:
None.
--*/
{
//
// TODO: Release all allocations.
// TODO: Ensure pdo's/DSMs have been torndown.
//
ObDereferenceObject(DriverObject);
return STATUS_SUCCESS;
}
NTSTATUS
MPPdoGlobalCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
All requests to the pdo's have this as the completion. It handles calling out to
the function-specific completions and deals with errors.
Arguments:
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDSM_ENTRY dsm = &diskExtension->DsmInfo;
PMPIO_CONTEXT context = Context;
PSCSI_REQUEST_BLOCK srb = NULL;
PMPIO_COMPLETION_ROUTINE specificCompletion;
PKDEVICE_QUEUE_ENTRY entry;
PREAL_DEV_INFO targetInfo;
PREAL_DEV_INFO newTargetInfo;
PDEVICE_OBJECT targetObject;
NTSTATUS status;
NTSTATUS status2;
PVOID dsmId;
PVOID dsmContext;
DSM_COMPLETION_ROUTINE dsmCompletion;
ULONG errorMask = 0;
ULONG completionState = context->CurrentState;
ULONG state;
ULONG state2;
LONG outstandingRequests;
LONG deviceRequests;
BOOLEAN fatal = FALSE;
BOOLEAN retry = FALSE;
BOOLEAN needsRemoval = FALSE;
BOOLEAN currentRequestHandled;
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
//
// Get the targetInfo. This describes the device.
//
targetInfo = context->TargetInfo;
//
// Get the number of requests on THIS device (vs. the entire disk).
//
deviceRequests = InterlockedDecrement(&targetInfo->Requests);
//
// One less request below us.
//
outstandingRequests = InterlockedDecrement(&diskExtension->OutstandingRequests);
ASSERT(diskExtension->OutstandingRequests >= 0);
if ((outstandingRequests == 0) && (deviceRequests != 0)) {
//
// If there are no requests for the disk, then there shouldn't be any of this
// deviceInfo.
//
if (DoASSERT) {
MPDebugPrint((0,
"GlobalCompletion: DeviceRequests (%x)\n",
deviceRequests));
ASSERT(deviceRequests == 0);
}
}
//
// Get the config. spinlock.
//
KeAcquireSpinLockAtDpcLevel(&diskExtension->SpinLock);
//
// See whether there is a remove pending on this targetInfo.
//
if (targetInfo->NeedsRemoval) {
needsRemoval = TRUE;
}
KeReleaseSpinLockFromDpcLevel(&diskExtension->SpinLock);
//
// For scsi requests, need to ensure that frozen queues
// get released.
//
if (irpStack->MajorFunction == IRP_MJ_SCSI) {
//
// Get the srb.
//
srb = irpStack->Parameters.Scsi.Srb;
//
// Handle any frozen queue stuff.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
//
// Get the target object for this request.
// NOTE: This assumes that the Function-Specific handler
// has set this up correctly.
//
targetObject = targetInfo->PortPdo;
MPLibReleaseQueue(targetObject);
}
}
//
// Extract the DSM-Specific stuff from the context.
//
dsmId = targetInfo->DsmID;
dsmContext = context->DsmCompletion.DsmContext;
dsmCompletion = context->DsmCompletion.DsmCompletionRoutine;
//
//
// Determine the current state.
//
state = deviceExtension->State;
if ((deviceExtension->CompletionState == MPIO_STATE_NORMAL) ||
(deviceExtension->CompletionState == MPIO_STATE_DEGRADED)) {
deviceExtension->CompletionState = state;
}
//
// Indicate that this request hasn't been dealt with yet.
//
currentRequestHandled = FALSE;
//
// This call will update state information, but the request coming in
// needs to be first handled with the current state.
//
state2 = MPIOHandleStateTransition(DeviceObject);
runState:
if ((state != MPIO_STATE_NORMAL) && (state != MPIO_STATE_DEGRADED)) {
switch (state) {
case MPIO_STATE_IN_FO:
//
// The fail-over is being executed. Put on resubmit Queue. (unless
// status == success.
//
status2 = MPIOQueueRequest(DeviceObject,
Irp,
Context,
&diskExtension->ResubmitQueue);
state = MPIOHandleStateTransition(DeviceObject);
if (state != MPIO_STATE_IN_FO) {
MPDebugPrint((0,
"GlobalCompletion: Going from IN_FO to (%x)\n",
state));
//
// See if we've gone into WAIT2 directly.
//
if (state == MPIO_STATE_WAIT2) {
//
// Get the targetInfo corresponding to the path set-up in the
// fail-over handler. Whatever was in the completion context is
// probably not valid.
// NOTE: This assumes that no-one is updating the CurrentPath
// in the diskExtension. Probably not a good assumption.
// TODO: Validate.
//
targetInfo = MPIOGetTargetInfo(diskExtension,
diskExtension->CurrentPath,
NULL);
//
// Submit the queued requests. These Resubmit requests all go
// down "newPath". TODO: Indicate this behaviour in dsm.h
//
status = MPIOIssueQueuedRequests(targetInfo,
&diskExtension->ResubmitQueue,
MPIO_STATE_WAIT2,
&diskExtension->OutstandingRequests);
} else {
//
// Went into WAIT1.
//
currentRequestHandled = TRUE;
goto runState;
}
}
if (needsRemoval && (deviceRequests == 0)) {
MPDebugPrint((0,
"MPIOGlobalCompletion (IN_FO): Removing %x\n",
targetInfo));
//
// This routine will queue a work item to handle the device removal.
//
MPIOHandleRemoveAsync(DeviceObject,
targetInfo);
}
return STATUS_MORE_PROCESSING_REQUIRED;
break;
case MPIO_STATE_WAIT1: {
//
// This indicates that the fail-over was successful, but there
// are still outstanding requests (on the failed path).
// Status should be flushed, or some error condition, though
// conceivable, a successful completion could have occurred.
// One less request below us.
//
if (currentRequestHandled == FALSE) {
//
// Enqueue this request on the Resubmit Queue.
//
status2 = MPIOQueueRequest(DeviceObject,
Irp,
Context,
&diskExtension->ResubmitQueue);
if (needsRemoval && (deviceRequests == 0)) {
MPDebugPrint((0,
"MPIOGlobalCompletion (WAIT1): Removing %x\n",
targetInfo));
//
// This routine will queue a work item to handle the device removal.
//
MPIOHandleRemoveAsync(DeviceObject,
targetInfo);
}
}
//
// See if the has caused a move into a different state.
//
state = MPIOHandleStateTransition(DeviceObject);
if (state != MPIO_STATE_WAIT1) {
MPDebugPrint((0,
"GlobalCompletion: Going from WAIT1 to (%x)\n",
state));
//
// See if we've gone into WAIT2 directly.
//
if (state == MPIO_STATE_WAIT2) {
//
// Get the targetInfo corresponding to the path set-up in the
// fail-over handler. Whatever was in the completion context is
// probably not valid.
// NOTE: This assumes that no-one is updating the CurrentPath
// in the diskExtension. Probably not a good assumption.
// TODO: Validate.
//
targetInfo = MPIOGetTargetInfo(diskExtension,
diskExtension->CurrentPath,
NULL);
//
// Submit the queued requests. These Resubmit requests all go
// down "newPath". TODO: Indicate this behaviour in dsm.h
//
status = MPIOIssueQueuedRequests(targetInfo,
&diskExtension->ResubmitQueue,
MPIO_STATE_WAIT2,
&diskExtension->OutstandingRequests);
} else {
currentRequestHandled = TRUE;
goto runState;
}
}
return STATUS_MORE_PROCESSING_REQUIRED;
break;
}
case MPIO_STATE_WAIT2: {
//
// Dec the # of resubmit requests when zero, we go into WAIT3.
// New requests on FOQ.
//
outstandingRequests = InterlockedDecrement(&diskExtension->ResubmitRequests);
ASSERT(diskExtension->ResubmitRequests >= 0);
state = MPIOHandleStateTransition(DeviceObject);
if (state != MPIO_STATE_WAIT2) {
MPDebugPrint((0,
"GlobalCompletion: Going from WAIT2 to (%x)\n",
state));
if (state == MPIO_STATE_WAIT3) {
//
// Get the targetInfo corresponding to CurrentPath.
//
targetInfo = MPIOGetTargetInfo(diskExtension,
diskExtension->CurrentPath,
NULL);
//
// Process the Fail-Over queue.
//
status2 = MPIOIssueQueuedRequests(targetInfo,
&diskExtension->FailOverQueue,
MPIO_STATE_WAIT3,
&diskExtension->OutstandingRequests);
}
}
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
MPDebugPrint((0,
"MPIOCompletion: !Success while in WAIT2. Irp (%x) Status (%x)\n",
Irp,
Irp->IoStatus.Status));
}
//
// Now, handle the request normally.
//
break;
}
case MPIO_STATE_WAIT3: {
//
// Dec the # of fail-over requests. When zero, we go into DEGRADED.
//
outstandingRequests = InterlockedDecrement(&diskExtension->FailOverRequests);
ASSERT(diskExtension->FailOverRequests >= 0);
state = MPIOHandleStateTransition(DeviceObject);
//
// Continue down and handle the request normally.
//
break;
}
default:
//
// Invalid state.
//
MPDebugPrint((0,
"MPIOCompletion: Unknown State (%x)\n",
state));
DbgBreakPoint();
break;
}
} else {
if (needsRemoval && (deviceRequests == 0)) {
MPDebugPrint((0,
"MPIOGlobalCompletion (WAIT1): Removing %x\n",
targetInfo));
//
// This routine will queue a work item to handle the device removal.
//
MPIOHandleRemoveAsync(DeviceObject,
targetInfo);
}
}
status = Irp->IoStatus.Status;
if (!NT_SUCCESS(status)) {
fatal = FALSE;
retry = FALSE;
if (irpStack->MajorFunction == IRP_MJ_SCSI) {
//
// Invoke the DSM's InterpretError with preset fatal and retry
// values.
//
errorMask = dsm->InterpretError(dsm->DsmContext,
dsmId,
srb,
&status,
&retry);
if (errorMask & DSM_FATAL_ERROR) {
fatal = TRUE;
}
}
}
if (fatal) {
// if ((deviceExtension->LastState == MPIO_STATE_NORMAL) ||
// (deviceExtension->LastState == MPIO_STATE_DEGRADED)) {
diskExtension->FailOver = TRUE;
state = MPIOHandleStateTransition(DeviceObject);
// }
//
// Put the request on the resubmit queue.
// This routine will also clean up the request and
// prepare it for resubmission.
//
// BUGBUG: If we queue this, and the FailOver is unsuccessful, the request
// will remain in the queue.
//
status2 = MPIOQueueRequest(DeviceObject,
Irp,
Context,
&diskExtension->ResubmitQueue);
MPDebugPrint((1,
"MPIOCompletion: Irp (%x) Srb (%x) on resubmit queue of (%x). Status (%x)\n",
Irp,
srb,
DeviceObject,
status));
//
// Call the Fail-over Handler.
//
status2 = MPIOFailOverHandler(DeviceObject,
errorMask,
targetInfo);
//
// If the fail-over was successful or pending, don't let Io get rid of this
// request.
// If it wasn't successful, pass the original status back.
//
if ((status2 == STATUS_SUCCESS) ||
(status2 == STATUS_PENDING)) {
status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
ASSERT(FALSE);
}
return status;
} else if (retry) {
//
// If the DSM has requested a retry, clean-up the request and call PdoDispatch.
//
if (irpStack->MajorFunction == IRP_MJ_SCSI) {
//
// Need to rebuild the Srb.
//
RtlCopyMemory(irpStack->Parameters.Scsi.Srb,
&context->Srb,
sizeof(SCSI_REQUEST_BLOCK));
}
//
// Re-do status and information.
//
Irp->IoStatus.Status = 0;
Irp->IoStatus.Information = 0;
//
// Rebuild port's stack location.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
//
// Resend.
//
status2 = MPIOPdoDispatch(DeviceObject,
Irp,
deviceExtension->State);
if (status2 == STATUS_PENDING) {
status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
status = status2;
}
return status;
}
//
// If the DSM specified a completion routine, invoke it now.
//
if (dsmCompletion) {
dsmCompletion(dsmId,
Irp,
srb,
dsmContext);
}
//
// Free the context structure.
//
MPIOFreeContext(deviceExtension,
Context);
return status;
}
NTSTATUS
MPIOPdoDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG State
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
ULONG sortKey = deviceExtension->SequenceNumber++;
PMPIO_CONTEXT context;
NTSTATUS status = STATUS_PENDING;
KIRQL irql;
UCHAR opCode = 0;
ASSERT(deviceExtension->Type == MPIO_MPDISK);
KeAcquireSpinLock(&diskExtension->SpinLock, &irql);
//
// Allocate the request context.
//
context = MPIOAllocateContext(deviceExtension);
RtlZeroMemory(context, sizeof(MPIO_CONTEXT));
//
// Indicate this I/O's sequence number. Used in tracking
// requests during fail-over and also as a key into the device queue.
//
context->CurrentState = State;
context->OriginalState = State;
context->Irp = Irp;
context->Allocated = TRUE;
//
// Clone the irpstack location.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
//
// Set the context in our stack.
//
irpStack->Parameters.Others.Argument4 = (PVOID)context;
switch (State) {
case MPIO_STATE_IN_FO:
case MPIO_STATE_WAIT1: {
//
// As no requests will be submitted, release the lock now.
//
KeReleaseSpinLock(&diskExtension->SpinLock, irql);
//
// If in FO, or WAIT1 simply queue to the
// Fail-Over Queue. Once the Fail-Over is complete and the
// resubmit queue has been handled, the F.O.Q. will be dealt
// with.
//
status = MPIOQueueRequest(DeviceObject,
Irp,
context,
&diskExtension->FailOverQueue);
//
// This routine will only return success or insufficient resources.
//
if (status == STATUS_INSUFFICIENT_RESOURCES) {
MPDebugPrint((1,
"MPIOQueueRequest returned (%x). State (%x)\n",
status,
State));
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
//
// Set pending.
//
IoMarkIrpPending(Irp);
//
// Return pending.
//
status = STATUS_PENDING;
break;
}
case MPIO_STATE_WAIT2: {
//
// As no requests will be submitted, release the lock now.
//
KeReleaseSpinLock(&diskExtension->SpinLock, irql);
//
// The resubmit queue is being handled. These (like IN_FO and WAIT1)
// go onto the Fail-Over queue.
// This is seperated from the above States merely for administrative
// reasons. (Unless it gets changed...)
//
status = MPIOQueueRequest(DeviceObject,
Irp,
context,
&diskExtension->FailOverQueue);
//
// This routine will only return success or insufficient resources.
//
if (status == STATUS_INSUFFICIENT_RESOURCES) {
MPDebugPrint((1,
"MPIOQueueRequest returned (%x). State (%x)\n",
status,
State));
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
//
// Set pending.
//
IoMarkIrpPending(Irp);
//
// Return pending.
//
status = STATUS_PENDING;
break;
}
case MPIO_STATE_WAIT3: {
//
// Draining the Fail-Over queue. Complete these requests
// with busy, if they are Srb's (build sense data, so that they
// are retried). Otherwise, just return BUSY and hope for the best.
// BUGBUG: Revisit handling of non-Srb requests. Maybe put these
// on the F.O.Q...
//
MPDebugPrint((0,
"PdoDispatch: Setting Irp(%x) Srb (%x) BUSY\n",
Irp,
srb));
//
// As no requests will be submitted, release the lock now.
//
KeReleaseSpinLock(&diskExtension->SpinLock, irql);
if (irpStack->MajorFunction == IRP_MJ_SCSI) {
RtlCopyMemory(&context->Srb,
srb,
sizeof(SCSI_REQUEST_BLOCK));
//
// This will build the appropriate sense data that indicates
// to the class driver that the device is busy.
//
MPIOSetRequestBusy(srb);
}
//
// All requests will be completed as BUSY.
//
status = STATUS_DEVICE_BUSY;
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
}
case MPIO_STATE_NORMAL:
case MPIO_STATE_DEGRADED: {
//
// All failure queues are empty and the new
// path is functioning (DEGRADED) or all is working
// fine (NORMAL).
// Submit these requests normally.
//
// Indicate another request has been submitted.
//
InterlockedIncrement(&diskExtension->OutstandingRequests);
//
// Now it's safe to release the lock as the config. code checks
// for outstanding requests.
//
KeReleaseSpinLock(&diskExtension->SpinLock, irql);
//
// Call FunctionSpecific handler.
// The function-specific handler will set-up it's private
// completion routine, along with the DSM information.
//
status = PdoDispatch[irpStack->MajorFunction](DeviceObject,
Irp);
break;
}
default:
//
// As no requests will be submitted, release the lock now.
//
KeReleaseSpinLock(&diskExtension->SpinLock, irql);
//
// TODO: Here only for test. Remove it.
//
MPDebugPrint((0,
"MPIOPdoDispatch: Unknown State (%x)\n",
State));
DbgBreakPoint();
break;
}
return status;
}
NTSTATUS
MPIOFdoDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG State
)
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status;
//
// State is less important for the FDO: Perhaps of no importance.
// Maybe:
// Set Sequence #
// Set completion.
// Call FunctionSpecific Handler.
//
switch (irpStack->MajorFunction) {
case IRP_MJ_PNP:
status = MPIOFdoPnP(DeviceObject,
Irp);
break;
case IRP_MJ_POWER:
status = MPIOFdoPower(DeviceObject,
Irp);
break;
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
status = MPIOFdoInternalDeviceControl(DeviceObject,
Irp);
break;
case IRP_MJ_SYSTEM_CONTROL:
status = MPIOFdoWmi(DeviceObject,
Irp);
break;
case IRP_MJ_DEVICE_CONTROL:
case IRP_MJ_SHUTDOWN:
case IRP_MJ_FLUSH_BUFFERS:
default:
status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
}
return status;
}
NTSTATUS
MPIOWmiDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status;
//
// Determine whether this is for the FDO or one of the PDOs.
//
if (deviceExtension->Type == MPIO_MPDISK) {
//
// Call the Pdo-specific handler
//
status = MPIOPdoWmi(DeviceObject,
Irp);
} else {
//
// Call the Fdo-specific handler (MPIO Control).
//
status = MPIOFdoDispatch(DeviceObject,
Irp,
deviceExtension->State);
}
return status;
}
NTSTATUS
MPIOGlobalDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the global dispatch routine for all requests.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the IO request block.
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status;
//
// Determine whether this is for the FDO or one of the PDOs.
//
if (deviceExtension->Type == MPIO_MPDISK) {
//
// Call the Pdo-specific handler (MPDISK). The routines under this will do
// everything needed, so just return the status.
//
status = MPIOPdoDispatch(DeviceObject,
Irp,
deviceExtension->State);
} else {
//
// Call the Fdo-specific handler (MPIO Control).
//
status = MPIOFdoDispatch(DeviceObject,
Irp,
deviceExtension->State);
}
return status;
}
//
// Routines exported to the DSMs
//
VOID
DsmSendDeviceIoControlSynchronous(
IN ULONG IoControlCode,
IN PDEVICE_OBJECT TargetDeviceObject,
IN PVOID InputBuffer OPTIONAL,
IN PVOID OutputBuffer OPTIONAL,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength,
IN BOOLEAN InternalDeviceIoControl,
OUT PIO_STATUS_BLOCK IoStatus
)
{
MPLIBSendDeviceIoControlSynchronous(IoControlCode,
TargetDeviceObject,
InputBuffer,
OutputBuffer,
InputBufferLength,
OutputBufferLength,
InternalDeviceIoControl,
IoStatus);
}
PDSM_IDS
DsmGetAssociatedDevice(
IN PVOID MPIOContext,
IN PDEVICE_OBJECT PortFdo,
IN UCHAR DeviceType
)
/*++
Routine Description:
If the DSM needs to acquire information from other devices (such as a controller), this
routine can be used to get a list of the PDO's associated with PortFdo.
Arguments:
PortFdo - Port driver FDO passed to InquireDriver.
DeviceType - Indicates the SCSI DeviceType to return.
Return Value:
Pointer to a DSM_ID structure, where IdList entries are PDEVICE_OBJECT. It is the
reponsibility of the DSM to free the buffer.
--*/
{
PDEVICE_OBJECT deviceObject = MPIOContext;
PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension;
PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension;
PADP_ASSOCIATED_DEVICES filterDevices;
PDSM_IDS deviceList;
ULONG numberMatched = 0;
ULONG i;
ULONG j;
ULONG length;
PFLTR_ENTRY fltrEntry;
PLIST_ENTRY entry;
for (entry = controlExtension->FilterList.Flink;
entry != &controlExtension->FilterList;
entry = entry->Flink) {
fltrEntry = CONTAINING_RECORD(entry, FLTR_ENTRY, ListEntry);
if (fltrEntry->PortFdo == PortFdo) {
//
// Get the current list from the fdo filter.
//
filterDevices = fltrEntry->FltrGetDeviceList(fltrEntry->FilterObject);
if (filterDevices) {
for (i = 0; i < filterDevices->NumberDevices; i++) {
if (filterDevices->DeviceInfo[i].DeviceType == DeviceType) {
numberMatched++;
}
}
//
// Allocate the dsm list.
//
length = sizeof(DSM_IDS) + (sizeof(PDEVICE_OBJECT) * (numberMatched - 1));
deviceList = ExAllocatePool(NonPagedPool, length);
deviceList->Count = numberMatched;
for (j = 0, i = 0; i < filterDevices->NumberDevices; i++) {
if (filterDevices->DeviceInfo[i].DeviceType == DeviceType) {
deviceList->IdList[j] = filterDevices->DeviceInfo[i].DeviceObject;
j++;
}
}
} else {
deviceList = NULL;
}
ExFreePool(filterDevices);
return deviceList;
}
}
return NULL;
}
NTSTATUS
DsmReleaseQueue(
IN PDEVICE_OBJECT ChildDevice
)
{
return MPLibReleaseQueue(ChildDevice);
}
NTSTATUS
DsmSendTUR(
IN PDEVICE_OBJECT TargetDevice
)
{
return MPLibSendTUR(TargetDevice);
}
NTSTATUS
DsmSendPassThroughDirect(
IN PDEVICE_OBJECT DeviceObject,
IN PSCSI_PASS_THROUGH_DIRECT ScsiPassThrough,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
{
return MPLibSendPassThroughDirect(DeviceObject,
ScsiPassThrough,
InputBufferLength,
OutputBufferLength);
}
NTSTATUS
DsmGetDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN PSTORAGE_PROPERTY_ID PropertyId,
OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
)
{
return MPLIBGetDescriptor(DeviceObject,
PropertyId,
Descriptor);
}
VOID
DsmNotification(
IN PVOID MPIOContext,
IN DSM_NOTIFICATION_TYPE Type,
IN PVOID AdditionalParameter
)
{
return;
}
NTSTATUS
DsmWriteEvent(
IN PVOID MPIOContext,
IN PWCHAR ComponentName,
IN PWCHAR EventDescription,
IN ULONG Severity
)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject = MPIOContext;
status = MPIOFireEvent(deviceObject,
ComponentName,
EventDescription,
Severity);
return status;
}
VOID
DsmDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
{
MPathDebugPrint(DebugPrintLevel,
DebugMessage);
}