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