mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2477 lines
67 KiB
2477 lines
67 KiB
#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;
|
|
}
|
|
|