#include "mpio.h" #include #include ULONG CheckState = 0; PDEVICE_OBJECT IoGetLowerDeviceObject( IN PDEVICE_OBJECT DeviceObject ); PREAL_DEV_INFO MPIOGetTargetInfo( IN PMPDISK_EXTENSION DiskExtension, IN PVOID PathId, IN PDEVICE_OBJECT Filter ) { PREAL_DEV_INFO targetInfo = DiskExtension->TargetInfo; ULONG i; MPDebugPrint((3, "MPIOGetTargetInfo: PathId(%x) Filter (%x) TargetInfo (%x)\n", PathId, Filter, targetInfo)); // // If PathId was passed in, the caller is looking for // the targetInfo match based on Path. // if (PathId) { // // Check each of the targetInfo structs for the // appropriate PathId. // for (i = 0; i < DiskExtension->TargetInfoCount; i++) { if (targetInfo->PathId == PathId) { return targetInfo; } // // Go to the next targetInfo. // targetInfo++; } } else if (Filter) { // // Looking for a DsmId match. // for (i = 0; i < DiskExtension->TargetInfoCount; i++) { if (targetInfo->AdapterFilter == Filter) { return targetInfo; } targetInfo++; } } else { ASSERT(PathId || Filter); } ASSERT(FALSE); // // PathId and DsmId were not found. // return NULL; } PDISK_ENTRY MPIOGetDiskEntry( IN PDEVICE_OBJECT DeviceObject, IN ULONG DiskIndex ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PLIST_ENTRY entry; ULONG i; // // Ensure that the Index is in range. // if ((DiskIndex + 1) > controlExtension->NumberDevices) { return NULL; } // // Run the list of MPDisk entries up to DiskIndex. // entry = controlExtension->DeviceList.Flink; for (i = 0; i < DiskIndex; entry = entry->Flink, i++) { #if DBG PDISK_ENTRY diskEntry; diskEntry = CONTAINING_RECORD(entry, DISK_ENTRY, ListEntry); ASSERT(diskEntry); MPDebugPrint((2, "MPIOGetDiskEntry: Index (%x) diskEntry (%x)\n", i, diskEntry)); #endif } // // Return the DISK_ENTRY // return CONTAINING_RECORD(entry, DISK_ENTRY, ListEntry); } BOOLEAN MPIOFindLowerDevice( IN PDEVICE_OBJECT MPDiskObject, IN PDEVICE_OBJECT LowerDevice ) { PDEVICE_EXTENSION deviceExtension = MPDiskObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; ULONG i; // // Search for LowerDevice in array of underlying PDO's. // for (i = 0; i < diskExtension->TargetInfoCount; i++) { if (diskExtension->TargetInfo[i].PortPdo == LowerDevice) { return TRUE; } } return FALSE; } PDSM_ENTRY MPIOGetDsm( IN PDEVICE_OBJECT DeviceObject, IN ULONG DsmIndex ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PLIST_ENTRY entry; ULONG i; // // See if requested index is in range. // if ((DsmIndex + 1) > controlExtension->NumberDSMs) { return NULL; } // // Get the first entry. // entry = controlExtension->DsmList.Flink; // // Run the list to DsmIndex. // for (i = 0; i < DsmIndex; entry = entry->Flink, i++) { #if DBG PDSM_ENTRY dsmEntry; dsmEntry = CONTAINING_RECORD(entry, DSM_ENTRY, ListEntry); ASSERT(dsmEntry); MPDebugPrint((2, "MPIOGetDsm: Index (%x) dsmEntry (%x)\n", i, dsmEntry)); #endif } // // Return the entry. // return CONTAINING_RECORD(entry, DSM_ENTRY, ListEntry); } PCONTROLLER_ENTRY MPIOFindController( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT ControllerObject, IN ULONGLONG Id ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PCONTROLLER_ENTRY controllerEntry; PLIST_ENTRY entry; // // Run the list of controller entries looking // for (entry = controlExtension->ControllerList.Flink; entry != &controlExtension->ControllerList; entry = entry->Flink) { controllerEntry = CONTAINING_RECORD(entry, CONTROLLER_ENTRY, ListEntry); if ((controllerEntry->ControllerInfo->ControllerIdentifier == Id) && (controllerEntry->ControllerInfo->DeviceObject == ControllerObject)){ return controllerEntry; } } return NULL; } PFLTR_ENTRY MPIOGetFltrEntry( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT PortFdo, IN PDEVICE_OBJECT AdapterFilter ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; 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 (PortFdo) { if (fltrEntry->PortFdo == PortFdo) { return fltrEntry; } } else if (AdapterFilter) { if (fltrEntry->FilterObject == AdapterFilter) { return fltrEntry; } } } return NULL; } ULONG MPIOGetPathCount( IN PDEVICE_OBJECT DeviceObject, IN PVOID PathId ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PDEVICE_EXTENSION mpdiskExtension; PMPDISK_EXTENSION diskExtension; PREAL_DEV_INFO deviceInfo; PDEVICE_OBJECT diskObject; PDISK_ENTRY diskEntry; ULONG deviceCount = 0; ULONG i; ULONG j; // // Get each mpdisk in turn. // for (i = 0; i < controlExtension->NumberDevices; i++) { diskEntry = MPIOGetDiskEntry(DeviceObject, i); diskObject = diskEntry->PdoObject; mpdiskExtension = diskObject->DeviceExtension; diskExtension = mpdiskExtension->TypeExtension; deviceInfo = diskExtension->TargetInfo; // // Find the path on this disk. // for (j = 0; j < diskExtension->TargetInfoCount; j++) { if (deviceInfo->PathId == PathId) { // // Found it, bump the total. // deviceCount++; } // // Go to the next deviceInfo. // deviceInfo++; } } MPDebugPrint((1, "MPIOGetPathCount: %u devices on Path (%x)\n", deviceCount, PathId)); return deviceCount; } NTSTATUS MPIOAddSingleDevice( IN PDEVICE_OBJECT ControlObject, IN PADP_DEVICE_INFO DeviceInfo, IN PDEVICE_OBJECT PortObject, IN PDEVICE_OBJECT FilterObject ) { PDSM_ENTRY entry; ULONG i; PVOID dsmExtension; NTSTATUS status = STATUS_SUCCESS; BOOLEAN claimed = FALSE; // // Run through each of the registered DSMs. The DO and // associated info will be passed to each, where they have // the opportunity to claim ownership. // i = 0; do { // // Get the next DSM entry. // entry = MPIOGetDsm(ControlObject, i); if (entry) { // // See if the DSM wants this device. // status = entry->InquireDriver(entry->DsmContext, DeviceInfo->DeviceObject, PortObject, DeviceInfo->DeviceDescriptor, DeviceInfo->DeviceIdList, &dsmExtension); if (status == STATUS_SUCCESS) { // // Ensure the DSM returned something. // ASSERT(dsmExtension); // // The DSM has indicated that it wants control of this device. // claimed = TRUE; // // Get more DSM info and handle setting up the MPDisk // status = MPIOHandleNewDevice(ControlObject, FilterObject, PortObject, DeviceInfo, entry, dsmExtension); if (!NT_SUCCESS(status)) { // // LOG an error. TODO. // claimed = FALSE; } } } i++; } while ((claimed == FALSE) && entry); return status; } NTSTATUS MPIOHandleDeviceArrivals( IN PDEVICE_OBJECT DeviceObject, IN PADP_DEVICE_LIST DeviceList, IN PDEVICE_RELATIONS CachedRelations, IN PDEVICE_RELATIONS Relations, IN PDEVICE_OBJECT PortObject, IN PDEVICE_OBJECT FilterObject, IN BOOLEAN NewList ) { NTSTATUS status; ULONG devicesAdded = 0; ULONG i; ULONG j; ULONG k; BOOLEAN matched = FALSE; ASSERT(DeviceList->NumberDevices == Relations->Count); // // The list in Relations and DeviceList contain the same objects // at this point. CachedRelations contains the state prior to this call. // if (NewList == FALSE) { // // Compare the two relations structs to find the added devices. // for (i = 0; i < Relations->Count; i++) { for (j = 0; j < CachedRelations->Count; j++) { if (Relations->Objects[i] == CachedRelations->Objects[j]) { matched = TRUE; break; } } if (matched == FALSE) { // // Find it in the DeviceList. // for (k = 0; k < DeviceList->NumberDevices; k++) { if (Relations->Objects[i] == DeviceList->DeviceList[k].DeviceObject) { // // Add this one. // status = MPIOAddSingleDevice(DeviceObject, &DeviceList->DeviceList[k], PortObject, FilterObject); devicesAdded++; break; } } } else { matched = FALSE; } } } else { // // All devices need to be added. // // for (i = 0; i < Relations->Count; i++) { // // Add this one. // status = MPIOAddSingleDevice(DeviceObject, &DeviceList->DeviceList[i], PortObject, FilterObject); devicesAdded++; } } MPDebugPrint((1, "HandleDeviceArrivals: Added (%u)\n", devicesAdded)); return STATUS_SUCCESS; } PDEVICE_RELATIONS MPIOHandleDeviceRemovals( IN PDEVICE_OBJECT DeviceObject, IN PADP_DEVICE_LIST DeviceList, IN PDEVICE_RELATIONS Relations ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PDEVICE_RELATIONS newRelations; ULONG i; ULONG j; ULONG k; ULONG devicesRemoved = 0; BOOLEAN matched = FALSE; BOOLEAN removed = FALSE; PDISK_ENTRY diskEntry; NTSTATUS status; // // For each device in the device relations (the current list), try to // find it in the DeviceList (the new list) // for (i = 0; i < Relations->Count; i++) { for (j = 0; j < DeviceList->NumberDevices; j++) { if (Relations->Objects[i] == DeviceList->DeviceList[j].DeviceObject) { matched = TRUE; MPDebugPrint((1, "HandleDeviceRemoval: Found (%x)\n", Relations->Objects[i])); break; } } if (matched == FALSE) { // // Remove Relations->Objects[i]. // MPDebugPrint((1, "HandleDeviceRemoval: Removing (%x)\n", Relations->Objects[i])); // // Find the correct mpdisk object. // for (k = 0; k < controlExtension->NumberDevices; k++) { diskEntry = MPIOGetDiskEntry(DeviceObject, k); if (MPIOFindLowerDevice(diskEntry->PdoObject, Relations->Objects[i])) { status = MPIORemoveSingleDevice(diskEntry->PdoObject, Relations->Objects[i]); if (status == STATUS_PENDING) { // // This indicates that the device has outstanding IO's. // It will be removed once these complete. // continue; } devicesRemoved++; removed = TRUE; } } if ((removed == FALSE) && (status != STATUS_PENDING)) { MPDebugPrint((0,"HandleDeviceRemoval: Device marked for removal wasn't (%x)\n", Relations->Objects[i])); ASSERT(removed); } } matched = FALSE; removed = FALSE; } MPDebugPrint((0, "HandleDeviceRemoval: Removed (%u) devices\n", devicesRemoved)); newRelations = MPIOBuildRelations(DeviceList); return newRelations; } NTSTATUS MPIORemoveDeviceEntry( IN PDEVICE_OBJECT DeviceObject, IN PREAL_DEV_INFO TargetInfo ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PVOID pathId; ULONG count; ULONG moveCount; ULONG newMask; ULONG i; ULONG k; NTSTATUS status; MPDebugPrint((0, "RemoveDeviceEntry: Removing %x from %x\n", TargetInfo, DeviceObject)); // // The caller should be holding the config spinlock. // // // Determine the array entry for this deviceInfo. // count = diskExtension->TargetInfoCount; for (i = 0; i < count; i++) { MPDebugPrint((1, "RemoveDeviceEntry: Checking %x vs. %x\n", diskExtension->TargetInfo[i].PortPdo, TargetInfo->PortPdo)); if (diskExtension->TargetInfo[i].PortPdo == TargetInfo->PortPdo) { diskExtension->DeviceMap &= ~ (1 << i); // // Move only those AFTER the removed entry. // moveCount = count - i; moveCount -= 1; // // Collapse the targetInfo array. // RtlMoveMemory(&diskExtension->TargetInfo[i], &diskExtension->TargetInfo[i+1], (moveCount * sizeof(REAL_DEV_INFO))); // // Indicate that there is one less entry. // diskExtension->TargetInfoCount--; // // Update the device map to reflect the new state of the world. // for (k = 0, newMask = 0; k < diskExtension->TargetInfoCount; k++) { newMask |= (1 << k); } MPDebugPrint((1, "RemoveDeviceEntry: Old Mask (%x) new mask (%x)\n", diskExtension->DeviceMap, newMask)); InterlockedExchange(&diskExtension->DeviceMap, newMask); // // Zero out the last vacated entry. // RtlZeroMemory(&diskExtension->TargetInfo[count - 1], sizeof(REAL_DEV_INFO)); break; } } return STATUS_SUCCESS; } NTSTATUS MPIORemoveSingleDevice( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT Pdo ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PREAL_DEV_INFO deviceInfo; PDEVICE_OBJECT diskObject; PDSM_ENTRY dsm; PVOID pathId = NULL; ULONG i; ULONG k; ULONG count; ULONG moveCount; ULONG removedIndex = (ULONG)-1; ULONG newMask; ULONG remainingDevices; NTSTATUS status; KIRQL irql; BOOLEAN matched = FALSE; dsm = &diskExtension->DsmInfo; // // Grab this disk's spinlock. The submit path does the same. // KeAcquireSpinLock(&diskExtension->SpinLock, &irql); // // Find the corresponding targetInfo. // count = diskExtension->TargetInfoCount; deviceInfo = diskExtension->TargetInfo; for (i = 0; i < count; i++) { // // If this deviceInfo has Pdo, then break. // if (deviceInfo->PortPdo == Pdo) { matched = TRUE; break; } else { deviceInfo++; } } ASSERT(matched == TRUE); if ((deviceInfo == NULL) || (matched == FALSE)) { MPDebugPrint((0, "RemoveSingleDevice: Device not found\n")); // // For some reason, the device has already been removed. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); return STATUS_DEVICE_NOT_CONNECTED; } MPDebugPrint((0, "RemoveSingleDevice: Removing %x from %x. DsmID %x\n", deviceInfo, DeviceObject, deviceInfo->DsmID)); // // Tell the DSM that this device is about to go away. // status = dsm->RemovePending(dsm->DsmContext, deviceInfo->DsmID); // // Collapse our DsmId list so that we are in sync with the dsm. // Do this even though we may not remove the targetInfo entry. // count = diskExtension->DsmIdList.Count; for (i = 0; i < count; i++) { if (diskExtension->DsmIdList.IdList[i] == deviceInfo->DsmID) { // // Set the index. This is used if we can actually remove the device from // our and the DSM's lists. // removedIndex = i; // // One less DsmId in the list. // diskExtension->DsmIdList.Count--; // // Determine the number of entries to move in order to collapse // all the entries after this one. // moveCount = count - i; moveCount -= 1; // // Collapse the array. // RtlMoveMemory(&diskExtension->DsmIdList.IdList[i], &diskExtension->DsmIdList.IdList[i + 1], (moveCount * sizeof(PVOID))); diskExtension->DsmIdList.IdList[count - 1] = NULL; break; } } // // The DSM ID has to have been in the list. // ASSERT(removedIndex != (ULONG)-1); // // If there are any outstanding IO's, then we can't remove this yet. // if (deviceInfo->Requests) { MPDebugPrint((0, "RemoveSingleDevice: Pending removal for DeviceInfo (%x). DsmID (%x)\n", deviceInfo, deviceInfo->DsmID)); // // The completion path will check this if outstanding requests go to zero and // handle the removal there. // deviceInfo->NeedsRemoval = TRUE; KeReleaseSpinLock(&diskExtension->SpinLock, irql); return STATUS_PENDING; } MPDebugPrint((1, "RemoveSingleDevice: Removing (%x). DeviceInfo (%x). DsmID (%x)\n", Pdo, deviceInfo, deviceInfo->DsmID)); // // Call the DSM to remove this DsmID. // dsm = &diskExtension->DsmInfo; status = dsm->RemoveDevice(dsm->DsmContext, deviceInfo->DsmID, deviceInfo->PathId); if (!NT_SUCCESS(status)) { // // LOG // } // // Save off the pathId. // pathId = deviceInfo->PathId; // // Remove the deviceInfo element. // status = MPIORemoveDeviceEntry(DeviceObject, deviceInfo); // // Release the config spinlock. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); // // Determine whether pathId needs to be removed. // remainingDevices = MPIOGetPathCount(diskExtension->ControlObject, pathId); if (remainingDevices == 0) { // // Can't remove the path if we aren't in a steady-state condition. // If not normal or degraded, mark the remove as pending. // queue a work-item and the state transition logic will fire the remove // when it's OK. // //if ((deviceExtension->State == MPIO_STATE_NORMAL) || // (deviceExtension->State == MPIO_STATE_DEGRADED)) { if (FALSE) { MPDebugPrint((0, "RemoveSingleDevice: Removing path (%x). Disk (%x) in State (%x)\n", pathId, DeviceObject, deviceExtension->State)); // // All devices on this path have been removed, // Go ahead and remove the path. // dsm->RemovePath(dsm->DsmContext, pathId); } else { PMPIO_REQUEST_INFO requestInfo; MPDebugPrint((0, "RemoveSingle: Queuing removal of path (%x). Disk (%x) in State (%x)\n", pathId, DeviceObject, deviceExtension->State)); // // Allocate a work item // Fill it in to indicate a path removal. // requestInfo = ExAllocatePool(NonPagedPool, sizeof(MPIO_REQUEST_INFO)); requestInfo->RequestComplete = NULL; requestInfo->Operation = PATH_REMOVAL; // // Set the Path that should be removed. // requestInfo->OperationSpecificInfo = pathId; requestInfo->ErrorMask = 0; ExInterlockedInsertTailList(&diskExtension->PendingWorkList, &requestInfo->ListEntry, &diskExtension->WorkListLock); // // Indicate that an item is in the pending list. // diskExtension->PendingItems++; } } return STATUS_SUCCESS; } NTSTATUS MPIOHandleRemoveAsync( IN PDEVICE_OBJECT DeviceObject, IN PREAL_DEV_INFO TargetInfo ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PMPIO_REQUEST_INFO requestInfo; PMPIO_DEVICE_REMOVAL deviceRemoval; // // Allocate the request packet and the removal info struct. // requestInfo = ExAllocatePool(NonPagedPool, sizeof(MPIO_REQUEST_INFO)); deviceRemoval = ExAllocatePool(NonPagedPool, sizeof(MPIO_DEVICE_REMOVAL)); // // No completion callback necessary. // requestInfo->RequestComplete = NULL; // // Indicate that this is a remove operation. // requestInfo->Operation = DEVICE_REMOVAL; // // Setup the removal info. // deviceRemoval->DeviceObject = DeviceObject; deviceRemoval->TargetInfo = TargetInfo; // // Set the removal info as SpecificInfo // requestInfo->OperationSpecificInfo = deviceRemoval; requestInfo->ErrorMask = 0; // // Queue it to the pending work list. The tick handler will put this // on the thread's work queue. // ExInterlockedInsertTailList(&diskExtension->PendingWorkList, &requestInfo->ListEntry, &diskExtension->WorkListLock); // // Tell the tick handler that there is work to do. // diskExtension->PendingItems++; return STATUS_SUCCESS; } NTSTATUS MPIORemoveDeviceAsync( IN PDEVICE_OBJECT DeviceObject, IN PREAL_DEV_INFO TargetInfo ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PDSM_ENTRY dsm; KIRQL irql; NTSTATUS status; PVOID dsmId; PVOID pathId; ULONG remainingDevices; // // BUGBUG: Need to document fully that RemoveDevice will be called // at DPC. If there is push-back from IHV's, then this will need to // be put on a thread. // // // Capture the dsm and path ID's as the call to RemoveDeviceEntry // will invalidate the TargetInfo structure. // dsmId = TargetInfo->DsmID; pathId = TargetInfo->PathId; KeAcquireSpinLock(&diskExtension->SpinLock, &irql); // // Remove our info structure. // MPIORemoveDeviceEntry(DeviceObject, TargetInfo); KeReleaseSpinLock(&diskExtension->SpinLock, irql); // // Call the DSM to remove this DsmID. // dsm = &diskExtension->DsmInfo; status = dsm->RemoveDevice(dsm->DsmContext, dsmId, pathId); // // Determine whether pathId needs to be removed. // remainingDevices = MPIOGetPathCount(diskExtension->ControlObject, pathId); if (remainingDevices == 0) { PMPIO_REQUEST_INFO requestInfo; MPDebugPrint((0, "RemoveSingleAsync: Queuing removal of path (%x). Disk (%x) in State (%x)\n", pathId, DeviceObject, deviceExtension->State)); // // Allocate a work item // Fill it in to indicate a path removal. // requestInfo = ExAllocatePool(NonPagedPool, sizeof(MPIO_REQUEST_INFO)); requestInfo->RequestComplete = NULL; requestInfo->Operation = PATH_REMOVAL; // // Set the Path that should be removed. // requestInfo->OperationSpecificInfo = pathId; requestInfo->ErrorMask = 0; ExInterlockedInsertTailList(&diskExtension->PendingWorkList, &requestInfo->ListEntry, &diskExtension->WorkListLock); // // Indicate that an item is in the pending list. // diskExtension->PendingItems++; #if 0 // // All devices on this path have been removed, // Go ahead and remove the path. // dsm->RemovePath(dsm->DsmContext, pathId); #endif } return status; } NTSTATUS MPIOHandleNewDevice( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT FilterObject, IN PDEVICE_OBJECT PortObject, IN PADP_DEVICE_INFO DeviceInfo, IN PDSM_ENTRY DsmEntry, IN PVOID DsmExtension ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PDEVICE_OBJECT diskObject; PDISK_ENTRY diskEntry; NTSTATUS status; ULONG i; BOOLEAN matched = FALSE; // // Run the list of MPDisk PDO's to see whether the new // device corresponds to an already existing one. // for (i = 0; i < controlExtension->NumberDevices; i++) { // // Get the next disk entry. // diskEntry = MPIOGetDiskEntry(DeviceObject, i); if (diskEntry) { // // Feed the MPDisk object to this routine, which will call // the DSMCompareRoutine to see if an MP state is present. // if (MPIOFindMatchingDevice(diskEntry->PdoObject, DeviceInfo, DsmEntry, DsmExtension)) { matched = TRUE; // // Got a match. Update the MPDisk. // PdoObject IS the mpdisk D.O. // status = MPIOUpdateDevice(diskEntry->PdoObject, FilterObject, PortObject, DeviceInfo, DsmExtension); return status; } } else { MPDebugPrint((1, "MPIOHandleNew: Some error.\n")); DbgBreakPoint(); // // This certainly shouldn't happen, unless the internal // state is hosed. // // Log Error. TODO // status = STATUS_UNSUCCESSFUL; } } if (matched == FALSE) { // // Didn't match up any. Build a new PDO. // status = MPIOCreateDevice(DeviceObject, FilterObject, PortObject, DeviceInfo, DsmEntry, DsmExtension, &diskObject); if (status == STATUS_SUCCESS) { // // Indicate that one more multi-path disk // has been created. // controlExtension->NumberDevices++; // // Build a disk entry // diskEntry = ExAllocatePool(NonPagedPool, sizeof(DISK_ENTRY)); ASSERT(diskEntry); RtlZeroMemory(diskEntry, sizeof(DISK_ENTRY)); // // Set the new mp disk's DO. // diskEntry->PdoObject = diskObject; // // Add it to the list of mpdisks // ExInterlockedInsertTailList(&controlExtension->DeviceList, &diskEntry->ListEntry, &controlExtension->SpinLock); IoInvalidateDeviceRelations(deviceExtension->Pdo, BusRelations); } } else { MPDebugPrint((2, "MPIOHandleNew: Not creating a disk.\n")); } return status; } NTSTATUS MPIOForwardRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine sends the Irp to the next driver in line when the Irp is not processed by this driver. Arguments: DeviceObject Irp Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(deviceExtension->LowerDevice, Irp); } NTSTATUS MPIOSyncCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: General-purpose completion routine, used for handling 'sync' requests such as PnP. Arguments: DeviceObject Irp Context - The event on which the caller is waiting. Return Value: NTSTATUS --*/ { PKEVENT event = Context; MPDebugPrint((2, "MPIOSyncCompletion: Irp %x\n", Irp)); if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } KeSetEvent(event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS MPIOGetScsiAddress( IN PDEVICE_OBJECT DeviceObject, OUT PSCSI_ADDRESS *ScsiAddress ) /*++ Routine Description: This routine issues the appropriate IOCTL to get the scsi address of DeviceObject. The storage allocated becomes the responsibility of the caller. Arguments: DeviceObject - Device Object for a scsiport pdo returned in QDR. ScsiAddress - pointer for the scsi address buffer. Return Value: Status of the request --*/ { PSCSI_ADDRESS scsiAddress; PIO_STATUS_BLOCK ioStatus; PIRP irp; NTSTATUS status; // // Initialize the return values. This routine will only return success or // insufficient resources. // *ScsiAddress = NULL; status = STATUS_INSUFFICIENT_RESOURCES; // // Allocate storage. It's the caller's responsibility to free // it. // scsiAddress = ExAllocatePool(NonPagedPool, sizeof(SCSI_ADDRESS)); if (scsiAddress) { // // Don't use a stack variable, as we could switch to a different // thread. // ioStatus = ExAllocatePool(NonPagedPool, sizeof(IO_STATUS_BLOCK)); // // Send the request // MPLIBSendDeviceIoControlSynchronous(IOCTL_SCSI_GET_ADDRESS, DeviceObject, NULL, scsiAddress, 0, sizeof(SCSI_ADDRESS), FALSE, ioStatus); status = ioStatus->Status; if (status == STATUS_SUCCESS) { // // Update the caller's pointer with the returned // information. // *ScsiAddress = scsiAddress; } } ExFreePool(ioStatus); return status; } PMPIO_CONTEXT MPIOAllocateContext( IN PDEVICE_EXTENSION DeviceExtension ) { PMPIO_CONTEXT context; ULONG freeIndex; ULONG i; KIRQL irql; // // Try to get the context structure from the list. // context = ExAllocateFromNPagedLookasideList(&DeviceExtension->ContextList); if (context == NULL) { // // No/Low memory condition. Use one of the emergency buffers. // KeAcquireSpinLock(&DeviceExtension->EmergencySpinLock, &irql); // // Find a clear bit. Never use '0', as the check in the FreeContext // routine keys off the value of EmergencyIndex; // for (i = 1; i < MAX_EMERGENCY_CONTEXT; i++) { if (!(DeviceExtension->EmergencyContextMap & (1 << i))) { // // Set the bit to indicate that this buffer is being used. // DeviceExtension->EmergencyContextMap |= (1 << i); freeIndex = i; break; } } if (i == MAX_EMERGENCY_CONTEXT) { // // LOG something. // } else { // // Pull one from the reserved buffer. // context = DeviceExtension->EmergencyContext[freeIndex]; context->EmergencyIndex = freeIndex; } KeReleaseSpinLock(&DeviceExtension->EmergencySpinLock, irql); } else { // // Indicate that this came from the Lookaside List. // context->EmergencyIndex = 0; } return context; } VOID MPIOFreeContext( IN PDEVICE_EXTENSION DeviceExtension, IN PMPIO_CONTEXT Context ) { KIRQL irql; if (Context->Freed) { MPDebugPrint((0, "FreeContext: Trying to free already freed context (%x)\n", Context)); DbgBreakPoint(); } Context->Freed = TRUE; // // Determine is this came from the emergency list or from the lookaside list // if (Context->EmergencyIndex == 0) { ExFreeToNPagedLookasideList(&DeviceExtension->ContextList, Context); } else { KeAcquireSpinLock(&DeviceExtension->EmergencySpinLock, &irql); // // Indicate that the buffer is now available. // DeviceExtension->EmergencyContextMap &= ~(1 << Context->EmergencyIndex); KeReleaseSpinLock(&DeviceExtension->EmergencySpinLock, irql); } return; } VOID MPIOCopyMemory( IN PVOID Destination, IN PVOID Source, IN ULONG Length ) { MPDebugPrint((0, "MPIOCopyMemory: Dest %x, Src %x, Length %x\n", Destination, Source, Length)); RtlCopyMemory(Destination, Source, Length); } NTSTATUS MPIOQueueRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PMPIO_CONTEXT Context, IN PMP_QUEUE Queue ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb; PMPQUEUE_ENTRY queueEntry; NTSTATUS status; KIRQL irql; // // Get a queue entry to package the request. // BUGBUG: Use LookAsideList, backed by pre-allocated entries. // queueEntry = ExAllocatePool(NonPagedPool, sizeof(MPQUEUE_ENTRY)); if (queueEntry == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(queueEntry, sizeof(MPQUEUE_ENTRY)); // // If this was a scsi request (vs. DeviceIoControl), the srb // was saved in the context. // if (irpStack->MajorFunction == IRP_MJ_SCSI) { // // Rebuild the srb using the saved request in the context. // srb = irpStack->Parameters.Scsi.Srb; RtlCopyMemory(srb, &Context->Srb, sizeof(SCSI_REQUEST_BLOCK)); } // // Indicate the Queue. // Context->QueuedInto = Queue->QueueIndicator; Irp->IoStatus.Status = 0; Irp->IoStatus.Information = 0; // // Save the irp address. // queueEntry->Irp = Irp; // // Jam onto the queue. // MPIOInsertQueue(Queue, queueEntry); return STATUS_SUCCESS; } PMPIO_FAILOVER_INFO MPIODequeueFailPacket( IN PDEVICE_OBJECT DeviceObject, IN PVOID PathId ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PMPIO_FAILOVER_INFO failPacket = NULL; PLIST_ENTRY entry; KIRQL irql; KeAcquireSpinLock(&controlExtension->SpinLock, &irql); for (entry = controlExtension->FailPacketList.Flink; entry != &controlExtension->FailPacketList; entry = entry->Flink) { failPacket = CONTAINING_RECORD(entry, MPIO_FAILOVER_INFO, ListEntry); if (failPacket->PathId == PathId) { break; } else { failPacket = NULL; } } if (failPacket) { // // Yank it out of the queue. // RemoveEntryList(entry); // // Dec the number of entries. // InterlockedDecrement(&controlExtension->NumberFOPackets); } KeReleaseSpinLock(&controlExtension->SpinLock, irql); MPDebugPrint((1, "DequeueFailPacket: Returning (%x). CurrentCount (%x)\n", failPacket, controlExtension->NumberFOPackets)); return failPacket; } PVOID CurrentFailurePath = NULL; VOID MPIOFailOverCallBack( IN PDEVICE_OBJECT DeviceObject, IN ULONG Operation, IN NTSTATUS Status ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PREAL_DEV_INFO targetInfo; NTSTATUS status; PDEVICE_OBJECT failDevice; PDEVICE_EXTENSION failDevExt; PMPDISK_EXTENSION failDiskExt; PMPIO_FAILOVER_INFO failPacket; ULONG state; ULONG numberPackets = 0; PLIST_ENTRY listEntry; WCHAR componentName[32]; // // The thread successfully handled the fail-over and we are in WAIT1 // or it was unsuccessfull and we are still IN_FO. // TODO: Handle failure case. // ASSERT(Status == STATUS_SUCCESS); ASSERT(Operation == INITIATE_FAILOVER); ASSERT(diskExtension->CurrentPath); MPDebugPrint((0, "FailOverCallBack: Fail-Over on (%x) complete.\n", DeviceObject)); // // TODO remove hack. // CurrentFailurePath = NULL; // // Check to see whether the F.O. was successful. // if ((Status != STATUS_SUCCESS) || (diskExtension->CurrentPath == NULL)) { // // Need to flush all of the queues and update State to STATE_FULL_FAILURE. // TODO BUGBUG. // MPDebugPrint((0, "FailOverCallBack: Status (%x). Path (%x)\n", Status, diskExtension->CurrentPath)); DbgBreakPoint(); } // // Run through the failOver list and extract all entries that match "PathId" // // For each one, run the code below. // do { failPacket = MPIODequeueFailPacket(diskExtension->ControlObject, diskExtension->FailedPath); if (failPacket) { failDevice = failPacket->DeviceObject; failDevExt = failDevice->DeviceExtension; failDiskExt = failDevExt->TypeExtension; numberPackets++; state = MPIOHandleStateTransition(failDevice); MPDebugPrint((0, "FailOverCallBack: Handling (%x). CurrentState (%x)\n", failDevice, state)); // // Get the target based on the new path that the fail-over handler // setup. // targetInfo = MPIOGetTargetInfo(failDiskExt, failDiskExt->CurrentPath, NULL); if (state == MPIO_STATE_WAIT2) { // // Queue a request for the timer to run rescans after the fail-back // period expires. // failDiskExt->RescanCount = 15; // // Issue all requests down the new path. // status = MPIOIssueQueuedRequests(targetInfo, &failDiskExt->ResubmitQueue, MPIO_STATE_WAIT2, &failDiskExt->OutstandingRequests); } else if (state == MPIO_STATE_WAIT3) { // // Issue all requests down the new path. // status = MPIOIssueQueuedRequests(targetInfo, &failDiskExt->FailOverQueue, MPIO_STATE_WAIT3, &failDiskExt->OutstandingRequests); } else if (state == MPIO_STATE_DEGRADED) { status = STATUS_SUCCESS; } else { ASSERT(state == MPIO_STATE_WAIT1); ASSERT(failDiskExt->OutstandingRequests); status = STATUS_SUCCESS; } ASSERT((status == STATUS_SUCCESS) || (status == STATUS_PENDING)); swprintf(componentName, L"MPIO Disk(%02d)", failDiskExt->DeviceOrdinal); status = MPIOFireEvent(failDevice, componentName, L"Fail-Over Complete", MPIO_INFORMATION); ExFreePool(failPacket); } } while (failPacket); if (numberPackets == 0) { MPDebugPrint((0, "FailoverCallBack: No fail-over packets for %x\n", diskExtension->FailedPath)); ASSERT(FALSE); } #if 0 // // Check for any pending work items to start. // if (failDiskExt->PendingItems) { // // Yank the packet from the pending list... // listEntry = ExInterlockedRemoveHeadList(&diskExtension->PendingWorkList, &diskExtension->WorkListLock); // // ...and jam it on the work list. // ExInterlockedInsertTailList(&failDiskExt->WorkList, listEntry, &failDiskExt->WorkListLock); InterlockedDecrement(&failDiskExt->PendingItems); // // Signal the thread to initiate the F.O. // KeSetEvent(&diskExtension->ThreadEvent, 8, FALSE); } #endif return; } BOOLEAN MPIOPathInFailOver( IN PVOID PathId, IN BOOLEAN Query ) { // // REDO THIS HACK. TODO. // if (Query) { return (PathId == CurrentFailurePath); } else { CurrentFailurePath = PathId; } return TRUE; } NTSTATUS MPIOQueueFailPacket( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT FailingDevice, IN PVOID PathId ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PMPIO_FAILOVER_INFO failPacket; // // Allocate a fail-over packet. // failPacket = ExAllocatePool(NonPagedPool, sizeof(MPIO_FAILOVER_INFO)); if (failPacket == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Set the path, the d.o. // failPacket->DeviceObject = FailingDevice; failPacket->PathId = PathId; // // Inc the number of entries. // InterlockedIncrement(&controlExtension->NumberFOPackets); // // Put it on the list. // ExInterlockedInsertTailList(&controlExtension->FailPacketList, &failPacket->ListEntry, &controlExtension->SpinLock); return STATUS_SUCCESS; } NTSTATUS MPIOFailOverHandler( IN PDEVICE_OBJECT DeviceObject, IN ULONG ErrorMask, IN PREAL_DEV_INFO FailingDevice ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PMPIO_REQUEST_INFO requestInfo; NTSTATUS status; ULONG i; PFLTR_ENTRY fltrEntry; WCHAR componentName[32]; ASSERT(FailingDevice->PathFailed == FALSE); // // Mark it as failed. TODO - Ensure if it's // brought back alive to update that. // FailingDevice->PathFailed = TRUE; diskExtension->FailedPath = FailingDevice->PathId; // // Check to see whether there is only one path. // It's not necessarily a bad thing, the DSM could be controlling // hardware that hides the alternate path until a fail-over. // BUT, this is currently not supported. // // Alternatively, one or more devices could have already been removed. // In this case, allow the FailOver to happen. // TODO: Figure out how to best handle this. Perhaps always allow a single path // fail-over and handle the error on the other side... // if (diskExtension->DsmIdList.Count <= 1) { MPDebugPrint((0, "MPIOFailOverHandler: Initiating F.O. on single path\n")); // // LOG // } // // Put this on the fail-over list. All of these devices will get notified // once the first F.O. completes. They will then start running the state-machine // to get any queued requests issued. // status = MPIOQueueFailPacket(diskExtension->ControlObject, DeviceObject, FailingDevice->PathId); if (!NT_SUCCESS(status)) { // // LOG the failure. // return status; } // // If the fail-over on this path has already been started, // don't do it again. There is a window where state is set by the first // failing device, but this one is in the completion routine. // if (MPIOPathInFailOver(FailingDevice->PathId, 1)) { return STATUS_SUCCESS; } MPDebugPrint((0, "MPIOFailOverHandler: Failing Device (%x) on (%x). Failing Path (%x)\n", FailingDevice, DeviceObject, FailingDevice->PathId)); // // Set this as the current failing path. // MPIOPathInFailOver(FailingDevice->PathId, 0); // // Get all of the fltrEntry's for this disk. // for (i = 0; i < diskExtension->TargetInfoCount; i++) { fltrEntry = MPIOGetFltrEntry(diskExtension->ControlObject, diskExtension->TargetInfo[i].PortFdo, NULL); if (fltrEntry) { fltrEntry->Flags |= FLTR_FLAGS_NEED_RESCAN; fltrEntry->Flags &= ~(FLTR_FLAGS_QDR_COMPLETE | FLTR_FLAGS_QDR); } } // // Log the failure. TODO: Build a request and submit to thread. // #if 0 swprintf(componentName, L"MPIO Disk(%02d)", diskExtension->DeviceOrdinal); status = MPIOFireEvent(DeviceObject, componentName, L"Fail-Over Initiated", MPIO_FATAL_ERROR); MPDebugPrint((0, "MPIOFailOver: Failing Device (%x) Path (%x) Status of Event (%x)\n", FailingDevice, FailingDevice->PathId, status)); #endif // // Allocate a work item // Fill it in to indicate a fail-over. // requestInfo = ExAllocatePool(NonPagedPool, sizeof(MPIO_REQUEST_INFO)); requestInfo->RequestComplete = MPIOFailOverCallBack; requestInfo->Operation = INITIATE_FAILOVER; // // Set the Path that should be failed. // requestInfo->OperationSpecificInfo = FailingDevice->PathId; requestInfo->ErrorMask = ErrorMask; ExInterlockedInsertTailList(&diskExtension->WorkList, &requestInfo->ListEntry, &diskExtension->WorkListLock); // // Signal the thread to initiate the F.O. // KeSetEvent(&diskExtension->ThreadEvent, 8, FALSE); return STATUS_SUCCESS; } VOID MPIOSetRequestBusy( IN PSCSI_REQUEST_BLOCK Srb ) { PSENSE_DATA senseBuffer; // // Ensure that there is a sense buffer. // Srb->SrbStatus = SRB_STATUS_ERROR; Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION; if ((Srb->SenseInfoBufferLength) && (Srb->SenseInfoBuffer)) { // // Build a sense buffer that will coerce the class drivers // into setting a delay, and retrying the request. // senseBuffer = Srb->SenseInfoBuffer; senseBuffer->ErrorCode = 0x70; senseBuffer->Valid = 1; senseBuffer->AdditionalSenseLength = 0xb; senseBuffer->SenseKey = SCSI_SENSE_NOT_READY; senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_LUN_NOT_READY; senseBuffer->AdditionalSenseCodeQualifier = SCSI_SENSEQ_BECOMING_READY; Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; } else { // // Hack based on class driver interpretation of errors. Returning this // will allow a retry interval. // Srb->ScsiStatus = SCSISTAT_BUSY; } return; } NTSTATUS MPIOSetNewPath( IN PDEVICE_OBJECT DeviceObject, IN PVOID PathId ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PDEVICE_EXTENSION diskExtension; PMPDISK_EXTENSION mpdiskExt; PDEVICE_OBJECT diskObject; PDISK_ENTRY diskEntry; NTSTATUS status; ULONG i; // // Go through each of the mpdisks and notify them that FailingPath // is considered dead. // for (i = 0; i < controlExtension->NumberDevices; i++) { diskEntry = MPIOGetDiskEntry(DeviceObject, i); diskObject = diskEntry->PdoObject; diskExtension = diskObject->DeviceExtension; mpdiskExt = diskExtension->TypeExtension; mpdiskExt->NewPathSet = TRUE; InterlockedExchange(&(ULONG)mpdiskExt->CurrentPath, (ULONG)PathId); } return STATUS_SUCCESS; } NTSTATUS MPIOGetAdapterAddress( IN PDEVICE_OBJECT DeviceObject, IN OUT PMPIO_ADDRESS Address ) { ULONG bus = 0; ULONG deviceFunction = 0; NTSTATUS status; ULONG bytesReturned; status = IoGetDeviceProperty(DeviceObject, DevicePropertyBusNumber, sizeof(ULONG), &bus, &bytesReturned); if (NT_SUCCESS(status)) { status = IoGetDeviceProperty(DeviceObject, DevicePropertyAddress, sizeof(ULONG), &deviceFunction, &bytesReturned); } if (NT_SUCCESS(status)) { Address->Bus = (UCHAR)bus; Address->Device = (UCHAR)(deviceFunction >> 16); Address->Function = (UCHAR)(deviceFunction & 0x0000FFFF); Address->Pad = 0; } return status; } NTSTATUS MPIOCreatePathEntry( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT FilterObject, IN PDEVICE_OBJECT PortFdo, IN PVOID PathID ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PLIST_ENTRY entry; PID_ENTRY id; MPIO_ADDRESS address; ULONG bytesReturned; NTSTATUS status; PDEVICE_OBJECT tempDO; // // See whether the pathId already exists. // for (entry = controlExtension->IdList.Flink; entry != &controlExtension->IdList; entry = entry->Flink) { id = CONTAINING_RECORD(entry, ID_ENTRY, ListEntry); if (id->PathID == PathID) { // // Already have an entry. Just return. // return STATUS_SUCCESS; } } // // Didn't find it. Allocate an entry. // id = ExAllocatePool(NonPagedPool, sizeof(ID_ENTRY)); if (id == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(id, sizeof(ID_ENTRY)); id->AdapterName.Buffer = ExAllocatePool(NonPagedPool, FDO_NAME_LENGTH); RtlZeroMemory(id->AdapterName.Buffer, FDO_NAME_LENGTH); // // Set the max. length of the name. // id->AdapterName.MaximumLength = FDO_NAME_LENGTH; // // Capture the rest of the values. // id->PathID = PathID; id->AdapterFilter = FilterObject; id->PortFdo = PortFdo; tempDO = IoGetLowerDeviceObject(PortFdo); // // Get the name. // status = IoGetDeviceProperty(tempDO, DevicePropertyDeviceDescription, id->AdapterName.MaximumLength, id->AdapterName.Buffer, &bytesReturned); // // Set the length - the terminating NULL. // id->AdapterName.Length = (USHORT)(bytesReturned - sizeof(UNICODE_NULL)); // // Get the address. // status = MPIOGetAdapterAddress(tempDO, &id->Address); // // // Jam this into the list. // ExInterlockedInsertTailList(&controlExtension->IdList, &id->ListEntry, &controlExtension->SpinLock); // // Indicate the number of entries. // InterlockedIncrement(&controlExtension->NumberPaths); return STATUS_SUCCESS; } LONGLONG MPIOCreateUID( IN PDEVICE_OBJECT DeviceObject, IN PVOID PathID ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PLIST_ENTRY entry; PID_ENTRY id; UUID uuid; PUCHAR index; NTSTATUS status; LONGLONG collapsedUID; ULONG retryCount = 10; for ( entry = controlExtension->IdList.Flink; entry != &controlExtension->IdList; entry = entry->Flink) { id = CONTAINING_RECORD(entry, ID_ENTRY, ListEntry); if (id->PathID == PathID) { if (id->UID == 0) { // // Wasn't found. Create a new one. // status = ExUuidCreate(&uuid); if (status == STATUS_SUCCESS) { // // Collapse this down into a 64-bit value. // index = (PUCHAR)&uuid; collapsedUID = (*((PLONGLONG) index)) ^ (*((PLONGLONG)(index + sizeof(LONGLONG)))); // // Set the value. // id->UID = collapsedUID; id->UIDValid = TRUE; } } return id->UID; } } return 0; } ULONGLONG MPIOBuildControllerInfo( IN PDEVICE_OBJECT ControlObject, IN PDSM_ENTRY Dsm, IN PVOID DsmID ) { PDEVICE_EXTENSION deviceExtension = ControlObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PCONTROLLER_INFO controllerInfo; PCONTROLLER_ENTRY tmpInfo; PCONTROLLER_ENTRY controllerEntry; ULONGLONG id = 0; NTSTATUS status; ASSERT(Dsm->GetControllerInfo); status = Dsm->GetControllerInfo(Dsm->DsmContext, DsmID, DSM_CNTRL_FLAGS_ALLOCATE | DSM_CNTRL_FLAGS_CHECK_STATE, &controllerInfo); if (NT_SUCCESS(status)) { ASSERT(controllerInfo); // // grab the ID. // id = controllerInfo->ControllerIdentifier; // // See whether the controller ID is part of the list. // tmpInfo = MPIOFindController(ControlObject, controllerInfo->DeviceObject, controllerInfo->ControllerIdentifier); if (tmpInfo) { // // The entry already exists so just update the state. // tmpInfo->ControllerInfo->State = controllerInfo->State; // // Free the DSM's allocation. // ExFreePool(controllerInfo); } else { // // Allocate an entry. // controllerEntry = ExAllocatePool(NonPagedPool, sizeof(CONTROLLER_ENTRY)); if (controllerEntry) { RtlZeroMemory(controllerEntry, sizeof(CONTROLLER_ENTRY)); // // Save this copy. // controllerEntry->ControllerInfo = controllerInfo; // // Save the dsm entry. It will be used in the WMI // handling routines to get it's name. // controllerEntry->Dsm = Dsm; // // Jam it into the list. // ExInterlockedInsertTailList(&controlExtension->ControllerList, &controllerEntry->ListEntry, &controlExtension->SpinLock); // // Indicate the additional entry. // InterlockedIncrement(&controlExtension->NumberControllers); } else { id = 0; } } } return id; } ULONG MPIOHandleStateTransition( IN PDEVICE_OBJECT DeviceObject ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; ULONG currentState; ULONG requests; KIRQL irql; BOOLEAN complete = FALSE; KeAcquireSpinLock(&diskExtension->SpinLock, &irql); // // Capture the current state. // currentState = deviceExtension->State; // // Run the state machine until there are no more 'events'. // do { switch (currentState) { case MPIO_STATE_IN_FO: // // See if the new path has been indicated. // if (diskExtension->NewPathSet) { MPDebugPrint((0, "HandleStateTransition: IN_FO moving to WAIT1\n")); // // Can now go to WAIT1 // currentState = MPIO_STATE_WAIT1; // // Reset the flags. // diskExtension->NewPathSet = FALSE; diskExtension->FailOver = FALSE; } else { complete = TRUE; } break; case MPIO_STATE_WAIT1: // // The DSM has set the new path. See if all of the outstanding // requests have come back (and are on the resubmit queue). // if (diskExtension->OutstandingRequests == 0) { MPDebugPrint((0, "HandleStateTransition: WAIT1 moving to WAIT2\n")); // // Go to WAIT2 // currentState = MPIO_STATE_WAIT2; diskExtension->ResubmitRequests = diskExtension->ResubmitQueue.QueuedItems; } else { complete = TRUE; } break; case MPIO_STATE_WAIT2: // // Resubmit queue is being handled in this state. // Check to see if it's empty. // requests = diskExtension->ResubmitRequests; if (requests == 0) { MPDebugPrint((0, "HandleStateTransition: WAIT2 moving to WAIT3\n")); // // Can go to WAIT3 // currentState = MPIO_STATE_WAIT3; diskExtension->FailOverRequests = diskExtension->FailOverQueue.QueuedItems; } else { // // Still draining the resubmit queue. // complete = TRUE; } break; case MPIO_STATE_WAIT3: // // The fail-over queue is being handled in WAIT3 // requests = diskExtension->FailOverRequests; if (requests == 0) { MPDebugPrint((0, "HandleStateTransition: WAIT3 moving to DEGRADED\n")); // // Can go to DEGRADED. // currentState = MPIO_STATE_DEGRADED; } else { // // Still have requests in the fail-over queue. // complete = TRUE; } break; case MPIO_STATE_DEGRADED: // // This indicates that we are minus at least one path. // Check to see whether it's been repaired. // if (diskExtension->PathBackOnLine) { // // Reset the flag. // diskExtension->PathBackOnLine = FALSE; // // See if that puts us back to where we were. // if (diskExtension->TargetInfoCount == diskExtension->MaxPaths) { // // Go to NORMAL. // currentState = MPIO_STATE_NORMAL; } } else { // // Stay in DEGRADED. // complete = TRUE; } break; case MPIO_STATE_NORMAL: // // Check the Fail-Over flag. // if (diskExtension->FailOver) { // // Go to FAIL-OVER. // currentState = MPIO_STATE_IN_FO; } else { complete = TRUE; } break; default: ASSERT(currentState == MPIO_STATE_NORMAL); complete = TRUE; break; } } while (complete == FALSE); // // Update the state. // deviceExtension->LastState = deviceExtension->State; deviceExtension->State = currentState; KeReleaseSpinLock(&diskExtension->SpinLock, irql); return currentState; } NTSTATUS MPIOForceRescan( IN PDEVICE_OBJECT AdapterFilter ) { IO_STATUS_BLOCK ioStatus; MPDebugPrint((0, "ForceRescan: Issueing rescan to (%x)\n", AdapterFilter)); MPLIBSendDeviceIoControlSynchronous(IOCTL_SCSI_RESCAN_BUS, AdapterFilter, NULL, NULL, 0, 0, FALSE, &ioStatus); MPDebugPrint((0, "ForceRescan: Rescan on (%x) status (%x)\n", AdapterFilter, ioStatus.Status)); return ioStatus.Status; }