307 lines
8.7 KiB
C
307 lines
8.7 KiB
C
#include "mpio.h"
|
||
|
||
|
||
NTSTATUS
|
||
MPIOInitiateFailOver(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PVOID FailingPath,
|
||
IN ULONG ErrorMask
|
||
)
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
||
PDSM_ENTRY dsm = &diskExtension->DsmInfo;
|
||
PREAL_DEV_INFO targetInfo;
|
||
PVOID newPath = NULL;
|
||
NTSTATUS status;
|
||
WCHAR buffer[64];
|
||
|
||
|
||
MPDebugPrint((0,
|
||
"InitiateFailOver: Invalidating %x\n",
|
||
FailingPath));
|
||
//
|
||
// Initiate the fail-over.
|
||
//
|
||
status = dsm->InvalidatePath(dsm->DsmContext,
|
||
ErrorMask,
|
||
FailingPath,
|
||
&newPath);
|
||
|
||
MPDebugPrint((0,
|
||
"InitiateFailOver: New Path (%x)\n",
|
||
newPath));
|
||
|
||
//
|
||
// TODO - Put in real Path identiifiers.
|
||
//
|
||
swprintf(buffer,L"Path (%x) Failed Over to (%x)", FailingPath, newPath);
|
||
MPIOFireEvent(DeviceObject,
|
||
L"MPIO WrkerThrd",
|
||
buffer,
|
||
MPIO_WARNING);
|
||
|
||
//
|
||
// If successful, go into DEGRADED
|
||
//
|
||
if ((status == STATUS_SUCCESS) && newPath) {
|
||
|
||
//
|
||
// Ensure that newPath actually is found in one of the TargetInfo
|
||
// structures that make up this mpdisk.
|
||
//
|
||
targetInfo = MPIOGetTargetInfo(diskExtension,
|
||
newPath,
|
||
NULL);
|
||
|
||
if (targetInfo == NULL) {
|
||
MPDebugPrint((0,
|
||
"MPIOInitiateFailOver: No match for path (%x)\n",
|
||
newPath));
|
||
DbgBreakPoint();
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
ASSERT(newPath == targetInfo->PathId);
|
||
|
||
//
|
||
// Verify that this path/device combo. is ready.
|
||
//
|
||
status = dsm->PathVerify(dsm->DsmContext,
|
||
targetInfo->DsmID,
|
||
targetInfo->PathId);
|
||
if (!NT_SUCCESS(status)) {
|
||
MPDebugPrint((0,
|
||
"MPIOInitiateFailOver: PathVerify failed (%x)\n",
|
||
status));
|
||
//
|
||
// Log.
|
||
//
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Ensure the new path is active.
|
||
// Invalidate path should have done everything to assure that
|
||
// the new device(s) are ready.
|
||
//
|
||
targetInfo->PathActive = dsm->IsPathActive(dsm->DsmContext,
|
||
newPath);
|
||
ASSERT(targetInfo->PathActive);
|
||
|
||
//
|
||
// Set CurrentPath to this new one.
|
||
//
|
||
diskExtension->CurrentPath = newPath;
|
||
|
||
//
|
||
// Update all that are using this path, that it has changed.
|
||
//
|
||
MPIOSetNewPath(diskExtension->ControlObject,
|
||
newPath);
|
||
} else {
|
||
if (newPath == NULL) {
|
||
MPDebugPrint((0,
|
||
"MPIOInitiateFailOver: DSM returned NULL path\n"));
|
||
|
||
}
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
|
||
//
|
||
// LOG
|
||
//
|
||
MPDebugPrint((0,
|
||
"MPIOInitiateFailOver: DSM returned (%x)\n",
|
||
status));
|
||
}
|
||
|
||
//
|
||
// Set the path to NULL. The callback will key off this and the bad status.
|
||
//
|
||
diskExtension->CurrentPath = NULL;
|
||
DbgBreakPoint();
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
MPIORecoveryThread(
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the thread proc that is started upon creation of an mpdisk.
|
||
When signaled, it will initiate the fail-over, insure that the new path is
|
||
ready, then call-back the mpdisk to indicate success or failure of the operation.
|
||
|
||
Arguments:
|
||
|
||
Context - Info needed to handle the fail-over.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PMPIO_THREAD_CONTEXT context = Context;
|
||
PDEVICE_OBJECT deviceObject = context->DeviceObject;
|
||
PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension;
|
||
PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension;
|
||
PKEVENT event = context->Event;
|
||
PVOID failingPath;
|
||
MPIO_CALLBACK RequestComplete;
|
||
PMPIO_REQUEST_INFO requestInfo;
|
||
PLIST_ENTRY entry;
|
||
NTSTATUS status;
|
||
ULONG i;
|
||
|
||
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Wait on the event.
|
||
//
|
||
KeWaitForSingleObject(event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
KeClearEvent(event);
|
||
|
||
//
|
||
// Yank the packet.
|
||
//
|
||
entry = ExInterlockedRemoveHeadList(&diskExtension->WorkList,
|
||
&diskExtension->WorkListLock);
|
||
ASSERT(entry);
|
||
|
||
//
|
||
// Get the actual work item.
|
||
//
|
||
requestInfo = CONTAINING_RECORD(entry, MPIO_REQUEST_INFO, ListEntry);
|
||
|
||
//
|
||
// Get the call-back proc.
|
||
//
|
||
RequestComplete = requestInfo->RequestComplete;
|
||
|
||
//
|
||
// Determine the operation.
|
||
//
|
||
if (requestInfo->Operation == INITIATE_FAILOVER) {
|
||
|
||
//
|
||
// Get the device that indicated the failure.
|
||
//
|
||
failingPath = requestInfo->OperationSpecificInfo;
|
||
|
||
//
|
||
// Asking for a fail-over. Call the handler.
|
||
//
|
||
status = MPIOInitiateFailOver(deviceObject,
|
||
failingPath,
|
||
requestInfo->ErrorMask);
|
||
|
||
|
||
} else if (requestInfo->Operation == SHUTDOWN) {
|
||
|
||
//
|
||
// Being signaled to die.
|
||
//
|
||
ExFreePool(requestInfo);
|
||
goto terminateThread;
|
||
|
||
} else if (requestInfo->Operation == FORCE_RESCAN) {
|
||
PFLTR_ENTRY fltrEntry;
|
||
|
||
fltrEntry = (PFLTR_ENTRY)(requestInfo->OperationSpecificInfo);
|
||
MPIOForceRescan(fltrEntry->FilterObject);
|
||
|
||
//
|
||
// Indicate that the rescan is complete.
|
||
//
|
||
fltrEntry->Flags &= ~FLTR_FLAGS_RESCANNING;
|
||
|
||
} else if (requestInfo->Operation == PATH_REMOVAL) {
|
||
|
||
PDSM_ENTRY dsm = &diskExtension->DsmInfo;
|
||
|
||
MPDebugPrint((0,
|
||
"RecoveryThread: Removing Path (%x)\n",
|
||
requestInfo->OperationSpecificInfo));
|
||
|
||
//
|
||
// Call the dsm to clean up. All of the devices have been
|
||
// removed, and any pending actions have been completed, so
|
||
// it's safe to do this now.
|
||
//
|
||
dsm->RemovePath(dsm->DsmContext,
|
||
requestInfo->OperationSpecificInfo);
|
||
|
||
} else if (requestInfo->Operation == DEVICE_REMOVAL) {
|
||
|
||
PMPIO_DEVICE_REMOVAL deviceRemoval = requestInfo->OperationSpecificInfo;
|
||
|
||
MPDebugPrint((0,
|
||
"RecoveryThread: Removing Device (%x) DsmID (%x)\n",
|
||
deviceRemoval->TargetInfo,
|
||
deviceRemoval->TargetInfo->DsmID));
|
||
//
|
||
// Invoke the remove routine.
|
||
//
|
||
status = MPIORemoveDeviceAsync(deviceRemoval->DeviceObject,
|
||
deviceRemoval->TargetInfo);
|
||
|
||
//
|
||
// Free this allocation.
|
||
//
|
||
ExFreePool(deviceRemoval);
|
||
|
||
} else {
|
||
MPDebugPrint((0,
|
||
"MPIORecoveryThread: Invalid operation (%x) in (%x)\n",
|
||
requestInfo->Operation,
|
||
requestInfo));
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
if (RequestComplete) {
|
||
|
||
//
|
||
// Invoke the call-back routine to indicate
|
||
// completion.
|
||
//
|
||
RequestComplete(deviceObject,
|
||
requestInfo->Operation,
|
||
status);
|
||
|
||
if (requestInfo->Operation == INITIATE_FAILOVER) {
|
||
|
||
//
|
||
// Force a rescan on the disks adapters.
|
||
//
|
||
for (i = 0; i < diskExtension->TargetInfoCount; i++ ) {
|
||
MPIOForceRescan(diskExtension->TargetInfo[i].DevFilter);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Dispose of the work item.
|
||
//
|
||
ExFreePool(requestInfo);
|
||
|
||
}
|
||
|
||
terminateThread:
|
||
PsTerminateSystemThread(STATUS_SUCCESS);
|
||
|
||
}
|