1535 lines
42 KiB
C
1535 lines
42 KiB
C
|
/*++
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
|