|
|
#include "mpio.h"
#include <stdio.h>
#include <ntddk.h>
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; }
|