From 344d26e6176003b72e8af45ff9202b2f7d11d3dc Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 17 May 2018 22:53:14 +0800 Subject: add wdk.wdm rules --- tests/projects/wdk/kmdf/msdsm/SampleDSM.inf | Bin 5354 -> 0 bytes tests/projects/wdk/kmdf/msdsm/dsmmain.c | 9752 ------------------------ tests/projects/wdk/kmdf/msdsm/dsmtrace.mof | 111 - tests/projects/wdk/kmdf/msdsm/intrface.c | 5198 ------------- tests/projects/wdk/kmdf/msdsm/msdsm.h | 1403 ---- tests/projects/wdk/kmdf/msdsm/msdsm.mof | 82 - tests/projects/wdk/kmdf/msdsm/msdsm.rc | 24 - tests/projects/wdk/kmdf/msdsm/msdsmdsm.mof | 141 - tests/projects/wdk/kmdf/msdsm/precomp.h | 34 - tests/projects/wdk/kmdf/msdsm/precompsrc.c | 1 - tests/projects/wdk/kmdf/msdsm/prototypes.h | 1436 ---- tests/projects/wdk/kmdf/msdsm/trace.h | 35 - tests/projects/wdk/kmdf/msdsm/utils.c | 7946 ------------------- tests/projects/wdk/kmdf/msdsm/wmi.c | 3822 ---------- tests/projects/wdk/kmdf/msdsm/xmake.lua | 30 - tests/projects/wdk/kmdf/perfcounters/kcs.c | 407 - tests/projects/wdk/kmdf/perfcounters/kcs.h | 34 - tests/projects/wdk/kmdf/perfcounters/kcs.man | 101 - tests/projects/wdk/kmdf/perfcounters/kcs.rc | 1 - tests/projects/wdk/kmdf/perfcounters/xmake.lua | 19 - tests/projects/wdk/wdm/msdsm/SampleDSM.inf | Bin 0 -> 5354 bytes tests/projects/wdk/wdm/msdsm/dsmmain.c | 9752 ++++++++++++++++++++++++ tests/projects/wdk/wdm/msdsm/dsmtrace.mof | 111 + tests/projects/wdk/wdm/msdsm/intrface.c | 5198 +++++++++++++ tests/projects/wdk/wdm/msdsm/msdsm.h | 1403 ++++ tests/projects/wdk/wdm/msdsm/msdsm.mof | 82 + tests/projects/wdk/wdm/msdsm/msdsm.rc | 24 + tests/projects/wdk/wdm/msdsm/msdsmdsm.mof | 141 + tests/projects/wdk/wdm/msdsm/precomp.h | 34 + tests/projects/wdk/wdm/msdsm/precompsrc.c | 1 + tests/projects/wdk/wdm/msdsm/prototypes.h | 1436 ++++ tests/projects/wdk/wdm/msdsm/trace.h | 35 + tests/projects/wdk/wdm/msdsm/utils.c | 7946 +++++++++++++++++++ tests/projects/wdk/wdm/msdsm/wmi.c | 3822 ++++++++++ tests/projects/wdk/wdm/msdsm/xmake.lua | 27 + tests/projects/wdk/wdm/perfcounters/kcs.c | 407 + tests/projects/wdk/wdm/perfcounters/kcs.h | 34 + tests/projects/wdk/wdm/perfcounters/kcs.man | 101 + tests/projects/wdk/wdm/perfcounters/kcs.rc | 1 + tests/projects/wdk/wdm/perfcounters/xmake.lua | 19 + xmake/rules/wdk/inf/xmake.lua | 2 +- xmake/rules/wdk/load.lua | 89 + xmake/rules/wdk/tracewpp/xmake.lua | 2 +- xmake/rules/wdk/xmake.lua | 23 + 44 files changed, 30688 insertions(+), 30579 deletions(-) delete mode 100644 tests/projects/wdk/kmdf/msdsm/SampleDSM.inf delete mode 100644 tests/projects/wdk/kmdf/msdsm/dsmmain.c delete mode 100644 tests/projects/wdk/kmdf/msdsm/dsmtrace.mof delete mode 100644 tests/projects/wdk/kmdf/msdsm/intrface.c delete mode 100644 tests/projects/wdk/kmdf/msdsm/msdsm.h delete mode 100644 tests/projects/wdk/kmdf/msdsm/msdsm.mof delete mode 100644 tests/projects/wdk/kmdf/msdsm/msdsm.rc delete mode 100644 tests/projects/wdk/kmdf/msdsm/msdsmdsm.mof delete mode 100644 tests/projects/wdk/kmdf/msdsm/precomp.h delete mode 100644 tests/projects/wdk/kmdf/msdsm/precompsrc.c delete mode 100644 tests/projects/wdk/kmdf/msdsm/prototypes.h delete mode 100644 tests/projects/wdk/kmdf/msdsm/trace.h delete mode 100644 tests/projects/wdk/kmdf/msdsm/utils.c delete mode 100644 tests/projects/wdk/kmdf/msdsm/wmi.c delete mode 100644 tests/projects/wdk/kmdf/msdsm/xmake.lua delete mode 100644 tests/projects/wdk/kmdf/perfcounters/kcs.c delete mode 100644 tests/projects/wdk/kmdf/perfcounters/kcs.h delete mode 100644 tests/projects/wdk/kmdf/perfcounters/kcs.man delete mode 100644 tests/projects/wdk/kmdf/perfcounters/kcs.rc delete mode 100644 tests/projects/wdk/kmdf/perfcounters/xmake.lua create mode 100644 tests/projects/wdk/wdm/msdsm/SampleDSM.inf create mode 100644 tests/projects/wdk/wdm/msdsm/dsmmain.c create mode 100644 tests/projects/wdk/wdm/msdsm/dsmtrace.mof create mode 100644 tests/projects/wdk/wdm/msdsm/intrface.c create mode 100644 tests/projects/wdk/wdm/msdsm/msdsm.h create mode 100644 tests/projects/wdk/wdm/msdsm/msdsm.mof create mode 100644 tests/projects/wdk/wdm/msdsm/msdsm.rc create mode 100644 tests/projects/wdk/wdm/msdsm/msdsmdsm.mof create mode 100644 tests/projects/wdk/wdm/msdsm/precomp.h create mode 100644 tests/projects/wdk/wdm/msdsm/precompsrc.c create mode 100644 tests/projects/wdk/wdm/msdsm/prototypes.h create mode 100644 tests/projects/wdk/wdm/msdsm/trace.h create mode 100644 tests/projects/wdk/wdm/msdsm/utils.c create mode 100644 tests/projects/wdk/wdm/msdsm/wmi.c create mode 100644 tests/projects/wdk/wdm/msdsm/xmake.lua create mode 100644 tests/projects/wdk/wdm/perfcounters/kcs.c create mode 100644 tests/projects/wdk/wdm/perfcounters/kcs.h create mode 100644 tests/projects/wdk/wdm/perfcounters/kcs.man create mode 100644 tests/projects/wdk/wdm/perfcounters/kcs.rc create mode 100644 tests/projects/wdk/wdm/perfcounters/xmake.lua diff --git a/tests/projects/wdk/kmdf/msdsm/SampleDSM.inf b/tests/projects/wdk/kmdf/msdsm/SampleDSM.inf deleted file mode 100644 index f632f6e50..000000000 Binary files a/tests/projects/wdk/kmdf/msdsm/SampleDSM.inf and /dev/null differ diff --git a/tests/projects/wdk/kmdf/msdsm/dsmmain.c b/tests/projects/wdk/kmdf/msdsm/dsmmain.c deleted file mode 100644 index bdf875184..000000000 --- a/tests/projects/wdk/kmdf/msdsm/dsmmain.c +++ /dev/null @@ -1,9752 +0,0 @@ -/*++ - -Copyright (C) 2004-2010 Microsoft Corporation - -Module Name: - - dsmmain.c - -Abstract: - - This driver is the Microsoft Device Specific Module (DSM). - It exports behaviours that mpio.sys will use to determine how to - multipath SPC-3 conforming devices. - - This file contains routines that are internal to MSDSM. - -Environment: - - kernel mode only - -Notes: - ---*/ - -#include "precomp.h" - -#ifdef DEBUG_USE_WPP -#include "dsmmain.tmh" -#endif - -#pragma warning (disable:4305) - -extern BOOLEAN DoAssert; - -#ifdef ALLOC_PRAGMA - #pragma alloc_text(PAGE, DsmpRegisterPersistentReservationKeys) -#endif - -VOID -DsmpFreeDSMResources( - _In_ IN PDSM_CONTEXT DsmContext - ) -/*++ - -Routine Description: - - This routine will free the resources allocated by the DSM. This routine - should be called when the DSM is being unloaded. - -Arguements: - - DsmContext - DSM context given to MPIO during initialization - -Return Value: - - None ---*/ -{ - PDSM_WMILIB_CONTEXT wmiInfo; - PVOID tempAddress = (PVOID)DsmContext; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmpFreeDSMResources (DsmCtxt %p): Entering function.\n", - DsmContext)); - - // - // First free the buffer allocated for storing the registry path. - // - wmiInfo = &gDsmInitData.DsmWmiInfo; - - if (wmiInfo->RegistryPath.Buffer) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_INIT, - "DsmpFreeDSMResources (DsmCtxt %p): Freeing wmiInfo's registry buffer.\n", - DsmContext)); - - DsmpFreePool(wmiInfo->RegistryPath.Buffer); - } - - if (DsmContext) { - PLIST_ENTRY entry; - PDSM_DEVICE_INFO deviceInfo; - PDSM_GROUP_ENTRY groupEntry; - PDSM_FAILOVER_GROUP failGroup; - PDSM_CONTROLLER_LIST_ENTRY controllerEntry; - - ExDeleteNPagedLookasideList(&(DsmContext->CompletionContextList)); - - // - // Free up the devices (DeviceInfo) list. - // - while (!IsListEmpty(&DsmContext->DeviceList)) { - - entry = DsmContext->DeviceList.Flink; - - NT_ASSERT(entry); - - deviceInfo = CONTAINING_RECORD(entry, DSM_DEVICE_INFO, ListEntry); - - if (deviceInfo) { - - DsmpRemoveDeviceFailGroup(DsmContext, deviceInfo->FailGroup, deviceInfo, TRUE); - DsmpRemoveDeviceEntry(DsmContext, deviceInfo->Group, deviceInfo); - } - } - - NT_ASSERT(!DsmContext->NumberDevices && - !DsmContext->NumberFOGroups && - !DsmContext->NumberGroups); - - // - // By now, there should be no group entries left but play it safe and - // free up the GROUP list. - // - while (!IsListEmpty(&DsmContext->GroupList)) { - - entry = DsmContext->GroupList.Flink; - - NT_ASSERT(entry); - - groupEntry = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); - - if (groupEntry) { - - DsmpRemoveGroupEntry(DsmContext, groupEntry, TRUE); - - DsmpFreePool(groupEntry); - } - } - - // - // By now there should be no FOG entries left but we play it safe and - // free up the FOG list. - // - while (!IsListEmpty(&DsmContext->FailGroupList)) { - - entry = RemoveHeadList(&DsmContext->FailGroupList); - - if (entry) { - - failGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); - - if (failGroup) { - - PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; - PLIST_ENTRY deviceEntry = NULL; - - while (!IsListEmpty(&failGroup->FOG_DeviceList)) { - - deviceEntry = RemoveHeadList(&failGroup->FOG_DeviceList); - - if (deviceEntry) { - - fogDeviceListEntry = CONTAINING_RECORD(deviceEntry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); - - if (!fogDeviceListEntry) { - continue; - } - - (fogDeviceListEntry->DeviceInfo)->FailGroup = NULL; - - DsmpFreePool(fogDeviceListEntry); - InterlockedDecrement((LONG volatile*)&failGroup->Count); - } - } - - DsmpFreeZombieGroupList(failGroup); - DsmpFreePool(failGroup); - InterlockedDecrement((LONG volatile*)&DsmContext->NumberFOGroups); - } - } - } - - // - // Free up the controller list. - // - while (!IsListEmpty(&DsmContext->ControllerList)) { - - entry = RemoveHeadList(&DsmContext->ControllerList); - - if (entry) { - - controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); - - if (controllerEntry) { - - DsmpFreeControllerEntry(DsmContext, controllerEntry); - - InterlockedDecrement((LONG volatile*)&DsmContext->NumberControllers); - } - } - } - - NT_ASSERT(!DsmContext->NumberControllers); - - // - // Free up the stale FOG list. - // - while (!IsListEmpty(&DsmContext->StaleFailGroupList)) { - - entry = RemoveHeadList(&DsmContext->StaleFailGroupList); - - if (entry) { - - failGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); - - if (failGroup) { - - InterlockedDecrement((LONG volatile*)&DsmContext->NumberStaleFOGroups); - NT_ASSERT(IsListEmpty(&failGroup->FOG_DeviceList)); - DsmpFreeZombieGroupList(failGroup); - DsmpFreePool(failGroup); - } - } - } - - // - // Free up the supported devices list buffer. - // - DsmpFreePool(DsmContext->SupportedDevices.Buffer); - - // - // It's the responsibility of the mpio bus driver to have already - // destroyed all devices and paths. As those functions free allocations - // for the objects, the only thing needed here is to free the DsmContext. - // - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_INIT, - "DsmpFreeDSMResources (DsmCtxt %p): Freeing the DsmContext.\n", - DsmContext)); - - DsmpFreePool(DsmContext); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmpFreeDSMResources (DsmCtxt %p): Exiting function.\n", - tempAddress)); - - return; -} - - -PDSM_GROUP_ENTRY -DsmpFindDevice( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN BOOLEAN AcquireDSMLockExclusive - ) -/*++ - -Routine Description: - - This routine searches for a serial number match between DeviceInfo and - the rest of the devices currently being driven by this DSM. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DeviceInfo - The deviceInfo containing serial number for which to search. - AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively - -Return Value: - - The multi-path group entry in which the device resides. - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo; - PLIST_ENTRY entry; - PDSM_GROUP_ENTRY groupEntry = NULL; - ULONG i; - KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFindDevice (DevInfo %p): Entering function.\n", - DeviceInfo)); - - if (AcquireDSMLockExclusive) { - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - } - - // - // Run through the DeviceInfo List - // - entry = DsmContext->DeviceList.Flink; - for (i = 0; i < DsmContext->NumberDevices; i++, entry = entry->Flink) { - - // - // Extract the deviceInfo structure. - // - deviceInfo = CONTAINING_RECORD(entry, DSM_DEVICE_INFO, ListEntry); - DSM_ASSERT(deviceInfo); - - if (deviceInfo) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpFindDevice (DevInfo %p): Comparing with %p.\n", - DeviceInfo, - deviceInfo)); - - // - // Call the Serial Number compare routine. - // - if (DsmCompareDevices(DsmContext, - DeviceInfo, - deviceInfo)) { - - groupEntry = deviceInfo->Group; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpFindDevice (DevInfo %p): Found matching multi-path group %p.\n", - DeviceInfo, - groupEntry)); - - break; - } - } - } - - if (AcquireDSMLockExclusive) { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFindDevice (DevInfo %p): Exiting function with groupEntry %p.\n", - DeviceInfo, - groupEntry)); - - return groupEntry; -} - - -PDSM_GROUP_ENTRY -DsmpBuildGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - This will allocate and partially initialise a multi-path group entry. - - N.B: This routine must be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DeviceInfo - The first device to be added to the group. - -Return Value: - - The new group entry. - ---*/ -{ - PDSM_GROUP_ENTRY group; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildGroupEntry (DevInfo %p): Entering function.\n", - DeviceInfo)); - - // - // Allocate the memory for the multi-path group. - // - group = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_GROUP_ENTRY), - DSM_TAG_GROUP_ENTRY); - - if (group) { - - InitializeListHead(&group->FailingDevInfoList); - group->GroupNumber = InterlockedIncrement((LONG volatile*)&DsmContext->NumberGroups); - group->GroupSig = DSM_GROUP_SIG; - group->State = DSM_GP_NORMAL; - - // - // Add it to the list of multi-path groups. - // - InsertTailList(&DsmContext->GroupList, &group->ListEntry); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildGroupEntry (DevInfo %p): Failed to allocate memory for the group.\n", - DeviceInfo)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildGroupEntry (DevInfo %p): Exiting function with group %p.\n", - DeviceInfo, - group)); - - return group; -} - - -NTSTATUS -DsmpParseTargetPortGroupsInformation( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, - _In_ IN ULONG TargetPortGroupsInfoLength - ) -/*++ - -Routine Description: - - This will parse the information returned back from a previously - made call to ReportTargetPortGroups and build new TPG entries or - update old ones. - - N.B: This routine must be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DsmContext - Group - group entry - TargetPortGroupsInfo - Pointer to the ReportTPG returned buffer. - TargetPortGroupsInfoLength - length of the buffer. - -Return Value: - - STATUS_SUCCESS or appropriate error code. - ---*/ -{ - PUCHAR targetPortGroupsInfoIndex; - ULONG bytes = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; - ULONG descriptorSize = 0; - NTSTATUS status = STATUS_SUCCESS; - ULONG index; - DSM_DEVICE_STATE tpgState = DSM_DEV_NOT_USED_STATE; - ULONG bytesLeft; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpParseTargetPortGroupsInformation (Group %p): Entering function.\n", - Group)); - - targetPortGroupsInfoIndex = TargetPortGroupsInfo + bytes; - bytesLeft = TargetPortGroupsInfoLength - bytes; - - while (bytes < TargetPortGroupsInfoLength && NT_SUCCESS(status)) { - - targetPortGroupEntry = DsmpFindTargetPortGroupEntry(DsmContext, - Group, - targetPortGroupsInfoIndex, - bytesLeft); - - if (targetPortGroupEntry) { - - targetPortGroupEntry = DsmpUpdateTargetPortGroupEntry(DsmContext, - targetPortGroupEntry, - targetPortGroupsInfoIndex, - bytesLeft, - &descriptorSize); - } else { - - targetPortGroupEntry = DsmpBuildTargetPortGroupEntry(DsmContext, - Group, - targetPortGroupsInfoIndex, - bytesLeft, - &descriptorSize); - - if (targetPortGroupEntry) { - - // - // Insert this TPG entry into array - // - for (index = 0; index < DSM_MAX_PATHS; index++) { - - if (!Group->TargetPortGroupList[index]) { - - Group->TargetPortGroupList[index] = targetPortGroupEntry; - InterlockedIncrement((LONG volatile*)&Group->NumberTargetPortGroups); - targetPortGroupEntry->Group = Group; - break; - } - } - - if (index == DSM_MAX_PATHS) { - - NT_ASSERT(index < DSM_MAX_PATHS); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpParseTargetPortGroupsInformation (Group %p): Number of paths exceeded max supported.\n", - Group)); - - status = STATUS_UNSUCCESSFUL; - goto __Exit_DsmpParseTargetPortGroupsInformation; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpParseTargetPortGroupsInformation (Group %p): Insufficient resources to build TPG.\n", - Group)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) { - - // - // If this is the first TPG being parsed, save off its AA state. - // - if (tpgState == DSM_DEV_NOT_USED_STATE) { - - tpgState = targetPortGroupEntry->AsymmetricAccessState; - - } else { - - // - // Check if this TPG's AA state differs from the previous one's. - // Symmetric LU access means that TPG access states must be the - // same for TPGs. If this one is different, we know that the - // device supports Asymmetric LU access. - // - if (tpgState != targetPortGroupEntry->AsymmetricAccessState) { - - Group->Symmetric = FALSE; - } - } - } - - if (targetPortGroupEntry) { - - // - // Set the flag to indicate that we've encountered this TPG in the RTPG information. - // - targetPortGroupEntry->Traversed = TRUE; - } - - bytes += descriptorSize; - targetPortGroupsInfoIndex += descriptorSize; - bytesLeft -= descriptorSize; - } - - // - // Since we've gone through the entire information reported by back RTPG, it - // is now time to delete the stale entries. - // - for (index = 0; index < DSM_MAX_PATHS; index++) { - - targetPortGroupEntry = Group->TargetPortGroupList[index]; - - if (targetPortGroupEntry) { - - if (targetPortGroupEntry->Traversed) { - - // - // Entry needs to continue to exist. Reset the flag and continue. - // - targetPortGroupEntry->Traversed = FALSE; - continue; - - } else { - - PLIST_ENTRY entry; - PLIST_ENTRY tempEntry; - PDSM_TARGET_PORT_LIST_ENTRY targetPort; - - // - // For this target port group, clean up all its target ports if - // the port doesn't expose any instance of this device. - // - for (entry = targetPortGroupEntry->TargetPortList.Flink; - entry != NULL && entry != &targetPortGroupEntry->TargetPortList; - entry = entry->Flink) { - - targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); - - if (targetPort) { - - // - // If the TP doesn't expose this device, it is safe - // to delete it. - // - if (IsListEmpty(&targetPort->TP_DeviceList)) { - - tempEntry = entry; - entry = entry->Blink; - - RemoveEntryList(tempEntry); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpParseTargetPortGroupsInformation (Group %p): Deleting empty target port %p from TPG %p list.\n", - Group, - targetPort, - targetPortGroupEntry)); - - DsmpFreePool(targetPort); - - InterlockedDecrement((LONG volatile*)&targetPortGroupEntry->NumberTargetPorts); - } - } - } - - // - // If the TPG doesn't have any TPs, it is safe to delete it. - // - if (!targetPortGroupEntry->NumberTargetPorts) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpParseTargetPortGroupsInformation (Group %p): Deleting target port group %p.\n", - Group, - targetPortGroupEntry)); - - DsmpFreePool(targetPortGroupEntry); - - InterlockedDecrement((LONG volatile*)&Group->NumberTargetPortGroups); - - Group->TargetPortGroupList[index] = NULL; - } - } - } - } - -__Exit_DsmpParseTargetPortGroupsInformation: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpParseTargetPortGroupsInformation (Group %p): Exiting function with status %x\n", - Group, - status)); - - return status; -} - - -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpFindTargetPortGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, - _In_ IN ULONG TPGs_BufferLength - ) -/*++ - -Routine Description: - - This will search the group's TPG array to look for an identifier match. - - N.B: This routine must be called with DsmContextLock held in either Shared - or Exclusive mode. - -Arguments: - - DsmContext - DsmContext - Group - group entry - TargetPortGroupsDescriptor - Pointer to the TPG descriptor. - TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. - -Return Value: - - Pointer to the array element that matches, else NULL. - ---*/ -{ - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; - PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; - ULONG index; - BOOLEAN found = FALSE; - USHORT identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); - - UNREFERENCED_PARAMETER(TPGs_BufferLength); - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPortGroupEntry (Group %p): Entering function.\n", - Group)); - - for (index = 0; index < DSM_MAX_PATHS && !found; index++) { - - targetPortGroup = Group->TargetPortGroupList[index]; - - if (targetPortGroup) { - - if (targetPortGroup->Identifier == identifier) { - - found = TRUE; - } - } - } - - if (!found) { - targetPortGroup = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPortGroupEntry (Group %p): Exiting function with targetPortGroup %p.\n", - Group, - targetPortGroup)); - - return targetPortGroup; -} - -_Success_(return!=0) -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpUpdateTargetPortGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, - _In_ IN ULONG TPGs_BufferLength, - _Out_ OUT PULONG DescriptorSize - ) -/*++ - -Routine Description: - - This routine will update the target port group with information contained - in the passed in descriptor. - - N.B: This routine must be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DsmContext - TargetPortGroup - Pointer to the TPG entry to update. - TargetPortGroupsDescriptor - Pointer to the TPG descriptor. - TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. - DescriptorSize - return value of the size of the descriptor. - -Return Value: - - The updated target port group entry on success, NULL in case of failure. - ---*/ -{ - PLIST_ENTRY entry; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = TargetPortGroup; - PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; - ULONG numberTargetPorts = 0; - PULONG descriptorIndex; - ULONG index; - PDSM_TARGET_PORT_LIST_ENTRY listEntry; - NTSTATUS status = STATUS_SUCCESS; - ULONG identifier; - PLIST_ENTRY tempEntry = NULL; - ULONG delCount; - PUCHAR endOfBuffer = TargetPortGroupsDescriptor + TPGs_BufferLength - 1; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Entering function.\n", - TargetPortGroup)); - - if (DescriptorSize == NULL) { - status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to null passed in DescriptorSize pointer\n", - TargetPortGroup, - status)); - - goto __Exit_DsmpUpdateTargetPortGroupEntry; - } - - *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - (targetPortGroup->NumberTargetPorts * sizeof(ULONG)); - - if (((PUCHAR)descriptor + sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) - 1) > endOfBuffer) { - - status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to incorrect passed in TPG buffer size (%u).\n", - TargetPortGroup, - status, - TPGs_BufferLength)); - - goto __Exit_DsmpUpdateTargetPortGroupEntry; - } - - identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); - NT_ASSERT(targetPortGroup->Identifier == (USHORT)identifier); - NT_ASSERT(targetPortGroup->ActiveOptimizedSupported == (descriptor->ActiveOptimizedSupported) ? TRUE : FALSE); - NT_ASSERT(targetPortGroup->ActiveUnoptimizedSupported == (descriptor->ActiveUnoptimizedSupported) ? TRUE : FALSE); - NT_ASSERT(targetPortGroup->StandBySupported == (descriptor->StandbySupported) ? TRUE : FALSE); - NT_ASSERT(targetPortGroup->UnavailableSupported == (descriptor->UnavailableSupported) ? TRUE : FALSE); - NT_ASSERT(targetPortGroup->TransitioningSupported == (descriptor->TransitioningSupported) ? TRUE : FALSE); - DSM_ASSERT(targetPortGroup->VendorUnique == descriptor->VendorUnique); - - // - // It is possible that the asymmetric access state, status code and number of port - // may have changed - // - if ((targetPortGroup->AsymmetricAccessState) != (descriptor->AsymmetricAccessState & 0xF)) - { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Asymmetric access state has changed.\n", - TargetPortGroup)); - - - targetPortGroup->AsymmetricAccessState = descriptor->AsymmetricAccessState & 0xF; - } - - targetPortGroup->Preferred = (descriptor->Preferred) ? TRUE : FALSE; - - targetPortGroup->StatusCode = descriptor->StatusCode; - - numberTargetPorts = descriptor->NumberTargetPorts; - - NT_ASSERT(numberTargetPorts > 0); - - // - // Point to first target port identifier - // - descriptorIndex = descriptor->TargetPortIds; - - for (index = 0; index < numberTargetPorts && NT_SUCCESS(status); index++) { - - if (((PUCHAR)descriptorIndex + ((index + 1) * sizeof(ULONG)) - 1) > endOfBuffer) { - - status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to incorrect TPG buffer size (%u) passed in.\n", - TargetPortGroup, - status, - TPGs_BufferLength)); - - goto __Exit_DsmpUpdateTargetPortGroupEntry; - } - - GetUlongFrom4ByteArray((PUCHAR)(&descriptorIndex[index]), identifier); - - listEntry = DsmpFindTargetPortListEntry(DsmContext, - targetPortGroup, - identifier); - - if (listEntry) { - - RemoveEntryList(&listEntry->ListEntry); - InsertHeadList(&targetPortGroup->TargetPortList, &listEntry->ListEntry); - - } else { - - listEntry = DsmpBuildTargetPortListEntry(DsmContext, - targetPortGroup, - identifier); - - if (listEntry) { - - InsertHeadList(&targetPortGroup->TargetPortList, &listEntry->ListEntry); - InterlockedIncrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); - - } else { - - status = STATUS_INSUFFICIENT_RESOURCES; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Failed to allocate TargetPort (identifier %x).\n", - TargetPortGroup, - identifier)); - } - } - } - - // - // Ignore the status & carry on. Even if we weren't able to build TP entries - // for the new target ports, we are no worse off than before. - // - DSM_ASSERT(NT_SUCCESS(status)); - - *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - (numberTargetPorts * sizeof(ULONG)); - - for (index = 0, entry = targetPortGroup->TargetPortList.Flink; - index < numberTargetPorts; - index++, entry = entry->Flink); - - delCount = targetPortGroup->NumberTargetPorts - numberTargetPorts; - - for (index = 0; index < delCount; index++) { - - tempEntry = entry; - entry = entry->Flink; - - RemoveEntryList(tempEntry); - InterlockedDecrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); - - listEntry = CONTAINING_RECORD(tempEntry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); - NT_ASSERT(listEntry); - - if (listEntry) { - - PLIST_ENTRY deviceEntry; - PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device; - - while (!IsListEmpty(&listEntry->TP_DeviceList)) { - - deviceEntry = RemoveHeadList(&listEntry->TP_DeviceList); - InterlockedDecrement((LONG volatile*)&listEntry->Count); - - if (deviceEntry) { - - tp_device = CONTAINING_RECORD(deviceEntry, DSM_TARGET_PORT_DEVICELIST_ENTRY, ListEntry); - - if (tp_device) { - - if (tp_device->DeviceInfo) { - - tp_device->DeviceInfo->TargetPort = NULL; - } - - DsmpFreePool(tp_device); - } - } - } - - DsmpFreePool(listEntry); - } - } - -__Exit_DsmpUpdateTargetPortGroupEntry: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupEntry (TPG %p): Exiting function.\n", - targetPortGroup)); - - return targetPortGroup; -} - - -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpBuildTargetPortGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, - _In_ IN ULONG TPGs_BufferLength, - _Out_ OUT PULONG DescriptorSize - ) -/*++ - -Routine Description: - - This will allocate and partially initialise a target port group entry. - - N.B: This routine must be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DsmContext - Group - The group that this newly going to be built TPG belongs to. - TargetPortGroupsDescriptor - Pointer to the TPG descriptor. - TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. - DescriptorSize - return value of the size of the descriptor. - -Return Value: - - The new target port group entry. - ---*/ -{ - PDSM_TARGET_PORT_GROUP_ENTRY entry = NULL; - PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; - ULONG numberTargetPorts = 0; - PULONG descriptorIndex; - ULONG index = 0; - PDSM_TARGET_PORT_LIST_ENTRY listEntry; - NTSTATUS status = STATUS_SUCCESS; - ULONG identifier; - PUCHAR endOfBuffer = TargetPortGroupsDescriptor + TPGs_BufferLength - 1; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Entering function.\n", - Group)); - - if (DescriptorSize == NULL) { - - status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to null passed in DescriptorSize pointer\n", - Group, - status)); - - goto __Exit_DsmpBuildTargetPortGroupEntry; - } - - *DescriptorSize = 0; - - if (((PUCHAR)descriptor + sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) - 1) > endOfBuffer) { - - status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to incorrect passed in TPG buffer size (%u).\n", - Group, - status, - TPGs_BufferLength)); - - goto __Exit_DsmpBuildTargetPortGroupEntry; - } - - // - // Allocate the memory for the multi-path group. - // - entry = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_TARGET_PORT_GROUP_ENTRY), - DSM_TAG_TARGET_PORT_GROUP_ENTRY); - - if (entry) { - - entry->TargetPortGroupSig = DSM_TARGET_PORT_GROUP_SIG; - - // - // Target Port Group's access state - // - entry->AsymmetricAccessState = descriptor->AsymmetricAccessState & 0xF; - - // - // Target Port Group's supported states - // - entry->ActiveOptimizedSupported = (descriptor->ActiveOptimizedSupported) ? TRUE : FALSE; - entry->ActiveUnoptimizedSupported = (descriptor->ActiveUnoptimizedSupported) ? TRUE : FALSE; - entry->StandBySupported = (descriptor->StandbySupported) ? TRUE : FALSE; - entry->UnavailableSupported = (descriptor->UnavailableSupported) ? TRUE : FALSE; - - // - // Target Port Group's Preference and support for reporting transitioning - // - entry->Preferred = (descriptor->Preferred) ? TRUE : FALSE; - entry->TransitioningSupported = (descriptor->TransitioningSupported) ? TRUE : FALSE; - - // - // Target Port Group's identifier - // - entry->Identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); - - // - // Target Port Group's status code - // - entry->StatusCode = descriptor->StatusCode; - - // - // Vendor unique - // - entry->VendorUnique = descriptor->VendorUnique; - - // - // Number of target ports - // - numberTargetPorts = descriptor->NumberTargetPorts; - - NT_ASSERT(numberTargetPorts > 0); - - // - // Point to first target port identifier - // - descriptorIndex = descriptor->TargetPortIds; - - InitializeListHead(&entry->TargetPortList); - - for (index = 0; index < numberTargetPorts && NT_SUCCESS(status); index++) { - - if (((PUCHAR)descriptorIndex + ((index + 1) * sizeof(ULONG)) - 1) > endOfBuffer) { - - status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to incorrect TPG buffer size (%u) passed in.\n", - Group, - status, - TPGs_BufferLength)); - - break; - } - - GetUlongFrom4ByteArray((PUCHAR)(&descriptorIndex[index]), identifier); - - listEntry = DsmpBuildTargetPortListEntry(DsmContext, - entry, - identifier); - - if (listEntry) { - - InsertTailList(&entry->TargetPortList, &listEntry->ListEntry); - InterlockedIncrement((LONG volatile*)&entry->NumberTargetPorts); - - } else { - - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Failed to allocate memory for TP (identifier %x) of TPG %p.\n", - Group, - identifier, - entry)); - } - } - - if (NT_SUCCESS(status)) { - *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - (numberTargetPorts * sizeof(ULONG)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Failed to allocate memory for the TPG.\n", - Group)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - - if (!NT_SUCCESS(status)) { - - // - // Delete the target port list and the target port group entry - // - numberTargetPorts = index - 1; - - if (entry) { - - PLIST_ENTRY delEntry; - - for (index = 0; index < numberTargetPorts; index++) { - - delEntry = RemoveHeadList(&entry->TargetPortList); - listEntry = CONTAINING_RECORD(delEntry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Cleaning up TPG %p's TP %x.\n", - Group, - entry, - listEntry->Identifier)); - - DsmpFreePool(listEntry); - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Cleaning up TPG %p.\n", - Group, - entry)); - - DsmpFreePool(entry); - entry = NULL; - } - } - -__Exit_DsmpBuildTargetPortGroupEntry: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortGroupEntry (Group %p): Exiting function with entry %p.\n", - Group, - entry)); - - return entry; -} - - -PDSM_TARGET_PORT_LIST_ENTRY -DsmpFindTargetPortListEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN ULONG RelativeTargetPortId - ) -/*++ - -Routine Description: - - This will search the passed in TPG's target port list for an identifier match. - - N.B: This routine must be called with DsmContextLock held in either Shared or - Exclusive mode. - -Arguments: - - DsmContext - DsmContext - TargetPortGroup - The Target Port Group whose target ports need to be searched. - RelativeTargetPortId - Identifier of the target port entry being matched. - -Return Value: - - The target port list entry if match found, else NULL. - ---*/ -{ - PLIST_ENTRY entry = NULL; - PDSM_TARGET_PORT_LIST_ENTRY targetPort = NULL; - BOOLEAN found = FALSE; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPortListEntry (TPG %p): Entering function.\n", - TargetPortGroup)); - - for (entry = TargetPortGroup->TargetPortList.Flink; - entry != &TargetPortGroup->TargetPortList && !found; - entry = entry->Flink) { - - targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); - NT_ASSERT(targetPort); - - if (targetPort) { - - if (targetPort->Identifier == RelativeTargetPortId) { - - NT_ASSERT(targetPort->TargetPortGroup == TargetPortGroup); - - found = TRUE; - } - } - } - - if (!found) { - targetPort = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPortListEntry (TPG %p): Exiting function with target port %p.\n", - TargetPortGroup, - targetPort)); - - return targetPort; -} - - -PDSM_TARGET_PORT_LIST_ENTRY -DsmpBuildTargetPortListEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN ULONG RelativeTargetPortId - ) -/*++ - -Routine Description: - - This will allocate and partially initialize a target port list entry. - - N.B: This routine must be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DsmContext - TargetPortGroup - The Target Port Group that this target port belongs to. - RelativeTargetPortId - Identifier of the target port entry being added. - -Return Value: - - The new target port list entry. - ---*/ -{ - PDSM_TARGET_PORT_LIST_ENTRY entry; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortListEntry (TPG %p): Entering function.\n", - TargetPortGroup)); - - entry = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_TARGET_PORT_LIST_ENTRY), - DSM_TAG_TARGET_PORT_LIST_ENTRY); - - if (entry) { - - InitializeListHead(&entry->TP_DeviceList); - - entry->Identifier = RelativeTargetPortId; - entry->TargetPortGroup = TargetPortGroup; - entry->TargetPortSig = DSM_TARGET_PORT_SIG; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortListEntry (TPG %p): Failed to allocate memory for target port (identifier %x).\n", - TargetPortGroup, - RelativeTargetPortId)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpBuildTargetPortListEntry (TPG %p): Exiting function with entry %p.\n", - TargetPortGroup, - entry)); - - return entry; -} - - -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpFindTargetPortGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PUSHORT TargetPortGroupId - ) -/*++ - -Routine Description: - - This routine searches the list of TargetPortGroups of a Group to - find a match for the passed in TargetPortGroupId. - - N.B: This routine must be called with DsmContextLock held in either Shared or - Exclusive mode. - -Arguments: - - DsmContext - DSM context. - Group - The group whose target port groups to search for a match. - TargetPortGroupId - Identifier of the target port group entry being searched. - -Return Value: - - The target port group entry which matches the passed in identifier. - ---*/ -{ - ULONG index; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; - BOOLEAN found = FALSE; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPortGroup (Group %p): Entering function.\n", - Group)); - - // - // Run through the target port group array - // - for (index = 0; index < DSM_MAX_PATHS && !found; index++) { - - targetPortGroupEntry = Group->TargetPortGroupList[index]; - - if (targetPortGroupEntry) { - - if (targetPortGroupEntry->Identifier == *TargetPortGroupId) { - - found = TRUE; - } - } - } - - if (!found) { - - targetPortGroupEntry = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPortGroup (Group %p): Exiting function with targetPortGroupEntry %p.\n", - Group, - targetPortGroupEntry)); - - return targetPortGroupEntry; -} - - -PDSM_TARGET_PORT_LIST_ENTRY -DsmpFindTargetPort( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN PULONG TargetPortGroupId - ) -/*++ - -Routine Description: - - This routine searches the list of TargetPorts to - find a match for the passed in TargetPortGroup and RelativeTargetPortId. - - N.B. Spin lock must be held by caller. - -Arguments: - - DsmContext - DSM context. - TargetPortGroup - the Target Port Group of which this target port is a member. - RelativeTargetPortId - Identifier of the target port entry being searched. - -Return Value: - - The target port entry which matches the passed in identifier. - ---*/ -{ - PLIST_ENTRY entry; - PDSM_TARGET_PORT_LIST_ENTRY targetPortListEntry = NULL; - BOOLEAN found = FALSE; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPort (TPG %p): Entering function.\n", - TargetPortGroup)); - - // - // Run through the Target Port List - // - for (entry = TargetPortGroup->TargetPortList.Flink; - entry != &TargetPortGroup->TargetPortList && !found; - entry = entry->Flink) { - - // - // Extract the target port group structure. - // - targetPortListEntry = CONTAINING_RECORD(entry, - DSM_TARGET_PORT_LIST_ENTRY, - ListEntry); - NT_ASSERT(targetPortListEntry); - - if (targetPortListEntry) { - - NT_ASSERT(TargetPortGroup == targetPortListEntry->TargetPortGroup); - - // - // Compare with passed in identifier. - // - if (targetPortListEntry->Identifier == *TargetPortGroupId) { - - found = TRUE; - } - } - } - - if (!found) { - - targetPortListEntry = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindTargetPort (TPG %p): Exiting function with targetPortListEntry %p.\n", - TargetPortGroup, - targetPortListEntry)); - - return targetPortListEntry; -} - - -NTSTATUS -DsmpAddDeviceEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - This routine adds DeviceInfo to an existing multi-path group. - - N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - Group - The multi-path group to which DeviceInfo should be added. - DeviceInfo - The new device. - DeviceState - The initial device state (active, passive,...) - -Return Value: - - UNSUCCESSFUL - If there are too many paths already. - SUCCESS - ---*/ -{ - ULONG numberDevices; - NTSTATUS status = STATUS_SUCCESS; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpAddDeviceEntry (DevInfo %p): Entering function.\n", - DeviceInfo)); - - // - // Ensure that this is a valid config - namely, it hasn't - // exceeded the number of paths supported. - // - numberDevices = * (volatile ULONG *) &Group->NumberDevices; - if (numberDevices < DSM_MAX_PATHS) { - -#if DBG - ULONG i; - - // - // Ensure that this isn't a second copy of the same pdo. - // - for (i = 0; i < numberDevices; i++) { - if (Group->DeviceList[i]->PortPdo == DeviceInfo->PortPdo) { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpAddDeviceEntry (DevInfo %p): Received same PDO %p twice.\n", - DeviceInfo, - DeviceInfo->PortPdo)); - } - } -#endif - - // - // Indicate one more device is present in this group. - // - Group->DeviceList[numberDevices] = DeviceInfo; - - // - // Indicate one more in the list. - // - InterlockedIncrement((LONG volatile*)&Group->NumberDevices); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpAddDeviceEntry (DevInfo %p): Adding Device to Group %p\n", - DeviceInfo, - Group)); - - // - // Set-up this device's group id. - // - DeviceInfo->Group = Group; - - // - // One more deviceInfo entry. - // - InterlockedIncrement((LONG volatile*)&DsmContext->NumberDevices); - - // - // Finally, add it to the global list of devices. - // - InsertTailList(&DsmContext->DeviceList, - &DeviceInfo->ListEntry); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpAddDeviceEntry (DevInfo %p): Max Paths already added for Group %p.\n", - DeviceInfo, - Group)); - - status = STATUS_UNSUCCESSFUL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpAddDeviceEntry (DevInfo %p): Exiting function with status %x.\n", - DeviceInfo, - status)); - - return status; -} - - -PDSM_CONTROLLER_LIST_ENTRY -DsmpFindControllerEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDEVICE_OBJECT PortObject, - _In_ IN PSCSI_ADDRESS ScsiAddress, - _In_reads_(ControllerSerialNumberLength) IN PSTR ControllerSerialNumber, - _In_ IN SIZE_T ControllerSerialNumberLength, - _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, - _In_ IN BOOLEAN AcquireLock - ) -/*++ - -Routine Description: - - This routine compares the passed in serial number and SCSI address with the - entries in the list of controller objects. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization. - PortObject - Port FDO exposing the controller. - ScsiAddress - The scsi address to match. - ControllerSerialNumber - The serial number for which to find a match. - ControllerSerialNumberLength - Length of the passed in serial number, in bytes. - CodeSet - Code set used when building the passed in serial number. - AcquireLock - FALSE indicates that the caller has already acquired the spin lock. - -Return Value: - - Controller list entry if a match is found, else NULL - ---*/ -{ - KIRQL oldIrql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error - PLIST_ENTRY entry; - PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; - BOOLEAN found = FALSE; - PDSM_CONTROLLER_LIST_ENTRY candidate = NULL; - - UNREFERENCED_PARAMETER(CodeSet); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFindControllerEntry (SN %s): Entering function.\n", - ControllerSerialNumber)); - - if (AcquireLock) { - - oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - } - - for (entry = DsmContext->ControllerList.Flink; - entry != &DsmContext->ControllerList && !found; - entry = entry->Flink) { - - controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); - NT_ASSERT(controllerEntry); - - if (!controllerEntry) { - - continue; - } - - // - // Serial numbers and Portal, Bus, and Target of the SCSI address must match. - // - if (!strncmp((const char*)controllerEntry->Identifier, - ControllerSerialNumber, - ControllerSerialNumberLength) && - (controllerEntry->ScsiAddress->PortNumber == ScsiAddress->PortNumber && - controllerEntry->ScsiAddress->PathId == ScsiAddress->PathId && - controllerEntry->ScsiAddress->TargetId == ScsiAddress->TargetId)) { - - if (controllerEntry->IdLength == ControllerSerialNumberLength) { - - found = TRUE; - - } else { - - if ((!candidate) || - (controllerEntry->IdLength > ControllerSerialNumberLength && ControllerSerialNumberLength == 32)) { - - candidate = controllerEntry; - } - } - } - } - - if (!found) { - - if (candidate) { - - controllerEntry = candidate; - - } else { - - controllerEntry = NULL; - } - } - - // - // If we found a matching controller entry, we need to make sure the Port - // Object (FDO) is updated. We also don't care about the LUN part of the - // SCSI address so we just set it to zero. - // - if (controllerEntry) { - controllerEntry->PortObject = PortObject; - controllerEntry->ScsiAddress->Lun = 0; - } - - if (AcquireLock) { - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFindControllerEntry (SN %s): Exiting function with controllerEntry %p\n", - ControllerSerialNumber, - controllerEntry)); - - return controllerEntry; -} - - -_Ret_maybenull_ -_Must_inspect_result_ -_When_(return != NULL, __drv_allocatesMem(Mem)) -PDSM_CONTROLLER_LIST_ENTRY -DsmpBuildControllerEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_opt_ IN PDEVICE_OBJECT DeviceObject, - _In_ IN PDEVICE_OBJECT PortObject, - _In_ IN PSCSI_ADDRESS ScsiAddress, - _In_ IN PSTR ControllerSerialNumber, - _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, - _In_ IN BOOLEAN AcquireLock - ) -/*++ - -Routine Description: - - This routine builds a new controller list entry with the passed in serial number info. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization. - DeviceObject - Controller's PDO. - PortObject - Port FDO exposing the controller. - ScsiAddress - scsi address of the controller. - ControllerSerialNumber - The serial number to associate with new entry. - CodeSet - Code set of the identifier that was used to build the serial number. - AcquireLock - TRUE indicates that the function must grab the spinlock. FALSE indicates - that caller has the spin lock held. - -Return Value: - - New controller list entry if we successfully built one, else NULL - ---*/ -{ - KIRQL oldIrql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error - PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildControllerEntry (SN %s): Entering function - Controller %p seen through PortFDO %p.\n", - ControllerSerialNumber, - DeviceObject, - PortObject)); - - if (AcquireLock) { - - oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - } - - controllerEntry = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_CONTROLLER_LIST_ENTRY), - DSM_TAG_CONTROLLER_LIST_ENTRY); - - if (controllerEntry) { - - // - // Note: - // ControllerSerialNumber's length fits in a 32-bit value. - // See implementation in DsmpParseDeviceID() - // - ULONG length = (ULONG)strlen(ControllerSerialNumber); - - controllerEntry->Identifier = DsmpAllocatePool(NonPagedPoolNx, - length + 1, - DSM_TAG_SERIAL_NUM); - - if (controllerEntry->Identifier) { - - controllerEntry->ScsiAddress = DsmpAllocatePool(NonPagedPoolNx, - sizeof(SCSI_ADDRESS), - DSM_TAG_SCSI_ADDRESS); - if (controllerEntry->ScsiAddress) { - - RtlCopyMemory(controllerEntry->ScsiAddress, ScsiAddress, sizeof(SCSI_ADDRESS)); - - controllerEntry->DeviceObject = DeviceObject; - controllerEntry->PortObject = PortObject; - controllerEntry->ControllerSig = DSM_CONTROLLER_SIG; - controllerEntry->IdLength = length; - controllerEntry->IdCodeSet = CodeSet; - - RtlCopyMemory(controllerEntry->Identifier, - ControllerSerialNumber, - length); - - controllerEntry->RefCount = 0; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildControllerEntry (SN %s): Failed to allocate resources for scsiaddress (controllerEntry %p).\n", - ControllerSerialNumber, - controllerEntry)); - - DsmpFreePool(controllerEntry->Identifier); - controllerEntry->Identifier = NULL; - DsmpFreePool(controllerEntry); - controllerEntry = NULL; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildControllerEntry (SN %s): Failed to allocate resources for identifier (controllerEntry %p).\n", - ControllerSerialNumber, - controllerEntry)); - - DsmpFreePool(controllerEntry); - controllerEntry = NULL; - } - - } else { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildControllerEntry (SN %s): Failed to allocate memory for ControllerEntry.\n", - ControllerSerialNumber)); - } - - if (AcquireLock) { - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildControllerEntry (SN %s): Exiting function with controllerEntry %p\n", - ControllerSerialNumber, - controllerEntry)); - - return controllerEntry; -} - - -VOID -DsmpFreeControllerEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ __drv_freesMem(Mem) IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry - ) -/*++ - -Routine Description: - - This routine frees the allocations of the passed in controller list entry. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization. - ControllerEntry - Controller list entry. - -Return Value: - - Nothing - ---*/ -{ - PVOID tempAddress = (PVOID)ControllerEntry; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFreeControllerEntry (Entry %p): Entering function.\n", - ControllerEntry)); - - if (ControllerEntry->Identifier) { - DsmpFreePool(ControllerEntry->Identifier); - } - - if (ControllerEntry->ScsiAddress) { - DsmpFreePool(ControllerEntry->ScsiAddress); - } - - DsmpFreePool(ControllerEntry); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFreeControllerEntry (Entry %p): Exiting function.\n", - tempAddress)); - - return; -} - - -BOOLEAN -DsmpIsDeviceBelongsToController( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry - ) -/*++ - -Routine Description: - - This routine determines if the device passed in was exposed via the passed - in controller. - The match is to be based on VID and SCSI Address (using the Port, Bus - and Target comparison). - -Arguments: - - DsmContext - DSM context given to MPIO during initialization. - DeviceInfo - The device instance to match. - ControllerEntry - The controller object which we need to determine whether - DeviceInfo is exposed from. - -Return Value: - - TRUE - if the controller's VID and scsi address match - FALSE - not matched - ---*/ -{ - BOOLEAN saMatch = FALSE; - BOOLEAN vMatch = FALSE; - BOOLEAN match = FALSE; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpIsDeviceBelongsToController (DevInfo %p): Entering function - ControllerEntry is %p.\n", - DeviceInfo, - ControllerEntry)); - - if (DeviceInfo->ScsiAddress && ControllerEntry->ScsiAddress) { - - saMatch = (DeviceInfo->ScsiAddress->PathId == ControllerEntry->ScsiAddress->PathId && - DeviceInfo->ScsiAddress->PortNumber == ControllerEntry->ScsiAddress->PortNumber && - DeviceInfo->ScsiAddress->TargetId == ControllerEntry->ScsiAddress->TargetId); - } - - if (saMatch) { - - INQUIRYDATA inquiryData = {0}; - UCHAR controllerVID[9] = {0}; - UCHAR deviceVID[9] = {0}; - - if (NT_SUCCESS(DsmpGetStandardInquiryData(ControllerEntry->DeviceObject, &inquiryData))) { - - RtlStringCchCopyA((PSTR)controllerVID, - ARRAYSIZE(controllerVID), - (PCSTR)(&inquiryData.VendorId)); - - RtlStringCchCopyA((PSTR)deviceVID, - ARRAYSIZE(deviceVID), - (PCSTR)(&DeviceInfo->Descriptor) + DeviceInfo->Descriptor.VendorIdOffset); - - - if (!strcmp((const char*)controllerVID, (const char*)deviceVID)) { - - vMatch = TRUE; - } - } - } - - match = saMatch & vMatch; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpIsDeviceBelongsToController (DevInfo %p): ControllerEntry %p. Exiting function with match = %x.\n", - DeviceInfo, - ControllerEntry, - match)); - - return match; -} - - -PDSM_DEVICE_INFO -DsmpFindDevInfoFromGroupAndFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_FAILOVER_GROUP FOGroup - ) -/*++ - -Routine Description: - - This routine will find the deviceInfo that is part of both the passed in Group - as well as passed in Fail-Over group. - - N.B: This routine MUST be called with DsmContextLock held in either Shared or - Exclusive mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - Group - The group that represents the device. - FOGroup - The FOG that the device is part of. - -Return Value: - - The deviceInfo that is part of both. - NULL - if not found. - ---*/ -{ - ULONG i; - PDSM_DEVICE_INFO deviceInfo = NULL; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpFindDevInfoFromGroupAndFOGroup (Group %p FOG %p): Entering function.\n", - Group, - FOGroup)); - - if (Group && FOGroup) { - - // - // Run through the list of devInfos in passed in Group - // - for (i = 0; i < DSM_MAX_PATHS; i++) { - - deviceInfo = Group->DeviceList[i]; - - if (deviceInfo) { - - if (deviceInfo->FailGroup == FOGroup) { - - break; - - } else { - - deviceInfo = NULL; - } - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpFindFOGroup (Group %p FOG %p): Exiting function with deviceInfo %p.\n", - Group, - FOGroup, - deviceInfo)); - - return deviceInfo; -} - - -PDSM_FAILOVER_GROUP -DsmpFindFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PVOID PathId - ) -/*++ - -Routine Description: - - This routine will find the Fail-Over group that corresponds to PathId. - - N.B: This routine MUST be called with DsmContextLock held in either Shared or - Exclusive mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - PathId - The Path Identifier that corresponds to - an adapter/adapter-controller - -Return Value: - - The fail-over group. - NULL - if not found. - ---*/ -{ - PDSM_FAILOVER_GROUP failOverGroup = NULL; - PDSM_FAILOVER_GROUP retFOGroup = NULL; - PLIST_ENTRY entry; - ULONG i; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindFOGroup (PathId %p): Entering function.\n", - PathId)); - - // - // Run through the list of Fail-Over Groups - // - entry = DsmContext->FailGroupList.Flink; - for (i = 0; i < DsmContext->NumberFOGroups; i++, entry = entry->Flink) { - - // - // Extract the fail-over group structure. - // - failOverGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); - NT_ASSERT(failOverGroup); - - if (!failOverGroup) { - continue; - } - - // - // Check for a match of the PathId. - // - if (failOverGroup->PathId == PathId) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpFindFOGroup (PathId %p): Found a FO group %p.\n", - PathId, - failOverGroup)); - - retFOGroup = failOverGroup; - - break; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindFOGroup (PathId %p): Exiting function with retFOGroup %p.\n", - PathId, - retFOGroup)); - - return retFOGroup; -} - - -PDSM_FAILOVER_GROUP -DsmpBuildFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PVOID *PathId - ) -/*++ - -Routine Description: - - This routine will build and partially initialise a fail-over group entry. - The FOG corresponds to the device list which will fail as a group. - - N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DeviceInfo - The first device to add to the group. - PathId - An identifier that is returned to mpio that id's the path. - -Return Value: - - The fail-over group entry. - NULL - on failed allocation. - ---*/ -{ - PDSM_FAILOVER_GROUP failOverGroup; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildFOGroup (PathId %p): Entering function.\n", PathId)); - - // - // Allocate a new Fail Over Group - // - failOverGroup = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_FAILOVER_GROUP), - DSM_TAG_FO_GROUP); - if (failOverGroup) { - - InitializeListHead(&failOverGroup->FOG_DeviceList); - InitializeListHead(&failOverGroup->ZombieGroupList); - - // - // Get the current number of groups, and add the one that's being created. - // - InterlockedIncrement((LONG volatile*)&DsmContext->NumberFOGroups); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpBuildFOGroup (PathId %p): Path that will be used for %p is %p.\n", - PathId, - DeviceInfo, - *PathId)); - - failOverGroup->PathId = *PathId; - - // - // Set the initial state to NORMAL. - // - failOverGroup->State = DSM_FG_NORMAL; - - failOverGroup->FailOverSig = DSM_FOG_SIG; - - // - // Add it to the global list. - // - InsertTailList(&DsmContext->FailGroupList, - &failOverGroup->ListEntry); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpBuildFOGroup (PathId %p): Added new FOGroup %p with path %p. Count of FO Group %d.\n", - PathId, - failOverGroup, - *PathId, - DsmContext->NumberFOGroups)); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildFOGroup (PathId %p): Failed to allocate memory for FailOverGroup.\n", - PathId)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildFOGroup (PathId %p): Exiting function with failOverGroup %p.\n", - PathId, - failOverGroup)); - - return failOverGroup; -} - - -NTSTATUS -DsmpUpdateFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_FAILOVER_GROUP FailGroup, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - This routine will add DeviceInfo to an existing FOG. - - N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - FailGroup - The fail-over group entry. - DeviceInfo - The new device. - -Return Value: - - STATUS_SUCCESS or appropriate error code. - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpUpdateFOGroup (FOG %p): Entering function. DeviceInfo %p.\n", - FailGroup, - DeviceInfo)); - - if (DeviceInfo && FailGroup) { - - fogDeviceListEntry = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_FOG_DEVICELIST_ENTRY), - DSM_TAG_FOG_DEV_ENTRY); - - if (fogDeviceListEntry) { - - // - // Add the device to the list of devices that are on this path. - // - fogDeviceListEntry->DeviceInfo = DeviceInfo; - InterlockedIncrement((LONG volatile*)&FailGroup->Count); - InsertTailList(&FailGroup->FOG_DeviceList, &fogDeviceListEntry->ListEntry); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpUpdateFOGroup (FOG %p): DevInfo %p added (current count: %d)\n", - FailGroup, - DeviceInfo, - FailGroup->Count)); - - // - // Set the device's F.O. Group. - // - DeviceInfo->FailGroup = FailGroup; - - } else { - - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpUpdateFOGroup (FOG %p): Failed to allocate memory for FOG devlist entry.\n", - FailGroup)); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpUpdateFOGroup (FOG %p): Exiting function with status %x.\n", - FailGroup, - status)); - - return status; -} - - -VOID -DsmpRemoveDeviceFailGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_FAILOVER_GROUP FailGroup, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN BOOLEAN AcquireDSMLockExclusive - ) -/*++ - -Routine Description: - - This routine will remove DeviceInfo from the FOG. - This routine is called in response to a removal of the device. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - FailGroup - The FOG from which DeviceInfo should be removed. - DeviceInfo - The now missing device. - AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively - -Return Value: - - NOTHING - ---*/ -{ - KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings - PLIST_ENTRY entry; - PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry; - PLIST_ENTRY zombieEntry; - PDSM_ZOMBIEGROUP_ENTRY zombieGroup; - PDSM_ZOMBIEGROUP_ENTRY newZombieGroup; - BOOLEAN groupInZombieList = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceFailGroup (FOG %p): Entering function. DeviceInfo %p.\n", - FailGroup, - DeviceInfo)); - - if (FailGroup && DeviceInfo) { - - if (AcquireDSMLockExclusive) { - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - } - - for (entry = FailGroup->FOG_DeviceList.Flink; - entry != &FailGroup->FOG_DeviceList; - entry = entry->Flink) { - - fogDeviceListEntry = CONTAINING_RECORD(entry, - DSM_FOG_DEVICELIST_ENTRY, - ListEntry); - DSM_ASSERT(fogDeviceListEntry); - - if (!fogDeviceListEntry) { - continue; - } - - if (fogDeviceListEntry->DeviceInfo == DeviceInfo) { - - DeviceInfo->FailGroup = NULL; - RemoveEntryList(entry); - DsmpFreePool(fogDeviceListEntry); - - InterlockedDecrement((LONG volatile*)&FailGroup->Count); - - // - // If a DeviceInfo is removed, we need to keep its group in a - // "zombie" list so that we can still access a fail-over group's - // associated groups even when all its devices are gone. - // - for (zombieEntry = FailGroup->ZombieGroupList.Flink; - zombieEntry != &(FailGroup->ZombieGroupList); - zombieEntry = zombieEntry->Flink) { - - zombieGroup = CONTAINING_RECORD(zombieEntry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); - if (zombieGroup != NULL && - zombieGroup->Group != NULL && - zombieGroup->Group == DeviceInfo->Group) { - - groupInZombieList = TRUE; - break; - } - } - - // - // Create a new entry if the group does not exist in the zombie group list. - // - if (groupInZombieList == FALSE) { - newZombieGroup = (PDSM_ZOMBIEGROUP_ENTRY)DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_ZOMBIEGROUP_ENTRY), - DSM_TAG_ZOMBIEGROUP_ENTRY); - if (newZombieGroup != NULL) { - newZombieGroup->Group = DeviceInfo->Group; - InsertTailList(&FailGroup->ZombieGroupList, &newZombieGroup->ListEntry); - } else { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceFailGroup (DevInfo %p): Failed to allocate memory for the zombie group.\n", - DeviceInfo)); - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceFailGroup (FOG %p): DevInfo %p removed from FOG (current count: %d)\n", - FailGroup, - DeviceInfo, - FailGroup->Count)); - - break; - } - } - - if (AcquireDSMLockExclusive) { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceFailGroup (FOG %p): Exiting function.\n", - FailGroup)); - - return; -} - - -ULONG -DsmpRemoveDeviceEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - This routine will remove DeviceInfo from Group. If it is the last DeviceInfo - in the Group, it has the added side-effect of cleaning up the Group entry - also. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - Group - The multi-path group from which DeviceInfo should be removed. - DeviceInfo - The device to remove. - -Return Value: - - Number of devices left in group. - ---*/ -{ - KIRQL irql; - ULONG i; - ULONG j; - ULONG numberDevices; - BOOLEAN freeGroup = FALSE; - PVOID tempAddress = (PVOID)Group; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceEntry (Group %p): Entering function. DeviceInfo %p.\n", - Group, - DeviceInfo)); - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Find it's offset in the array of devices. - // - for (i = 0; i < Group->NumberDevices; i++) { - - if (Group->DeviceList[i] == DeviceInfo) { - - // - // Zero out it's entry. - // - Group->DeviceList[i] = NULL; - - // - // Reduce the number in the group. - // - InterlockedDecrement((LONG volatile*)&Group->NumberDevices); - - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceEntry (Group %p): Removing Device %p (desiredState %u) from Group\n", - Group, - DeviceInfo, - DeviceInfo->DesiredState)); - - // - // Collapse the array. - // Holding the spinlock, so that the state is consistent in other - // routines. - // - for (j = i; j < Group->NumberDevices; j++) { - - // - // Shuffle all entries down to fill the hole. - // - Group->DeviceList[j] = Group->DeviceList[j + 1]; - } - - // - // Zero out the last one. - // - Group->DeviceList[j] = NULL; - break; - } - } - - // - // Remove this devInfo from the TargetPort deviceList - // - DsmpRemoveDeviceFromTargetPortList(DeviceInfo); - - numberDevices = Group->NumberDevices; - - // - // See if anything is left in the Group. - // - if (Group->NumberDevices == 0) { - - Group->State = DSM_GP_FAILED; - - // - // Yank it from the Group list. - // - DsmpRemoveGroupEntry(DsmContext, Group, FALSE); - - freeGroup = TRUE; - } - - // - // Yank the device out of the Global list. - // - RemoveEntryList(&DeviceInfo->ListEntry); - InterlockedDecrement((LONG volatile*)&DsmContext->NumberDevices); - - // - // If the serial number buffer was allocated, need to free it. - // - if (DeviceInfo->SerialNumberAllocated) { - DsmpFreePool(DeviceInfo->SerialNumber); - } - - if (DeviceInfo->ScsiAddress) { - DsmpFreePool(DeviceInfo->ScsiAddress); - } - - // - // Fix up the Reservation List, if needed. - // - if (!freeGroup && Group->ReservationList) { - ULONG oldList; - - // - // Capture the list for debugging. - // - oldList = Group->ReservationList; - Group->ReservationList = 0; - - // - // Go through all devices in this group and find the one(s) registered. - // - for (i = 0; i < Group->NumberDevices; i++) { - - if (Group->DeviceList[i]->RegisterServiced) { - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmRemoveDeviceEntry (Group %p): Device %p at %d registered.\n", - Group, - Group->DeviceList[i], - i)); - - // - // Indicate its place. - // - Group->ReservationList |= (1 << i); - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmRemoveDeviceEntry (Group %p): Reservations Old (%x) New (%x).\n", - Group, - oldList, - Group->ReservationList)); - } - - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - // - // Free the allocation. - // - DsmpFreePool(DeviceInfo); - - if (freeGroup) { - - // - // Free the allocations. - // - if (Group->RegistryKeyName) { - DsmpFreePool(Group->RegistryKeyName); - } - - if (Group->HardwareId) { - DsmpFreePool(Group->HardwareId); - } - - DsmpFreePool(Group); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceEntry (Group %p): Exiting function - numberDevices = %x.\n", - tempAddress, - numberDevices)); - - return numberDevices; -} - - -VOID -DsmpRemoveDeviceFromTargetPortList( - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - This will remove a DeviceInfo from its target port device list. - - The caller should ensure that the DsmContext->SpinLock is held before - calling this function. - -Arguments: - - DeviceInfo - The DeviceInfo to be removed. - -Return Value: - - None - ---*/ -{ - if (DeviceInfo->TargetPort) { - - PLIST_ENTRY entry; - PDSM_TARGET_PORT_DEVICELIST_ENTRY listEntry; - - for (entry = DeviceInfo->TargetPort->TP_DeviceList.Flink; - entry != NULL && entry != &DeviceInfo->TargetPort->TP_DeviceList; - entry = entry->Flink) { - - listEntry = CONTAINING_RECORD(entry, DSM_TARGET_PORT_DEVICELIST_ENTRY, ListEntry); - - if (listEntry) { - - if (listEntry->DeviceInfo == DeviceInfo) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRemoveDeviceFromTargetPortList: Removing device %p from target port entry %p.\n", - DeviceInfo, - listEntry)); - - RemoveEntryList(entry); - InterlockedDecrement((LONG volatile*)&DeviceInfo->TargetPort->Count); - DsmpFreePool(listEntry); - - DeviceInfo->TargetPort = NULL; - DeviceInfo->TargetPortGroup = NULL; - - break; - } - } - } - } -} - - -VOID -DsmpRemoveZombieGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY ZombieGroup - ) -/* ++ - -Routine Description: - - This will scan through all the Failover Groups and remove the given Group - from each Failover Group's zombie group list. - - The DSM lock should be aquired by the caller. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - ZombieGroup - Group entry that should be removed from FOGs' ZombieGroupList - -Return Value: - - None - --- */ -{ - // - // Run through the list of Fail-Over Groups - // - ULONG i; - PDSM_FAILOVER_GROUP failOverGroup = NULL; - PLIST_ENTRY fogEntry; - PLIST_ENTRY groupEntry; - PDSM_ZOMBIEGROUP_ENTRY zombieGroupEntry; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveZombieGroupEntry (Group %p): Entering function.\n", - ZombieGroup)); - - fogEntry = DsmContext->FailGroupList.Flink; - for (i = 0; fogEntry != NULL && i < DsmContext->NumberFOGroups; i++, fogEntry = fogEntry->Flink) { - - failOverGroup = CONTAINING_RECORD(fogEntry, DSM_FAILOVER_GROUP, ListEntry); - if (failOverGroup != NULL) { - - for (groupEntry = failOverGroup->ZombieGroupList.Flink; - groupEntry != &(failOverGroup->ZombieGroupList); - groupEntry = groupEntry->Flink) { - - zombieGroupEntry = CONTAINING_RECORD(groupEntry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); - if (zombieGroupEntry != NULL && - zombieGroupEntry->Group != NULL && - zombieGroupEntry->Group == ZombieGroup) { - - RemoveEntryList(groupEntry); - DsmpFreePool(zombieGroupEntry); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveZombieGroupEntry (Group %p): Found and removed a zombie group in (FOG %p)\n", - ZombieGroup, - failOverGroup)); - - // - // We removed the zombie group entry from this fail-over - // group, so we can move on to the next fail-over group. - // - break; - } - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveZombieGroupEntry (Group %p): Exiting function.\n", - ZombieGroup)); -} - - -VOID -DsmpRemoveGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY GroupEntry, - _In_ IN BOOLEAN AcquireDSMLockExclusive - ) -/*++ - -Routine Description: - - This will remove a group entry from the DSM's list. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - GroupEntry - Group entry that should be removed from DSM's list - AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively - -Return Value: - - None - ---*/ -{ - KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings - ULONG index; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; - PLIST_ENTRY entry; - PDSM_TARGET_PORT_LIST_ENTRY targetPort; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveGroupEntry (Group %p): Entering function.\n", - GroupEntry)); - - if (AcquireDSMLockExclusive) { - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - } - - NT_ASSERT(GroupEntry && GroupEntry->ListEntry.Flink && GroupEntry->ListEntry.Blink); - - // - // Since this group is being removed, we need to make sure it is also - // removed from all fail-over groups' zombie group lists. - // - DsmpRemoveZombieGroupEntry(DsmContext, GroupEntry); - - // - // Add it to the list of multi-path groups. - // - RemoveEntryList(&GroupEntry->ListEntry); - - GroupEntry->ListEntry.Flink = GroupEntry->ListEntry.Blink = NULL; - - InterlockedDecrement((LONG volatile*)&DsmContext->NumberGroups); - - for (index = 0; index < DSM_MAX_PATHS; index++) { - - // - // Clean up all its Target Port Groups - // - targetPortGroup = GroupEntry->TargetPortGroupList[index]; - - if (targetPortGroup) { - - GroupEntry->TargetPortGroupList[index] = NULL; - - // - // For each target port group, clean up all its target ports - // - while (!IsListEmpty(&targetPortGroup->TargetPortList)) { - - entry = RemoveHeadList(&targetPortGroup->TargetPortList); - - if (entry) { - - targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); - - if (targetPort) { - - PLIST_ENTRY deviceEntry; - PDSM_TARGET_PORT_DEVICELIST_ENTRY listEntry; - - while (!IsListEmpty(&targetPort->TP_DeviceList)) { - - deviceEntry = RemoveHeadList(&targetPort->TP_DeviceList); - - if (deviceEntry) { - - listEntry = CONTAINING_RECORD(deviceEntry, - DSM_TARGET_PORT_DEVICELIST_ENTRY, - ListEntry); - - if (listEntry) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRemoveGroupEntry (Group %p): Deleting device %p from TP %p list (TPG %p).\n", - GroupEntry, - listEntry->DeviceInfo, - targetPort, - targetPortGroup)); - - DsmpFreePool(listEntry); - - InterlockedDecrement((LONG volatile*)&targetPort->Count); - } - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRemoveGroupEntry (Group %p): Deleting target port %p from TPG %p list.\n", - GroupEntry, - targetPort, - targetPortGroup)); - - DsmpFreePool(targetPort); - - InterlockedDecrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); - } - } - } - - NT_ASSERT(targetPortGroup->NumberTargetPorts == 0); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRemoveGroupEntry (Group %p): Deleting target port group %p.\n", - GroupEntry, - targetPortGroup)); - - DsmpFreePool(targetPortGroup); - - InterlockedDecrement((LONG volatile*)&GroupEntry->NumberTargetPortGroups); - } - } - - NT_ASSERT(GroupEntry->NumberTargetPortGroups == 0); - - if (AcquireDSMLockExclusive) { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRemoveGroupEntry (Group %p): Exiting function.\n", - GroupEntry)); - - return; -} - - -PDSM_FAILOVER_GROUP -DsmpSetNewPath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDevice - ) -/*++ - -Routine Description: - - This routine will assign a new path to the multi-path group in - which FailingDevice resides. - - Caller must NOT hold spin lock. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - - FailingDevice - The device-path that is being moved - (due to failure, or admin. request) - -Return Value: - - The FOG containing the new path. - ---*/ -{ - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpSetNewPath (DevInfo %p): Entering function.\n", - FailingDevice)); - - if (DsmpIsSymmetricAccess(FailingDevice)) { - - DsmpSetLBForPathRemoval(DsmContext, FailingDevice, NULL, SpecialHandlingFlag); - - } else { - - DsmpSetLBForPathRemovalALUA(DsmContext, FailingDevice, NULL, SpecialHandlingFlag); - } - - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpSetNewPath (DevInfo %p): Exiting function with path (failGroup) %p.\n", - FailingDevice, - FailingDevice->Group->PathToBeUsed)); - - return FailingDevice->Group->PathToBeUsed; -} - - -PDSM_FAILOVER_GROUP -DsmpSetNewPathUsingGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group - ) -/*++ - -Routine Description: - - This routine will try to assign a new path using the given multi-path group. - This function should only be called during failover in the event that there - is no DeviceInfo with which to call DsmpSetNewPath(). - - Typically this will be called with one of a fail-over group's zombie groups. - - Caller must NOT hold spin lock. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization. - - Group - The multi-path group which to assign a new path. - -Return Value: - - The FOG containing the new path or NULL if no path was found. - ---*/ - -{ - ULONG i; - PDSM_DEVICE_INFO pDevInfo = NULL; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetNewPathUsingGroup (Group %p): Entering function.\n", - Group)); - - // - // Get the first available DeviceInfo. - // - for (i = 0; i < DSM_MAX_PATHS; i ++) { - if (Group->DeviceList[i] != NULL) { - pDevInfo = Group->DeviceList[i]; - break; - } - } - - if (pDevInfo == NULL) { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetNewPathForZombieGroup (ZombieGroup %p): No failover group can be found.\n", - Group)); - return NULL; - } - - - if (DsmpIsSymmetricAccess(pDevInfo)) { - - DsmpSetLBForPathRemoval(DsmContext, pDevInfo, Group, SpecialHandlingFlag); - - } else { - - DsmpSetLBForPathRemovalALUA(DsmContext, pDevInfo, Group, SpecialHandlingFlag); - } - - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetNewPathForZombieGroup (ZombieGroup %p): Exiting function with path (failGroup) %p.\n", - Group, - Group->PathToBeUsed)); - - return Group->PathToBeUsed; - -} - - -NTSTATUS -DsmpUpdateTargetPortGroupDevicesStates( - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN DSM_DEVICE_STATE NewState - ) -/*++ - -Routine Description: - - This routine will update the target port group and all its appropriate - devInfos (ones not in remove pending, removed, or invalidated) with the - new state. The ALUAState will only be updated, NOT the real State. - Caller needs to update the real State based on the current LB policy. - - Note: This should be called with DsmContext Lock held and should only be - called after a SetTargetPortGroups request was sent down. - -Arguments: - - TargetPortGroup - TargetPortGroup whose state and deviceInfos need to be - updated - - NewState - The new state - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PLIST_ENTRY entry = NULL; - PDSM_TARGET_PORT_LIST_ENTRY targetPort = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Entering function.\n", - TargetPortGroup)); - - if (!TargetPortGroup) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Invalid TPG passed in.\n", - TargetPortGroup)); - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpUpdateTargetPortGroupDevicesStates; - } - - // - // First update TPG's asymmetric access state. - // - TargetPortGroup->AsymmetricAccessState = NewState; - - // - // Now update state of each of the devices belonging to this TPG. - // - for (entry = TargetPortGroup->TargetPortList.Flink; - entry != &TargetPortGroup->TargetPortList; - entry = entry->Flink) { - - targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); - NT_ASSERT(targetPort); - - if (targetPort) { - - PLIST_ENTRY deviceEntry; - PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device; - - for (deviceEntry = targetPort->TP_DeviceList.Flink; - deviceEntry != &targetPort->TP_DeviceList; - deviceEntry = deviceEntry->Flink) { - - tp_device = CONTAINING_RECORD(deviceEntry, - DSM_TARGET_PORT_DEVICELIST_ENTRY, - ListEntry); - - if (tp_device) { - - tp_device->DeviceInfo->ALUAState = NewState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Updated device %p alua state to %x.\n", - TargetPortGroup, - tp_device->DeviceInfo, - tp_device->DeviceInfo->ALUAState)); - } - } - } - } - -__Exit_DsmpUpdateTargetPortGroupDevicesStates: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Exiting function with status %x.\n", - TargetPortGroup, - status)); - - return status; -} - - -VOID -DsmpIncrementCounters( - _In_ PDSM_FAILOVER_GROUP FailGroup, - _In_ PSCSI_REQUEST_BLOCK Srb - ) -{ - ULONG bytes = 0; - PCDB cdb = NULL; - ULONG cdbLength = 0; - BOOLEAN isReadWrite = FALSE; - ULONGLONG lastLba = 0; - ULONG numBlocks = 0; - ULONGLONG startLba = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpIncrementCounters (FOG %p): Entering function.\n", - FailGroup)); - - if (Srb) { - - cdb = SrbGetCdb(Srb); - - if (cdb && DsmIsReadWrite(cdb->AsByte[0])) { - - isReadWrite = TRUE; - } - } - - InterlockedIncrement(&FailGroup->NumberOfRequestsInFlight); - - // - // Update counters that apply to read/write requests - // - if (isReadWrite) { - - bytes = SrbGetDataTransferLength(Srb); - - InterlockedExchangeAdd64((LONGLONG volatile*)&FailGroup->OutstandingBytesOfIO, bytes); - - cdbLength = SrbGetCdbLength(Srb); - - if (cdbLength == 16) { - - REVERSE_BYTES_QUAD(&startLba, &cdb->CDB16.LogicalBlock); - REVERSE_BYTES(&numBlocks, &cdb->CDB16.TransferLength); - - } else { - - REVERSE_BYTES(&startLba, &cdb->CDB10.LogicalBlockByte0); - REVERSE_BYTES_SHORT(&numBlocks, &cdb->CDB10.TransferBlocksMsb); - } - - lastLba = startLba + numBlocks - 1; - - InterlockedExchange64((LONGLONG volatile*)&FailGroup->LastLba, lastLba); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpIncrementCounters (FOG %p): Exiting function.\n", - FailGroup)); - - return; -} - - -BOOLEAN -DsmpDecrementCounters( - _In_ PDSM_FAILOVER_GROUP FailGroup, - _In_ PSCSI_REQUEST_BLOCK Srb - ) -{ - ULONG bytes = 0; - PCDB cdb = NULL; - BOOLEAN isReadWrite = FALSE; - BOOLEAN isDeletionEligible = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpDecrementCounters (FOG %p): Entering function.\n", - FailGroup)); - - if (Srb) { - - cdb = SrbGetCdb(Srb); - - if (cdb && DsmIsReadWrite(cdb->AsByte[0])) { - - isReadWrite = TRUE; - } - } - - // - // Update counters that apply to read/write requests - // - if (isReadWrite) { - - bytes = SrbGetDataTransferLength(Srb); - - InterlockedExchangeAdd64((LONGLONG volatile*)&FailGroup->OutstandingBytesOfIO, -(LONGLONG)bytes); - } - - NT_ASSERT(FailGroup->NumberOfRequestsInFlight > 0); - if (InterlockedCompareExchange(&FailGroup->NumberOfRequestsInFlight, 0, 0) > 0) { - - if(InterlockedDecrement(&FailGroup->NumberOfRequestsInFlight) == 0){ - - // - // If the inflight requests on the path is zero, if needed path can be removed. - // - isDeletionEligible = TRUE; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpDecrementCounters (FOG %p): Exiting function.\n", - FailGroup)); - - return isDeletionEligible; -} - - -PDSM_FAILOVER_GROUP -DsmpGetPath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmList, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine will pick a path, for processing a request, based - on the current LoadBalance policy that is set. - - N.B: This routine must be called with DSM Context Lock held in Shared mode. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DsmList - List of DSM Ids sent by MPIO - Srb - The read/write/verify request - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - FailOver Group that should be used for processing the request ---*/ -{ - // - // Algorithm: - // ========== - // Failover-only: - // -------------- - // If symmetric LUA (ie. ALUA not supported, or symmetric LUA using ALUA semantics (viz. storage reports - // implicit-only transitions and all TPGs in A/O): - // One path AO, <- this will be the only path used for I/O - // M paths in SB, <- one of these will be made active on failure of the above active path - // Rest of the paths Failed (Invalidated/PendingRemove/Removed) - // - // If ALUA: - // One path AO, <- this will be the only path used for I/O - // M paths in AU, SB or UA <- one of these made active on failover. Pref: AU > SB > UA. Also, controller affinity. - // Rest of the paths Failed - // - // Automatic failback will happen only if Preferred path has been set. - // This is the only policy that will support failback. - // - // Round-Robin: - // ------------ - // If symmetric LUA: - // N paths AO, <- round robin among these - // Rest of the paths Failed - // - // If ALUA: - // Round Robin policy not supported since all paths can't be in A/O state. - // - // Round-Robin With Subset: - // ------------------------ - // If symmetric LUA: - // N paths AO, <- round robin among these - // M paths SB, <- if no active paths left, make one of these active - // Rest of the paths Failed - // - // If ALUA: - // N paths AO, <- round robin among these (NOTE: paths in AU not considered) - // M paths AU, SB or UA <- if no active paths, make subset of these active (based on TPG states after transition) - // Rest of the paths Failed - // - // Least-Queue Depth: - // ------------------ - // If symmetric LUA: - // N paths AO, <- one with least outstanding I/O is chosen - // Rest of the paths Failed - // - // If ALUA: - // N paths AO, <- one with least outstanding I/O is chosen - // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG - // states after transition) - one with least outstanding I/O is chosen. - // Rest of the paths Failed - // - // Least-Weighted: - // --------------- - // If symmetric LUA: - // N paths AO, <- every path has an associated weight, path with least weight is used. - // Rest of the paths Failed - // - // If ALUA: - // N paths AO, - // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG - // states after transition) - path with least weight used. - // Rest of the paths Failed - // - // Least-Blocks: - // ------------- - // If symmetric LUA: - // N paths AO, <- one with least cumulative outstanding IO is chosen - // Rest of the paths Failed - // - // If ALUA: - // N paths AO, <- one with least cumulative outstanding IO is chosen - // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG - // states after transition) - one with least cumulative outstanding is chosen. - // - // Actual implementation of algorithm happens in the following routines: DsmpGetAnyActivePath, - // DsmpGetActivePathToBeUsed, flavors of DsmpSetLBForPathXXX. - // - - PDSM_FAILOVER_GROUP failGroup = NULL; - PDSM_DEVICE_INFO deviceInfo = DsmList->IdList[0]; - PDSM_GROUP_ENTRY groupEntry; - ULONG inx = 0; - - UNREFERENCED_PARAMETER(DsmContext); - UNREFERENCED_PARAMETER(SpecialHandlingFlag); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Entering function.\n", - DsmList)); - - if (!(DsmList->Count && deviceInfo)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Called with no available paths.\n", - DsmList)); - - goto __Exit_DsmpGetPath; - } - - groupEntry = deviceInfo->Group; - DSM_ASSERT(groupEntry->GroupSig == DSM_GROUP_SIG); - - switch (groupEntry->LoadBalanceType) { - - case DSM_LB_FAILOVER: - case DSM_LB_WEIGHTED_PATHS: { - - // - // For FailOverOnly there is only one active path so we can - // just grab it from the cached location and go with it. - // For LeastWeightPath we always choose the lowest weighted - // one so we grab that and go - // - failGroup = groupEntry->PathToBeUsed; - - break; - } - - case DSM_LB_ROUND_ROBIN: - case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { - - PDSM_DEVICE_INFO candidateDevice = NULL; - PDSM_GROUP_ENTRY newGroup = NULL; - ULONG newPath; - BOOLEAN foundPath = FALSE; - ULONG jnx = 0; - ULONG counter = 0; - BOOLEAN reset = FALSE; - - for (inx = 0; inx < DsmList->Count; inx++) { - - deviceInfo = DsmList->IdList[inx]; - - if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { - - continue; - } - - - if (deviceInfo->FailGroup == groupEntry->PathToBeUsed) { - - // - // We've reached the devInfo that corresponds to the path - // that we should be using. If this devInfo is not in the - // right state to be used, we need to find the first candidate - // starting from this one to satisfy the request. - // To play it safe, we may have already considered a previous - // devInfo to be a candidate, that now needs to be reset to - // the one that we now find. - // - reset = TRUE; - } - -#if DBG - if (deviceInfo->TargetPortGroup && !DsmpIsDeviceFailedState(deviceInfo->State)) { - - if (deviceInfo->State != deviceInfo->ALUAState) { - - DSM_ASSERT(groupEntry->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && - deviceInfo->State == DSM_DEV_ACTIVE_UNOPTIMIZED && - deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED); - } - } -#endif - - if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { - - if (!candidateDevice || reset) { - - candidateDevice = deviceInfo; - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Candidate device %p.\n", - DsmList, - candidateDevice)); - - jnx = inx; - } - - if (!groupEntry->PathToBeUsed || deviceInfo->FailGroup == groupEntry->PathToBeUsed) { - - // - // The devInfo that corresponds to the path that we were - // supposed to use, is in a state that makes it usable. - // So we've found our devInfo. - // - InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)deviceInfo->FailGroup); - foundPath = TRUE; - candidateDevice = NULL; - - break; - } - } - } - - if (!foundPath) { - - if (candidateDevice) { - - InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)candidateDevice->FailGroup); - inx = jnx; - candidateDevice = NULL; - - } else { - - inx = 0; - } - } - - failGroup = groupEntry->PathToBeUsed; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Path to be used is %p.\n", - DsmList, - groupEntry->PathToBeUsed)); - - // - // The current chosen path is given by failGroup. Find the next path - // that should be chosen in the RoundRobin policy. Start with the - // device at index inx + 1, and look for the one with Active state. - // - for (counter = 0, jnx = inx + 1; - counter < DsmList->Count && !newGroup; - counter++, jnx++) { - - newPath = jnx % DsmList->Count; - - deviceInfo = DsmList->IdList[newPath]; - - if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { - - continue; - } - - - if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { - - newGroup = deviceInfo->Group; - DSM_ASSERT(newGroup == groupEntry); - - InterlockedExchangePointer(&(newGroup->PathToBeUsed), (PVOID)deviceInfo->FailGroup); - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): New Path is %p.\n", - DsmList, - newGroup->PathToBeUsed)); - - break; - } - } - - break; - } - - case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { - - LONG leastQueueDepth = 0x7FFFFFFF; - - for (inx = 0; inx < DsmList->Count; inx++) { - - deviceInfo = DsmList->IdList[inx]; - - if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { - - continue; - } - - - if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && - deviceInfo->FailGroup->NumberOfRequestsInFlight < leastQueueDepth) { - - leastQueueDepth = deviceInfo->FailGroup->NumberOfRequestsInFlight; - failGroup = deviceInfo->FailGroup; - } - } - - if (failGroup) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Path to be used for LQD is %p.\n", - DsmList, - failGroup)); - - } else { - - // - // For ALUA storage there are two cases where we are left with no - // TPG in the A/O state: - // 1) On storage that supports implicit transitions, a transition - // was initiated that left no TPG in the A/O state. - // 2) On storage that has explicit only transitions enabled, we tried - // making at least one path as A/O and failed. This can happen, - // for example, when STPG fails because this initiator is not - // registered or does not hold exclusive reservation over the - // target. - // - // For such storages, we should return some path instead of just - // failing the I/O. The path will likely be an A/U path until the - // storage does a transition to make a TPG A/O. - // - if (!DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmList->IdList[0])) { - - // - // Use the same path as the one used for the previous request. - // - failGroup = groupEntry->PathToBeUsed; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpGetPath (DsmIds %p): Using same path (FOG %p) as previous request for LQD.\n", - DsmList, - failGroup)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Failed to find a path for LQD.\n", - DsmList)); - } - } - - break; - } - - case DSM_LB_LEAST_BLOCKS: { - - ULONG bytes = 0; - PCDB cdb = NULL; - ULONG cdbLength = 0; - BOOLEAN isRead = FALSE; - BOOLEAN isWrite = FALSE; - PDSM_FAILOVER_GROUP lastPathUsed = groupEntry->PathToBeUsed; - ULONGLONG leastOutstandingIO = MAXULONGLONG; - ULONGLONG startLba = 0; - - // - // Use the last path under the following conditions: - // - // 1. This is not a read/write request or - // 2. This is a read/write request and - // a. The request is sequential and - // b. The cache is not exhausted - // - - if (Srb) { - - cdb = SrbGetCdb(Srb); - - if (cdb && DsmIsReadRequest(cdb->AsByte[0])) { - - isRead = TRUE; - } - - if (cdb && DsmIsWriteRequest(cdb->AsByte[0])) { - - isWrite = TRUE; - } - } - - if (isRead || isWrite) { - - if (groupEntry->UseCacheForLeastBlocks) { - - bytes = SrbGetDataTransferLength(Srb); - - cdbLength = SrbGetCdbLength(Srb); - - if (cdbLength == 16) { - - REVERSE_BYTES_QUAD(&startLba, &cdb->CDB16.LogicalBlock); - - } else { - - REVERSE_BYTES(&startLba, &cdb->CDB10.LogicalBlockByte0); - } - - // - // Check if: - // 1. The IO is sequential, AND - // 2. It is either: - // a. read request, OR - // b. write request and outstanding bytes will be within the cache limit - // - if ((lastPathUsed != NULL) && - (startLba >= lastPathUsed->LastLba) && - ((isRead) || - (isWrite && lastPathUsed->OutstandingBytesOfIO + bytes <= groupEntry->CacheSizeForLeastBlocks))) { - - failGroup = groupEntry->PathToBeUsed; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Sequential IO, so using same path %p for LeastBlocks.\n", - DsmList, - failGroup)); - } - } - - } else { - // - // The request is neither a read nor a write so use the same path. - // - failGroup = groupEntry->PathToBeUsed; - } - - if (!failGroup) { - - // - // Choose whichever Active/Optimized path has the least outstanding bytes. - // - for (inx = 0; inx < DsmList->Count; inx++) { - - deviceInfo = DsmList->IdList[inx]; - - if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { - - continue; - } - - - if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && - deviceInfo->FailGroup->OutstandingBytesOfIO < leastOutstandingIO) { - - leastOutstandingIO = deviceInfo->FailGroup->OutstandingBytesOfIO; - failGroup = deviceInfo->FailGroup; - } - } - } - - if (failGroup) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Path to be used for LeastBlocks is %p.\n", - DsmList, - failGroup)); - - } else { - - // - // For ALUA storage there are two cases where we are left with no - // TPG in the A/O state: - // 1) On storage that supports implicit transitions, a transition - // was initiated that left no TPG in the A/O state. - // 2) On storage that has explicit only transitions enabled, we tried - // making at least one path as A/O and failed. This can happen, - // for example, when STPG fails because this initiator is not - // registered or does not hold exclusive reservation over the - // target. - // - // For such storages, we should return some path instead of just - // failing the I/O. The path will likely be an A/U path until the - // storage does a transition to make a TPG A/O. - // - if (!DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmList->IdList[0])) { - - // - // Use the same path as the one used for the previous request. - // - failGroup = groupEntry->PathToBeUsed; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpGetPath (DsmIds %p): Using same path (FOG %p) as previous request for LeastBlocks.\n", - DsmList, - failGroup)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Failed to find a path for LeastBlocks.\n", - DsmList)); - } - } - - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Invalid LB Type %d set for group %p.\n", - DsmList, - groupEntry->LoadBalanceType, - groupEntry)); - - DSM_ASSERT(FALSE); - - break; - } - } - -__Exit_DsmpGetPath: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpGetPath (DsmIds %p): Exiting function with failGroup %p.\n", - DsmList, - failGroup)); - - return failGroup; -} - - -PVOID -DsmpGetPathIdFromPassThroughPath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmList, - _In_ PIRP Irp, - _Inout_ IN OUT NTSTATUS *Status - ) -/*++ - -Routine Description: - - This routine will pick the path that corresponds to the PathId - in the mpio pass through structure. - - NOTE: Caller must ensure that the IRP is either MPTP or MPTPD. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DsmList - List of DSM Ids sent by MPIO - Irp - The MPTP or MPTPD request - Status - Returned status - -Return Value: - - The PathId to which the request should be sent ---*/ -{ - PDSM_FAILOVER_GROUP failGroup = NULL; - PDSM_GROUP_ENTRY groupEntry; - PDSM_DEVICE_INFO deviceInfo; - ULONG inx = 0; - NTSTATUS status = STATUS_INVALID_PARAMETER; - KIRQL irql; - PVOID newPath = NULL; - BOOLEAN found = FALSE; - BOOLEAN useScsiAddress = FALSE; - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); - ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; - UCHAR pathId = 0; - UCHAR targetId = 0; - UCHAR portNumber = 0; - ULONGLONG mpioPathId = 0; - -#if DBG - BOOLEAN useMpioPathId = FALSE; -#endif - - // - // Extract the parameters from the passthrough based on the bitness of the - // process (32 or 64) and the type of passthrough (legacy or extended). - // -#if defined (_WIN64) - if (IoIs32bitProcess(Irp)) { - - if (DsmpIsMPIOPassThroughEx(controlCode)) { - PMPIO_PASS_THROUGH_PATH32_EX mpioPassThroughPath32 = (PMPIO_PASS_THROUGH_PATH32_EX)(Irp->AssociatedIrp.SystemBuffer); - PSCSI_PASS_THROUGH32_EX passThrough32 = (PSCSI_PASS_THROUGH32_EX)((PUCHAR)mpioPassThroughPath32 + mpioPassThroughPath32->PassThroughOffset); - - useScsiAddress = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; - #if DBG - useMpioPathId = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_PATHID; - #endif - - if (useScsiAddress) { - PSTOR_ADDRESS address; - if (passThrough32->StorAddressOffset < sizeof(SCSI_PASS_THROUGH_EX) || - passThrough32->StorAddressLength < sizeof(STOR_ADDRESS)) { - *Status = STATUS_INVALID_PARAMETER; - return NULL; - } - address = (PSTOR_ADDRESS)((PUCHAR)passThrough32 + passThrough32->StorAddressOffset); - if (address->Type != STOR_ADDRESS_TYPE_BTL8 || - address->AddressLength < STOR_ADDR_BTL8_ADDRESS_LENGTH) { - *Status = STATUS_INVALID_PARAMETER; - return NULL; - } - pathId = ((PSTOR_ADDR_BTL8)address)->Path; - targetId = ((PSTOR_ADDR_BTL8)address)->Target; - portNumber = mpioPassThroughPath32->PortNumber; - } else { - mpioPathId = mpioPassThroughPath32->MpioPathId; - } - - } else { - PMPIO_PASS_THROUGH_PATH32 mpioPassThroughPath32 = (PMPIO_PASS_THROUGH_PATH32)(Irp->AssociatedIrp.SystemBuffer); - - useScsiAddress = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; - #if DBG - useMpioPathId = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_PATHID; - #endif - - if (useScsiAddress) { - pathId = mpioPassThroughPath32->PassThrough.PathId; - targetId = mpioPassThroughPath32->PassThrough.TargetId; - portNumber = mpioPassThroughPath32->PortNumber; - } else { - mpioPathId = mpioPassThroughPath32->MpioPathId; - } - } - } else -#endif - if (DsmpIsMPIOPassThroughEx(controlCode)) { - PMPIO_PASS_THROUGH_PATH_EX mpioPassThroughPath = (PMPIO_PASS_THROUGH_PATH_EX)(Irp->AssociatedIrp.SystemBuffer); - PSCSI_PASS_THROUGH_EX passThrough = (PSCSI_PASS_THROUGH_EX)((PUCHAR)mpioPassThroughPath + mpioPassThroughPath->PassThroughOffset); - - useScsiAddress = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; - #if DBG - useMpioPathId = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_PATHID; - #endif - - if (useScsiAddress) { - PSTOR_ADDRESS address; - if (passThrough->StorAddressOffset < sizeof(SCSI_PASS_THROUGH_EX) || - passThrough->StorAddressLength < sizeof(STOR_ADDRESS)) { - *Status = STATUS_INVALID_PARAMETER; - return NULL; - } - address = (PSTOR_ADDRESS)((PUCHAR)passThrough + passThrough->StorAddressOffset); - if (address->Type != STOR_ADDRESS_TYPE_BTL8 || - address->AddressLength < STOR_ADDR_BTL8_ADDRESS_LENGTH) { - *Status = STATUS_INVALID_PARAMETER; - return NULL; - } - pathId = ((PSTOR_ADDR_BTL8)address)->Path; - targetId = ((PSTOR_ADDR_BTL8)address)->Target; - portNumber = mpioPassThroughPath->PortNumber; - } else { - mpioPathId = mpioPassThroughPath->MpioPathId; - } - } else { - PMPIO_PASS_THROUGH_PATH mpioPassThroughPath = (PMPIO_PASS_THROUGH_PATH)(Irp->AssociatedIrp.SystemBuffer); - - useScsiAddress = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; - #if DBG - useMpioPathId = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_PATHID; - #endif - - if (useScsiAddress) { - pathId = mpioPassThroughPath->PassThrough.PathId; - targetId = mpioPassThroughPath->PassThrough.TargetId; - portNumber = mpioPassThroughPath->PortNumber; - } else { - mpioPathId = mpioPassThroughPath->MpioPathId; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Entering function.\n", - DsmList)); - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - deviceInfo = DsmList->IdList[0]; - groupEntry = deviceInfo->Group; - DSM_ASSERT(groupEntry->GroupSig == DSM_GROUP_SIG); - // - // useMpioPathId is BOOLEAN (0 or 1) since MPIO_IOCTL_FLAG_USE_PATHID = 1 - // But since MPIO_IOCTL_FLAG_USE_SCSIADDRESS = 0x2, - // useScsiAddress could have a value of 2 if set. Use logical NOT to make boolean before comparing below - // - DSM_ASSERT(useMpioPathId == !useScsiAddress); - - for (inx = 0; inx < DSM_MAX_PATHS; inx++) { - - deviceInfo = groupEntry->DeviceList[inx]; - - if (deviceInfo) { - - failGroup = deviceInfo->FailGroup; - - if (failGroup) { - - if (useScsiAddress) { - - if (portNumber == deviceInfo->ScsiAddress->PortNumber && - pathId == deviceInfo->ScsiAddress->PathId && - targetId == deviceInfo->ScsiAddress->TargetId) { - - found = TRUE; - break; - } - } else { - - NT_ASSERT(useMpioPathId); - - if ((ULONGLONG)((ULONG_PTR)(failGroup->PathId)) == mpioPathId) { - - found = TRUE; - break; - } - } - } - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - if (found) { - - newPath = failGroup->PathId; - status = STATUS_SUCCESS; - - // - // This should not affect the next path chosen based on the - // current LB policy, so do NOT update groupEntry->PathToBeUsed - // - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Failed to get corresponding path.\n", - DsmList)); - } - - if (Status) { - - *Status = status; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Exiting function with path %p and status %x.\n", - DsmList, - newPath, - status)); - - return newPath; -} - - -BOOLEAN -DsmpShouldRetryTPGRequest( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ) -/*++ - -Routine Description: - - This routine determines if a Report/Set TargetPortGroup request (sent either - as a passThrough or as an IRP_MJ_SCSI) needs to be retried. - -Arguments: - - SenseData - Pointer to Sense Data information buffer. - SenseDataSize - Size of the passed in sense data buffer. - -Return Value: - - TRUE if sense information indicates a retry-able error, else FALSE. - ---*/ -{ - BOOLEAN retry = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryTPGRequest (SenseData %p): Entering function.\n", - SenseData)); - - // - // Two types of conditions need to be retried: - // 1. Asymmetric Access State Changed - // 2. Asymmetric Access State Transition - // - - // - // Check if asymmetric access state changed - // - retry = DsmpShouldRetryPassThroughRequest(SenseData, SenseDataSize); - if (!retry) { - - BOOLEAN validSense = FALSE; - UCHAR senseKey = 0; - UCHAR addSenseCode = 0; - UCHAR addSenseCodeQualifier = 0; - - validSense = ScsiGetSenseKeyAndCodes(SenseData, - SenseDataSize, - SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, - &senseKey, - &addSenseCode, - &addSenseCodeQualifier); - if (validSense) { - - if (senseKey == SCSI_SENSE_NOT_READY) { - - switch (addSenseCode) { - case SCSI_ADSENSE_LUN_NOT_READY: { - - // - // Check if asymmetric access state transitioning - // - if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION) { - - retry = TRUE; - } - break; - } - - case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: { - - if (addSenseCodeQualifier == SCSI_SENSEQ_REPORTED_LUNS_DATA_CHANGED) { - - retry = TRUE; - } - break; - } - - default: { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryTPGRequest (SenseData %p): AddSenseCode %x. Not retrying.\n", - SenseData, - addSenseCode)); - - retry = FALSE; - break; - } - } - } - } else { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryTPGRequest (SenseData %p): Sense data size %d not big enough.\n", - SenseData, - SenseDataSize)); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryTPGRequest (SenseData %p): Exiting function with retry %x.\n", - SenseData, - retry)); - - return retry; -} - - -BOOLEAN -DsmpIsDeviceRemoved( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ) -/*++ - -Routine Description: - - This routine evaluate Sense Data and determine if LUN is available or not. - -Arguments: - - SenseData - Pointer to Sense Data information buffer. - SenseDataSize - Size of the passed in sense data buffer. - -Return Value: - - TRUE if device is no longer available, else FALSE. - ---*/ -{ - BOOLEAN validSense = FALSE; - UCHAR senseKey = 0; - UCHAR addSenseCode = 0; - UCHAR addSenseCodeQualifier = 0; - BOOLEAN bRemoved = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpIsDeviceRemoved (SenseData %p): Entering function.\n", - SenseData)); - - validSense = ScsiGetSenseKeyAndCodes(SenseData, - SenseDataSize, - SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, - &senseKey, - &addSenseCode, - &addSenseCodeQualifier); - - if (validSense) { - // - // SPC 3 6.25 suggests response should follow Test Unit Ready responses - // For now, we accept Ileegal Request as an indication of device not in available - // state. - // - if (senseKey == SCSI_SENSE_ILLEGAL_REQUEST) { - - ASSERT(addSenseCodeQualifier == 0); //LOGICAL UNIT NOT SUPPORTED - - bRemoved = TRUE; - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpIsDeviceRemoved (SenseData %p): SenseKey %x AddSenseCode %x. Remove %x\n", - SenseData, - senseKey, - addSenseCode, - bRemoved)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpIsDeviceRemoved (SenseData %p): Exiting function. Removed %x.\n", - SenseData, - bRemoved)); - - return bRemoved; -} - - -BOOLEAN -DsmpReservationCommand( - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb - ) -/*++ - -Routine Description: - - This routine examines the DeviceIoControlCode and Srb OpCode to determine - if this is PR request. - -Arguments: - - Irp - The Irp containing Srb. - Srb - The current non-read/write Srb. - -Return Value: - - TRUE - If it's a special-case command (some reservation-handling request). - ---*/ -{ - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); - UCHAR opCode = 0; - BOOLEAN isReservationCommand = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpReservationCommand (Irp %p): Entering function.\n", - Irp)); - - // - // Ensure it's a scsi request before checking the opcode. - // - if (irpStack->MajorFunction == IRP_MJ_SCSI) { - - PCDB cdb = SrbGetCdb(Srb); - if (cdb != NULL) { - opCode = cdb->AsByte[0]; - - if (opCode == SCSIOP_PERSISTENT_RESERVE_IN || opCode == SCSIOP_PERSISTENT_RESERVE_OUT) { - - // - // Set or release a reservation. - // - isReservationCommand = TRUE; - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpReservationCommand (Irp %p): Exiting function - IsReservationCmd %x.\n", - Irp, - isReservationCommand)); - - return isReservationCommand; -} - - -BOOLEAN -DsmpMpioPassThroughPathCommand( - _In_ IN PIRP Irp - ) -/*++ - -Routine Description: - - This routine examines the DeviceIoControlCode to determine whether this is - either a mpio pass through or a mpio pass through direct. If so, it needs - to be handled via a specific path indicated by the caller. - -Arguments: - - Irp - The Irp. - -Return Value: - - TRUE - If it is either MPTP or MPTPD. - FALSE - Otherwise. - ---*/ -{ - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); - ULONG ioctlCode; - BOOLEAN isMPTPCommand = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpMpioPassThroughPathCommand (Irp %p): Entering function.\n", - Irp)); - - if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { - - // - // Check whether this is a MPTP, MPTPD, or an extended flavor. - // - ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; - - if (ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH || - ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT || - ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_EX || - ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT_EX) { - - isMPTPCommand = TRUE; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpMpioPassThroughPathCommand (Irp %p): Exiting function - IsMpioPassThruPathCmd %!bool!.\n", - Irp, - isMPTPCommand)); - - return isMPTPCommand; -} - - -VOID -DsmpRequestComplete( - _In_ IN PVOID DsmId, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PVOID DsmContext - ) -/*++ - -Routine Description: - - This routine is called from mpio's completion routine when the Irp - has been completed by the port driver. Currently, it updates some counters - and free's the context back to the look-aside list. - -Arguments: - - DsmIds - The collection of DSM IDs that pertain to the MPDISK. - Irp - Irp containing SRB. - Srb - Scsi request block - DsmContext - DSM context given to MPIO during initialization - -Return Value: - - NONE - ---*/ - -{ - PDSM_DEVICE_INFO deviceInfo = DsmId; - PDSM_CONTEXT dsmContext = (PDSM_CONTEXT)DsmContext; - UCHAR opCode = 0xFF; - ULONG dataTransferLength = 0; - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); - PDSM_FAILOVER_GROUP failGroup = irpStack->Parameters.Others.Argument3; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpRequestComplete (DevInfo %p): Entering function.\n", - DsmId)); - - DSM_ASSERT(DsmContext); - - if (Srb) { - PCDB cdb = SrbGetCdb(Srb); - if (cdb) { - opCode = cdb->AsByte[0]; - } - dataTransferLength = SrbGetDataTransferLength(Srb); - } - - // - // Extract the interesting bits from the context struct. - // - - if (failGroup) { - - if (DsmpDecrementCounters(failGroup, Srb)) { - - // - // If there are no requests on a path that is supposed to be removed, remove it now. - // - if (failGroup->State == DSM_FG_PENDING_REMOVE) { - - KIRQL oldIrql; - - NT_ASSERT(failGroup->Count == 0); - - oldIrql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - RemoveEntryList(&failGroup->ListEntry); - InterlockedDecrement((LONG volatile*)&dsmContext->NumberStaleFOGroups); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpRequestComplete (DevInfo %p): Removing FOGroup %p with path %p.\n", - DsmId, - failGroup, - failGroup->PathId)); - - DsmpFreePool(failGroup); - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), oldIrql); - } - } - } - - // - // Note: We use the deviceInfo passed in since the one saved off in the - // context may be stale in the case of a retried I/O - // - if (deviceInfo) { - - // - // If statistics gathering is enabled update the inflight request count - // for this device-path pairing. - // - if (!dsmContext->DisableStatsGathering) { - - // - // Indicate one less request on this device. - // Update the path that on which the increment was done. - // - if (InterlockedCompareExchange((LONG volatile*)&deviceInfo->NumberOfRequestsInProgress, 0, 0) > 0) { - InterlockedDecrement(&(deviceInfo->NumberOfRequestsInProgress)); - } - } - - // - // If statistics gathering is enabled, we are interested in read/write requests - // - if (!dsmContext->DisableStatsGathering) { - - // - // If it's a read or a write, update the stats. - // Use the path that was cached during dispatch. - // - if (DsmIsReadRequest(opCode)) { - - if (deviceInfo->DeviceStats.NumberReads <= MAXULONG) { - - InterlockedIncrement((LONG volatile*)&deviceInfo->DeviceStats.NumberReads); - } - - if ((MAXULONGLONG - dataTransferLength) > deviceInfo->DeviceStats.BytesRead) { - - deviceInfo->DeviceStats.BytesRead += dataTransferLength; - - } else { - - deviceInfo->DeviceStats.BytesRead = MAXULONGLONG; - } - - } else if (DsmIsWriteRequest(opCode)) { - - if (deviceInfo->DeviceStats.NumberWrites <= MAXULONG) { - - InterlockedIncrement((LONG volatile*)&deviceInfo->DeviceStats.NumberWrites); - } - - if ((MAXULONGLONG - dataTransferLength) > deviceInfo->DeviceStats.BytesWritten) { - - deviceInfo->DeviceStats.BytesWritten += dataTransferLength; - - } else { - - deviceInfo->DeviceStats.BytesWritten = MAXULONGLONG; - } - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpRequestComplete (DevInfo %p): Exiting function.\n", - DsmId)); - - return; -} - - -NTSTATUS -DsmpRegisterPersistentReservationKeys( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN BOOLEAN Register - ) -/*++ - -Routine Description: - - This routine is used to build and send down the request to register - or unregister the persistent reservation keys to the device down - the path given by DeviceInfo. - -Arguments: - - DeviceInfo - Device-path pair to use for sending down the request - Register - Flag to indicate whether to register or unregister the keys. - -Return Value: - - STATUS_SUCCESS on success, else appropriate failure code. - ---*/ -{ - PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; - PCDB cdb; - PPRO_PARAMETER_LIST parameters; - IO_STATUS_BLOCK ioStatus; - NTSTATUS status = STATUS_SUCCESS; - ULONG length; - PDSM_DEVICE_INFO deviceInfo = DeviceInfo; - PDSM_GROUP_ENTRY group; - ULONGLONG saKey; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Entering function - Register = %x.\n", - deviceInfo, - Register)); - - group = DeviceInfo->Group; - - NT_ASSERT(group && group->PRKeyValid); - - if (DeviceInfo->State >= DSM_DEV_FAILED) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Unusable - state %d.\n", - deviceInfo, - deviceInfo->State)); - - status = STATUS_UNSUCCESSFUL; - goto __Exit_DsmpRegisterPersistentReservationKeys; - } - - // - // Build a pass through command to process Persistent Reserve Out - // for registering the device. - // - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - passThrough = DsmpAllocatePool(NonPagedPoolNx, - length, - DSM_TAG_PASS_THRU); - if (!passThrough) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Failed to allocate memory for persistent reserve.\n", - deviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegisterPersistentReservationKeys; - } - - REVERSE_BYTES_QUAD(&saKey, &group->PersistentReservationRegisteredKey); - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Attempting PR-Out SA %u, Type %u, Scope %u, PR-Key %I64x.\n", - deviceInfo, - group->PRServiceAction, - group->PRType, - group->PRScope, - saKey)); - -__RetryRequest: - - // - // Build the cdb to reserve the device (Logical Unit). The type of reservation - // scope and service action is whatever cluster service provided at the time of - // sending down registration to this device before this particular path was available. - // - cdb = (PCDB) passThrough->ScsiPassThrough.Cdb; - cdb->PERSISTENT_RESERVE_OUT.OperationCode = SCSIOP_PERSISTENT_RESERVE_OUT; - cdb->PERSISTENT_RESERVE_OUT.ServiceAction = group->PRServiceAction; - cdb->PERSISTENT_RESERVE_OUT.Scope = group->PRScope; - cdb->PERSISTENT_RESERVE_OUT.Type = group->PRType; - cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = sizeof(PRO_PARAMETER_LIST); - - passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); - passThrough->ScsiPassThrough.CdbLength = 10; - passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; - passThrough->ScsiPassThrough.DataIn = 0; - passThrough->ScsiPassThrough.DataTransferLength = sizeof(PRO_PARAMETER_LIST); - passThrough->ScsiPassThrough.TimeOutValue = 20; - passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); - passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); - - parameters = (PPRO_PARAMETER_LIST)(passThrough->DataBuffer); - - // - // Copy the persistent reservation key given by cluster service to - // Service Action Reservation Key. This key will be registered - // with the device. - // - // Set ServiceActionReservationKey to the well-known key if we are registering. - // Note that to unregister ServiceActionReservationKey needs to be set to 0. - // - if (Register) { - - RtlCopyMemory(parameters->ServiceActionReservationKey, group->PersistentReservationRegisteredKey, 8); - - } else { - - RtlCopyMemory(parameters->ReservationKey, group->PersistentReservationRegisteredKey, 8); - RtlZeroMemory(parameters->ServiceActionReservationKey, 8); - } - - DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, - DeviceInfo->TargetObject, - passThrough, - passThrough, - length, - length, - FALSE, - &ioStatus); - - status = ioStatus.Status; - - if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(ioStatus.Status))) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Persistent Reserve (Register Key) succeeded using %p.\n", - deviceInfo, - DeviceInfo)); - - } else { - - PUCHAR senseData; - UCHAR senseInfoLength; - - senseData = (PUCHAR)(passThrough->SenseInfoBuffer); - senseInfoLength = passThrough->ScsiPassThrough.SenseInfoLength; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): DevInfo %p, Register keys (%d): NTStatus %x, ScsiStatus %x.\n", - deviceInfo, - DeviceInfo, - Register, - ioStatus.Status, - passThrough->ScsiPassThrough.ScsiStatus)); - - if (DsmpShouldRetryPassThroughRequest((PVOID)senseData, senseInfoLength)) { - - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - RtlZeroMemory(passThrough, length); - - goto __RetryRequest; - - } else if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Will change success to error status for register\n", - deviceInfo)); - - status = STATUS_INVALID_DEVICE_REQUEST; - } - } - - // - // Free the passthrough + data buffer. - // - DsmpFreePool(passThrough); - -__Exit_DsmpRegisterPersistentReservationKeys: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpRegisterPersistentReservationKeys (DevInfo %p): Exiting function with status %x.\n", - DeviceInfo, - status)); - - return status; -} - - - -BOOLEAN -DsmpShouldRetryPassThroughRequest( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ) -/*++ - -Routine Description: - - This routine determines if a passthrough request needs to be retried based on the - information in the passed in sense data. - -Arguments: - - SenseData - Pointer to Sense Data information buffer. - SenseDataSize - Size of the passed in sense data buffer. - -Return Value: - - TRUE if sense information indicates a retry-able error, else FALSE. - ---*/ -{ - BOOLEAN validSense = FALSE; - UCHAR senseKey = 0; - UCHAR addSenseCode = 0; - BOOLEAN retry = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPassThroughRequest (SenseData %p): Entering function.\n", - SenseData)); - -#if DBG - if (SenseDataSize > 0) { - - ULONG inx; - PUCHAR senseInfo; - - - senseInfo = (PUCHAR) SenseData; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPassThroughRequest (SenseData %p): Sense info length %d. Sense Info : ", - SenseData, - SenseDataSize)); - - for (inx = 0; inx < SenseDataSize; inx++) { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "%x ", - senseInfo[inx])); - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "\n")); - } -#endif - - validSense = ScsiGetSenseKeyAndCodes(SenseData, - SenseDataSize, - SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, - &senseKey, - &addSenseCode, - NULL); - if (validSense) { - if (senseKey == SCSI_SENSE_UNIT_ATTENTION) { - - switch (addSenseCode) { - case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: - case SCSI_ADSENSE_BUS_RESET: - case SCSI_ADSENSE_PARAMETERS_CHANGED: { - retry = TRUE; - break; - } - - default: { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPassThroughRequest (SenseData %p): AddSenseCode %x. Not retrying.\n", - SenseData, - addSenseCode)); - - retry = FALSE; - break; - } - } - } - } else { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPassThroughRequest (SenseData %p): Sense data size %d not big enough.\n", - SenseData, - SenseDataSize)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPassThroughRequest (SenseData %p): Exiting function with retry %x.\n", - SenseData, - retry)); - - return retry; -} - - -BOOLEAN -DsmpShouldRetryPersistentReserveCommand( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ) -/*++ - -Routine Description: - - This routine determines if a a PR request needs to be retried based on the - information in the passed in sense data. - -Arguments: - - SenseData - Pointer to Sense Data information buffer. - SenseDataSize - Size of the passed in sense data buffer. - -Return Value: - - TRUE if sense information indicates a retry-able error, else FALSE. - ---*/ -{ - BOOLEAN retry = FALSE; - BOOLEAN validSense = FALSE; - UCHAR senseKey = 0; - UCHAR addSenseCode = 0; - UCHAR addSenseCodeQualifier = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Entering function.\n", - SenseData)); - - retry = DsmpShouldRetryPassThroughRequest(SenseData, SenseDataSize); - - if (!retry) { - validSense = ScsiGetSenseKeyAndCodes(SenseData, - SenseDataSize, - SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, - &senseKey, - &addSenseCode, - &addSenseCodeQualifier); - if (validSense) { - - // - // If the TPG is in transitioning state, retry the request - // - if ((senseKey == SCSI_SENSE_UNIT_ATTENTION || senseKey == SCSI_SENSE_NOT_READY) && - (addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && - addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION)) { - - retry = TRUE; - } - } else { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Sense data size %d not big enough.\n", - SenseData, - SenseDataSize)); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Exiting function with retry %x.\n", - SenseData, - retry)); - - return retry; -} - - -VOID -DsmpAllowStandbyPathsToRest( - _In_ PDSM_GROUP_ENTRY Group - ) -/*++ - -Routine Description: - - This routine is called when a new path is available for a device - and has a desired state of ACTIVE_O. Since this will be an ACTIVE_O - path we see if there are any paths with a desired state of Standby - but that are currently active. These paths can be safely moved by - to standby. - - This routine assumes that the lock is held - -Arguements: - - Group is the multipath group - -Return Value: - - None ---*/ -{ - PDSM_DEVICE_INFO existingDeviceInfo; - ULONG inx; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpAllowStandbyPathsToRest (Group %p): Entering function.\n", - Group)); - - for (inx = 0; inx < Group->NumberDevices; inx++) { - - existingDeviceInfo = Group->DeviceList[inx]; - - if ((existingDeviceInfo->DesiredState == DSM_DEV_STANDBY) && - (existingDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED)) { - - existingDeviceInfo->State = DSM_DEV_STANDBY; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpAllowStandbyPathsToRest (Group %p): DevInfo %p changed to state %d at %d\n", - Group, - existingDeviceInfo, - existingDeviceInfo->State, - __LINE__)); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpAllowStandbyPathsToRest (Group %p): Exiting function.\n", - Group)); - return; -} - - -PDSM_DEVICE_INFO -DsmpGetAnyActivePath( - _In_ PDSM_GROUP_ENTRY Group, - _In_ BOOLEAN Exception, - _In_opt_ PDSM_DEVICE_INFO DeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine will return an active path from the list - - This routine assumes that the DSM lock is held - -Arguements: - - Group is the multipath group - Exception - if TRUE, indicates that the returned devInfo must not be the same - as the one passed in. - DeviceInfo - the must-not-match devInfo. Valid parameter only if Exception is - TRUE. - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - active path or NULL - ---*/ -{ - PDSM_DEVICE_INFO existingDeviceInfo; - PDSM_DEVICE_INFO candidateDevInfo = NULL; - ULONG inx; - - UNREFERENCED_PARAMETER(SpecialHandlingFlag); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetAnyActivePath (Group %p): Entering function.\n", - Group)); - - for (inx = 0; inx < DSM_MAX_PATHS; inx++) { - - existingDeviceInfo = Group->DeviceList[inx]; - - if (existingDeviceInfo && - existingDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && - DsmpIsDeviceInitialized(existingDeviceInfo) && - DsmpIsDeviceUsable(existingDeviceInfo) && - DsmpIsDeviceUsablePR(existingDeviceInfo)) { - - - if (Exception && existingDeviceInfo == DeviceInfo) { - continue; - } - - candidateDevInfo = existingDeviceInfo; - break; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetAnyActivePath (Group %p): Exiting function with DevInfo %p\n", - Group, - candidateDevInfo)); - - return candidateDevInfo; -} - - -PDSM_DEVICE_INFO -DsmpGetActivePathToBeUsed( - _In_ PDSM_GROUP_ENTRY Group, - _In_ BOOLEAN Symmetric, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine will return an active path from the list that should - be the next one used by the DSM - - This routine assumes that the DSM lock is held - -Arguements: - - Group is the multipath group - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - active path or NULL - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetActivePathToBeUsed (Group %p): Entering function.\n", - Group)); - - deviceInfo = NULL; - - switch (Group->LoadBalanceType) { - - case DSM_LB_LEAST_BLOCKS: - case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { - - // - // Since we choose the path with the smallest queue or cumulative size in - // DsmpGetPath, we just pick any path now - // - - // fall through - } - - case DSM_LB_ROUND_ROBIN_WITH_SUBSET: - case DSM_LB_ROUND_ROBIN: { - - // - // For RR and RRS we just pick any active path to start with - // and the DsmpGetPath will do the round robining - // - } - - case DSM_LB_FAILOVER: { - - deviceInfo = DsmpGetAnyActivePath(Group, FALSE, NULL, SpecialHandlingFlag); - - break; - } - - case DSM_LB_WEIGHTED_PATHS: { - - PDSM_DEVICE_INFO workDeviceInfo; - ULONG weight = (ULONG) -1; - ULONG inx; - - for (inx = 0; inx < Group->NumberDevices; inx++) { - - workDeviceInfo = Group->DeviceList[inx]; - - if ((workDeviceInfo) && - (DsmpIsDeviceInitialized(workDeviceInfo)) && - (DsmpIsDeviceUsable(workDeviceInfo)) && - (DsmpIsDeviceUsablePR(workDeviceInfo)) && - (workDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) && - (workDeviceInfo->PathWeight < weight)) { - - // - // We found a path that is active and is at - // the lowest weight. Remember it. - // - weight = workDeviceInfo->PathWeight; - - deviceInfo = workDeviceInfo; - } - } - - break; - } - - default: { - - break; - } - } - - if (!deviceInfo && !Symmetric) { - - // - // In the case of implicit transitions, it is possible that a TPG hasn't yet - // been made A/O. So instead of not setting any path, fall back to using some - // other path. IO sent down this path may fail, but will be retried in - // InterpretError(). Hopefully by then, at least one TPG will have transitioned - // to A/O state. - // - // The same argument holds true if the storage supports both implicit and - // explicit transitions, since it is possible that after we explicitly changed - // the TPG states, an implicit transition left us with no path in A/O state. - // - // In the case of explicit only transitions, we tried making at least one path - // as A/O and failed. This can happen, for example, when STPG fails because - // this initiator is not registered or does not hold exclusive reservation over - // the target. Instead of not using any path, we can consider a path in A/U state, - // A/U being just a functional path state. - // - BOOLEAN sendTPG = FALSE; - - deviceInfo = DsmpFindStandbyPathToActivateALUA(Group, &sendTPG, SpecialHandlingFlag); - - if ((deviceInfo != NULL) && - ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_EXPLICIT) || - ((deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT) && - (deviceInfo->State <= DSM_DEV_ACTIVE_UNOPTIMIZED)))) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpGetActivePathToBeUsed (Group %p): Using best alternative candidate device %p\n", - Group, - deviceInfo)); - } else { - - deviceInfo = NULL; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpGetActivePathToBeUsed (Group %p): No active/alternative path available for group\n", - Group)); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetActivePathToBeUsed (Group %p): Exiting function with devInfo %p.\n", - Group, - deviceInfo)); - - return deviceInfo; -} - - -PDSM_DEVICE_INFO -DsmpFindStandbyPathToActivate( - _In_ PDSM_GROUP_ENTRY Group, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine will find another path in the group that is active - - This routine assumes that the DSM lock is held - - This is used by devices that support symmetric LUA. - -Arguements: - - Group is the multipath group - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Standby path or NULL if no standby path is available - ---*/ -{ - PDSM_DEVICE_INFO existingDeviceInfo; - ULONG inx; - PDSM_DEVICE_INFO candidateDevInfo = NULL; - - UNREFERENCED_PARAMETER(SpecialHandlingFlag); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindStandbyPathToActivate(Group %p): Entering function.\n", - Group)); - - for (inx = 0; inx < Group->NumberDevices; inx++) { - - existingDeviceInfo = Group->DeviceList[inx]; - - if (existingDeviceInfo && - existingDeviceInfo->State == DSM_DEV_STANDBY && - DsmpIsDeviceInitialized(existingDeviceInfo) && - DsmpIsDeviceUsable(existingDeviceInfo) && - DsmpIsDeviceUsablePR(existingDeviceInfo)) { - - // - // If we don't as yet have a candidate, pick the first available one. - // However, our preference is one that is through the preferred TPG. - // - if (!candidateDevInfo || - existingDeviceInfo->TargetPortGroup && existingDeviceInfo->TargetPortGroup->Preferred) { - - candidateDevInfo = existingDeviceInfo; - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindStandbyPathToActivate (Group %p): Exiting function with devInfo %p.\n", - Group, - candidateDevInfo)); - - return candidateDevInfo; -} - - -PDSM_DEVICE_INFO -DsmpFindStandbyPathToActivateALUA( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PBOOLEAN SendTPG, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine will find another path in the group that is active - - This is used by devices that don't support symmetric LUA. - - N.B: This routine MUST be called with DsmContextLock held in either Shared or - Exclusive mode. - -Arguements: - - Group is the multipath group - SendTPG - output parameter that indicates if TPG command need to be sent down. - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Standby path or NULL if no standby path is available - ---*/ -{ - PDSM_DEVICE_INFO existingDeviceInfo; - ULONG inx; - PDSM_DEVICE_INFO candidateDevInfo = NULL; - - UNREFERENCED_PARAMETER(SpecialHandlingFlag); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindStandbyPathToActivateALUA (Group %p): Entering function.\n", - Group)); - - for (inx = 0; inx < Group->NumberDevices; inx++) { - - existingDeviceInfo = Group->DeviceList[inx]; - - // - // The candidate for making A/O obviously mustn't be in a failed state - // and should have a path assigned. - // - if (existingDeviceInfo && - !DsmpIsDeviceFailedState(existingDeviceInfo->State) && - DsmpIsDeviceInitialized(existingDeviceInfo) && - DsmpIsDeviceUsable(existingDeviceInfo) && - DsmpIsDeviceUsablePR(existingDeviceInfo)) { - - // - // If we don't have any candidate currently, choose the very first - // one that is in a non-failure state, regardless of what state it - // may be in. - // - if (!candidateDevInfo) { - - candidateDevInfo = existingDeviceInfo; - *SendTPG = TRUE; - } - - // - // Might as well use one that the Admin desires for to be in A/O - // - if (existingDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { - - // - // However, such a devInfo is not a better candidate if our candidate - // devInfo is also one that the Admin desires be in A/O, and it is - // through a preferred TPG. - // - if (!(existingDeviceInfo->DesiredState == candidateDevInfo->DesiredState && - candidateDevInfo->TargetPortGroup->Preferred)) { - - candidateDevInfo = existingDeviceInfo; - *SendTPG = TRUE; - } - } - - // - // Check if the current one is at least better than the candidate. - // - if (DsmpIsBetterDeviceState(candidateDevInfo->State, existingDeviceInfo->State)) { - - candidateDevInfo = existingDeviceInfo; - *SendTPG = TRUE; - } - - // - // We found one that we may have just masked as non-A/O. This is the - // best option as we don't have to send down an STPG. - // - if (existingDeviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { - - candidateDevInfo = existingDeviceInfo; - *SendTPG = FALSE; - break; - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindStandbyPathToActivateALUA (Group %p): Exiting function with devInfo %p.\n", - Group, - candidateDevInfo)); - - return candidateDevInfo; -} - - -PDSM_DEVICE_INFO -DsmpFindStandbyPathInAlternateTpgALUA( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine will find another path in the group that is not in the same - TPG as the passed in DeviceInfo. - - This routine assumes that the DSM lock is held - - This is used by devices that support ALUA. - -Arguements: - - Group is the multipath group - DeviceInfo is the devInfo whose TPG must not be matched - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Standby path not in same TPG as passed in DeviceInfo - or NULL if no standby path is available - ---*/ -{ - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = DeviceInfo->TargetPortGroup; - PDSM_DEVICE_INFO existingDeviceInfo; - ULONG inx; - PDSM_DEVICE_INFO candidateDevInfo = NULL; - - UNREFERENCED_PARAMETER(SpecialHandlingFlag); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindStandbyPathInAlternateTpgALUA (DevInfo %p): Entering function.\n", - DeviceInfo)); - - for (inx = 0; inx < Group->NumberDevices; inx++) { - - existingDeviceInfo = Group->DeviceList[inx]; - - // - // We only care about deviceInfo if TPG is different - // - if (existingDeviceInfo && existingDeviceInfo->TargetPortGroup != targetPortGroup) { - - // - // The candidate for making A/O obviously mustn't be in a failed state - // and must be initialized - // - if (!DsmpIsDeviceFailedState(existingDeviceInfo->State) && - DsmpIsDeviceInitialized(existingDeviceInfo) && - DsmpIsDeviceUsable(existingDeviceInfo) && - DsmpIsDeviceUsablePR(existingDeviceInfo)) { - - // - // If we don't have any candidate currently, choose the very first - // one that is in a non-failure state, regardless of what state it - // may be in. - // - if (!candidateDevInfo) { - - candidateDevInfo = existingDeviceInfo; - continue; - } - - // - // Check if the current one is at least better than the candidate. - // - if (DsmpIsBetterDeviceState(candidateDevInfo->State, existingDeviceInfo->State)) { - - candidateDevInfo = existingDeviceInfo; - } - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFindStandbyPathInAlternateTpgALUA (DevInfo %p): Exiting function with devInfo %p.\n", - DeviceInfo, - candidateDevInfo)); - - return candidateDevInfo; -} - - -NTSTATUS -DsmpSetLBForDsmPolicyAdjustment( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ) - -/*++ - -Routine Description: - - This routine is called when a change is made to the DSM-wide default - load balance policy. It goes through each LUN representation (ie. Group - entry) and updates the appropriate ones (ie. ones for which the policy - was not chosen based on VID/PID or because of an explicit settings on - the LUN). It also then updates the path states in accordance with the - new LB policy. - -Arguements: - - DsmContext is the DSM context - LoadBalanceType is the new load balance policy to be applied - PreferredPath is the preferred failback path to be used (applicable only - if LB policy is Failover) - -Return Value: - - Success - ---*/ - -{ - NTSTATUS status = STATUS_SUCCESS; - KIRQL oldIrql; - PLIST_ENTRY entry; - ULONG groupIndex = 0; - ULONG devInfoIndex; - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO devInfo; - DSM_LOAD_BALANCE_TYPE newLoadBalancePolicy; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetLBForDsmPolicyAdjustment (DsmContext %p): Entering function.\n", - DsmContext)); - - oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - for (entry = DsmContext->GroupList.Flink; entry != &(DsmContext->GroupList); entry = entry->Flink, groupIndex++) { - - group = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); - - newLoadBalancePolicy = LoadBalanceType; - - // - // Only LUNs that don't have their policy explicitly set - // and ones that don't have it set based on VID/PID are - // of interest to us here. - // - if (group->LBPolicySelection == DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY || - group->LBPolicySelection == DSM_DEFAULT_LB_POLICY_DSM_WIDE) { - - // - // Also, if the caller is trying to clear the DSM-wide - // default policy, then we don't even care about those - // LUNs whose policies were not set using this value. - // - if (newLoadBalancePolicy < DSM_LB_FAILOVER && - group->LBPolicySelection != DSM_DEFAULT_LB_POLICY_DSM_WIDE) { - - continue; - } - - // - // If the DSM-wide setting is being cleared, we need to fall back - // to using the default based on the array's ALUA capabilities. - // - if (newLoadBalancePolicy < DSM_LB_FAILOVER) { - - newLoadBalancePolicy = DSM_LB_ROUND_ROBIN; - group->PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; - - - } else { - - // - // Since a new policy has been selected for the DSM-wide - // one, it needs to be applied to this LUN. - // - group->PreferredPath = (ULONGLONG)((ULONG_PTR)PreferredPath); - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; - } - - // - // If Round Robin is set and ALUA is enabled, we need to change the - // policy to Round Robin with Subset. - // - if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && newLoadBalancePolicy == DSM_LB_ROUND_ROBIN) { - - newLoadBalancePolicy = DSM_LB_ROUND_ROBIN_WITH_SUBSET; - } - - // - // Finally set the new load balance policy. - // - group->LoadBalanceType = newLoadBalancePolicy; - - // - // Path states need to be updated in accordance with the new policy. - // - for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { - - devInfo = group->DeviceList[devInfoIndex]; - DsmpSetNewDefaultLBPolicy(DsmContext, devInfo, group->LoadBalanceType, SpecialHandlingFlag); - } - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetLBForDsmPolicyAdjustment (DsmContext %p): Exiting function with status %x\n", - DsmContext, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForVidPidPolicyAdjustment( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PWSTR TargetHardwareId, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ) - -/*++ - -Routine Description: - - This routine is called when a change is made to the default load balance - policy for a VID/PID. It goes through each LUN representation (ie. Group - entry) and updates the appropriate ones (ie. ones for which the policy - was not because of an explicit settings on the LUN). It also then updates - the path states in accordance with the new LB policy. - -Arguements: - - DsmContext is the DSM context - TargetHardwareId is the VID/PID whose matching LUNs policy need to be updated - LoadBalanceType is the new load balance policy to be applied - PreferredPath is the preferred failback path to be used (applicable only - if LB policy is Failover) - -Return Value: - - Success - ---*/ - -{ - NTSTATUS status = STATUS_SUCCESS; - KIRQL oldIrql; - PLIST_ENTRY entry; - ULONG groupIndex = 0; - ULONG devInfoIndex; - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO devInfo; - DSM_LOAD_BALANCE_TYPE dsmLoadBalanceType; - ULONGLONG dsmPreferredPath; - BOOLEAN useDsmLBSettings = FALSE; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetLBForVidPidPolicyAdjustment (%ws): Entering function.\n", - TargetHardwareId)); - - status = DsmpQueryDsmLBPolicyFromRegistry(&dsmLoadBalanceType, &dsmPreferredPath); - - if (NT_SUCCESS(status)) { - - useDsmLBSettings = TRUE; - - } else { - - if (status == STATUS_OBJECT_NAME_NOT_FOUND) { - - status = STATUS_SUCCESS; - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_WMI, - "DsmpSetLBForVidPidPolicyAdjustment (%ws): MSDSM-wide default LB policy not set.\n", - TargetHardwareId)); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLBForVidPidPolicyAdjustment (%ws): Failed to query MSDMS-wide default LB setting. Status %x.\n", - TargetHardwareId, - status)); - } - } - - oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - for (entry = DsmContext->GroupList.Flink; entry != &(DsmContext->GroupList); entry = entry->Flink, groupIndex++) { - - group = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); - - // - // Only LUNs that don't have their policy explicitly set - // are of interest to us here. - // - if (group->LBPolicySelection < DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT) { - - // - // Figure out if this LUN matches the VID/PID of interest - // Device not of interest if it doesn't match the passed in target ID - // - if (wcscmp(group->HardwareId, TargetHardwareId) != 0) { - - continue; - } - - // - // Also, if the caller is trying to clear the VID/PID - // default policy, then we don't even care about those - // LUNs whose policies were not set using this value. - // - if (LoadBalanceType < DSM_LB_FAILOVER && - group->LBPolicySelection != DSM_DEFAULT_LB_POLICY_VID_PID) { - - continue; - } - - // - // If the VID/PID setting is being cleared, we need to fall back - // to using the DSM-wide default policy if it has been set, else - // we need to use the default based on the array's ALUA capabilities. - // - if (LoadBalanceType < DSM_LB_FAILOVER) { - - if (useDsmLBSettings) { - - // - // Even if the MSDSM-wide policy is specified as RR, if the storage - // is ALUA, we can't have the policy as RR, so we'll change it to - // RRWS instead. - // - if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && dsmLoadBalanceType == DSM_LB_ROUND_ROBIN) { - - group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; - - } else { - - group->LoadBalanceType = dsmLoadBalanceType; - } - - group->PreferredPath = (ULONGLONG)((ULONG_PTR)dsmPreferredPath); - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; - - } else { - - // - // Default LB type: - // is Round Robin if ALUA is not supported, or if ALUA support is implicit but access is symmetric, - // else Round Robin With Subset (since in ALUA, all paths aren't in A/O). - // - if (DsmpIsSymmetricAccess(group->DeviceList[0])) { - - group->LoadBalanceType = DSM_LB_ROUND_ROBIN; - - } else { - - group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; - } - - - group->PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; - } - - } else { - - // - // Since a new policy has been selected for the DSM-wide - // one, it needs to be applied to this LUN. - // - // However, if the VID/PID policy is specified as RR but the storage - // is ALUA, we can't have the policy as RR, so we'll change it to - // RRWS instead. - // - if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && LoadBalanceType == DSM_LB_ROUND_ROBIN) { - - group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; - - } else { - - group->LoadBalanceType = LoadBalanceType; - } - - group->PreferredPath = (ULONGLONG)((ULONG_PTR)PreferredPath); - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; - } - - // - // Path states need to be updated in accordance with the new policy. - // - for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { - - devInfo = group->DeviceList[devInfoIndex]; - DsmpSetNewDefaultLBPolicy(DsmContext, devInfo, group->LoadBalanceType, SpecialHandlingFlag); - } - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetLBForVidPidPolicyAdjustment (%ws): Exiting function with status %x\n", - TargetHardwareId, - status)); - - return status; -} - - -NTSTATUS -DsmpSetNewDefaultLBPolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_opt_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called to adjust the path state of an instance of a - LUN for which a new default load balance policy was applied - following an admin request for such a change. - - This routine must be called with spinlock held. - -Arguements: - - DsmContext is the DSM context - DeviceInfo is the device info on which the new path state needs to be set - LoadBalanceType is the load balance policy in accordance with which the path state needs to be adjusted - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - NTSTATUS status = STATUS_SUCCESS; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetNewDefaultLBPolicy (DevInfo %p): Entering function\n", - DeviceInfo)); - - if (!DeviceInfo) { - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpSetNewDefaultLBPolicy; - } - - if (!(DsmpIsDeviceInitialized(DeviceInfo) && DsmpIsDeviceUsable(DeviceInfo) && DsmpIsDeviceUsablePR(DeviceInfo)) || - DsmpIsDeviceFailedState(DeviceInfo->State)) { - - status = STATUS_UNSUCCESSFUL; - goto __Exit_DsmpSetNewDefaultLBPolicy; - } - - - group = DeviceInfo->Group; - - if (!DsmpIsSymmetricAccess(DeviceInfo)) { - - DsmpAdjustDeviceStatesALUA(group, NULL, SpecialHandlingFlag); - - } else { - - switch (LoadBalanceType) { - - // - // For failover, it is important the right path is - // chosen, ie. preferred path needs to be taken into - // consideration. - // - case DSM_LB_FAILOVER: { - - DsmpSetLBForPathArrival(DsmContext, DeviceInfo, SpecialHandlingFlag); - - break; - } - - // - // For all other policies, the state must be A/O. - // - default: { - - DeviceInfo->PreviousState = DeviceInfo->State; - DeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - break; - } - } - } - -__Exit_DsmpSetNewDefaultLBPolicy: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetNewDefaultLBPolicy (DevInfo %p): Exiting function with status %x\n", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForPathArrival( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called when a new path arrives for a multipath - group that doesn't support ALUA. The routine will set the path - to the appropriate state and fix up the other paths state if - they need to change. - - This is used by devices NOT supporting ALUA. - - This routine must be called with spinlock held. - -Arguements: - - DsmContext is the DSM context - NewDeviceInfo is the device info for the newly arrived path - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO deviceInfo; - NTSTATUS status = STATUS_SUCCESS; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): Entering function.\n", - NewDeviceInfo)); - - group = NewDeviceInfo->Group; - - if (!(DsmpIsDeviceInitialized(NewDeviceInfo) && DsmpIsDeviceUsable(NewDeviceInfo) && DsmpIsDeviceUsablePR(NewDeviceInfo))) { - - // - // Bad device instance. Nothing can be done about it. - // - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_UNDETERMINED; - - NT_ASSERT(NewDeviceInfo->FailGroup == NULL); - - goto __Exit_DsmpSetLBForPathArrival; - } - - - if (group->NumberDevices == 1) { - - // - // if this is the only device for the group then we will always - // be active as every group must have at least one active path - // - if (NewDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { - - // - // All's good - // - goto __Exit_DsmpSetLBForPathArrival; - - } else { - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): State changed to %d at %d\n", - NewDeviceInfo, - NewDeviceInfo->State, - __LINE__)); - - goto __Exit_DsmpSetLBForPathArrival; - } - - switch(group->LoadBalanceType) { - case DSM_LB_FAILOVER: { - - // - // Get the current active path. - // - deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); - - // - // If the newly arriving path is the preferred path, this now should - // become our active path. - // - if (group->PreferredPath == ((ULONGLONG)((ULONG_PTR)(NewDeviceInfo->FailGroup->PathId)))) { - - // - // If current active path is not the preferred path, change its - // path state to standby. - // - if (deviceInfo && deviceInfo != NewDeviceInfo) { - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_STANDBY; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (Group %p): Preferred path back online. DevInfo %p changed to state %d\n", - group, - deviceInfo, - deviceInfo->State)); - } - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - } else { - - // - // In the case of failover, we must have only a single - // active path. If this newly added path is configured as - // the one that is desired to be active then we make this - // active and set the standby path that was active back to - // standby unless the preferred path is the currently active - // path. If the newly added path is supposed to be - // standby then we leave it as standby. - // - if (NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { - - if (deviceInfo) { - - // - // If the preferred path is currently active, don't change - // it regardless of this path wanting to be in active state. - // - if (group->PreferredPath == ((ULONGLONG)((ULONG_PTR)(deviceInfo->FailGroup->PathId)))) { - - if (NewDeviceInfo != deviceInfo) { - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_STANDBY; - } - - } else { - - // - // Since the preferred path is not active, make this - // path active since it wants to be so. This means - // changing the current active path to standby. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_STANDBY; - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - } - } else { - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - } - - } else { - - if (deviceInfo) { - - if (deviceInfo != NewDeviceInfo) { - - // - // This newly arrived device doesn't want to be in - // A/O, and we already have an active path, so make - // it standby. - // - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_STANDBY; - } - } else { - - // - // Since we currently don't have an active path, this one - // needs to be made active, regardless of its path it wishes - // to be in. - // - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - } - } - } - - break; - } - - case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { - - // - // In RRWS, a set of paths can be active and another set of - // paths can be standby. We set the new path to the desired - // state unless the desired state is standby, but there are - // no active paths. Also if the desired state is Active we - // need to check if there are any existing paths that are - // also active but have a desired state of standby. For - // those we can move them back to standby. - // - if (NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { - - // - // We are the active path coming back. Find out who has - // been the active one and place him back to standby - // - DsmpAllowStandbyPathsToRest(group); - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): Changed to state %d at %d\n", - NewDeviceInfo, - NewDeviceInfo->State, - __LINE__)); - - } else { - - // - // if there are no paths already active then we've got - // to make this one AO, otherwise we can be non-AO - // - deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); - - if (!deviceInfo || deviceInfo == NewDeviceInfo) { - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - } else { - - NewDeviceInfo->State = DSM_DEV_STANDBY; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (%p): Changed to state %d at %d. Status %x\n", - NewDeviceInfo, - NewDeviceInfo->State, - __LINE__, - status)); - } - - status = STATUS_SUCCESS; - - break; - } - - case DSM_LB_LEAST_BLOCKS: - case DSM_LB_ROUND_ROBIN: - case DSM_LB_DYN_LEAST_QUEUE_DEPTH: - case DSM_LB_WEIGHTED_PATHS: { - - // - // In RR, LWP, LB and LQD all paths are active so the new device - // becomes AO or AU. - // - if (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { - - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): Changed to state %d at %d. Status %x\n", - NewDeviceInfo, - NewDeviceInfo->State, - __LINE__, - status)); - - status = STATUS_SUCCESS; - - break; - } - - default: { - status = STATUS_INVALID_PARAMETER; - break; - } - } - -__Exit_DsmpSetLBForPathArrival: - - // - // Update the next path to be used for the group - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess(NewDeviceInfo), - SpecialHandlingFlag); - if (deviceInfo != NULL) { - - InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)deviceInfo->FailGroup); - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): Updating PathToBeUsed in %p to %p\n", - NewDeviceInfo, - group, - group->PathToBeUsed)); - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): No FOG available for group %p\n", - NewDeviceInfo, - group)); - - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrival (DevInfo %p): Exiting function with status %x\n", - NewDeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForPathArrivalALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called when a new path arrives for a multipath - group. The routine will set the path to the appropriate state and - fix up the other paths state if they need to change. - - Spin lock must NOT be held by caller - -Arguements: - - DsmContext is the DSM context - NewDeviceInfo is the device info for the newly arrived path - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO deviceInfo = NULL; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings; - BOOLEAN lockHeld = FALSE; - PDSM_DEVICE_INFO preferredActiveDeviceInfo = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): Entering function.\n", - NewDeviceInfo)); - - group = NewDeviceInfo->Group; - - if (!(DsmpIsDeviceInitialized(NewDeviceInfo) && DsmpIsDeviceUsable(NewDeviceInfo) && DsmpIsDeviceUsablePR(NewDeviceInfo))) { - - // - // Bad device instance. Nothing can be done about it. - // - NewDeviceInfo->PreviousState = NewDeviceInfo->State; - NewDeviceInfo->State = DSM_DEV_UNDETERMINED; - - NT_ASSERT(NewDeviceInfo->FailGroup == NULL); - - goto __Exit_DsmpSetLBForPathArrivalALUA; - } - - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - lockHeld = TRUE; - - if (group->NumberDevices == 1) { - - // - // If this is the only device for the group then it should be the - // active instance as every group must have at least one active path. - // - if (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - lockHeld = FALSE; - - if (NewDeviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { - - // - // If the device supports explicit transitions, set its state to A/O - // - status = DsmpSetDeviceALUAState(DsmContext, NewDeviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); - - } else { - - // - // Since the device supports only implicit transitions, send down - // RTPG and hope that the controller has made this one the A/O path. - // - status = DsmpGetDeviceALUAState(DsmContext, NewDeviceInfo, NULL); - - if (NT_SUCCESS(status)) { - - // - // Remember that at this point it is possible that this path - // is still non-A/O. We'll need to handle this in DsmGetPath - // as a special case where we don't find an A/O path but the - // storage supports implicit-only transitions. At that time, - // we mustn't blindly return a NULL path back. - // - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): RTPG returned state = %x, ALUA state = %x.\n", - NewDeviceInfo, - NewDeviceInfo->State, - NewDeviceInfo->ALUAState)); - } - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): State changed to %d at %d\n", - NewDeviceInfo, - NewDeviceInfo->State, - __LINE__)); - - } else { - - deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); - - // - // Irrespecitve of the policy, we must have at least one A/O path. - // - // For RRWS and FOO, this path is of interest if it is desired to be in A/O. - // - // Also, for failover-only, this new path is of interest if it is - // the preferred path. - // - // This path is also of interest if it is not A/O and desired state has not - // explicitly been set to non-A/O and it has been exposed through the - // preferred TPG. - // - if ((!deviceInfo) || - ((NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) && - (group->LoadBalanceType == DSM_LB_FAILOVER || - group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET)) || - (group->PreferredPath == (ULONGLONG)((ULONG_PTR)(NewDeviceInfo->FailGroup->PathId)) && - group->LoadBalanceType == DSM_LB_FAILOVER) || - (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED && - NewDeviceInfo->DesiredState == DSM_DEV_UNDETERMINED && - NewDeviceInfo->TargetPortGroup->Preferred)) { - - // - // Since this path is supposed to be active, we make it - // active and then allow any paths that are supposed to - // be standby go back to being standby - // - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - lockHeld = FALSE; - - // - // If explicit ALUA is supported, we need to send down STPG to make the change. - // - if (NewDeviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { - - status = DsmpSetDeviceALUAState(DsmContext, NewDeviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); - - } else { - - // - // If implicit ALUA, the controller may have made some - // changes to the TPG states. We just need to query it. - // We'll try and honor the Admin's request but can't - // guarantee it. - // - status = DsmpGetDeviceALUAState(DsmContext, NewDeviceInfo, NULL); - } - - // - // We prefer this newly arrived devInfo to be A/O - // - preferredActiveDeviceInfo = NewDeviceInfo; - } - } - - if (!lockHeld) { - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - lockHeld = TRUE; - } - - if (NT_SUCCESS(status)) { - - DsmpAdjustDeviceStatesALUA(group, preferredActiveDeviceInfo, SpecialHandlingFlag); - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): Trying to query ALUA state failed with %x\n", - NewDeviceInfo, - status)); - } - - status = STATUS_SUCCESS; - -__Exit_DsmpSetLBForPathArrivalALUA: - - // - // Update the next path to be used for the group - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess(NewDeviceInfo), - SpecialHandlingFlag); - if (deviceInfo != NULL) { - - InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)deviceInfo->FailGroup); - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", - NewDeviceInfo, - group, - group->PathToBeUsed)); - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): No active/alternative path available for group %p\n", - NewDeviceInfo, - group)); - - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - } - - if (lockHeld) { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathArrivalALUA (DevInfo %p): Exiting function with status %x\n", - NewDeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForPathRemoval( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, - _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called when a path is removed from a multipath - group. The routine will set the path to the appropriate state and - fix up the other paths state if they need to change. - - Note: This is used by devices NOT supporting ALUA. - -Arguements: - - DsmContext is the DSM context - - RemovedDeviceInfo is the device info for failing/going-away path - - Group is an optional group override. That is, if Group is not NULL, this - function will run the load balance policy on the given Group and not - the Group from the RemovedDeviceInfo. This should only be used when - it's impossible to get a pointer to the RemovedDeviceInfo. - - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO deviceInfo; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p, Group %p): Entering function.\n", - RemovedDeviceInfo, - Group)); - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - if (Group == NULL) { - - if (!(DsmpIsDeviceFailedState(RemovedDeviceInfo->State))) { - - RemovedDeviceInfo->LastKnownGoodState = RemovedDeviceInfo->State; - } - - RemovedDeviceInfo->PreviousState = RemovedDeviceInfo->State; - RemovedDeviceInfo->State = DSM_DEV_FAILED; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): changed to state %d at %d\n", - RemovedDeviceInfo, - RemovedDeviceInfo->State, - __LINE__)); - - group = RemovedDeviceInfo->Group; - - } else { - // - // The caller has chosen to override the RemovedDeviceInfo->Group. - // - group = Group; - } - - switch(group->LoadBalanceType) { - case DSM_LB_FAILOVER: - case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { - - // - // In the case of failover, we must have only a single - // active path. If the removed path was the active path we - // need to find another path to become active - // - // In RRWS, a set of paths can be active and another set of - // paths can be standby. If the removed path is an active - // path then we need to make sure there is another active - // path. If there is already another active path then there - // is nothing to do. If not then a path needs to be made - // active. - // - if (!DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag)) { - - deviceInfo = DsmpFindStandbyPathToActivate(group, SpecialHandlingFlag); - if (deviceInfo) { - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): DevInfo %p changed to state %d at %d\n", - RemovedDeviceInfo, - deviceInfo, - deviceInfo->State, - __LINE__)); - } - } else { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): LB Policy FO/RRWS and other paths active, no path made active at %d\n", - RemovedDeviceInfo, - __LINE__)); - } - - break; - } - - case DSM_LB_LEAST_BLOCKS: - case DSM_LB_ROUND_ROBIN: - case DSM_LB_WEIGHTED_PATHS: - case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { - - // - // In RR, LQD, LB and LWP, all paths are active so we don't - // need to worry about activating a new path - // - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): LB Policy RR, LWP or LQD, no path made active at %d\n", - RemovedDeviceInfo, - __LINE__)); - break; - } - - default: { - status = STATUS_INVALID_PARAMETER; - break; - } - } - - // - // Update the next path to be used for the group - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess(RemovedDeviceInfo), - SpecialHandlingFlag); - if (deviceInfo != NULL) { - - InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): Removal: Updating PathToBeUsed in %p to %p\n", - RemovedDeviceInfo, - group, - group->PathToBeUsed)); - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): After remove No FOG available for group %p\n", - RemovedDeviceInfo, - group)); - - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemoval (DevInfo %p): Exiting function with status %x\n", - RemovedDeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForPathRemovalALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, - _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called when a path is removed from a multipath - group. The routine will set the path to the appropriate state and - fix up the other paths state if they need to change. - - Note: This should NOT be called with DsmContext Lock held - This is used for devices supporting ALUA. - -Arguements: - - DsmContext is the DSM context - - RemovedDeviceInfo is the device info for the failing/going-away path - - Group is an optional group override. That is, if Group is not NULL, this - function will run the load balance policy on the given Group and not - the Group from the RemovedDeviceInfo. This should only be used when - it's impossible to get a pointer to the RemovedDeviceInfo. - - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO deviceInfo = NULL; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql; - BOOLEAN lockHeld = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p, Group %p): Entering function.\n", - RemovedDeviceInfo, - Group)); - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - lockHeld = TRUE; - - if (Group == NULL) { - - if (!(DsmpIsDeviceFailedState(RemovedDeviceInfo->State))) { - - RemovedDeviceInfo->LastKnownGoodState = RemovedDeviceInfo->State; - } - - RemovedDeviceInfo->PreviousState = RemovedDeviceInfo->State; - RemovedDeviceInfo->State = DSM_DEV_FAILED; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): changed to state %d at %d\n", - RemovedDeviceInfo, - RemovedDeviceInfo->State, - __LINE__)); - - group = RemovedDeviceInfo->Group; - - } else { - // - // The caller has chosen to override the RemovedDeviceInfo->Group. - // - group = Group; - } - - if (group->LoadBalanceType < DSM_LB_FAILOVER || - group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { - - status = STATUS_INVALID_PARAMETER; - - } else { - - // - // In the case of failover, we must have only a single - // active path. If the removed path was the active path we - // need to find another path to become active - // - // In rest of policies, set of paths can be active and another set of - // paths can be standby. If the removed path is an active - // path then we need to make sure there is another active - // path. If there is already another active path then there - // is nothing to do. If not then a path needs to be made - // active. - // - if (!DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag)) { - - BOOLEAN sendTPG = TRUE; - - deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); - - if (deviceInfo) { - - if (sendTPG) { - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - lockHeld = FALSE; - - // - // If explicit transition supported, we need to send down STPG to make the change. - // - if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { - - status = DsmpSetDeviceALUAState(DsmContext, deviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); - - } else { - - // - // If implicit ALUA, the controller may have made necessary - // changes to the TPG states. We just need to query it. - // - status = DsmpGetDeviceALUAState(DsmContext, deviceInfo, NULL); - } - - if (!lockHeld) { - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - lockHeld = TRUE; - } - - if (NT_SUCCESS(status)) { - - DsmpAdjustDeviceStatesALUA(group, deviceInfo, SpecialHandlingFlag); - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): Trying to query for ALUA state failed with status %x\n", - RemovedDeviceInfo, - status)); - } - } else { - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - } - - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): Device %p changed to state %d at %d\n", - RemovedDeviceInfo, - deviceInfo, - deviceInfo->State, - __LINE__)); - } - } - } else { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): Other paths active, no path made active at %d\n", - RemovedDeviceInfo, - __LINE__)); - } - - status = STATUS_SUCCESS; - } - - // - // Update the next path to be used for the group - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess(RemovedDeviceInfo), - SpecialHandlingFlag); - if (deviceInfo != NULL) { - - InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): Removal: Updating PathToBeUsed in %p to %p\n", - RemovedDeviceInfo, - group, - group->PathToBeUsed)); - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): No active/alternative path available for group %p\n", - RemovedDeviceInfo, - group)); - - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - } - - if (lockHeld) { - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): Exiting function with status %x.\n", - RemovedDeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForPathFailing( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, - _In_ IN BOOLEAN MarkDevInfoFailed, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called when an IO that was sent using this path fails with - a fatal error. The routine will set the path to the appropriate state and - fix up the other paths state if they need to change. - - Note: This is used by devices NOT supporting ALUA. - -Arguements: - - DsmContext is the DSM context - - FailingDeviceInfo is the device info for the path on which IO failed - - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - NTSTATUS status; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailing (DevInfo %p): Entering function.\n", - FailingDeviceInfo)); - - // - // We need to do exactly what DsmpSetLBForPathRemoval() does, except - // that the devInfo may not really go away (may come back before a - // Pnp remove comes down for the real LUN) - // - if (MarkDevInfoFailed) { - status = DsmpSetLBForPathRemoval(DsmContext, FailingDeviceInfo, NULL, SpecialHandlingFlag); - } else { - status = DsmpSetLBForPathRemoval(DsmContext, FailingDeviceInfo, FailingDeviceInfo->Group, SpecialHandlingFlag); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailing (DevInfo %p): Exiting function with status %x\n", - FailingDeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLBForPathFailingALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, - _In_ IN BOOLEAN MarkDevInfoFailed, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - This routine is called when an IO that was sent using this path fails with - a fatal error. The routine will set the path to the appropriate state and - send down a Set Target Port Groups command asynchronously to fix up the - other paths state if they need to change (actual work done in the completion - routine). - - Note: This should NOT be called with DsmContext Lock held - This is used for devices supporting ALUA. - -Arguements: - - DsmContext is the DSM context - - FailingDeviceInfo is the device info for the failing/going-away path - - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; - PDSM_DEVICE_INFO deviceInfo = NULL; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql; - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength; - PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; - PDSM_COMPLETION_CONTEXT completionContext = NULL; - PVOID senseInfo = NULL; - PSCSI_REQUEST_BLOCK srb = NULL; - PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Entering function.\n", - FailingDeviceInfo)); - - if (MarkDevInfoFailed) { - if (!(DsmpIsDeviceFailedState(FailingDeviceInfo->State))) { - - FailingDeviceInfo->LastKnownGoodState = FailingDeviceInfo->State; - } - - FailingDeviceInfo->PreviousState = FailingDeviceInfo->State; - FailingDeviceInfo->State = DSM_DEV_FAILED; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): changed to state %d at %d\n", - FailingDeviceInfo, - FailingDeviceInfo->State, - __LINE__)); - } - - group = FailingDeviceInfo->Group; - - if (group->LoadBalanceType < DSM_LB_FAILOVER || - group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { - - status = STATUS_INVALID_PARAMETER; - - } else { - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Check if there are any active paths that can be used. - // - deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); - if (!deviceInfo) { - - // - // Check if an Set/Report TPG has already been sent for this failing devInfo - // - failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(DsmContext, group, FailingDeviceInfo); - - if (!failDevInfoListEntry) { - - BOOLEAN sendTPG = TRUE; - - deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); - - if (deviceInfo) { - - if (sendTPG) { - - tpgCompletionContext = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_TPG_COMPLETION_CONTEXT), - DSM_TAG_TPG_COMPLETION_CONTEXT); - - if (tpgCompletionContext) { - UCHAR senseInfoLength = SENSE_BUFFER_SIZE_EX; - - senseInfo = DsmpAllocatePool(NonPagedPoolNx, - senseInfoLength, - DSM_TAG_SCSI_SENSE_INFO); - - if (senseInfo) { - - srb = DsmpAllocatePool(NonPagedPoolNx, - sizeof(SCSI_REQUEST_BLOCK), - DSM_TAG_SCSI_REQUEST_BLOCK); - - if (srb) { - - srb->Length = SCSI_REQUEST_BLOCK_SIZE; - srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - - completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); - if (completionContext) { - - // - // Update the target port group that needs to be made - // active/optimized. We will send down an STPG for - // storages that support both implicit and explicit. - // If the storage does NOT like our choice of A/O TPG, - // it will make an implicit transition. This is still - // a better option than solely relying on the storage's - // implicit transitions at this stage and ending up with - // no path in A/O state. - // - if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { - - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); - - } else { - - // - // Find an active/optimized target port group that should - // have been set by the controller - // - // Take care of worst case scenario, which is: - // 1. 4-byte header (for allocation length) - // 2. 32 8-byte descriptors (for TPGs) - // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) - // - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - DSM_MAX_PATHS * sizeof(ULONG))); - } - - targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, - targetPortGroupsInfoLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo) { - - failDevInfoListEntry = DsmpBuildFailPathDevInfoEntry(DsmContext, - group, - FailingDeviceInfo, - deviceInfo); - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - if (failDevInfoListEntry) { - - tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); - tpgDescriptor->AsymmetricAccessState = DSM_DEV_ACTIVE_OPTIMIZED; - REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &deviceInfo->TargetPortGroup->Identifier); - - // - // Prevent the device info from being removed when a TPG is in-flight. - // - InterlockedIncrement(&FailingDeviceInfo->BlockRemove); - - completionContext->DeviceInfo = FailingDeviceInfo; - completionContext->DsmContext = DsmContext; - completionContext->RequestUnique1 = deviceInfo; - completionContext->RequestUnique2 = FALSE; - - tpgCompletionContext->CompletionContext = completionContext; - tpgCompletionContext->Srb = srb; - tpgCompletionContext->SenseInfoBuffer = senseInfo; - tpgCompletionContext->SenseInfoBufferLength = senseInfoLength; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Sending down TPG asynchronously for %p using devInfo %p (path %p).\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId, - deviceInfo, - deviceInfo->FailGroup->PathId)); - - if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { - - status = DsmpSetTargetPortGroupsAsync(deviceInfo, - DsmpPhase1ProcessPathFailingALUA, - tpgCompletionContext, - targetPortGroupsInfoLength, - targetPortGroupsInfo); - } else { - - status = DsmpReportTargetPortGroupsAsync(deviceInfo, - DsmpPhase2ProcessPathFailingALUA, - tpgCompletionContext, - targetPortGroupsInfoLength, - targetPortGroupsInfo); - } - - if (status != STATUS_PENDING) { - - // - // Request not sent down successfully. Free the allocations. - // - DsmpFreePool(targetPortGroupsInfo); - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - DsmpFreePool(srb); - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - - // - // Allow the failing device to be removed. - // - InterlockedDecrement(&FailingDeviceInfo->BlockRemove); - } - } else { - - // - // Fail to build DevInfo entry. Free the allocations. - // - DsmpFreePool(targetPortGroupsInfo); - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - DsmpFreePool(srb); - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - DsmpFreePool(srb); - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - NT_ASSERT(completionContext != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate completion context. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - DsmpFreePool(srb); - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - NT_ASSERT(srb != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate SRB. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - NT_ASSERT(senseInfo != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate senseInfo. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - DsmpFreePool(tpgCompletionContext); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - NT_ASSERT(tpgCompletionContext != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate TPG completion context. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Found alternative devInfo %p for failing path %p without need for TPG\n", - FailingDeviceInfo, - deviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Couldn't find a standby path to activate for failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - deviceInfo = failDevInfoListEntry->TempDeviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): There is an RTPG/STPG already in progress for this path %p. Returning alternative %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId, - deviceInfo)); - } - } else { - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Other paths active, no path made active at %d\n", - FailingDeviceInfo, - __LINE__)); - } - - status = STATUS_SUCCESS; - } - - if (deviceInfo) { - - // - // Update temporarily the next path to be used for the group as this devInfo - // - InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", - FailingDeviceInfo, - group, - group->PathToBeUsed)); - - } else { - - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpSetLBForPathRemovalALUA (DevInfo %p): No FOG available for group %p\n", - FailingDeviceInfo, - group)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetLBForPathFailingALUA (DevInfo %p): Exiting function with status %x.\n", - FailingDeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpSetPathForIoRetryALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, - _In_ IN BOOLEAN TPGException, - _In_ IN BOOLEAN DeviceInfoException - ) -/*++ - -Routine Description: - - This routine is called when an IO that was sent using this path fails with - a retry-able "ALUA" error. What this basically means is that most likely an - implicit transition has taken place and we need an RTPG to get the updated - path states. The routine will send down a Report Target Port Groups command - asynchronously to get the paths states (actual work done in the completion - routine). - - Note: This should NOT be called with DsmContext Lock held - This is used for devices supporting ALUA. - -Arguements: - - DsmContext is the DSM context - - FailingDeviceInfo is the device info for the failing/going-away path - - TPGException is a flag used to indicate if the selected path must be from a - TPG that is different from FailingDeviceInfo's. This is - special handling for UA with sense "TPG in SB/UA state" - - DeviceInfoException is a flag used to indicate that the current FailindDeviceInfo - itself needs to be used again. This is special handling for - UA with sense "Asymmetric Access State Changed" - - (NOTE: TPGException and DeviceInfoException are mutually exclusive, although - it is okay for both to be FALSE) - -Return Value: - - Status - ---*/ -{ - PDSM_GROUP_ENTRY group; - PDSM_DEVICE_INFO deviceInfo = NULL; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql; - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength; - PDSM_COMPLETION_CONTEXT completionContext = NULL; - PVOID senseInfo = NULL; - PSCSI_REQUEST_BLOCK srb = NULL; - PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = NULL; - NTSTATUS throttleStatus = STATUS_UNSUCCESSFUL; - ULONG inflightRTPG; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Entering function.\n", - FailingDeviceInfo)); - - group = FailingDeviceInfo->Group; - - - if (group->LoadBalanceType < DSM_LB_FAILOVER || - group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { - - status = STATUS_INVALID_PARAMETER; - - } else { - - // - // First check to see if we need to find a candidate from a different TPG. - // - if (TPGException) { - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Find a candidate in a TPG that is different from this one - // - deviceInfo = DsmpFindStandbyPathInAlternateTpgALUA(group, FailingDeviceInfo, SpecialHandlingFlag); - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Need to try a different TPG deviceInfo %p.\n", - FailingDeviceInfo, - deviceInfo)); - - } else if (DeviceInfoException) { - - // - // Retry on the same path - // - deviceInfo = FailingDeviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Will retry using same deviceInfo %p.\n", - FailingDeviceInfo, - deviceInfo)); - } - - // - // Check if an Report TPG has already been sent for this group - // - inflightRTPG = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 0); - - // - // If there is no RTPG currently in flight, use the best candidate found - // above to send down the RTPG. If there is already an RTPG inflight, - // we're done. The result of the RTPG should fix the path states. - // - if (!inflightRTPG) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): No RTPG in flight. Try sending one down.\n", - FailingDeviceInfo)); - - // - // If we need a candidate device, first get the currently active one. - // If we can't find one that way, resort to finding the best alternative. - // Basic idea is find SOME path instead of failing IOs back to the application. - // - if (!deviceInfo) { - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Find the best candidate - ie. either a currently A/O path or - // the best alternative path to be made A/O. - // - deviceInfo = DsmpGetAnyActivePath(group, TRUE, deviceInfo, SpecialHandlingFlag); - if (!deviceInfo) { - - BOOLEAN sendTPG = TRUE; - - deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): No active path. Best alternative %p.\n", - FailingDeviceInfo, - deviceInfo)); - } - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - - if (deviceInfo) { - - tpgCompletionContext = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_TPG_COMPLETION_CONTEXT), - DSM_TAG_TPG_COMPLETION_CONTEXT); - - if (tpgCompletionContext) { - UCHAR senseInfoLength = SENSE_BUFFER_SIZE_EX; - - senseInfo = DsmpAllocatePool(NonPagedPoolNx, - senseInfoLength, - DSM_TAG_SCSI_SENSE_INFO); - - if (senseInfo) { - - srb = DsmpAllocatePool(NonPagedPoolNx, - sizeof(SCSI_REQUEST_BLOCK), - DSM_TAG_SCSI_REQUEST_BLOCK); - - if (srb) { - - srb->Length = SCSI_REQUEST_BLOCK_SIZE; - srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - - completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); - - if (completionContext) { - - // - // Find an active/optimized target port group that should - // have been set by the controller - // - // Take care of worst case scenario, which is: - // 1. 4-byte header (for allocation length) - // 2. 32 8-byte descriptors (for TPGs) - // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) - // - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - DSM_MAX_PATHS * sizeof(ULONG))); - - targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, - targetPortGroupsInfoLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo) { - - completionContext->DeviceInfo = FailingDeviceInfo; - completionContext->DsmContext = DsmContext; - completionContext->RequestUnique1 = deviceInfo; - completionContext->RequestUnique2 = TRUE; - - tpgCompletionContext->CompletionContext = completionContext; - tpgCompletionContext->Srb = srb; - tpgCompletionContext->SenseInfoBuffer = senseInfo; - tpgCompletionContext->SenseInfoBufferLength = senseInfoLength; - - // - // Now we are all set to send the RTPG request. - // check and set InFlightRTPG to make sure this thread is the only one with - // the RTPG active for this group, since it is possible to have more than one - // threads reaching up to this point in parallel - // - inflightRTPG = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 1, 0); - if (inflightRTPG) { - - DsmpFreePool(targetPortGroupsInfo); - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - DsmpFreePool(srb); - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - } else { - - // - // Prevent the device info from being removed when a TPG is in-flight. - // - InterlockedIncrement(&FailingDeviceInfo->BlockRemove); - - // - // First try and throttle the IO. The completion routine will - // take care of resuming the IO. - // - if (!InterlockedCompareExchange((LONG volatile*)&group->Throttled, 1, 0)) { - - DsmNotification(((PDSM_CONTEXT)DsmContext)->MPIOContext, - ThrottleIO_V2, - deviceInfo, - FALSE, - &throttleStatus, - 0); - - if (NT_SUCCESS(throttleStatus)) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Successfully throttled IO. About to send RTPG. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - } else { - - // - // Throttle can fail when the MPDisk is - // 1. Being removed. (or) - // 2. In any other state other than Normal or Degraded. - // - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Throttle before RTPG failed. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - InterlockedDecrement((LONG volatile*)&FailingDeviceInfo->Group->Throttled); - } - } else { - - // - // Currently we don't expect this to happen - // - NT_ASSERT(FALSE); - } - - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Sending RTPG asynchronously. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - if (STATUS_PENDING != DsmpReportTargetPortGroupsAsync(deviceInfo, - DsmpPhase2ProcessPathFailingALUA, - tpgCompletionContext, - targetPortGroupsInfoLength, - targetPortGroupsInfo)) { - - - - // - // Request not sent down successfully. Free the allocations. - // - DsmpFreePool(targetPortGroupsInfo); - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - DsmpFreePool(srb); - DsmpFreePool(senseInfo); - DsmpFreePool(tpgCompletionContext); - - // - // Allow the failing device to be removed. - // - InterlockedDecrement(&FailingDeviceInfo->BlockRemove); - - // - // Resume IO if we throttled requests before calling DsmpReportTargetPortGroupsAsync - // - if (InterlockedCompareExchange((LONG volatile*)&group->Throttled, 0, 1)) { - - NTSTATUS resumeStatus = STATUS_UNSUCCESSFUL; - - DsmNotification(((PDSM_CONTEXT)deviceInfo->DsmContext)->MPIOContext, - ResumeIO_V2, - deviceInfo, - TRUE, - &resumeStatus, - 0); - - if (!NT_SUCCESS(resumeStatus)) { - - // - // Resume can fail when - // 1. The MPDisk is being removed (or) - // 2. The MPDisk is any other state other than throttled (or) - // 3. There is a problem dispatching throttled requests. - // - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Resume IO failed.\n", - deviceInfo)); - } - - } - - InterlockedDecrement((LONG volatile*)&group->InFlightRTPG); - } - } - } else { - - NT_ASSERT(targetPortGroupsInfo != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate TPG info buffer. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - ExFreePool(srb); - ExFreePool(senseInfo); - ExFreePool(tpgCompletionContext); - } - } else { - - NT_ASSERT(completionContext != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate completion context. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - ExFreePool(srb); - ExFreePool(senseInfo); - ExFreePool(tpgCompletionContext); - } - } else { - - NT_ASSERT(srb != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate SRB. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - ExFreePool(senseInfo); - ExFreePool(tpgCompletionContext); - } - } else { - - NT_ASSERT(senseInfo != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate senseInfo. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - - ExFreePool(tpgCompletionContext); - } - } else { - - NT_ASSERT(tpgCompletionContext != NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate TPG completion context. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - } - } else { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't find a path for RTPG. Failing path %p.\n", - FailingDeviceInfo, - FailingDeviceInfo->FailGroup->PathId)); - } - } else { - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Other paths active, no path made active at %d\n", - FailingDeviceInfo, - __LINE__)); - } - - if(!deviceInfo) { - - // - // It is possible that there are only two TPGs with one of them in U/A - // state and the other in transitioning state. In such a case, we would - // not find an alternative TPG deviceInfo. In addition, if there is an - // RTPG in flight, we won't go down the path of forcibly picking any - // deviceInfo. This is to cover that scenario, else we're left with no - // deviceInfo to do the retry and we'll end up setting the group's PTBU - // to NULL thus failing the retried request (if for eg. the LB is RRWS). - // Need to ensure that we handle this exception case. - // - if (inflightRTPG) { - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Find the best candidate - ie. either a currently A/O path or - // the best alternative path to be made A/O. - // - deviceInfo = DsmpGetAnyActivePath(group, TRUE, deviceInfo, SpecialHandlingFlag); - if (!deviceInfo) { - - BOOLEAN sendTPG = TRUE; - - deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - } - - if(deviceInfo) { - - // - // Update temporarily the next path to be used for the group as this devInfo - // - InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", - FailingDeviceInfo, - group, - group->PathToBeUsed)); - - } else { - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): No FOG available for group %p\n", - FailingDeviceInfo, - group)); - } - - status = STATUS_SUCCESS; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetPathForIoRetryALUA (DevInfo %p): Exiting function with status %x.\n", - FailingDeviceInfo, - status)); - - return status; -} - - -PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY -DsmpFindFailPathDevInfoEntry( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO FailingDevInfo - ) -/*++ - -Routine Description: - - This routine finds the entry that contains the alternate devInfo to use for - a failing one for the passed in devInfo. - - N.B: This routine MUST be called with DsmContextLock held in either Shared or - Exclusive mode. - -Arguments: - - Context is the DSM's context info. - - Group is the group entry representing the device. - - FailingDevInfo is the device info whose entry needs to be found. - -Return Value: - - Pointer to the entry. NULL if it doesn't exist. - ---*/ -{ - PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; - PLIST_ENTRY entry = NULL; - - UNREFERENCED_PARAMETER(Context); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpFindFailPathDevInfoEntry (DevInfo %p): Entering function.\n", - FailingDevInfo)); - - for (entry = Group->FailingDevInfoList.Flink; - entry != &Group->FailingDevInfoList; - entry = entry->Flink) { - - failDevInfoListEntry = CONTAINING_RECORD(entry, DSM_FAIL_PATH_PROCESSING_LIST_ENTRY, ListEntry); - NT_ASSERT(failDevInfoListEntry); - - if (failDevInfoListEntry) { - - if (failDevInfoListEntry->FailingDeviceInfo == FailingDevInfo) { - - break; - - } else { - - failDevInfoListEntry = NULL; - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpFindFailPathDevInfoEntry (DevInfo %p): Exiting function returning entry %p.\n", - FailingDevInfo, - failDevInfoListEntry)); - - return failDevInfoListEntry; -} - - -PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY -DsmpBuildFailPathDevInfoEntry( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO FailingDevInfo, - _In_ IN PDSM_DEVICE_INFO AlternateDevInfo - ) -/*++ - -Routine Description: - - When InterpretError() is called with an IRP that has failed with a fatal error, - if the device is ALUA it is possible that STPG needs to be sent to update a new - devInfo as being Active/Optimized. However, for in-flight IOs that weren't - queued by MPIO, we still need to return a path that can be used. - - This routine is builds an entry that contains the alternate devInfo to use for - a failing one. - - NOTE: Calling function should be holding the spin lock. - -Arguments: - - Context is the DSM's context info. - - Group is the group entry representing the device. - - FailingDevInfo is the device info that was used when the IRP failed. - - AlternateDevInfo is the new one to temporarily use until its state can be properly set. - -Return Value: - - Pointer to the newly built entry. - NULL if there were any errors building it. - ---*/ -{ - PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; - - UNREFERENCED_PARAMETER(Context); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Entering function.\n", - FailingDevInfo)); - - failDevInfoListEntry = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_FAIL_PATH_PROCESSING_LIST_ENTRY), - DSM_TAG_FAIL_DEVINFO_LIST_ENTRY); - - if (failDevInfoListEntry) { - - failDevInfoListEntry->FailingDeviceInfo = FailingDevInfo; - failDevInfoListEntry->TempDeviceInfo = AlternateDevInfo; - InsertTailList(&Group->FailingDevInfoList, &failDevInfoListEntry->ListEntry); - InterlockedIncrement((LONG volatile*)&Group->NumberFailingDevInfos); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Failed to allocate memory for entry.\n", - FailingDevInfo)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Exiting function returning entry %p.\n", - FailingDevInfo, - failDevInfoListEntry)); - - return failDevInfoListEntry; -} - - -NTSTATUS -DsmpPhase1ProcessPathFailingALUA( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context - ) -/*++ - -Routine Description: - - This is the completion routine that is called when the STPG is sent down by - DsmpSetLBForPathFailingALUA. - - The caller SHOULD NOT acquire the DSM Context lock before calling this routine. - -Arguements: - - DeviceObject is the target device object to which Irp was sent - - Irp is the scsi pass through request for STPG - - Context is the completion context. - -Return Value: - - Status - ---*/ -{ - PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); - PDSM_TPG_COMPLETION_CONTEXT context = (PDSM_TPG_COMPLETION_CONTEXT)Context; - PSCSI_REQUEST_BLOCK srb = context->Srb; - PVOID senseData = context->SenseInfoBuffer; - UCHAR senseDataLength = context->SenseInfoBufferLength; - NTSTATUS status = Irp->IoStatus.Status; - ULONG targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); - PUCHAR targetPortGroupsInfo; - PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)(context->CompletionContext->RequestUnique1); - PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; - BOOLEAN releaseCompletionContextResources = TRUE; - KIRQL irql; - UCHAR scsiStatus = SrbGetScsiStatus(srb); - - UNREFERENCED_PARAMETER(DeviceObject); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): Entering function.\n", - deviceInfo)); - -#if DBG - KeQuerySystemTime(&context->CompletionContext->TickCount); -#endif - - if ((scsiStatus == SCSISTAT_GOOD) && - (NT_SUCCESS(status))) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): STPG succeeded.\n", - deviceInfo)); - - } else if (NT_SUCCESS(status) && - scsiStatus == SCSISTAT_CHECK_CONDITION && - DsmpShouldRetryTPGRequest(senseData, senseDataLength)) { - - if ((context->NumberRetries)--) { - - // - // Retry the request - // - - NT_ASSERT(SrbGetDataBuffer(srb) == MmGetMdlVirtualAddress(Irp->MdlAddress)); - - // - // Reset byte count of transfer in SRB Extension. - // - SrbSetDataTransferLength(srb, Irp->MdlAddress->ByteCount); - - // - // Zero SRB statuses. - // - srb->SrbStatus = 0; - SrbSetScsiStatus(srb, 0); - - nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; - nextIrpStack->MinorFunction = IRP_MN_SCSI_CLASS; - - // - // Save SRB address in next stack for port driver. - // - nextIrpStack->Parameters.Scsi.Srb = srb; - IoSetCompletionRoutine(Irp, DsmpPhase1ProcessPathFailingALUA, Context, TRUE, TRUE, TRUE); - - IoMarkIrpPending(Irp); - - // - // Send the IRP asynchronously - // - DsmSendRequestEx(context->CompletionContext->DsmContext->MPIOContext, - deviceInfo->TargetObject, - Irp, - deviceInfo, - DSM_CALL_COMPLETION_ON_MPIO_ERROR); - - // - // We know that the completion routine will always be called. - // - status = STATUS_PENDING; - goto __Exit_DsmpPhase1ProcessPathFailingALUA; - } - } else { - - irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); - - failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - context->CompletionContext->DeviceInfo); - - if (failDevInfoListEntry) { - - DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - failDevInfoListEntry); - } - - ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): NTStatus 0%x, ScsiStatus 0x%x.\n", - deviceInfo, - status, - scsiStatus)); - } - - if (NT_SUCCESS(status)) { - - // - // An explicit transition may cause changes to some other TPGs. - // So we need to query for the states of all the TPGs and update - // our internal list and its elements. - // - - // - // Take care of worst case scenario, which is: - // 1. 4-byte header (for allocation length) - // 2. 32 8-byte descriptors (for TPGs) - // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) - // - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - DSM_MAX_PATHS * sizeof(ULONG))); - - targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, - targetPortGroupsInfoLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo) { - - if (STATUS_PENDING == DsmpReportTargetPortGroupsAsync(deviceInfo, - DsmpPhase2ProcessPathFailingALUA, - Context, - targetPortGroupsInfoLength, - targetPortGroupsInfo)) { - - releaseCompletionContextResources = FALSE; - - } else { - - DsmpFreePool(targetPortGroupsInfo); - } - } else { - - irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); - - failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - context->CompletionContext->DeviceInfo); - - if (failDevInfoListEntry) { - - DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - failDevInfoListEntry); - } - - ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); - } - } - - // - // Free the allocations. - // - IoFreeMdl(Irp->MdlAddress); - Irp->MdlAddress = NULL; - - DsmpFreePool(Irp->UserBuffer); - - IoFreeIrp(Irp); - Irp = (PIRP) NULL; - - if (releaseCompletionContextResources) { - - // - // Release our hold on the device info so that it can be removed. - // - InterlockedDecrement(&context->CompletionContext->DeviceInfo->BlockRemove); - - ExFreeToNPagedLookasideList(&(context->CompletionContext->DsmContext)->CompletionContextList, context->CompletionContext); - DsmpFreePool(context->Srb); - DsmpFreePool(context->SenseInfoBuffer); -#pragma warning(suppress:6001) // DevDiv 818965 - DsmpFreePool(context); - } - -__Exit_DsmpPhase1ProcessPathFailingALUA: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): Exiting function.\n", - deviceInfo)); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - - -NTSTATUS -DsmpRemoveFailPathDevInfoEntry( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY FailPathDevInfoEntry - ) -/*++ - -Routine Description: - - This routine removes the entry pointed to from the passed in Group's list. - - NOTE: Calling function should be holding the spin lock. - -Arguments: - - Context is the DSM's context info. - - Group is the group entry representing the device. - - FailingPathDevInfoEntry is the entry that needs to be removed. - -Return Value: - - STATUS_SUCCESS if successful, else appropriate NT error code. - ---*/ -{ - PLIST_ENTRY entry = &FailPathDevInfoEntry->ListEntry; - PDSM_DEVICE_INFO deviceInfo = FailPathDevInfoEntry->FailingDeviceInfo; - NTSTATUS status = STATUS_SUCCESS; - - UNREFERENCED_PARAMETER(Context); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpRemoveFailPathDevInfoEntry (DevInfo %p): Entering function.\n", - deviceInfo)); - - RemoveEntryList(entry); - DsmpFreePool(FailPathDevInfoEntry); - InterlockedDecrement((LONG volatile*)&Group->NumberFailingDevInfos); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpRemoveFailPathDevInfoEntry (DevInfo %p): Exiting function with status %x.\n", - deviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpPhase2ProcessPathFailingALUA( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context - ) -/*++ - -Routine Description: - - This is the completion routine that is called when the RTPG is sent down by - DsmpPhase1ProcessPathFailingALUA. - - The caller SHOULD NOT acquire the DSM Context lock before calling this routine. - -Arguements: - - DeviceObject is the target device object to which Irp was sent - - Irp is the scsi pass through request for RTPG - - Context is the completion context. - -Return Value: - - Status - ---*/ -{ - PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); - PDSM_TPG_COMPLETION_CONTEXT context = (PDSM_TPG_COMPLETION_CONTEXT)Context; - PSCSI_REQUEST_BLOCK srb = context->Srb; - PVOID senseData = context->SenseInfoBuffer; - UCHAR senseDataLength = context->SenseInfoBufferLength; - NTSTATUS status = Irp->IoStatus.Status; - PUCHAR header; - ULONG returnedDataLength = 0; - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength = 0; - PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)(context->CompletionContext->RequestUnique1); - BOOLEAN decrementRTPGcount = (BOOLEAN)(context->CompletionContext->RequestUnique2); - KIRQL irql; - ULONG index; - PDSM_DEVICE_INFO devInfo; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; - PDSM_GROUP_ENTRY group = deviceInfo->Group; - PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; - UCHAR scsiStatus = SrbGetScsiStatus(srb); - - UNREFERENCED_PARAMETER(DeviceObject); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Entering function.\n", - deviceInfo)); - -#if DBG - KeQuerySystemTime(&(context->CompletionContext)->TickCount); -#endif - - if ((status == STATUS_BUFFER_OVERFLOW) || - (NT_SUCCESS(status) && - (scsiStatus == SCSISTAT_GOOD))) { - - header = (PUCHAR)((PUCHAR)SrbGetDataBuffer(srb)); - GetUlongFrom4ByteArray(header, returnedDataLength); - - status = STATUS_SUCCESS; - if (returnedDataLength > SrbGetDataTransferLength(srb)) { - - status = STATUS_BUFFER_OVERFLOW; - } - } - - if ((scsiStatus == SCSISTAT_GOOD) && - (NT_SUCCESS(status))) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): RTPG using path %p succeeded.\n", - deviceInfo, - deviceInfo->FailGroup->PathId)); - - header = (PUCHAR)((PUCHAR)SrbGetDataBuffer(srb)); - GetUlongFrom4ByteArray(header, returnedDataLength); - - // - // Allocate a buffer to hold the TPG info. - // - targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, - SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo) { - - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength; - - // - // Copy it over. - // - RtlCopyMemory(targetPortGroupsInfo, - header, - targetPortGroupsInfoLength); - - } else { - - irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); - - failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - context->CompletionContext->DeviceInfo); - - if (failDevInfoListEntry) { - - DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - failDevInfoListEntry); - } - - ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Failed to allocate mem for TPG.\n", - deviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - - } else if (NT_SUCCESS(status) && - scsiStatus == SCSISTAT_CHECK_CONDITION && - DsmpShouldRetryTPGRequest(senseData, senseDataLength)) { - - if ((context->NumberRetries)--) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Retrying check condition using path %p. Retries remaining %u.\n", - deviceInfo, - deviceInfo->FailGroup->PathId, - context->NumberRetries)); - - // - // Retry the request - // - - NT_ASSERT(SrbGetDataBuffer(srb) == MmGetMdlVirtualAddress(Irp->MdlAddress)); - - // - // Reset byte count of transfer in SRB Extension to true length. - // - SrbSetDataTransferLength(srb, targetPortGroupsInfoLength); - - // - // Zero SRB statuses. - // - srb->SrbStatus = 0; - SrbSetScsiStatus(srb, 0); - - nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; - nextIrpStack->MinorFunction = IRP_MN_SCSI_CLASS; - - // - // Save SRB address in next stack for port driver. - // - nextIrpStack->Parameters.Scsi.Srb = srb; - IoSetCompletionRoutine(Irp, DsmpPhase2ProcessPathFailingALUA, Context, TRUE, TRUE, TRUE); - - IoMarkIrpPending(Irp); - - // - // Send the IRP asynchronously - // - DsmSendRequestEx(context->CompletionContext->DsmContext->MPIOContext, - deviceInfo->TargetObject, - Irp, - deviceInfo, - DSM_CALL_COMPLETION_ON_MPIO_ERROR); - - // - // We know that the completion routine will always be called. - // - status = STATUS_PENDING; - goto __Exit_DsmpPhase2ProcessPathFailingALUA; - } - } else { - - irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); - - failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - context->CompletionContext->DeviceInfo); - - if (failDevInfoListEntry) { - - DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - failDevInfoListEntry); - } - - ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): NTStatus 0%x, ScsiStatus 0x%x.\n", - deviceInfo, - status, - SrbGetScsiStatus(srb))); - - // Failed to get TPG Info. - // Here it is possible status is success, but scsiStatus is not. - // If so, set status to unsuccessful. - - if (NT_SUCCESS(status)) { - status = STATUS_UNSUCCESSFUL; - } - } - - if (NT_SUCCESS(status)) { - - irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); - - // - // Parse the TPG information and update the device path states - // - status = DsmpParseTargetPortGroupsInformation(context->CompletionContext->DsmContext, - deviceInfo->Group, - targetPortGroupsInfo, - targetPortGroupsInfoLength); - - for (index = 0; index < DSM_MAX_PATHS; index++) { - - targetPortGroup = deviceInfo->Group->TargetPortGroupList[index]; - - if (targetPortGroup) { - - DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); - } - } - - if (NT_SUCCESS(status)) { - - PDSM_DEVICE_INFO tempDevice = NULL; - - // - // Update all the devInfo states. If the device is AO but - // not this device, make it fake AU. - // If device not in AO, make sure that it matches the TPG - // state. - // Ensure that: - // 1. All devices match their ALUA state. - // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it. - // 3. For FOO there must be only one A/O device. Preferably the preferred path. - // - for (index = 0; index < DSM_MAX_PATHS; index++) { - - devInfo = group->DeviceList[index]; - - if (devInfo) { - - if (devInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { - - // - // In implicit transitions, there is no guarantee that - // the TPG of chosen "deviceInfo" is in A/O state. So - // to play it safe, we hang on to the very first devInfo - // whose TPG is in A/O state. - // - if (!tempDevice && - !DsmpIsDeviceFailedState(devInfo->State)) { - - devInfo->PreviousState = devInfo->State; - devInfo->State = devInfo->ALUAState; - - tempDevice = devInfo; - } - - if (devInfo != deviceInfo) { - - if (!DsmpIsDeviceFailedState(devInfo->State)) { - - // - // For FOO, only one path can be in A/O. - // For RRWS, mask an A/O path if that isn't the desired state. - // - if ((group->LoadBalanceType == DSM_LB_FAILOVER) || - (group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && - devInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - devInfo->DesiredState != DSM_DEV_UNDETERMINED)) { - - // - // For implicit transitions, we may have saved off an A/O path. - // Don't undo that. - // - if (tempDevice != devInfo) { - - devInfo->PreviousState = devInfo->State; - devInfo->State = DSM_DEV_ACTIVE_UNOPTIMIZED; - } - - } else { - - devInfo->PreviousState = devInfo->State; - devInfo->State = devInfo->ALUAState; - } - } - } else { - - devInfo->PreviousState = devInfo->State; - - // - // For FOO, only one path can be in A/O state. - // The TPG of the selected "deviceInfo" is in A/O, so this - // can now very well be made the candidate. However, since - // it is possible that we saved off another candidate, we - // now need to replace that with this. - // - if (!DsmpIsDeviceFailedState(devInfo->State) && - devInfo->Group->LoadBalanceType == DSM_LB_FAILOVER && - tempDevice) { - - tempDevice->State = DSM_DEV_ACTIVE_UNOPTIMIZED; - } - - devInfo->State = devInfo->ALUAState; - - tempDevice = devInfo; - } - } else { - - if (!DsmpIsDeviceFailedState(devInfo->State)) { - - devInfo->PreviousState = devInfo->State; - devInfo->State = devInfo->ALUAState; - } - } - } - } - } - - failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - context->CompletionContext->DeviceInfo); - - if (failDevInfoListEntry) { - - DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, - context->CompletionContext->DeviceInfo->Group, - failDevInfoListEntry); - } - - ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); - } - - - // - // Resume IO if we throttled requests. - // - if (InterlockedCompareExchange((LONG volatile*)&group->Throttled, 0, 1)) { - - NTSTATUS resumeStatus = STATUS_UNSUCCESSFUL; - - DsmNotification(((PDSM_CONTEXT)deviceInfo->DsmContext)->MPIOContext, - ResumeIO_V2, - deviceInfo, - TRUE, - &resumeStatus, - 0); - - if (!NT_SUCCESS(resumeStatus)) { - - // - // Resume can fail when - // 1. The MPDisk is being removed (or) - // 2. The MPDisk is any other state other than throttled (or) - // 3. There is a problem dispatching throttled requests. - // - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevObj %p): Resume IO failed.\n", - DeviceObject)); - } - - } - - if (decrementRTPGcount) { - - // - // Resetting InFlightRTPG after resume so that we don't get into situation where - // new DsmpSetPathForIoRetryALUA caller thread finds that InFlightRTPG is not set but Throttled is set - // - ULONG count = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 1); - - // - // If decrementRTPGCount flag is set, there must be atleast one RTPG in flight. - // - NT_ASSERT(count); - - UNREFERENCED_PARAMETER(count); - - } - - // - // Free the allocations. - // - if (targetPortGroupsInfo) { - - DsmpFreePool(targetPortGroupsInfo); - } - - // - // Release our hold on the device info so that it can be removed. - // - InterlockedDecrement(&context->CompletionContext->DeviceInfo->BlockRemove); - - IoFreeMdl(Irp->MdlAddress); - Irp->MdlAddress = NULL; - - DsmpFreePool(Irp->UserBuffer); - - IoFreeIrp(Irp); - Irp = (PIRP) NULL; - - ExFreeToNPagedLookasideList(&(context->CompletionContext->DsmContext)->CompletionContextList, context->CompletionContext); - DsmpFreePool(srb); - DsmpFreePool(senseData); -#pragma warning(suppress:6001) // DevDiv 818965 - DsmpFreePool(context); - -__Exit_DsmpPhase2ProcessPathFailingALUA: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Exiting function.\n", - deviceInfo)); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - - -NTSTATUS -DsmpPersistentReserveOut( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ) -/*++ - -Routine Description: - - This routine will handle determine which devices to send the request to based - on the service action of the PR-out command. - On REGISTER/REGISTER_AND_IGNORE_EXISTING, it will send the command down all - paths. If any path succeeds, the PR key will be stored. If failure down any - path, return failure. - On REGISTER/REGISTER_AND_IGNORE_EXISTING with key == 0 (ie. UNREGISTER), - the request is sent down every path. Failure is returned if request fails down - any path (but error is ignored if path happens to be one where prior - REGISTER/REGISTER_AND_IGNORE_EXISTING had failed in the first place). The - stored PR key is cleared irrespective of success/failure being returned. - On RESERVE/RELEASE, the command is sent down one path. If it fails, another - path is tried. Failure is returned only if none succeed. - On CLEAR, command is sent down one path. If it fails, another path is tried. - Failure is returned only if none succeed. The stored PR key is cleared - irrespective of success/failure being returned. - On PREEMPT, command is sent down one path. If it fails, another path is - tried. Failure is returned only if none succeed. - On PREEMPT_AND_ABORT, command is sent down one path. Failed request is not - retried. - - NOTE: If a path shows up later, REGISTER_AND_IGNORE_EXISTING request is - built by the IsPathActive routine using the saved PR key and sent down new path. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DsmIds - The collection of DSM IDs that pertain to the MPDISK. - Irp - Irp containing SRB. - Srb - Scsi request block - Event - The event to - -Return Value: - - NTSTATUS of the operation. - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo; - PDSM_DEVICE_INFO servicingDeviceInfo = NULL; - PDSM_GROUP_ENTRY group; - LONG i; - ULONG count; - NTSTATUS status = STATUS_UNSUCCESSFUL; - PDSM_COMPLETION_CONTEXT completionContext; - PCDB cdb = SrbGetCdb(Srb); - UCHAR serviceAction; - NTSTATUS returnStatus = STATUS_SUCCESS; - BOOLEAN sendDownAll = FALSE; - BOOLEAN savePRKeyIfAnySucceed = FALSE; - BOOLEAN retryOnAnother = FALSE; - BOOLEAN passOnlyIfAllSucceed = FALSE; - BOOLEAN ignoreIfPreviousFailed = FALSE; - BOOLEAN clearPRKey = FALSE; - KEVENT event; - PPRO_PARAMETER_LIST prOutParam = Irp->AssociatedIrp.SystemBuffer; - PUCHAR index = NULL; - UCHAR prKey[8] = {0}; - PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; - PIO_STACK_LOCATION irpStack; - PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); - BOOLEAN statusUpdated = FALSE; - ULONGLONG currentTickCount; - ULONGLONG finalTickCount; - ULONG tickLength = KeQueryTimeIncrement(); - PVOID senseInfoBuffer = NULL; - UCHAR senseInfoBufferLength = 0; - BOOLEAN srbCopySucceeded = FALSE; - UCHAR prType; - UCHAR prScope; - ULONGLONG saKey; - ULONGLONG resKey; - ULONG SpecialHandlingFlag = 0; - - UNREFERENCED_PARAMETER(Event); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // Cache away a copy of the SRB - // - srbCopy = SrbAllocateCopy(Srb, NonPagedPoolNx, DSM_TAG_SCSI_REQUEST_BLOCK); - if (srbCopy == NULL) { - returnStatus = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpPersistentReserveOut; - } - - deviceInfo = DsmIds->IdList[0]; - group = deviceInfo->Group; - - prType = cdb->PERSISTENT_RESERVE_OUT.Type; - prScope = cdb->PERSISTENT_RESERVE_OUT.Scope; - serviceAction = cdb->PERSISTENT_RESERVE_OUT.ServiceAction; - - NT_ASSERT(serviceAction >= RESERVATION_ACTION_REGISTER && serviceAction <= RESERVATION_ACTION_REGISTER_IGNORE_EXISTING); - - index = prOutParam->ServiceActionReservationKey; - RtlCopyMemory(&prKey, index, 8); - REVERSE_BYTES_QUAD(&saKey, &prOutParam->ServiceActionReservationKey); - - REVERSE_BYTES_QUAD(&resKey, &prOutParam->ReservationKey); - - switch (serviceAction) { - case RESERVATION_ACTION_REGISTER: - case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING: { - - // - // The command must be sent down all paths. - // - sendDownAll = TRUE; - - // - // Return failure if it fails down even one of the paths. - // - passOnlyIfAllSucceed = TRUE; - - if (DsmpIsPersistentReservationKeyZeroKey(ARRAY_SIZE(prKey), prKey)) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): NULL PR key, service action %u\n", - DsmIds, - serviceAction)); - - // - // If unregister fails, don't report it back as error if the - // previous register/register_and_ignore_existing down that - // path had failed too. - // - ignoreIfPreviousFailed = TRUE; - - // - // Clear the group PR key irrespective of the status that is - // going to be returned to clusdisk. - // - clearPRKey = TRUE; - - } else { - - // - // If register/register_and_ignore_existing succeed down any of - // the paths, save off the PR key for the group entry. - // - savePRKeyIfAnySucceed = TRUE; - } - - break; - } - - case RESERVATION_ACTION_RESERVE: - case RESERVATION_ACTION_RELEASE: - case RESERVATION_ACTION_PREEMPT: - case RESERVATION_ACTION_PREEMPT_ABORT: - case RESERVATION_ACTION_CLEAR: { - - if (serviceAction != RESERVATION_ACTION_PREEMPT_ABORT) { - - // - // Apart from preempt_abort, all the others must be retried - // (down another path) if they fail down the chosen path. - // - retryOnAnother = TRUE; - } - - if (serviceAction == RESERVATION_ACTION_CLEAR) { - - // - // Clear the stored PR key for the group entry irrespective of - // the status that is going to be returned back. - // - clearPRKey = TRUE; - } - - break; - } - - default: { - - returnStatus = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): Invalid service action %u.\n", - DsmIds, - serviceAction)); - - goto __Exit_DsmpPersistentReserveOut; - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): Srb %p, Service Action %u, Type %u, Scope %u, \ - \n\t\t\t\tservice action reservation key %I64x, reservation key %I64x.\n", - DsmIds, - Srb, - serviceAction, - prType, - prScope, - saKey, - resKey)); - - // - // Allocate a context for the completion routine. - // - completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); - if (!completionContext) { - - returnStatus = STATUS_INSUFFICIENT_RESOURCES; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Failed to allocate completion context.\n", - DsmIds, - serviceAction)); - - goto __Exit_DsmpPersistentReserveOut; - } - - KeInitializeEvent(&event, NotificationEvent, FALSE); - - // - // Indicate the target for this request. - // - completionContext->DsmContext = DsmContext; - completionContext->RequestUnique1 = (PVOID)&event; - completionContext->RequestUnique2 = cdb->PERSISTENT_RESERVE_OUT.OperationCode; - - count = group->NumberDevices; - - for (i = count - 1; i >= 0; i--) { - - // - // A PR command may fail with a "retry-able" UA when reservation is - // released or preempted (on every I_T_L nexus except the one on which - // it was released/preempted). In such a case we should retry the PR - // command on the same path. - // - KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); - finalTickCount = currentTickCount + (DSM_SECONDS_TO_TICKS(group->MaxPRRetryTimeDuringStateTransition) / tickLength); - - if (!sendDownAll && !retryOnAnother) { - - // - // If the request doesn't need to be retried (down another path) on - // failure, better choose the path that has maximum chances of - // success. - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), - SpecialHandlingFlag); - - if (!deviceInfo) { - - returnStatus = STATUS_UNSUCCESSFUL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - No active/alternative path for device %p.\n", - DsmIds, - serviceAction, - group)); - - break; - } - - } else { - - deviceInfo = group->DeviceList[i]; - - // - // Ignore "bad" paths for now. If the path becomes "good" again, - // IsPathActive() will send down the register. - // Also, don't consider newly arrived paths for which the group has - // a reservation but register has not yet been sent down. This rule - // applies only to requests that are not Register. - // - if ((DsmpIsDeviceFailedState(deviceInfo->State) || !DsmpIsDeviceInitialized(deviceInfo)) || - (!DsmpIsDeviceUsablePR(deviceInfo) && !sendDownAll)) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): Ignoring bad instance - state %x, init %x, key reg %x (key valid %x).\n", - DsmIds, - deviceInfo->State, - deviceInfo->Initialized, - deviceInfo->PRKeyRegistered, - deviceInfo->Group->PRKeyValid)); - - deviceInfo = NULL; - } - - } - - if (!deviceInfo) { - - // - // Maybe a remove came through and caused a collapse of the device - // list, thus making this entry empty. - // - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Couldn't find path for device %p.\n", - DsmIds, - serviceAction, - group)); - - continue; - } - -__DsmpPersistentReserveOut_RetryRequest: - - IoMarkIrpPending(Irp); - - completionContext->DeviceInfo = deviceInfo; - - // - // Set-up a completion routine. - // - IoSetCompletionRoutine(Irp, - DsmpPersistentReserveCompletion, - completionContext, - TRUE, - TRUE, - TRUE); - - // - // Always send the original request down a new path - // - irpStack = IoGetNextIrpStackLocation(Irp); - srbCopySucceeded = SrbCopySrb(Srb, SrbGetSrbLength(Srb), srbCopy); - NT_ASSERT(srbCopySucceeded == TRUE); - irpStack->Parameters.Scsi.Srb = Srb; - - // - // Clear the sense buffer if it exists - // - senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); - senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); - if (senseInfoBuffer) { - RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); - } - - servicingDeviceInfo = deviceInfo; - - // - // Issue the request and wait. - // - status = DsmSendRequest(DsmContext->MPIOContext, - deviceInfo->TargetObject, - Irp, - deviceInfo); - - if (status == STATUS_PENDING) { - - KeWaitForSingleObject(&event, - Executive, - KernelMode, - FALSE, - NULL); - - status = Irp->IoStatus.Status; - } - - if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down successfully on %p.\n", - DsmIds, - serviceAction, - deviceInfo->FailGroup->PathId)); - - if (!passOnlyIfAllSucceed) { - - // - // Success down any one path means success - // - returnStatus = status; - } - - if (savePRKeyIfAnySucceed) { - - RtlCopyMemory(&group->PersistentReservationRegisteredKey, &prKey, 8); - - group->PRServiceAction = serviceAction; - group->PRType = prType; - group->PRScope = prScope; - group->PRKeyValid = TRUE; - deviceInfo->PRKeyRegistered = TRUE; - } - - if (!sendDownAll) { - - // - // Need for retrying on another path only necessary in the case - // of request failing down the chosen path. Since the request - // succeeded down this path, we are done. - // - break; - } - } else { - - BOOLEAN recordFailure; - - // - // Check to see if the request failed because of a "transient error", - // like reservations released for example. If so, this is NOT an actual - // error and the request must be retried. Multiple retries may be required - // if for example the UA indicates that the TPGs are in transitioning state. - // - if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && - Srb->SrbStatus & SRB_STATUS_ERROR && - SrbGetScsiStatus(Srb) == SCSISTAT_CHECK_CONDITION) { - - KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); - - senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); - senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); - - if (DsmpShouldRetryPersistentReserveCommand(senseInfoBuffer, senseInfoBufferLength) && - currentTickCount < finalTickCount) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u returned UA with error %x. Retrying same path %p.\n", - DsmIds, - serviceAction, - status, - deviceInfo->FailGroup->PathId)); - - KeResetEvent(&event); - Irp->IoStatus.Status = 0; - - goto __DsmpPersistentReserveOut_RetryRequest; - } - } - - // - // The return status is STATUS_SUCCESS by default. This means that if the - // request failed on the first path and was retried down every other path - // but fails down all of them, the return status is never updated. - // So cache the first failure status to cover the above scenario. - // - if (!statusUpdated) { - - returnStatus = status; - statusUpdated = TRUE; - } - - recordFailure = TRUE; - if (ignoreIfPreviousFailed && !deviceInfo->PRKeyRegistered) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Ignoring status %x for path %p.\n", - DsmIds, - serviceAction, - status, - deviceInfo->FailGroup->PathId)); - - // - // Okay to ignore this failure if the previous failed. - // - recordFailure = FALSE; - } - - if (passOnlyIfAllSucceed && recordFailure) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Saving status %x for return.\n", - DsmIds, - serviceAction, - status)); - - // - // Save the failure status to return back. - // - returnStatus = status; - } - - // - // If the request is not to be sent down all paths, and also - // a retry (along a different path) on failure is not required, - // we're done - just return this failure. - // - if (!(sendDownAll || retryOnAnother)) { - - returnStatus = status; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down %p failed with %x. Breaking out.\n", - DsmIds, - serviceAction, - deviceInfo->FailGroup->PathId, - status)); - - break; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down %p failed with %x. Sending down another path.\n", - DsmIds, - serviceAction, - deviceInfo->FailGroup->PathId, - status)); - } - } - - // - // If we are here, it is either because the request needs to be sent down - // all paths, or because the request failed down the chosen path and needs - // to be retried down a new path. - // - KeResetEvent(&event); - Irp->IoStatus.Status = 0; - } - - if (clearPRKey) { - - for (i = 0; (ULONG)i < group->NumberDevices; i++) { - - deviceInfo = group->DeviceList[i]; - - if (deviceInfo) { - - deviceInfo->RegisterServiced = FALSE; - deviceInfo->PRKeyRegistered = FALSE; - } - } - group->PersistentReservationRegisteredKey[0] = group->PersistentReservationRegisteredKey[1] = - group->PersistentReservationRegisteredKey[2] = group->PersistentReservationRegisteredKey[3] = - group->PersistentReservationRegisteredKey[4] = group->PersistentReservationRegisteredKey[5] = - group->PersistentReservationRegisteredKey[6] = group->PersistentReservationRegisteredKey[7] = 0; - - group->PRKeyValid = FALSE; - group->ReservationList = 0; - } - - if (savePRKeyIfAnySucceed && group->PRKeyValid) { - - ULONG ordinal; - - for (i = 0; (ULONG)i < group->NumberDevices; i++) { - - deviceInfo = group->DeviceList[i]; - - if (deviceInfo) { - - deviceInfo->RegisterServiced = TRUE; - ordinal = (1 << i); - group->ReservationList |= ordinal; - } - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): PR_OUT for %u completed with status %x.\n", - DsmIds, - serviceAction, - returnStatus)); - - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - -__Exit_DsmpPersistentReserveOut: - - if (srbCopy != NULL) { - DsmpFreePool(srbCopy); - } - - currentIrpStack->Parameters.Others.Argument3 = servicingDeviceInfo; - Irp->IoStatus.Status = returnStatus; - if ((!NT_SUCCESS(returnStatus)) && - (SrbGetSrbStatus(Srb) == SRB_STATUS_SUCCESS)) { - SrbSetSrbStatus(Srb, DsmpNtStatusToSrbStatus(returnStatus)); - } - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveOut (DsmIds %p): Exiting function returning IRP status %x.\n", - DsmIds, - returnStatus)); - - return returnStatus; -} - - - -NTSTATUS -DsmpPersistentReserveIn( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ) -/*++ - -Routine Description: - - This routine will handle determine which devices to send the request to based - on the service action of the PR-in command. - On READ KEYS, it will send down one path. In case of failure, other paths will - be tried until one succeeds. Failure is returned only if it fails down all paths. - On READ_RESERVATION/REPORT_CAPABILITIES, command is sent down one path. Failed - request is not retried. - -Arguments: - - DsmContext - DSM context given to MPIO during initialization - DsmIds - The collection of DSM IDs that pertain to the MPDISK. - Irp - Irp containing SRB. - Srb - Scsi request block - Event - The event to - -Return Value: - - NTSTATUS of the operation. - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo; - PDSM_DEVICE_INFO servicingDeviceInfo = NULL; - PDSM_GROUP_ENTRY group; - LONG i; - ULONG count; - NTSTATUS status = STATUS_UNSUCCESSFUL; - PDSM_COMPLETION_CONTEXT completionContext; - PCDB cdb = SrbGetCdb(Srb); - UCHAR serviceAction; - BOOLEAN retryOnAnother = FALSE; - KEVENT event; - PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; - PIO_STACK_LOCATION irpStack; - PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); - ULONGLONG currentTickCount; - ULONGLONG finalTickCount; - ULONG tickLength = KeQueryTimeIncrement(); - PVOID senseInfoBuffer = NULL; - UCHAR senseInfoBufferLength = 0; - BOOLEAN srbCopySucceeded = FALSE; - ULONG SpecialHandlingFlag = 0; - - UNREFERENCED_PARAMETER(Event); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // Cache away a copy of the SRB - // - srbCopy = SrbAllocateCopy(Srb, NonPagedPoolNx, DSM_TAG_SCSI_REQUEST_BLOCK); - if (srbCopy == NULL) { - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpPersistentReserveIn; - } - - deviceInfo = DsmIds->IdList[0]; - group = deviceInfo->Group; - - serviceAction = cdb->PERSISTENT_RESERVE_IN.ServiceAction; - - switch (serviceAction) { - case RESERVATION_ACTION_READ_RESERVATIONS: - case RESERVATION_ACTION_READ_KEYS: { - - // - // If there is a failure on the chosen path, retry on another path. - // - retryOnAnother = TRUE; - break; - } - - case SPC3_RESERVATION_ACTION_REPORT_CAPABILITIES: { - - break; - } - - default: { - - NT_ASSERT(FALSE); - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpPersistentReserveIn; - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): Srb %p. Service Action %u.\n", - DsmIds, - Srb, - serviceAction)); - - // - // Allocate a context for the completion routine. - // - completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); - if (!completionContext) { - - status = STATUS_INSUFFICIENT_RESOURCES; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - Failed to allocate completion context.\n", - DsmIds, - serviceAction)); - - goto __Exit_DsmpPersistentReserveIn; - } - - KeInitializeEvent(&event, NotificationEvent, FALSE); - - // - // Indicate the target for this request. - // - completionContext->DsmContext = DsmContext; - completionContext->RequestUnique1 = (PVOID)&event; - completionContext->RequestUnique2 = cdb->PERSISTENT_RESERVE_IN.OperationCode; - - count = group->NumberDevices; - - for (i = count - 1; i >= 0; i--) { - - // - // A PR command may fail with a "retry-able" UA when reservation is - // released or preempted (on every I_T_L nexus except the one on which - // it was released/preempted). In such a case we should retry the PR - // command on the same path. - // - KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); - finalTickCount = currentTickCount + (DSM_SECONDS_TO_TICKS(group->MaxPRRetryTimeDuringStateTransition) / tickLength); - - if (!retryOnAnother) { - - // - // If the request doesn't need to be retried (down another path) on - // failure, better choose the path that has maximum chances of - // success. - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), - SpecialHandlingFlag); - - if (!deviceInfo) { - - status = STATUS_UNSUCCESSFUL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - No active/alternative path for device %p.\n", - DsmIds, - serviceAction, - group)); - - break; - } - - } else { - - deviceInfo = group->DeviceList[i]; - - if (DsmpIsDeviceFailedState(deviceInfo->State) || !DsmpIsDeviceInitialized(deviceInfo)) { - - // - // Ignore "bad" paths for now. - // - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): Ignoring bad instance - state %x, init %x.\n", - DsmIds, - deviceInfo->State, - deviceInfo->Initialized)); - - deviceInfo = NULL; - } - } - - if (!deviceInfo) { - - // - // Maybe a remove came through and caused a collapse of the device - // list, thus making this entry empty. - // - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - Couldn't find path for device %p.\n", - DsmIds, - serviceAction, - group)); - - continue; - } - -__DsmpPersistentReserveIn_RetryRequest: - - IoMarkIrpPending(Irp); - - completionContext->DeviceInfo = deviceInfo; - - // - // Set-up a completion routine. - // - IoSetCompletionRoutine(Irp, - DsmpPersistentReserveCompletion, - completionContext, - TRUE, - TRUE, - TRUE); - - // - // Always send the original request down a new path - // - irpStack = IoGetNextIrpStackLocation(Irp); - srbCopySucceeded = SrbCopySrb(Srb, SrbGetSrbLength(Srb), srbCopy); - NT_ASSERT(srbCopySucceeded == TRUE); - irpStack->Parameters.Scsi.Srb = Srb; - - // - // Clear the sense buffer if it exists - // - senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); - senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); - if (senseInfoBuffer) { - RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); - } - - servicingDeviceInfo = deviceInfo; - - // - // Issue the request and wait. - // - status = DsmSendRequest(DsmContext->MPIOContext, - deviceInfo->TargetObject, - Irp, - deviceInfo); - - if (status == STATUS_PENDING) { - - KeWaitForSingleObject(&event, - Executive, - KernelMode, - FALSE, - NULL); - - status = Irp->IoStatus.Status; - } - - if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u sent down successfully on %p.\n", - DsmIds, - serviceAction, - deviceInfo->FailGroup->PathId)); -#if DBG - if (serviceAction == RESERVATION_ACTION_READ_KEYS) { - - PPRI_REGISTRATION_LIST prInRegistrationList = Irp->AssociatedIrp.SystemBuffer; - ULONG numberOfKeys; - ULONG keyIndex; - ULONGLONG prKey; - - REVERSE_BYTES(&numberOfKeys, &prInRegistrationList->AdditionalLength); - numberOfKeys /= 8; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): %u registrations keys present:\n", - DsmIds, - numberOfKeys)); - - for (keyIndex = 0; keyIndex < numberOfKeys; keyIndex++) { - - REVERSE_BYTES_QUAD(&prKey, &(prInRegistrationList->ReservationKeyList[keyIndex])); - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): Registration Key %u: %I64x\n", - DsmIds, - keyIndex, - prKey)); - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "\n")); - - } else if (serviceAction == RESERVATION_ACTION_READ_RESERVATIONS) { - - PPRI_RESERVATION_LIST prInReservationList = Irp->AssociatedIrp.SystemBuffer; - ULONG numberOfDescriptors; - PPRI_RESERVATION_DESCRIPTOR prInReservationDescriptor = prInReservationList->Reservations; - ULONGLONG prKey = 0; - - REVERSE_BYTES(&numberOfDescriptors, &prInReservationList->AdditionalLength); - numberOfDescriptors /= sizeof(PRI_RESERVATION_DESCRIPTOR); - NT_ASSERT(numberOfDescriptors <= 1); - - if (numberOfDescriptors == 1) { - REVERSE_BYTES_QUAD(&prKey, &prInReservationDescriptor->ReservationKey); - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): %u Reservation Key: %I64x\n", - DsmIds, - numberOfDescriptors, - prKey)); - } -#endif - // - // Done. - // - break; - - } else { - - // - // Check to see if the request failed because of a "transient error", - // like reservations released for example. If so, this is NOT an actual - // error and the request must be retried. Multiple retries may be required - // if for example the UA indicates that the TPGs are in transitioning state. - // - if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && - Srb->SrbStatus & SRB_STATUS_ERROR && - SrbGetScsiStatus(Srb) == SCSISTAT_CHECK_CONDITION) { - - KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); - - senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); - senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); - - if (group->PRKeyValid && - DsmpShouldRetryPersistentReserveCommand(senseInfoBuffer, senseInfoBufferLength) && - currentTickCount < finalTickCount) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u returned UA with error %x. Retrying same path %p.\n", - DsmIds, - serviceAction, - status, - deviceInfo->FailGroup->PathId)); - - KeResetEvent(&event); - Irp->IoStatus.Status = 0; - - goto __DsmpPersistentReserveIn_RetryRequest; - } - } - - // - // If a retry (along a different path) on failure is not required, - // we're done - just return this failure. - // - if (!retryOnAnother) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u down %p failed with %x. Breaking out.\n", - DsmIds, - serviceAction, - deviceInfo->FailGroup->PathId, - status)); - - break; - } - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u down %p failed with %x. Sending down another path.\n", - DsmIds, - serviceAction, - deviceInfo->FailGroup->PathId, - status)); - - // - // If we are here, it is because the request failed down the chosen path - // and needs to be retried down a new path. - // - KeResetEvent(&event); - Irp->IoStatus.Status = 0; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u completed with status %x.\n", - DsmIds, - serviceAction, - status)); - - ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); - -__Exit_DsmpPersistentReserveIn: - - if (srbCopy != NULL) { - DsmpFreePool(srbCopy); - } - - currentIrpStack->Parameters.Others.Argument3 = servicingDeviceInfo; - Irp->IoStatus.Status = status; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveIn (DsmIds %p): Exiting function returning IRP status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpPersistentReserveCompletion( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context - ) -/*++ - -Routine Description: - - General-purpose completion routine for PR in and out commands sent synchronously. - -Arguments: - - DeviceObject - Target of the request. - Irp - Command being sent. - Context - The event on which the caller is waiting. - -Return Value: - - NTSTATUS - ---*/ - -{ - PDSM_COMPLETION_CONTEXT context = Context; - PKEVENT event; - - // It is required to specify a DSM completion context - // when setting DsmpPersistentReserveCompletion as completion routine. - _Analysis_assume_(context != NULL); - - event = (PKEVENT)(context->RequestUnique1); - - UNREFERENCED_PARAMETER(DeviceObject); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpPersistentReserveCompletion: DevInfo %p, IRP %p, Context %p\n", - context->DeviceInfo, - Irp, - Context)); - - if (Irp->PendingReturned) { - - IoMarkIrpPending(Irp); - } - - KeSetEvent(event, 0, FALSE); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - diff --git a/tests/projects/wdk/kmdf/msdsm/dsmtrace.mof b/tests/projects/wdk/kmdf/msdsm/dsmtrace.mof deleted file mode 100644 index d9e8cf4cc..000000000 --- a/tests/projects/wdk/kmdf/msdsm/dsmtrace.mof +++ /dev/null @@ -1,111 +0,0 @@ -#pragma classflags("forceupdate") -#pragma namespace("\\\\.\\root\\WMI") -// -// Copyright (C) 2004 Microsoft Corporation -// -// WPP Generated File -// - -//ModuleName = wppCtlGuid (Init called in Function DriverEntry) -[Dynamic, - Description("MSDSM Driver Tracing Provider"), - guid("{DEDADFF5-F99F-4600-B8C9-2D4D9B806B5B}"), - locale("MS\\0x409")] -class MSDSMGuid : EventTrace -{ - [Description ("Enable Flags"), - ValueDescriptions{ - "TRACE_FLAG_GENERAL Flag", - "TRACE_FLAG_PNP Flag", - "TRACE_FLAG_POWER Flag", - "TRACE_FLAG_RW Flag", - "TRACE_FLAG_IOCTL Flag", - "TRACE_FLAG_QUEUE Flag", - "TRACE_FLAG_WMI Flag", - "TRACE_FLAG_TIMER Flag", - "TRACE_FLAG_INIT Flag", - "TRACE_FLAG_LOCK Flag", - "TRACE_FLAG_DEBUG1 Flag", - "TRACE_FLAG_DEBUG2 Flag", - "TRACE_FLAG_MCN Flag", - "TRACE_FLAG_ISR Flag", - "TRACE_FLAG_ENUM Flag"}, - DefineValues{ - "TRACE_FLAG_GENERAL", - "TRACE_FLAG_PNP", - "TRACE_FLAG_POWER", - "TRACE_FLAG_RW", - "TRACE_FLAG_IOCTL", - "TRACE_FLAG_QUEUE", - "TRACE_FLAG_WMI", - "TRACE_FLAG_TIMER", - "TRACE_FLAG_INIT", - "TRACE_FLAG_LOCK", - "TRACE_FLAG_DEBUG1", - "TRACE_FLAG_DEBUG2", - "TRACE_FLAG_MCN", - "TRACE_FLAG_ISR", - "TRACE_FLAG_ENUM"}, - Values{ - "TRACE_FLAG_GENERAL", - "TRACE_FLAG_PNP", - "TRACE_FLAG_POWER", - "TRACE_FLAG_RW", - "TRACE_FLAG_IOCTL", - "TRACE_FLAG_QUEUE", - "TRACE_FLAG_WMI", - "TRACE_FLAG_TIMER", - "TRACE_FLAG_INIT", - "TRACE_FLAG_LOCK", - "TRACE_FLAG_DEBUG1", - "TRACE_FLAG_DEBUG2", - "TRACE_FLAG_MCN", - "TRACE_FLAG_ISR", - "TRACE_FLAG_ENUM"}, - ValueMap{ - "0x00000001", - "0x00000002", - "0x00000004", - "0x00000008", - "0x00000010", - "0x00000020", - "0x00000040", - "0x00000080", - "0x00000100", - "0x00000200", - "0x00000400", - "0x00000800", - "0x00001000", - "0x00002000", - "0x00004000"} - ] - uint32 Flags; - [Description ("Levels"), - ValueDescriptions{ - "Abnormal exit or termination", - "Severe errors that need logging", - "Warnings such as allocation failure", - "Includes non-error cases", - "Detailed traces from intermediate steps" }, - DefineValues{ - "TRACE_LEVEL_FATAL", - "TRACE_LEVEL_ERROR", - "TRACE_LEVEL_WARNING" - "TRACE_LEVEL_INFORMATION", - "TRACE_LEVEL_VERBOSE" }, - Values{ - "Fatal", - "Error", - "Warning", - "Information", - "Verbose" }, - ValueMap{ - "0x1", - "0x2", - "0x3", - "0x4", - "0x5" }, - ValueType("index") - ] - uint32 Level; -}; diff --git a/tests/projects/wdk/kmdf/msdsm/intrface.c b/tests/projects/wdk/kmdf/msdsm/intrface.c deleted file mode 100644 index eeebf7f93..000000000 --- a/tests/projects/wdk/kmdf/msdsm/intrface.c +++ /dev/null @@ -1,5198 +0,0 @@ -/*++ - -Copyright (C) 2004-2010 Microsoft Corporation - -Module Name: - - intrface.c - -Abstract: - - This driver is the Microsoft Device Specific Module (DSM) - devices that conform with SPC-3 specs. - It exports behaviors that mpio.sys will use to determine how to - multipath these devices. - - This file contains DriverEntry and all the functions that are - exported to MPIO. - - This DSM is targetted towards Windows 2008 and above. - -Environment: - - kernel mode only - ---*/ - -#include "precomp.h" - -#ifdef DEBUG_USE_WPP -#include "intrface.tmh" -#endif - -#pragma warning (disable:4305) - - -// -// Flag to indicate whether to NT_ASSERT or ignore a particular condition. -// -BOOLEAN DoAssert = TRUE; - -// -// OS Version Info -// MSDSM is targetted towards Windows Server 2008 and above. -// -BOOLEAN gServer2008AndAbove = FALSE; - -// -// Global to cache MPIO's Control Object. -// -PDEVICE_OBJECT gMPIOControlObject = NULL; - -// -// Flag to indicate if the MPIO control object was referenced. -// -BOOLEAN gMPIOControlObjectRefd = FALSE; - -// -// Global to cache the Driver Object. -// -PDRIVER_OBJECT gDsmDriverObject = NULL; - - -#ifdef ALLOC_PRAGMA - #pragma alloc_text(INIT, DriverEntry) -#endif - -// -// The code. -// -NTSTATUS -DriverEntry( - IN PDRIVER_OBJECT DriverObject, - IN PUNICODE_STRING RegistryPath - ) -/*++ - -Routine Description: - - This routine is called when the driver is loaded. - -Arguments: - - DriverObject - Supplies the driver object. - RegistryPath - Supplies the registry path. - -Return Value: - - NTSTATUS - ---*/ -{ - PDSM_CONTEXT dsmContext = NULL; - PFILE_OBJECT fileObject; - WCHAR dosDeviceName[64] = DSM_MPIO_CONTROL_OBJECT_SYMLINK; - UNICODE_STRING mpUnicodeName; - NTSTATUS status = STATUS_SUCCESS; - MPIO_VERSION_INFO versionInfo = {0}; - DSM_TYPE dsmMode = DsmType3; - DSM_MPIO_CONTEXT mpctlContext; - IO_STATUS_BLOCK ioStatus; - - - // - // Initialize the tracing subsystem. - // Any failure is handled by ETW itself. - // - WPP_INIT_TRACING(DriverObject, RegistryPath); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Entering function.\n", - DriverObject)); - - gDsmDriverObject = DriverObject; - - // - // Determine the OS version. - // - gServer2008AndAbove = RtlIsNtDdiVersionAvailable(NTDDI_VISTA); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Server2008AndAbove is %!bool!.\n", - DriverObject, - gServer2008AndAbove)); - - // - // MSDSM is supported only on Server 2008 and above. - // - if (!gServer2008AndAbove) { - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DriverEntry; - } - - // - // Build the mpio symbolic link name. - // - RtlInitUnicodeString(&mpUnicodeName, dosDeviceName); - - // - // Get a pointer to mpio's deviceObject. - // - status = IoGetDeviceObjectPointer(&mpUnicodeName, - FILE_READ_ATTRIBUTES, - &fileObject, - &gMPIOControlObject); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_FATAL, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Failed to communicate with MPIO control object. Status %x.\n", - DriverObject, - status)); - - goto __Exit_DriverEntry; - } - - ObReferenceObject(gMPIOControlObject); - gMPIOControlObjectRefd = TRUE; - ObDereferenceObject(fileObject); - - status = DsmGetVersion(&versionInfo, sizeof(MPIO_VERSION_INFO)); - - if (!NT_SUCCESS(status)) { - - // - // If we can't get the version, that means we aren't using a compatible - // version of MPIO drivers and so should not continue. - // - TracePrint((TRACE_LEVEL_FATAL, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): MPIO version unknown - DSM exiting.\n", - DriverObject)); - - status = STATUS_UNSUCCESSFUL; - goto __Exit_DriverEntry; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): MPIO version %d.%d.%d.%d.\n", - DriverObject, - versionInfo.MajorVersion, - versionInfo.MinorVersion, - versionInfo.ProductBuild, - versionInfo.QfeNumber)); - - RtlZeroMemory(&gDsmInitData, sizeof(DSM_INIT_DATA)); - - // - // Must be newer than 1.0.7.0 to support DSM type 2 upwards. - // - if ((versionInfo.MajorVersion > 1) || - (versionInfo.MinorVersion >= 1) || - (versionInfo.ProductBuild > 7) || - (versionInfo.QfeNumber >= 1)) { - - // - // Must be newer than 1.18 to support DSM's versioning - // - if (versionInfo.MajorVersion > 1 || - versionInfo.MinorVersion > 17) { - - dsmMode = DsmType6; - - { - RTL_OSVERSIONINFOW osVersion = {0}; - - osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); - RtlGetVersion(&osVersion); - - gDsmInitData.DsmVersion.MajorVersion = osVersion.dwMajorVersion; - gDsmInitData.DsmVersion.MinorVersion = osVersion.dwMinorVersion; - gDsmInitData.DsmVersion.ProductBuild = osVersion.dwBuildNumber; - gDsmInitData.DsmVersion.QfeNumber = 0; - } - } - } else { - - // - // We cannot use this DSM with older versions of the MPIO drivers. - // - TracePrint((TRACE_LEVEL_FATAL, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): MPIO version not supported - DSM exiting.\n", - DriverObject)); - - status = STATUS_UNSUCCESSFUL; - goto __Exit_DriverEntry; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Setting DSM type to %d.\n", - DriverObject, - dsmMode)); - - // - // Build the init data structure. - // - dsmContext = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_CONTEXT), - DSM_TAG_DSM_CONTEXT); - if (!dsmContext) { - - TracePrint((TRACE_LEVEL_FATAL, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Failed to allocate memory for DSM Context.\n", - DriverObject)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DriverEntry; - } - - // - // Set-up the init data - // - gDsmInitData.DsmContext = (PVOID) dsmContext; - gDsmInitData.InitDataSize = sizeof(DSM_INIT_DATA); - - gDsmInitData.DsmInquireDriver = DsmInquire; - gDsmInitData.DsmCompareDevices = DsmCompareDevices; - gDsmInitData.DsmGetControllerInfo = DsmGetControllerInfo; - gDsmInitData.DsmSetDeviceInfo = DsmSetDeviceInfo; - gDsmInitData.DsmIsPathActive = DsmIsPathActive; - gDsmInitData.DsmPathVerify = DsmPathVerify; - gDsmInitData.DsmInvalidatePath = DsmInvalidatePath; - gDsmInitData.DsmMoveDevice = DsmMoveDevice; - gDsmInitData.DsmRemovePending = DsmRemovePending; - gDsmInitData.DsmRemoveDevice = DsmRemoveDevice; - gDsmInitData.DsmRemovePath = DsmRemovePath; - gDsmInitData.DsmSrbDeviceControl = DsmSrbDeviceControl; - gDsmInitData.DsmLBGetPath = DsmLBGetPath; - gDsmInitData.DsmInterpretErrorEx = DsmInterpretError; - gDsmInitData.DsmUnload = DsmUnload; - gDsmInitData.DsmSetCompletion = DsmSetCompletion; - gDsmInitData.DsmCategorizeRequest = DsmCategorizeRequest; - gDsmInitData.DsmBroadcastSrb = DsmBroadcastRequest; - gDsmInitData.DsmIsAddressTypeSupported = DsmIsAddressTypeSupported; - gDsmInitData.DsmDeviceNotUsed = DsmDeviceNotUsed; - - // - // Since MSDSM is for SPC-3 compliant devices, MPIO should be able to build - // a serial number for the device. - // - gDsmInitData.DsmDeviceSerialNumber = NULL; - - // - // Notifies MPIO of the appropriate Type support - // - gDsmInitData.DsmType = dsmMode; - - gDsmInitData.DriverObject = DriverObject; - - - // - // Set-up the WMI Info. - // - DsmpWmiInitialize(&gDsmInitData.DsmWmiInfo, RegistryPath); - DsmpDsmWmiInitialize(&gDsmInitData.DsmWmiGlobalInfo, RegistryPath); - - RtlInitUnicodeString(&gDsmInitData.DisplayName, DSM_FRIENDLY_NAME); - - // - // Initialize some of the fields in DSM Context structure. - // - KeInitializeSpinLock(&dsmContext->SupportedDevicesListLock); - InitializeListHead(&dsmContext->GroupList); - InitializeListHead(&dsmContext->DeviceList); - InitializeListHead(&dsmContext->FailGroupList); - InitializeListHead(&dsmContext->ControllerList); - InitializeListHead(&dsmContext->StaleFailGroupList); - - // - // Build the list context structures used for completion processing. - // - ExInitializeNPagedLookasideList(&dsmContext->CompletionContextList, - NULL, - NULL, - POOL_NX_ALLOCATION, - sizeof(DSM_COMPLETION_CONTEXT), - DSM_TAG_GENERIC, - 0); - - RtlZeroMemory(&mpctlContext, sizeof(DSM_MPIO_CONTEXT)); - - // - // Send the IOCTL to mpio.sys to register ourselves. - // - DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_REGISTER, - gMPIOControlObject, - &gDsmInitData, - &mpctlContext, - sizeof(DSM_INIT_DATA), - sizeof(DSM_MPIO_CONTEXT), - TRUE, - &ioStatus); - - status = ioStatus.Status; - - if (NT_SUCCESS(status)) { - - dsmContext->MPIOContext = mpctlContext.MPIOContext; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Registered with MPIO.\n", - DriverObject)); - - DriverObject->DriverUnload = DsmDriverUnload; - - // - // Query the registry for disabling/enabling statistics gathering - // - if (STATUS_OBJECT_NAME_NOT_FOUND == DsmpGetStatsGatheringChoice(dsmContext, (PULONG)&dsmContext->DisableStatsGathering)) { - - // - // If the value does not exist, write the default to registry. - // - DsmpSetStatsGatheringChoice(dsmContext, (ULONG)dsmContext->DisableStatsGathering); - } - } - -__Exit_DriverEntry: - - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Exiting function successfully.\n", - DriverObject)); - } else { - - // - // Since the DSM is going to be unloaded but without DriverUnload being - // called, we need to perform cleanup here. - // - if (dsmContext != NULL) { - DsmpFreeDSMResources(dsmContext); - dsmContext = NULL; - } - - if (gMPIOControlObjectRefd) { - - // - // Drop the reference on MPIO's control object. - // - ObDereferenceObject(gMPIOControlObject); - gMPIOControlObjectRefd = FALSE; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DriverEntry (DrvObj %p): Exiting function with status %x.\n", - DriverObject, - status)); - - // - // Stop the tracing subsystem. - // NOTE: once we unregister ETW, no more TracePrint can be done, so we - // must ensure that ETW unregister is the last thing that happens. - // - WPP_CLEANUP(gDsmDriverObject); - } - - return status; -} - - -VOID -DsmDriverUnload( - _In_ IN PDRIVER_OBJECT DriverObject - ) -/*++ - -Routine Description: - - This routine is called when the driver is unloaded. - -Arguments: - - DriverObject - Supplies the driver object. - -Return Value: - - Nothing - ---*/ -{ - DSM_DEREGISTER_DATA deregisterData; - IO_STATUS_BLOCK ioStatus; - - deregisterData.DeregisterDataSize = sizeof(DSM_DEREGISTER_DATA); - deregisterData.DriverObject = DriverObject; - deregisterData.DsmContext = gDsmInitData.DsmContext; - deregisterData.MpioContext = ((PDSM_CONTEXT)(gDsmInitData.DsmContext))->MPIOContext; - // - // Send the IOCTL to mpio.sys to de-register ourselves. - // - DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_DEREGISTER, - gMPIOControlObject, - &deregisterData, - NULL, - sizeof(DSM_DEREGISTER_DATA), - 0, - TRUE, - &ioStatus); - - NT_ASSERT(NT_SUCCESS(ioStatus.Status)); - - - - return; -} - - -NTSTATUS -DsmInquire( - _In_ IN PVOID DsmContext, - _In_ IN PDEVICE_OBJECT TargetDevice, - _In_ IN PDEVICE_OBJECT PortObject, - _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, - _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, - _Out_ OUT PVOID *DsmIdentifier - ) -/*++ - -Routine Description: - - This routine is used to determine if TargetDevice belongs to - the DSM. If this is a supported device DsmIdentifier will be - updated with 'deviceInfo'. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - TargetDevice - DeviceObject for the child device. - PortObject - The Port driver FDO on which TargetDevice resides. - Descriptor - Pointer to the device descriptor corresponding to TargetDevice. - Rehash of inquiry data, plus serial number information - (if applicable). - DeviceIdList - VPD Page 0x83 information. - DsmIdentifier - Pointer to be filled in by the DSM on success. - -Return Value: - - STATUS_NOT_SUPPORTED - if not on the SupportList. - STATUS_INSUFFICIENT_RESOURCES - No mem. - STATUS_SUCCESS ---*/ -{ - PDSM_CONTEXT dsmContext = DsmContext; - PDSM_DEVICE_INFO deviceInfo = NULL; - PDSM_GROUP_ENTRY group; - BOOLEAN newGroup; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; - PDSM_TARGET_PORT_LIST_ENTRY targetPortEntry = NULL; - PSTR serialNumber = NULL; - SIZE_T serialNumberLength = 0; - NTSTATUS status; - ULONG allocationLength; - BOOLEAN serialNumberAllocated = FALSE; - KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error - BOOLEAN supported = FALSE; - BOOLEAN spinlockHeld = FALSE; - UCHAR vendorId[9] = {0}; - UCHAR productId[17] = {0}; - INQUIRYDATA inquiryData; - UCHAR alua = DSM_DEVINFO_ALUA_NOT_SUPPORTED; - ULONG index; - PDSM_IDS controllerObjects = NULL; - PDEVICE_OBJECT controllerDeviceObject; - PLIST_ENTRY entry = NULL; - PSTORAGE_DESCRIPTOR_HEADER controllerIdHeader = NULL; - PULONG relativeTargetPortId = NULL; - PUSHORT targetPortGroupId = NULL; - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength = 0; - PSTR controllerSerialNumber; - BOOLEAN match = FALSE; - BOOLEAN doneUpdating = FALSE; - PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; - PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device = NULL; - PWSTR hardwareId = NULL; - PWCHAR deviceName = NULL; - ULONG tempResult = 0; - ULONG maxPRRetryTimeDuringStateTransition = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME; - BOOLEAN useCacheForLeastBlocks = FALSE; - ULONGLONG cacheSizeForLeastBlocks = 0; - BOOLEAN fakeControllerEntryExists = FALSE; - STORAGE_IDENTIFIER_CODE_SET serialNumberCodeSet = StorageIdCodeSetReserved; - -#if DBG - BOOLEAN multiport; -#endif - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Entering function.\n", - TargetDevice)); - - // - // 1. Get standard inquiry for the device. Check if SPC-3 compliant. - // If not compliant, check SupportedDeviceList. - // 2. Create device serial number. - // 3. Create a partially populated deviceInfo. - // DeviceDescriptor. - // SCSI address. - // Save off serial number. - // ALUA, port FDO, etc. - // 4. Create device name. - // 5. If ALUA support, send down Report Target Port Groups. - // 6. Find the group. If none, build one. - // 7. If new group, build target port groups and target ports info. - // Else, update target port groups and target ports info. - // 8. If both implicit as well as explicit transitions allowed, disable implicit. - // 9. Get list of controllers objects and get VPD 0x83 for each (only if no - // match for existing ones). - // Match returned ids of type 0x5 with what was returned in Report Target Port Groups. - // If no type 0x5 identifier, use SCSI address. - // Create controller list (delete stale entries). - // - - - // - // Query the registry to find out what devices are being supported - // on this machine. - // - DsmpGetDeviceList(dsmContext); - - status = DsmpGetStandardInquiryData(TargetDevice, &inquiryData); - - if (NT_SUCCESS(status)) { - - supported = DsmpCheckScsiCompliance(TargetDevice, - &inquiryData, - Descriptor, - DeviceIdList); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to get inquiry data with status %x.\n", - TargetDevice, - status)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // Since the device isn't SPC-3 compliant, check if the device is on the - // SupportedDeviceList. - // - if (!supported) { - - - if (!supported) { - - // - // Get the inquiry data embedded in the device descriptor. - // - RtlStringCchCopyA((LPSTR)vendorId, - sizeof(vendorId) / sizeof(vendorId[0]), - (LPCSTR)(&inquiryData.VendorId)); - - RtlStringCchCopyA((LPSTR)productId, - sizeof(productId) / sizeof(productId[0]), - (LPCSTR)(&inquiryData.ProductId)); - - supported = DsmpDeviceSupported(dsmContext, - (PCSZ)vendorId, - (PCSZ)productId); - } - - if (!supported) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Unsupported Device.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - } - - // - // Find out if device can be accessed via mulitple ports. This info is - // important since it will determine whether or not to send down a - // ReportTargetPortGroups command. - // -#if DBG - multiport = (inquiryData.MultiPort & 0x10) ? TRUE : FALSE; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Is %ws multiported.\n", - TargetDevice, - multiport ? L"" : L"not")); -#endif - - // - // Query the assymmetric states transition method - // - switch ((inquiryData.Reserved >> 0x4) & 0x3) { - case 1: alua = DSM_DEVINFO_ALUA_IMPLICIT; - break; - - case 2: alua = DSM_DEVINFO_ALUA_EXPLICIT; - break; - - case 3: alua = DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT; - break; - - default: alua = DSM_DEVINFO_ALUA_NOT_SUPPORTED; - break; - } - - // - // Get some information about this device. The preferred info is - // from the Device ID Page. - // - if (DeviceIdList) { - - // - // This will parse out the 'best' identifier and return - // a NULL-terminated ascii string. - // - serialNumber = (PSTR)DsmpParseDeviceID(DeviceIdList, - DSM_DEVID_SERIAL_NUMBER, - NULL, - &serialNumberCodeSet, - FALSE); - - if (!serialNumber) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): NULL serial number.\n", - TargetDevice)); - - // - // Either an allocation failed, or the DeviceIdList is malformed. - // - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // Indicate that the serialnumber buffer is allocated. - // - serialNumberAllocated = TRUE; - serialNumberLength = strlen((const char*)serialNumber); - - } else { - - // - // Get the serial number of this device. Use the serial number - // page (0x80). Ensure that the device's serial number is - // present. If not, can't claim support for this drive. - // - - if (!Descriptor || - (Descriptor->SerialNumberOffset == MAXULONG) || - (Descriptor->SerialNumberOffset == 0)) { - - // - // The port driver currently doesn't get the VPD page 0x80, - // if the device doesn't support GET_SUPPORTED_PAGES. Check to - // see whether there actually is a serial number. - // - serialNumber = DsmpGetSerialNumber(TargetDevice); - - if (!serialNumber) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): serialNumber = NULL.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - - } else { - serialNumberAllocated = TRUE; - serialNumberLength = strlen((const char*)serialNumber); - } - } - } - - // - // Allocate for the device. This is also used as DsmId. - // - allocationLength = sizeof(DSM_DEVICE_INFO); - - // - // As DSM_DEVICE_INFO has storage for the descriptor, add only - // the additional stuff that's at the end. - // - if (Descriptor) { - status = RtlULongSub(Descriptor->Size, sizeof(STORAGE_DEVICE_DESCRIPTOR), &tempResult); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Arithmetic underflow - status %x.\n", - TargetDevice, - status)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - } - - status = RtlULongAdd(allocationLength, tempResult, &allocationLength); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Arithmetic overflow - status %x.\n", - TargetDevice, - status)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - deviceInfo = DsmpAllocatePool(NonPagedPoolNx, - allocationLength, - DSM_TAG_DEV_INFO); - if (!deviceInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to allocate Device Info.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - deviceInfo->State = deviceInfo->PreviousState = deviceInfo->TempPreviousStateForLB = deviceInfo->ALUAState = deviceInfo->LastKnownGoodState = DSM_DEV_NOT_USED_STATE; - deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; - // - // Copy over the StorageDescriptor. - // - if (Descriptor) { - RtlCopyMemory(&deviceInfo->Descriptor, - Descriptor, - Descriptor->Size); - } - - // - // Get the scsi address for this device. Note that on success, DsmGetScsiAddress() - // will allocate memory which we are responsible for freeing. - // - status = DsmGetScsiAddress(TargetDevice, - &deviceInfo->ScsiAddress); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Error %x while getting scsi address.\n", - TargetDevice, - status)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // Capture the serial number allocated flag. - // - deviceInfo->SerialNumberAllocated = serialNumberAllocated; - - // - // Set the serial number. - // - if (!serialNumberAllocated) { - - PSTORAGE_DEVICE_DESCRIPTOR descriptor; - - // - // serialNumber is not pointing to the buffer passed by MPIO. Update - // it to point to the Device Descriptor allocated by the DSM. - // - descriptor = &(deviceInfo->Descriptor); - - NT_ASSERT(descriptor->SerialNumberOffset != 0 && descriptor->SerialNumberOffset != MAXULONG); - - serialNumber = (PCHAR)descriptor + descriptor->SerialNumberOffset; - serialNumberLength = strlen((const char*)serialNumber); - } - - if (alua == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT)) { - - BOOLEAN disableImplicit = FALSE; - - status = DsmpDisableImplicitStateTransition(TargetDevice, &disableImplicit); - - if (NT_SUCCESS(status)) { - - if (disableImplicit) { - - alua &= ~DSM_DEVINFO_ALUA_IMPLICIT; - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Disabled implicit ALUA state transition.\n", - TargetDevice)); - - // - // Record that the storage actually supported implicit also, but we - // turned it OFF. - // - deviceInfo->ImplicitDisabled = TRUE; - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Storage support both transitions but does NOT allow disabling Implicit.\n", - TargetDevice)); - } - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to disable implicit ALUA state transitions - status %x.\n", - TargetDevice, - status)); - } - } - - deviceInfo->SerialNumber = serialNumber; - - // - // Save the Physical Device Object (PDO) of the device. - // Used to verify that no two devices have the same PDO. - // - deviceInfo->PortPdo = TargetDevice; - - // - // Save the FDO of the adapter. Used for handling reserve\release - // - deviceInfo->PortFdo = PortObject; - - // - // Set the signature. - // - deviceInfo->DeviceSig = DSM_DEVICE_SIG; - - deviceInfo->DsmContext = DsmContext; - - deviceInfo->ALUASupport = alua; - - // - // Build the name (using serialnumber) that will be used as registry key - // to store Load Balance settings for this device. - // - deviceName = DsmpBuildDeviceName(deviceInfo, serialNumber, serialNumberLength); - - if (!deviceName) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to allocate device name for %p.\n", - TargetDevice, - deviceInfo)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - - // - // Send down ReportTargetPortGroups command and keep the info handy. - // - if (alua != DSM_DEVINFO_ALUA_NOT_SUPPORTED) { - - status = DsmpReportTargetPortGroups(TargetDevice, - &targetPortGroupsInfo, - &targetPortGroupsInfoLength); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to report target port groups for %p. Status %x.\n", - TargetDevice, - deviceInfo, - status)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // We've just sent down an RTPG (relatively expensive operation), and it - // succeeded, so sending down one more as part part of the initialization - // in PathVerify() since it is going to be called almost immediately. - // - deviceInfo->IgnorePathVerify = TRUE; - } - - // - // Query the registry for max time to retry failed PR requests - // - DsmpGetMaxPRRetryTime(DsmContext, &maxPRRetryTimeDuringStateTransition); - - // - // Query the registry to see if the user has overridden the default - // Least Blocks settings. - // - status = DsmpQueryCacheInformationFromRegistry(DsmContext, - &useCacheForLeastBlocks, - &cacheSizeForLeastBlocks); - - if (!NT_SUCCESS(status)) { - // - // Couldn't get the settings from the registry so fall back on the - // default for Least Blocks. - // - useCacheForLeastBlocks = TRUE; - cacheSizeForLeastBlocks = DSM_LEAST_BLOCKS_DEFAULT_THRESHOLD; - } - - // - // Build LUN's hardware id. Needs to be called at PASSIVE_LEVEL, so - // do it before grabbing the lock. The hardware id of the group is - // later set under the protection of the lock. - // - hardwareId = DsmpBuildHardwareId(deviceInfo); - - irql = ExAcquireSpinLockExclusive(&(((PDSM_CONTEXT)DsmContext)->DsmContextLock)); - spinlockHeld = TRUE; - - status = STATUS_SUCCESS; - - // - // See if there is an existing Multi-path group to which this belongs. - // (same serial number). - // - group = DsmpFindDevice(DsmContext, deviceInfo, FALSE); - if (!group) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): First device %p in the group.\n", - TargetDevice, - deviceInfo)); - - newGroup = TRUE; - - // - // This device doesn't belong to any group yet. So Build a multi-path - // group entry. This'll represents all paths to a particular device. - // - group = DsmpBuildGroupEntry(DsmContext, deviceInfo); - if (group) { - - // - // Set the registry key name for the new group - // - group->RegistryKeyName = deviceName; - deviceName = NULL; - - // - // Cache the LUN's hardware id - // - if (!hardwareId) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to build a hardwareId for %p.\n", - TargetDevice, - deviceInfo)); - } - - group->HardwareId = hardwareId; - hardwareId = NULL; - - group->UseCacheForLeastBlocks = useCacheForLeastBlocks; - group->CacheSizeForLeastBlocks = cacheSizeForLeastBlocks; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to allocate Group Entry for %p.\n", - TargetDevice, - deviceInfo)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - } else { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Found group %p for device %p.\n", - TargetDevice, - group, - deviceInfo)); - - newGroup = FALSE; - - if (!group->HardwareId) { - - // - // If we weren't successful in previously building the hardware id for this LUN, - // retry doing it again now. - // - hardwareId = DsmpBuildHardwareId(deviceInfo); - if (!hardwareId) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to build a hardwareId for %p.\n", - TargetDevice, - deviceInfo)); - } - - group->HardwareId = hardwareId; - hardwareId = NULL; - } - - // - // Sanity check that we haven't been presented with device instances - // with different ALUA support. So compare with the first device instance. - // - for (index = 0; index < DSM_MAX_PATHS; index++) { - - if (group->DeviceList[index]) { - - break; - } - } - - if (index < DSM_MAX_PATHS) { - - // - // Only acceptable conditions are: - // 1. both have same support, - // 2. one has explicit, while other has both explicit-and-implicit (this - // is a potential valid case because DsmpDisableImplicitStateTransition - // may have failed). - // - if (!((deviceInfo->ALUASupport == group->DeviceList[index]->ALUASupport) || - ((deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT && deviceInfo->ImplicitDisabled) && - (group->DeviceList[index]->ALUASupport == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT))) || - ((group->DeviceList[index]->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT && group->DeviceList[index]->ImplicitDisabled) && - (deviceInfo->ALUASupport == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT))))) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Mismatch in device instances' ALUA support %d vs %d.\n", - TargetDevice, - deviceInfo->ALUASupport, - group->DeviceList[index]->ALUASupport)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - } - } - - if (NT_SUCCESS(status)) { - - NT_ASSERT(group); - - group->MaxPRRetryTimeDuringStateTransition = maxPRRetryTimeDuringStateTransition; - - if (alua == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { - - // - // Since the device doesn't support ALUA, it is automatically - // symmetric LU access. - // - group->Symmetric = TRUE; - - if (newGroup) { - - // - // This is the first in the group, so make it the active device. - // The actual active/passive devices will be set-up when - // LB policies are set by the user. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - } else { - - // - // Already something active, this will be the fail-over device - // until the load-balance groups are set-up. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_STANDBY; - } - - } else { - - if (DeviceIdList == NULL) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): No Device ID List.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - if (alua == DSM_DEVINFO_ALUA_IMPLICIT) { - - // - // Assume that the LU access is symmetric. When parsing the TPG - // info, if we find that not all TPGs are in the same LU access - // state, then we know that this the access is asymmetric. - // - group->Symmetric = TRUE; - } - - // - // Build TPG and TP info - // - status = DsmpParseTargetPortGroupsInformation(DsmContext, - group, - targetPortGroupsInfo, - targetPortGroupsInfoLength); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to build TPG information - status %x.\n", - TargetDevice, - status)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - for (index = 0; index < DSM_MAX_PATHS; index++) { - - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; - - targetPortGroup = group->TargetPortGroupList[index]; - - if (targetPortGroup) { - - DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); - } - } - - // - // Find the target port through which this devInfo was exposed. - // - relativeTargetPortId = (PULONG)DsmpParseDeviceID(DeviceIdList, - DSM_DEVID_RELATIVE_TARGET_PORT, - NULL, - NULL, - FALSE); - NT_ASSERT(relativeTargetPortId); - - if (!relativeTargetPortId) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Couldn't retrieve relative TP id.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // Find the target port group - // - targetPortGroupId = (PUSHORT)DsmpParseDeviceID(DeviceIdList, - DSM_DEVID_TARGET_PORT_GROUP, - NULL, - NULL, - FALSE); - NT_ASSERT(targetPortGroupId); - - if (targetPortGroupId) { - - // - // Find the target port group entry - // - targetPortGroupEntry = DsmpFindTargetPortGroup(DsmContext, - group, - targetPortGroupId); - - NT_ASSERT(targetPortGroupEntry); - - if (targetPortGroupEntry) { - - // - // Look through the target port group to find the target port - // - targetPortEntry = DsmpFindTargetPort(DsmContext, - targetPortGroupEntry, - relativeTargetPortId); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Couldn't find TPG Id %x's entry.\n", - TargetDevice, - *targetPortGroupId)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - NT_ASSERT(targetPortEntry); - - if (!targetPortEntry) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Couldn't find relative TP %x's entry.\n", - TargetDevice, - *relativeTargetPortId)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // Update the devInfo with the target port and target port group - // info - // - deviceInfo->TargetPortGroup = targetPortGroupEntry; - deviceInfo->TargetPort = targetPortEntry; - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = deviceInfo->ALUAState = deviceInfo->TargetPortGroup->AsymmetricAccessState; - - tp_device = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_TARGET_PORT_DEVICELIST_ENTRY), - DSM_TAG_TP_DEVICE_LIST_ENTRY); - - if (!tp_device) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Insufficient resources allocating TP device list entry.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - - // - // Add the device to the list of devices that are exposed via this target port. - // - tp_device->DeviceInfo = deviceInfo; - InterlockedIncrement((LONG volatile*)&targetPortEntry->Count); - InsertTailList(&targetPortEntry->TP_DeviceList, &tp_device->ListEntry); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to retrieve TPG Id.\n", - TargetDevice)); - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - } - - if (NT_SUCCESS(status)) { - - // - // Add the deviceInfo to the list. DO NOT modify the status - // variable if this function returns SUCCESS. - // - status = DsmpAddDeviceEntry(DsmContext, - group, - deviceInfo); - if (NT_SUCCESS(status)) { - - *DsmIdentifier = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Added device %p to group %p.\n", - TargetDevice, - *DsmIdentifier, - group)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to add device %p to group %p - status %x.\n", - TargetDevice, - deviceInfo, - group, - status)); - - // - // We weren't able to add this deviceInfo to the list so we must - // remove its entry on the target port list before the deviceInfo - // is freed. - // - DsmpRemoveDeviceFromTargetPortList(deviceInfo); - - if (newGroup) { - - DsmpRemoveGroupEntry(DsmContext, group, FALSE); - - DsmpFreePool(group); - group = NULL; - } - - status = STATUS_NOT_SUPPORTED; - goto __Exit_DsmInquire; - } - } - } - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - spinlockHeld = FALSE; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Device %p added. State %d, Desired State %d\n", - TargetDevice, - deviceInfo, - deviceInfo->State, - deviceInfo->DesiredState)); - - // - // Update the global list of controller objects - // - controllerObjects = DsmGetAssociatedDevice(dsmContext->MPIOContext, - PortObject, - 0x0C); - if (controllerObjects) { - - // - // This loop needs its own status variable so that it does not - // inadvertently overwrite a STATUS_SUCCESS from the code above. - // - NTSTATUS matchStatus = STATUS_SUCCESS; - PSCSI_ADDRESS controllerScsiAddress = NULL; - - // - // Walk through the list and get VPD 0x83 data and associate the devInfo - // with the controller object. - // - for (index = 0; index < controllerObjects->Count; index++) { - - STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; - - // - // Free the previously allocated SCSI address, if any. - // - if (controllerScsiAddress) { - DsmpFreePool(controllerScsiAddress); - controllerScsiAddress = NULL; - } - - controllerDeviceObject = (PDEVICE_OBJECT)controllerObjects->IdList[index]; - NT_ASSERT(controllerDeviceObject); - - if (!controllerDeviceObject) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Controller list %p's index %x is NULL.\n", - TargetDevice, - controllerObjects, - index)); - - continue; - } - - matchStatus = DsmpGetDeviceIdList(controllerDeviceObject, &controllerIdHeader); - NT_ASSERT(NT_SUCCESS(matchStatus)); - - if (!NT_SUCCESS(matchStatus)) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to get DeviceId list for controller %p - status %x.\n", - TargetDevice, - controllerDeviceObject, - matchStatus)); - - continue; - } - - controllerSerialNumber = DsmpParseDeviceID((PSTORAGE_DEVICE_ID_DESCRIPTOR)controllerIdHeader, - DSM_DEVID_SERIAL_NUMBER, - NULL, - &codeSet, - FALSE); - NT_ASSERT(controllerSerialNumber); - DsmpFreePool(controllerIdHeader); - - if (!controllerSerialNumber) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to parse serial number for controller %p.\n", - TargetDevice, - controllerDeviceObject)); - - continue; - } - - // - // Note that on success, DsmGetScsiAddress() will allocate memory - // which we are responsible for freeing. - // - matchStatus = DsmGetScsiAddress(controllerDeviceObject, &controllerScsiAddress); - NT_ASSERT(NT_SUCCESS(matchStatus)); - - if (!NT_SUCCESS(matchStatus)) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to get controller %p's scsi address - status %x.\n", - TargetDevice, - controllerDeviceObject, - matchStatus)); - - continue; - } - - controllerEntry = DsmpFindControllerEntry(DsmContext, - PortObject, - controllerScsiAddress, - controllerSerialNumber, - strlen(controllerSerialNumber), - codeSet, - TRUE); - - if (!controllerEntry) { - - controllerEntry = DsmpBuildControllerEntry(DsmContext, - controllerDeviceObject, - PortObject, - controllerScsiAddress, - controllerSerialNumber, - codeSet, - TRUE); - - if (!controllerEntry) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to build an entry for controller %p.\n", - TargetDevice, - controllerDeviceObject)); - - continue; - } - - InsertHeadList(&dsmContext->ControllerList, &controllerEntry->ListEntry); - InterlockedIncrement((LONG volatile*)&dsmContext->NumberControllers); - } - - controllerEntry->DeviceObject = controllerDeviceObject; - - // - // Parse the DeviceIdList for all the 0x5 type identifiers - // and for each, compare the target port groups and target ports to match - // the device to its controller. - // - if (!match) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Failed to match devInfo %p with controller %p's Ids.\n", - TargetDevice, - deviceInfo, - controllerDeviceObject)); - - match = DsmpIsDeviceBelongsToController(DsmContext, - deviceInfo, - controllerEntry); - } - - if (match && !doneUpdating) { - - InterlockedIncrement((LONG volatile*)&(controllerEntry->RefCount)); - deviceInfo->Controller = controllerEntry; - doneUpdating = TRUE; - } - } - - // - // Free the last SCSI address allocated in the loop, if any. - // - if (controllerScsiAddress) { - DsmpFreePool(controllerScsiAddress); - controllerScsiAddress = NULL; - } - } - - // - // If there was no controller to associate this device with, use a fake one. - // Note that we only really care about matching on the Port and Target - // portions of the SCSI address. - // - if (!deviceInfo->Controller) { - - for (entry = dsmContext->ControllerList.Flink; - entry != &dsmContext->ControllerList; - entry = entry->Flink) { - - controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); - - if ((controllerEntry->IsFakeController) && - (controllerEntry->ScsiAddress->PortNumber == deviceInfo->ScsiAddress->PortNumber) && - (controllerEntry->ScsiAddress->TargetId == deviceInfo->ScsiAddress->TargetId)) { - - fakeControllerEntryExists = TRUE; - break; - } - } - - // - // If no fake one exists as yet for this port FDO, create one now. - // - if (!fakeControllerEntryExists) { - - CHAR fakeControllerSerialNumber[] = "FakeController"; - SCSI_ADDRESS fakeControllerScsiAddress = {0}; - fakeControllerScsiAddress.PortNumber = deviceInfo->ScsiAddress->PortNumber; - fakeControllerScsiAddress.TargetId = deviceInfo->ScsiAddress->TargetId; - - controllerEntry = DsmpBuildControllerEntry(DsmContext, - NULL, - PortObject, - &fakeControllerScsiAddress, - fakeControllerSerialNumber, - StorageIdCodeSetBinary, - TRUE); - - if (controllerEntry) { - - InsertHeadList(&dsmContext->ControllerList, &controllerEntry->ListEntry); - InterlockedIncrement((LONG volatile*)&dsmContext->NumberControllers); - controllerEntry->IsFakeController = TRUE; - } - } - - if (controllerEntry) { - InterlockedIncrement((LONG volatile*)&(controllerEntry->RefCount)); - } - - deviceInfo->Controller = controllerEntry; - } - -__Exit_DsmInquire: - - if (spinlockHeld) { - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - } - - if (NT_SUCCESS(status)) { - - NT_ASSERT(*DsmIdentifier); - - } else { - - // - // If there was any sort of ERROR, the deviceInfo will NOT be put on - // MSDSM's internal list that is accessible to other threads. Thus, - // we are safe to free the memory below and we do not require any - // synchronization mechanism to do so. - // - - // - // Check to see whether the serial number buffer was allocated, or just - // an offset into the Descriptor. - // - if (serialNumberAllocated) { - - // - // Need to free this before returning. - // - DsmpFreePool(serialNumber); - } - - if (deviceInfo) { - - if (deviceInfo->ScsiAddress) { - DsmpFreePool(deviceInfo->ScsiAddress); - } - - DsmpFreePool(deviceInfo); - } - } - - // - // If deviceName is not NULL then it hasn't been assigned to any GROUP. - // Free the allocated memory. - // - if (deviceName) { - DsmpFreePool(deviceName); - } - - // - // If hardwareId is not NULL then it hasn't been assigned to any GROUP. - // Free the allocated memory. - // - if (hardwareId) { - DsmpFreePool(hardwareId); - } - - if (targetPortGroupsInfo) { - DsmpFreePool(targetPortGroupsInfo); - } - - if (relativeTargetPortId) { - DsmpFreePool(relativeTargetPortId); - } - - if (targetPortGroupId) { - DsmpFreePool(targetPortGroupId); - } - - if (controllerObjects) { - DsmpFreePool(controllerObjects); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmInquire (DevObj %p): Exiting function with status %x.\n", - TargetDevice, - status)); - - return status; -} - - -BOOLEAN -DsmCompareDevices( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId1, - _In_ IN PVOID DsmId2 - ) -/*++ - -Routine Description: - - This routine is called to determine if the device ids represent - the same underlying physical device. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - DsmId1/2 - Identifers returned from DMS_INQUIRE_DRIVER. - -Return Value: - - TRUE if DsmIds correspond to the same underlying device. - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo0 = DsmId1; - PDSM_DEVICE_INFO deviceInfo1 = DsmId2; - PSTR serialNumber0; - PSTR serialNumber1; - SIZE_T length; - BOOLEAN match = FALSE; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmCompareDevices (DevInfo %p): Entering function - comparing with %p.\n", - deviceInfo0, - deviceInfo1)); - - // - // Get the two serial numbers. They were either embedded in - // the STORAGE_DEVICE_DESCRIPTOR or built by directly issuing - // the VPD request. - // - serialNumber0 = deviceInfo0->SerialNumber; - serialNumber1 = deviceInfo1->SerialNumber; - - if (serialNumber0 && serialNumber1) { - - // - // Get the length of the base-device Serial Number. - // - length = strlen((const char*)serialNumber0); - - // - // If the lengths match, compare the contents. - // - if (length == strlen((const char*)serialNumber1)) { - - if (RtlEqualMemory(serialNumber0, serialNumber1, length)) { - match = TRUE; - } - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmCompareDevices (DevInfo %p): Serialnumber not assigned for %p and\\or %p.\n", - DsmId1, - deviceInfo0, - deviceInfo1)); - } - - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmCompareDevices (DevInfo %p): Exiting function with match = %!bool!.\n", - DsmId1, - match)); - - return match; -} - - -NTSTATUS -DsmGetControllerInfo( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN ULONG Flags, - _Inout_ IN OUT PCONTROLLER_INFO *ControllerInfo - ) -/*++ - -Routine Description: - - This routine is used to get information about the controller that - the device corresponding to DsmId in on. Currently this DSM controls - hardware that doesn't expose controllers directly. Therefore State - is always NO_CNTRL. This information is used mainly by whatever - WMI admin utilities want it. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - - DsmId - Value returned from DMSInquireDriver. - - Flags - Bitfield of modifiers. If ALLOCATE is not set, ControllerInfo - will have a valid buffer for the DSM to operate on. - - ControllerInfo - Pointer for the DSM to place the allocated controller - info pertaining to DsmId - -Return Value: - - STATUS_INSUFFICIENT_RESOURCES if memory allocation fails. - - STATUS_SUCCESS on success - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo = DsmId; - PDSM_CONTROLLER_LIST_ENTRY controllerEntry = deviceInfo->Controller; - PCONTROLLER_INFO controllerInfo = NULL; - LARGE_INTEGER time; - ULONG controllerId = 0; - NTSTATUS status = STATUS_SUCCESS; - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmGetControllerInfo (DevInfo %p): Entering function.\n", - DsmId)); - - // - // Check to see whether a controller id has already been made-up. - // - if (!controllerEntry) { - - // - // Since this device is in an enclosure that doesn't have controllers, - // e.g. JBOD, make one up. - // - KeQuerySystemTime(&time); - - // - // Use only the lower 32-bits. - // - controllerId = time.LowPart; - } - - // - // Check the Flags - // - if (Flags & DSM_CNTRL_FLAGS_ALLOCATE) { - - // - // This is the first call. Need to allocate the controller structure. - // - controllerInfo = DsmpAllocatePool(NonPagedPoolNx, - sizeof(CONTROLLER_INFO), - DSM_TAG_CTRL_INFO); - if (!controllerInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmGetControllerInfo (DevInfo %p): Failed to allocate memory for Controller Info\n", - DsmId)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmGetControllerInfo; - } - - if (!controllerEntry) { - - // - // Indicate that there are no specific controllers. - // - controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; - - // - // Set the identifier to the value generated earlier. - // Indicate that it's Binary, not ASCII. - // - controllerInfo->Identifier.Type = StorageIdCodeSetBinary; - controllerInfo->Identifier.Length = 8; - - RtlCopyMemory(controllerInfo->Identifier.SerialNumber, - &controllerId, - sizeof(controllerId)); - - } else { - - // - // If either implicit or explicit ALUA state transition is supported, - // every controller is active. Else, if the devInfo's is in Active - // state, the controller is obviously in the active state. - // - if ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED) || - (DsmpIsDeviceStateActive(deviceInfo->State))) { - - controllerInfo->State = DSM_CONTROLLER_ACTIVE; - - } else { - - controllerInfo->State = DSM_CONTROLLER_STANDBY; - } - - controllerInfo->Identifier.Type = controllerEntry->IdCodeSet; - controllerInfo->Identifier.Length = controllerEntry->IdLength; - - if (controllerInfo->Identifier.Length > 32) { - - controllerInfo->Identifier.Length = 32; - } - - RtlCopyMemory(controllerInfo->Identifier.SerialNumber, - controllerEntry->Identifier, - controllerInfo->Identifier.Length); - - controllerInfo->DeviceObject = controllerEntry->DeviceObject; - } - - *ControllerInfo = controllerInfo; - - } else if (Flags & DSM_CNTRL_FLAGS_CHECK_STATE) { - - // - // Get the passed in struct. - // - controllerInfo = *ControllerInfo; - - // - // If the enclosures supported by this DSM actually had controllers, - // there would be a list of them and a search based on - // ControllerIdentifier would be made. - // - controllerEntry = deviceInfo->Controller; - - if (!controllerEntry) { - - controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; - - } else { - - // - // If either implicit or explicit ALUA state transition is supported, - // every controller is active. Else, if the devInfo's is in Active - // state, the controller is obviously in the active state. - // - if ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED) || - (DsmpIsDeviceStateActive(deviceInfo->State))) { - - controllerInfo->State = DSM_CONTROLLER_ACTIVE; - - } else { - - controllerInfo->State = DSM_CONTROLLER_STANDBY; - } - } - } - -__Exit_DsmGetControllerInfo: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmGetControllerInfo (DevInfo %p): Exiting function with status %x.\n", - DsmId, - status)); - - return status; -} - - -NTSTATUS -DsmSetDeviceInfo( - _In_ IN PVOID DsmContext, - _In_ IN PDEVICE_OBJECT TargetObject, - _In_ IN PVOID DsmId, - _Inout_ IN OUT PVOID *PathId - ) -/*++ - -Routine Description: - - This routine associates the DsmId to the controlling MPDisk PDO, - the targetObject for DSM-initiated requests, and to a Path - (given by PathId). - This routine will update the PathId in a way that better explains - the topology to MPIO. - Additionally, if we are in failover LB policy, failback if this - path is preferred path. - Also, if PR is being used, send registration down this path. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - TargetObject - The D.O. to which DSM-initiated requests should be sent. - DsmId - Value returned from DMSInquireDriver. - PathId - Id that represents the path. The value passed in may be used - as is, or the DSM optionally can update it if it requires - additional state info to be kept. - -Return Value: - - INSUFFICENT_RESOURCES for no-mem conditions. - STATUS_SUCCESS - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo = DsmId; - PDSM_GROUP_ENTRY group = deviceInfo->Group; - PDSM_FAILOVER_GROUP failGroup; - PDSM_CONTEXT dsmContext; - PSCSI_ADDRESS scsiAddress; - ULONG primaryPath = 0; - ULONG optimizedPath = 0; - ULONG pathWeight = 0; - ULONG pathId; - NTSTATUS status = STATUS_SUCCESS; - WCHAR registryKeyName[256] = {0}; - BOOLEAN newFOGroup = FALSE; - BOOLEAN registryKeyExists = FALSE; - KIRQL irql; - PVOID tempPathId = *PathId; - DSM_LOAD_BALANCE_TYPE loadBalanceType; - ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - UCHAR explicitlySet = FALSE; - BOOLEAN vidpidPolicySet = FALSE; - BOOLEAN overallPolicySet = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Entering function.\n", - DsmId)); - - // - // 1. Set default LB policy. - // 2. Query LB policy from registry and update if necessary. - // 3. Set default value for primaryPath and optimizedPath based on device's - // access state - // 4. Map deviceInfo to real LUN by saving off the target for I/O - // 5. Build pathId from SCSI address - // 6. Find FOG for device. If none found, build one. - // Add deviceInfo to FOG. - // 7. Query registry for pathWeight, primaryPath and optimizedPath - // Update deviceInfo with results of query. - // 8. Compare deviceInfo access state with persistent value (based on - // primaryPath and optimizedPath) and update its DesiredState. - // - - if (!TargetObject) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): No target object.\n", - deviceInfo)); - - // - // This deviceInfo will have no path or targetObject associated with it. - // Mark it in a failed state so it won't be used to handle any requests. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_UNDETERMINED; - - goto __Exit_DsmSetDeviceInfo; - } - - // - // Default LB type is Round Robin. - // - loadBalanceType = DSM_LB_ROUND_ROBIN; - - // - // Override the default with whatever is the overall policy that needs to be - // applied for all LUNs controlled by MSDSM. - // - // Override that policy if one has been set for this device's VID/PID. - // - // Override that policy with whatever has been explicitly set for this particular - // device. - // - // In order to perform the above, first query the policy for this particular device. - // If it has not been explicity set, use MSDSM's overall policy or VID/PID policy. - // - status = DsmpQueryDeviceLBPolicyFromRegistry(deviceInfo, - group->RegistryKeyName, - &loadBalanceType, - &preferredPath, - &explicitlySet); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Failed to query LB policy from registry. Status %x.\n", - deviceInfo, - status)); - - NT_ASSERT(NT_SUCCESS(status)); - - // - // This deviceInfo will have no path or targetObject associated with it. - // Mark it in a failed state so it won't be used to handle any requests. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_UNDETERMINED; - - goto __Exit_DsmSetDeviceInfo; - } - - // - // If this device's policy was not explicitly set, check to see if a policy - // was set for this device's VID/PID and use that. - // If VID/PID policy is not set, query the overall default policy - // that needs to be applied to all devices controlled by this DSM. - // If this setting hasn't been set, we'll fall back to using the default that was - // determined based on the storage's ALUA capabilities. - // - if (!explicitlySet) { - - status = DsmpQueryTargetLBPolicyFromRegistry(deviceInfo, - &loadBalanceType, - &preferredPath); - - if (NT_SUCCESS(status)) { - - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; - vidpidPolicySet = TRUE; - - } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { - - // - // Since the policy hasn't been set for this VID/PID, check if - // overall MSDSM-wide policy has been set. - // - status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, - &preferredPath); - if (NT_SUCCESS(status)) { - - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; - overallPolicySet = TRUE; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Failed to query Dsm overall LB policy from registry. Status %x.\n", - deviceInfo, - status)); - - NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); - status = STATUS_SUCCESS; - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Failed to query VID/PID LB policy from registry. Status %x.\n", - deviceInfo, - status)); - - NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); - status = STATUS_SUCCESS; - } - } else { - - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT; - } - - if (!explicitlySet && !vidpidPolicySet && !overallPolicySet) { - - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; - - } - - // - // If ALUA is enabled and the load balance policy is set to Round Robin, - // we need to set it to Round Robin with Subset instead. - // - if (!DsmpIsSymmetricAccess(deviceInfo) && loadBalanceType == DSM_LB_ROUND_ROBIN) { - loadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; - } - - group->LoadBalanceType = loadBalanceType; - group->PreferredPath = preferredPath; - dsmContext = (PDSM_CONTEXT) DsmContext; - - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - - // - // Save the registry key name under which Load balance policies - // are stored. This will be used to query the LB policy later. - // - if (group->RegistryKeyName) { - - registryKeyExists = TRUE; - - if (!NT_SUCCESS(RtlStringCchCopyNW(registryKeyName, - sizeof(registryKeyName) / sizeof(registryKeyName[0]), - group->RegistryKeyName, - ((sizeof(registryKeyName) / sizeof(registryKeyName[0])) - sizeof(WCHAR))))) { - - registryKeyName[(sizeof(registryKeyName) / sizeof(registryKeyName[0])) - 1] = L'\0'; - } - } - - // - // TargetObject is the destination for any requests created by this driver. - // Save this for future reference. - // - deviceInfo->TargetObject = TargetObject; - - // - // Set the PathId - All devices on the same PathId will - // failover together. Currently the pathId is constructed - // from Port Number, Bus Number, and Target Id of the device. - // - scsiAddress = deviceInfo->ScsiAddress; - NT_ASSERT(scsiAddress); - - pathId = 0x77; - pathId <<= 8; - pathId |= scsiAddress->PortNumber; - pathId <<= 8; - pathId |= scsiAddress->PathId; - pathId <<= 8; - pathId |= scsiAddress->TargetId; - - *PathId = ((PVOID)((ULONG_PTR)(pathId))); - - // - // PathId indicates the path on which this device resides. Meaning - // that when a Fail-Over occurs all device's on the same path fail - // together. Search for a matching F.O. Group - // - failGroup = DsmpFindFOGroup(DsmContext, *PathId); - - // - // If not found, create a new failover group - // - if (!failGroup) { - - failGroup = DsmpBuildFOGroup(DsmContext, deviceInfo, PathId); - - if (failGroup) { - - newFOGroup = TRUE; - failGroup->MPIOPath = tempPathId; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Failed to build FO Group.\n", - DsmId)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) { - - // - // If this path is in the midst of failover processing, mark it as "good" - // again. - // - failGroup->State = DSM_FG_NORMAL; - - // - // add this deviceInfo to the f.o. group. - // - status = DsmpUpdateFOGroup(DsmContext, failGroup, deviceInfo); - NT_ASSERT(NT_SUCCESS(status)); - } - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - - if (NT_SUCCESS(status)) { - - if (registryKeyExists) { - - NTSTATUS queryStatus = STATUS_INVALID_PARAMETER; - ULONGLONG pathId64; - - // - // If the overall default policy or a target-level policy has been set and - // this device's policy has not been explicitly set, there's no use querying - // its individual path (desired) states. - // - if ((!overallPolicySet && !vidpidPolicySet) || (explicitlySet)) { - - // - // Created a new failover group. Query the LB policy - // for this device from registry. - // - pathId64 = (ULONGLONG)((ULONG_PTR)*PathId); - - queryStatus = DsmpQueryLBPolicyForDevice(registryKeyName, - pathId64, - loadBalanceType, - &primaryPath, - &optimizedPath, - &pathWeight); - } - - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - - if (NT_SUCCESS(queryStatus)) { - - deviceInfo->PathWeight = pathWeight; - - // - // If device doesn't support ALUA, update the device state - // based on the primary path info in the registry. - // - if (DsmpIsSymmetricAccess(deviceInfo)) { - - if (primaryPath) { - - deviceInfo->DesiredState = DSM_DEV_ACTIVE_OPTIMIZED; - - } else { - - deviceInfo->DesiredState = DSM_DEV_STANDBY; - } - - } else { - - DSM_DEVICE_STATE devState; - - if (primaryPath) { - - devState = optimizedPath ? DSM_DEV_ACTIVE_OPTIMIZED : DSM_DEV_ACTIVE_UNOPTIMIZED; - - } else { - - devState = optimizedPath ? DSM_DEV_STANDBY : DSM_DEV_UNAVAILABLE; - } - - // - // For ALUA, desired state makes sense for FOO. - // For RRWS, we assume desired state was explicitly selected - // by Admin if the ALUA state is different from the path - // state. Only under such cases would the path state have - // been saved in registry. - // In all other policies, state must just match the TPG state. - // - if (group->LoadBalanceType == DSM_LB_FAILOVER || - group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { - - deviceInfo->DesiredState = devState; - - } else { - - deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; - } - } - } else if (queryStatus == STATUS_OBJECT_NAME_NOT_FOUND) { - - deviceInfo->PathWeight = pathWeight; - deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; - - } else { - - deviceInfo->PathWeight = 0; - deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; - } - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - } - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): PathWeight %x, DesiredState %x, State %x, PrevState %x.\n", - deviceInfo, - deviceInfo->PathWeight, - deviceInfo->DesiredState, - deviceInfo->State, - deviceInfo->PreviousState)); - - if (NT_SUCCESS(status)) { - - deviceInfo->Initialized = TRUE; - - } else if (!NT_SUCCESS(status) && newFOGroup) { - - // - // This deviceInfo will have no path associated with it. - // Mark it in a failed state so it won't be used to handle any requests. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_UNDETERMINED; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): No path associated with instance. Changing state from %u to %u.\n", - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - - DsmpRemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo, TRUE); - - if (failGroup->Count == 0) { - - // - // Yank it from the list. - // - RemoveEntryList(&failGroup->ListEntry); - InterlockedDecrement((LONG volatile*)&dsmContext->NumberFOGroups); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Removing FOGroup %p with path %p. Count of FOGroups %d.\n", - DsmId, - failGroup, - failGroup->PathId, - dsmContext->NumberFOGroups)); - - // - // Free the zombie group list and then the failover group. - // - DsmpFreeZombieGroupList(failGroup); - DsmpFreePool(failGroup); - } - } - -__Exit_DsmSetDeviceInfo: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmSetDeviceInfo (DevInfo %p): Exiting function with status %x.\n", - DsmId, - status)); - - return status; -} - - -BOOLEAN -DsmIsPathActive( - _In_ IN PVOID DsmContext, - _In_ IN PVOID PathId, - _In_ IN PVOID DsmId - ) -/*++ - -Routine Description: - - This routine is used to determine whether the path to DsmId is usable - (ie. able to handle requests without a failover). - - Also, after a failover, the path validity will be queried. - If the path error was transitory and the DSM feels that the path is good, - then this request will be re-issued to determine whether it is usable. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - PathId - Value set in SetPathId. - DsmId - DSM Id returned during DsmInquire. - -Return Value: - - TRUE if the path is active. FALSE otherwise. ---*/ -{ - PDSM_FAILOVER_GROUP foGroup; - PDSM_DEVICE_INFO deviceInfo = DsmId; - PDSM_GROUP_ENTRY group = deviceInfo->Group; - PDSM_CONTEXT dsmContext = (PDSM_CONTEXT) DsmContext; - KIRQL irql; - BOOLEAN retVal; - ULONG SpecialHandlingFlag = 0; - - // - // 1. If PR and reserved by this node, register the PR keys. - // 2. Find the FOG for the passed in PathId - // 3. Depending on the LB policy, set the appropriate devInfo states - // If FailOver, and DesiredState is AO, change the active - // devInfos to non-active state and make this one AO. - // If ALUA supported, send down SetTPG to make this change, - // else directly make the change. - // If RR/LWP/LQD, make this DevInfo ActiveOptimized. - // If RRS, and DesiredState is AO, change the active devInfos to - // their desired states and then make this one AO. - // If DesiredState is not AO, find a devInfo in AO state. If - // one is found, make this devInfo's state its desired state, - // else if one isn't found, make this one AO. - // 3. If this is preferredPath, and LB policy is failover-only, change the - // access state of deviceInfo to AO. - // If there is another devInfo currently in AO, change its state too. - // If ALUA supported, send down SetTPG to make these changes. - // 4. Get the appropriate AO DeviceInfo and mark the group's PTBU to its - // pathId. - // - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): Entering function.\n", - DsmId)); - - // - // Initialize this instance to be usable so that during the possible processing - // of PR register, this device can be a candidate for certain kind of requests. - // - deviceInfo->Usable = TRUE; - - // - // New path arriving. If this Node owns the reservation register this path. - // - if (group->PRKeyValid) { - - NTSTATUS prRegStatus; - ULONG i; - PDSM_DEVICE_INFO devInfo; - ULONG ordinal; - - prRegStatus = DsmpRegisterPersistentReservationKeys(deviceInfo, TRUE); - - deviceInfo->RegisterServiced = TRUE; - - if (NT_SUCCESS(prRegStatus)) { - - deviceInfo->PRKeyRegistered = TRUE; - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): Failed (status %x) to register PR key\n", - deviceInfo, - prRegStatus)); - } - - for (i = 0; i < group->NumberDevices; i++) { - - devInfo = group->DeviceList[i]; - if (devInfo && devInfo == deviceInfo) { - - ordinal = (1 << i); - group->ReservationList |= ordinal; - break; - } - } - } - - - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - - // - // Get the F.O. Group information. - // - foGroup = DsmpFindFOGroup(DsmContext, PathId); - - // - // If there are any devices on this path, and it's not in a failed state - // it's capable of handling requests. So it's active. - // - if ((foGroup) && - (foGroup->Count) && - (foGroup->State == DSM_FG_NORMAL)) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): Path %p is usable.\n", - DsmId, - PathId)); - - retVal = TRUE; - - // - // Update the next path to be used for the group if it not set already. - // - deviceInfo = (PDSM_DEVICE_INFO)DsmId; - - group = deviceInfo->Group; - DSM_ASSERT(group != NULL); - DSM_ASSERT(group->GroupSig == DSM_GROUP_SIG); - - // - // If an invalidated path came back online before PnP removes came in, - // then MPIO's path recovery thread would have sent down a PathVerify - // just moments before by which we changed the state of the FOG to - // normal. Now it is time to change the deviceInfo's state to a "good" - // state. - // - if (deviceInfo->State >= DSM_DEV_FAILED) { - - DSM_ASSERT(deviceInfo->State == DSM_DEV_INVALIDATED); - - if (DsmpIsSymmetricAccess(deviceInfo)) { - - // - // Mark it as AO. The SetLBForPathArrival will update the state - // appropriately. - // - deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - - } else { - - // - // Set it to the state that was reported during the last RTPG - // call that was made. - // - deviceInfo->State = deviceInfo->ALUAState; - } - } - - if (DsmpIsSymmetricAccess(deviceInfo)) { - - DsmpSetLBForPathArrival(DsmContext, deviceInfo, SpecialHandlingFlag); - - } else { - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - DsmpSetLBForPathArrivalALUA(DsmContext, deviceInfo, SpecialHandlingFlag); - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): State set to %d\n", - deviceInfo, - deviceInfo->State)); - - if (group->PathToBeUsed == NULL) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): Will set PathToBeUsed for %p\n", - deviceInfo, - group)); - - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess(deviceInfo), - SpecialHandlingFlag); - if (deviceInfo != NULL) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): FOG %p set for PathToBeUsed for %p\n", - deviceInfo, - deviceInfo->FailGroup, - group)); - - InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): No active/alternative path available for group %p\n", - DsmId, - group)); - - InterlockedExchangePointer(&(group->PathToBeUsed), NULL); - } - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): Path %p is NOT usable.\n", - DsmId, - PathId)); - - retVal = FALSE; - } - - ((PDSM_DEVICE_INFO)DsmId)->Usable = retVal; - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmIsPathActive (DevInfo %p): Exiting function with retVal = %!bool!.\n", - DsmId, - retVal)); - - return retVal; -} - - -NTSTATUS -DsmPathVerify( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PVOID PathId - ) -/*++ - -Routine Description: - - This routine ensures that the path to the device indicated by DsmId - is healthy. It's called periodically by the bus driver, and also - after a fail-over condition has been dealt with to ensure that - the path is able to handle requests. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - DsmId - Value returned from DMSInquire. - PathId - Value set in SetPathId. - -Return Value: - - NTSTATUS ---*/ - -{ - PDSM_CONTEXT dsmCtxt = (PDSM_CONTEXT) DsmContext; - PDSM_DEVICE_INFO deviceInfo = DsmId; - PDSM_FAILOVER_GROUP foGroup; - NTSTATUS status = STATUS_UNSUCCESSFUL; - BOOLEAN found = FALSE; - KIRQL irql; - PLIST_ENTRY entry; - PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; - PDSM_GROUP_ENTRY group = deviceInfo->Group; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmPathVerify (DevInfo %p): Entering function.\n", - DsmId)); - - if (DsmpIsDeviceInitialized(deviceInfo)) { - - irql = ExAcquireSpinLockExclusive(&(dsmCtxt->DsmContextLock)); - - // - // Get the failover group - // - foGroup = DsmpFindFOGroup(DsmContext, PathId); - - if (foGroup) { - - // - // Find the device. - // - for (entry = foGroup->FOG_DeviceList.Flink; - entry != &foGroup->FOG_DeviceList; - entry = entry->Flink) { - - fogDeviceListEntry = CONTAINING_RECORD(entry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); - - if (fogDeviceListEntry && fogDeviceListEntry->DeviceInfo == deviceInfo) { - - status = STATUS_SUCCESS; - found = TRUE; - - break; - } - } - } else { - - // - // This is not a good thing. It indicates that either we - // returned a bogus path to the bus-driver on a fail-over, - // or that the path evaporated between polls and PnP hasn't - // torn stuff down. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmPathVerify (DevInfo %p): Failed to find failover group for path %p.\n", - DsmId, - PathId)); - - status = STATUS_DEVICE_NOT_CONNECTED; - } - - ExReleaseSpinLockExclusive(&(dsmCtxt->DsmContextLock), irql); - - if (NT_SUCCESS(status)) { - - if (found) { - - // - // Send down TUR if ALUA is not supported. - // Else, send down ReportTargetPortGroups (sending TUR down non-A/O path will - // always result in a check condition). - // - if (deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmPathVerify (DevInfo %p): Sending TUR using %p to verify path %p.\n", - DsmId, - deviceInfo, - deviceInfo->FailGroup->PathId)); - - status = DsmSendTUR(deviceInfo->TargetObject); - - } else { - - // - // Check for whether we should ignore sending down an RTPG: - // Flag set indicates that this PathVerify() is happening in response to device - // arrival and can be skipped since Inquire() has just already sent down an RTPG. - // All that needs to be done is to clear the flag so that subsequent PathVerify() - // sent in response to InitiateFO will send RTPG as a ping. - // This is an optimization with the idea of helping speed up boot time, which is - // is adversely impacted, especially if there are many LUNs, each with many paths. - // - if (deviceInfo->IgnorePathVerify) { - - deviceInfo->IgnorePathVerify = FALSE; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmPathVerify (DevInfo %p): Returning success immediately since RTPG was already just sent.\n", - DsmId)); - - status = STATUS_SUCCESS; - - } else { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmPathVerify (DevInfo %p): Sending RTPG using %p to verify path %p.\n", - DsmId, - deviceInfo, - deviceInfo->FailGroup->PathId)); - - status = DsmpGetDeviceALUAState(dsmCtxt, deviceInfo, NULL); - - // - // Since this RTPG may have resulted in us losing a UA, adjust - // the states if needed. - // - if (NT_SUCCESS(status)) { - - DsmpAdjustDeviceStatesALUA(group, NULL, SpecialHandlingFlag); - } - } - } - } - - if (NT_SUCCESS(status)) { - - if (deviceInfo->State >= DSM_DEV_FAILED) { - - foGroup->State = DSM_FG_NORMAL; - deviceInfo->State = deviceInfo->LastKnownGoodState; - } - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmPathVerify (DevInfo %p): Exiting function with status %x.\n", - DsmId, - status)); - - return status; -} - - -NTSTATUS -DsmInvalidatePath( - _In_ IN PVOID DsmContext, - _In_ IN ULONG ErrorMask, - _In_ IN PVOID PathId, - _Inout_ IN OUT PVOID *NewPathId - ) -/*++ - -Routine Description: - - This routine will mark up devices as failed on PathId, and find - an appropriate path to return to MPIO. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - ErrorMask - Value returned from InterpretError. - PathId - The failing path. - NewPathId - Pointer to the new path. - -Return Value: - - NTSTATUS of the operation. - ---*/ -{ - PDSM_CONTEXT context = DsmContext; - PDSM_FAILOVER_GROUP failGroup; - PDSM_FAILOVER_GROUP newPath = NULL; - PDSM_FAILOVER_GROUP pathId; - PDSM_DEVICE_INFO deviceInfo; - LIST_ENTRY reservedDeviceList; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql; - PLIST_ENTRY entry; - PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; - BOOLEAN lockHeld = FALSE; - - UNREFERENCED_PARAMETER(ErrorMask); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): Entering function.\n", - PathId)); - - DSM_ASSERT(ErrorMask & DSM_FATAL_ERROR); - - *NewPathId = NULL; - - InitializeListHead(&reservedDeviceList); - - irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); - lockHeld = TRUE; - - // - // Get the fail-over group corresponding to the PathId. - // - failGroup = DsmpFindFOGroup(DsmContext, PathId); - - if (!failGroup || failGroup->State == DSM_FG_FAILED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): Failed to find FailOver group.\n", - PathId)); - - status = STATUS_NO_SUCH_DEVICE; - goto __Exit_DsmInvalidatePath; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): Context %p, FOG %p failing.\n", - PathId, - DsmContext, - failGroup)); - - // - // Mark the path as failed. - // - failGroup->State = DSM_FG_FAILED; - - // - // Check to see whether the port driver and PnP removed the devices - // BEFORE the fail-over indication actually occurred. Work-around - // of several Fibre miniports. - // - if (failGroup->Count == 0) { - - // - // There are no longer any devices in this fail-over group, which means - // in order to get a back-pointer to the groups using this fail-over - // group, we need to go through the "zombie" group list. This should - // allow us to find a new path ID to return. - //Then go through failGroup->ZombieGroupList to do failover for each group. - // - PDSM_ZOMBIEGROUP_ENTRY group; - PDSM_GROUP_ENTRY groupEntry; - - // - // Initialize all the entries to indicate that they haven't been processed. - // - for (entry = failGroup->ZombieGroupList.Flink; entry != &(failGroup->ZombieGroupList); entry = entry->Flink) { - - group = CONTAINING_RECORD(entry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); - group->Processed = FALSE; - } - - // - // Since we need to drop the spin lock while processing an entry, it is possible - // that a removal in parallel frees up this entry during that time, thus making it - // impossible for us to move to the next entry in the list. - // In order to safely access each of the entries, we mark an entry as being processed - // just before dropping the spinlock, and always start processing from the beginning - // of the list, skipping over the already processed ones. - // - entry = failGroup->ZombieGroupList.Flink; - - while (entry != &(failGroup->ZombieGroupList)) { - - group = CONTAINING_RECORD(entry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); - entry = entry->Flink; - - if (!group || !group->Group || group->Processed) { - continue; - } - - group->Processed = TRUE; - groupEntry = group->Group; - - ExReleaseSpinLockExclusive(&context->DsmContextLock, irql); - lockHeld = FALSE; - - pathId = DsmpSetNewPathUsingGroup((PDSM_CONTEXT)DsmContext, groupEntry); - - if (!newPath) { - newPath = pathId; // Save off first good alternative path that we find - } - - if (!lockHeld) { - irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); - lockHeld = TRUE; - entry = failGroup->ZombieGroupList.Flink; - } - } - - if (!newPath) { - // - // This indicates that all of the devices have already been removed. - // If there were reservations outstanding, the RemoveDevice code - // should have updated them. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): Failed to find new path using zombie group list.\n", - PathId)); - } - - } else { - - - // - // Process each device in the fail-over group - // - for (entry = failGroup->FOG_DeviceList.Flink; - entry != &failGroup->FOG_DeviceList; - entry = entry->Flink) { - - fogDeviceListEntry = CONTAINING_RECORD(entry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); - - if (!fogDeviceListEntry) { - continue; - } - - // - // Get the deviceInfo. - // - deviceInfo = fogDeviceListEntry->DeviceInfo; - - if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { - - deviceInfo->LastKnownGoodState = deviceInfo->State; - } - - // - // Set the state of the Failing Device - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_INVALIDATED; - - InterlockedIncrement(&deviceInfo->BlockRemove); - - ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); - lockHeld = FALSE; - - pathId = DsmpSetNewPath(DsmContext, deviceInfo); - - if (!newPath) { - newPath = pathId; // Save off first good alternative path that we find - } - - if (!lockHeld) { - irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); - lockHeld = TRUE; - } - - InterlockedDecrement(&deviceInfo->BlockRemove); - } - } - - if (!newPath) { - - // - // This indicates that no acceptable paths - // were found. Return the error to mpctl. - // - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): No valid path found.\n", - PathId)); - - status = STATUS_NO_SUCH_DEVICE; - - } else { - - // - // return the new path. - // - *NewPathId = newPath->PathId; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): Returning %p as newPath.\n", - PathId, - newPath->PathId)); - } - -__Exit_DsmInvalidatePath: - - if (lockHeld) { - ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmInvalidatePath (PathId %p): Exiting function with status %x.\n", - PathId, - status)); - - return status; -} - - -NTSTATUS -DsmMoveDevice( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PVOID MPIOPath, - _In_ IN PVOID SuggestedPath, - _In_ IN ULONG Flags - ) -/*++ - -Routine Description: - - This routine is invoked in response to an administrative request. - The device that's associated with SuggestedPath will be made active, and the - current active device, moved to stand-by. - -Arguments: - - DsmContext - Context value given to the multipath driver during registration. - DsmIds - The collection of DSM IDs that pertain to the MPDisk. - MPIOPath - The original path value passed to SetDeviceInfo. - SuggestedPath - The path which should become the active path. - Flags - Bitmask indicating the intent of the move. - -Return Value: - - NTSTATUS - STATUS_SUCCESS, unless SuggestedPath is somehow invalid. - STATUS_INVALID_PARAMETER is ADMIN is set and the path is invalid. - ---*/ -{ - PDSM_CONTEXT context = DsmContext; - PDSM_DEVICE_INFO deviceInfo; - PDSM_FAILOVER_GROUP failGroup; - ULONG i; - NTSTATUS status; - KIRQL irql; - BOOLEAN adminRequest = FALSE; - PDSM_GROUP_ENTRY group = NULL; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmMoveDevice (DsmIds %p): Entering function - DsmContext %p MPIOPath (%p) SuggestedPath %p.\n", - DsmIds, - DsmContext, - MPIOPath, - SuggestedPath)); - - // - // Capture the value of the ADMIN flag bit. - // Currently, permanent assignment of the device to "preferred path" isn't supported. - // This driver doesn't care about the pending remove flag (currently). - // - adminRequest = (BOOLEAN)(Flags & DSM_MOVE_ADMIN_REQUEST); - - irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); - - group = ((PDSM_DEVICE_INFO)(DsmIds->IdList[0]))->Group; - - // - // Find the first active device. - // - deviceInfo = DsmpGetActivePathToBeUsed(group, - DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), - SpecialHandlingFlag); - - if (!deviceInfo) { - - // - // Didn't find an active device. Should LOG. - // Use the first one to piggy-back the request. - // - deviceInfo = DsmIds->IdList[0]; - } - - // - // Get the fail-over group associated with the Path. - // - failGroup = DsmpFindFOGroup(DsmContext, - SuggestedPath); - - if (!failGroup) { - - // - // The caller has made a terrible mistake. - // If it's an ADMIN request, blow it off. - // - if (adminRequest) { - status = STATUS_INVALID_PARAMETER; - } else { - - // - // Try to set another path. - // - // Note that failGroup will be NULL going into - // SetNewPath. This is OK. - // - status = STATUS_SUCCESS; - } - } else { - status = STATUS_SUCCESS; - } - - if (status == STATUS_SUCCESS) { - - // - // Set the new path, using SuggestedPath. - // - InterlockedIncrement(&deviceInfo->BlockRemove); - ExReleaseSpinLockExclusive(&context->DsmContextLock, irql); - failGroup = DsmpSetNewPath(context, - deviceInfo); - irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); - InterlockedDecrement(&deviceInfo->BlockRemove); - - // - // If we were able to make the suggested path active, that should be used. - // - for (i = 0, status = STATUS_UNSUCCESSFUL; i < DsmIds->Count && !NT_SUCCESS(status); i++) { - - deviceInfo = DsmIds->IdList[i]; - - if (deviceInfo->FailGroup == failGroup) { - - if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { - - InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)failGroup); - status = STATUS_SUCCESS; - } - } - } - } - - ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmMoveDevice (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmRemovePending( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId - ) -/*++ - -Routine Description: - - This routine indicates that the device represented by DsmId will be - removed, so the deviceInfo is marked up to indicate the pending removal, - so that it won't be used. - -Arguments: - - DsmContext - Context value given to the multipath driver - during registration. - DsmId - Value referring to the failed device. - -Return Value: - - STATUS_SUCCESS - ---*/ - -{ - PDSM_CONTEXT dsmContext = DsmContext; - PDSM_DEVICE_INFO deviceInfo = DsmId; - KIRQL irql; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmRemovePending (DevInfo %p): Entering function.\n", - DsmId)); - - // - // DsmpSetNewPath then finds the next available device. This is basically a - // fail-over for just this device. - // - InterlockedIncrement(&deviceInfo->BlockRemove); - DsmpSetNewPath(DsmContext, deviceInfo); - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - InterlockedDecrement(&deviceInfo->BlockRemove); - - if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { - - deviceInfo->LastKnownGoodState = deviceInfo->State; - } - - // - // Mark the device as being unavailable since remove will be sent shortly. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_REMOVE_PENDING; - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmRemovePending (DevInfo %p): Exiting function.\n", - DsmId)); - - return STATUS_SUCCESS; -} - -NTSTATUS -DsmRemoveDevice( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PVOID PathId - ) -/*++ - -Routine Description: - - The device is gone and the port pdo has been removed. This routine will - update the internal structures and free any allocations. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - DsmId - Value referring to the failed device. - PathId - The path on which the Device lives. - -Return Value: - - STATUS_SUCCESS - ---*/ - -{ - PDSM_CONTEXT dsmContext = DsmContext; - PDSM_DEVICE_INFO deviceInfo = DsmId; - KIRQL irql; - PDSM_FAILOVER_GROUP failGroup = deviceInfo->FailGroup; - PDSM_GROUP_ENTRY group = deviceInfo->Group; - LONG block; - - UNREFERENCED_PARAMETER(PathId); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmRemoveDevice (DevInfo %p): Entering function.\n", - DsmId)); - - do { - - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - block = deviceInfo->BlockRemove; - NT_ASSERT(block >= 0); - - if (block) { - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - KeStallExecutionProcessor(10000); - } - - } while (block); - - if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { - - deviceInfo->LastKnownGoodState = deviceInfo->State; - } - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = DSM_DEV_REMOVED; - - // - // Decrement the reference count for this device's controller entry and - // delete the entry if its reference count is now zero. - // - if (deviceInfo->Controller) { - - if (InterlockedDecrement((LONG volatile*)&(deviceInfo->Controller->RefCount)) == 0) { - - RemoveEntryList(&(deviceInfo->Controller->ListEntry)); - DsmpFreeControllerEntry(dsmContext, deviceInfo->Controller); - deviceInfo->Controller = NULL; - InterlockedDecrement((LONG volatile*)&(dsmContext->NumberControllers)); - } - } - - // - // Ensure that the device has been fully initialized before trying to - // remove it from the FOG. If SetDeviceInfo has yet to be invoked, there - // will yet to be an association set. - // - if (failGroup) { - - // - // Remove its entry from the Fail-Over Group. - // - DsmpRemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo, FALSE); - } - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - - // - // Remove it from it's multi-path group. This has the side-effect - // of cleaning up the Group if the number of devices goes to zero. - // - DsmpRemoveDeviceEntry(DsmContext, group, deviceInfo); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmRemoveDevice (DevInfo %p): Exiting function.\n", - DsmId)); - - return STATUS_SUCCESS; -} - - -NTSTATUS -DsmRemovePath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PVOID PathId - ) -/*++ - -Routine Description: - - This routine indicates that the path is no longer valid, and that it should - be removed. Internal counts will be updated and any allocations associated - with this path freed. - -Arguments: - - DsmContext - Context value given to the multipath driver during registration. - PathId - The path to remove. - -Return Value: - - NTSTATUS of the operation. - ---*/ - -{ - PDSM_FAILOVER_GROUP failGroup; - KIRQL irql; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmRemovePath (PathId %p): Entering function.\n", - PathId)); - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - failGroup = DsmpFindFOGroup(DsmContext, PathId); - - if (failGroup) { - - // - // The claim is that a path won't be removed, until all - // the devices on it are. - // - if (failGroup->Count == 0) { - - // - // Yank it from the list. - // - RemoveEntryList(&failGroup->ListEntry); - InterlockedDecrement((LONG volatile*)&DsmContext->NumberFOGroups); - - // - // Move this over to the stale FOG list if there are inflight requests. - // Otherwise free the allocation. - // - if (InterlockedCompareExchange(&failGroup->NumberOfRequestsInFlight, 0, 0) > 0) { - - failGroup->State = DSM_FG_PENDING_REMOVE; - InsertTailList(&DsmContext->StaleFailGroupList, &failGroup->ListEntry); - InterlockedIncrement((LONG volatile*)&DsmContext->NumberStaleFOGroups); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmRemovePath (PathId %p): Outstanding requests %d. Moving FOGroup %p with path %p to stale path list.\n", - PathId, - failGroup->NumberOfRequestsInFlight, - failGroup, - failGroup->PathId)); - } else { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmRemovePath (PathId %p): Removing FOGroup %p with path %p. Count of FOGroups %d.\n", - PathId, - failGroup, - failGroup->PathId, - DsmContext->NumberFOGroups)); - - // - // Free the zombie group list and then the failover group. - // - DsmpFreeZombieGroupList(failGroup); - DsmpFreePool(failGroup); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmRemovePath (PathId %p): Count %d. Not removing FOGroup %p.\n", - PathId, - failGroup->Count, - failGroup)); - - // - // Should never be here. - // - NT_ASSERT(failGroup->Count == 0); - } - } else { - - // - // It's already been removed. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmRemovePath (PathId %p): Did not find the FO group.\n", - PathId)); - - NT_ASSERT(failGroup); - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmRemovePath (PathId %p): Exiting function.\n", - PathId)); - - return STATUS_SUCCESS; -} - - -PVOID -DsmLBGetPath( - _In_ IN PVOID DsmContext, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PDSM_IDS DsmList, - _In_ IN PVOID CurrentPath, - _Out_ OUT NTSTATUS *Status - ) -/*++ - -Routine Description: - - This routine is used by mpio to handle load-balancing. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - Srb - The current read/write Srb. - DsmList - List of our DSM IDs. - CurrentPath - The last path that was returned for this multi-path group. - Status - Storage to place NTSTATUS of the call. - -Return Value: - - The path ID to which the request should be sent. - ---*/ - -{ - PDSM_CONTEXT dsmContext = DsmContext; - PDSM_DEVICE_INFO deviceInfo; - PDSM_GROUP_ENTRY group; - PDSM_FAILOVER_GROUP failGroup = NULL; - PVOID newPath = NULL; - PDSM_FAILOVER_GROUP oldFailGroup = NULL; - PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failPathDevInfoEntry = NULL; - PCDB cdb = NULL; - UCHAR opCode = 0xFF; - BOOLEAN lockInExclusiveMode = FALSE; - ULONG SpecialHandlingFlag = 0; - - - if (Srb) { - cdb = SrbGetCdb(Srb); - if (cdb) { - opCode = cdb->AsByte[0]; - - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmLBGetPath (DsmIds %p): Entering function.\n", - DsmList)); - - // - // Up-front checking to minimally validate the list of - // DsmId's being passed in. - // - NT_ASSERT(DsmList->Count && DsmList->IdList[0]); - if (!(DsmList->Count && DsmList->IdList[0])) { - - *Status = STATUS_NO_SUCH_DEVICE; - goto __Exit_DsmLBGetPath; - } - - deviceInfo = DsmList->IdList[0]; - group = deviceInfo->Group; - - - failGroup = DsmpGetPath(dsmContext, DsmList, Srb, SpecialHandlingFlag); - - // - // If there wasn't a single active/optimized path found, check to see if - // there is an STPG in progress that may be making a path A/O. - // - if (!failGroup) { - - // - // Take the last path used. - // - oldFailGroup = DsmpFindFOGroup(dsmContext, CurrentPath); - - // - // Find the devInfo corresponding to this path. - // - deviceInfo = DsmpFindDevInfoFromGroupAndFOGroup(dsmContext, - group, - oldFailGroup); - - if (deviceInfo) { - - // - // Check if there is an alternate devInfo to be used temporarily - // for this deviceInfo - // - failPathDevInfoEntry = DsmpFindFailPathDevInfoEntry(dsmContext, - group, - deviceInfo); - - if (failPathDevInfoEntry) { - - // - // Use the alternate devInfo for now temporarily while the STPG - // that was previously sent (asynchronously) works on making the - // appropriate path active/optimized. - // - failGroup = (failPathDevInfoEntry->TempDeviceInfo)->FailGroup; - } - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmLBGetPath (DsmIds %p): Couldn't find FOG but FO in progress, so returning devInfo %p (FOG %p path %p).\n", - DsmList, - deviceInfo, - deviceInfo->FailGroup, - deviceInfo->FailGroup->PathId)); - } else { - - // - // Check if there is an RTPG in progress, if yes, return some path - // for the IO to be sent down. - // - if (InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 0)) { - - BOOLEAN sendTPG = FALSE; - deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); - - if (deviceInfo) { - - failGroup = deviceInfo->FailGroup; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmLBGetPath (DsmIds %p): Couldn't find FOG but RTPG inflight, so returning devInfo %p (FOG %p path %p).\n", - DsmList, - deviceInfo, - deviceInfo->FailGroup, - deviceInfo->FailGroup->PathId)); - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmLBGetPath (DsmIds %p): Couldn't find FOG but RTPG inflight, even then couldn't find alternative devInfo.\n", - DsmList)); - } - } - } - } - - if (failGroup) { - - newPath = failGroup->PathId; - *Status = STATUS_SUCCESS; - - // - // If this is a retried request, our SetCompletion would have been bypassed, - // and our completion routine won't yet get called, so update the old and - // the new paths' stats. - // - if (Srb && DsmIsReadWrite(opCode)) { - - PDSM_FAILOVER_GROUP oldPath; - PIRP irp = (PIRP)SrbGetOriginalRequest(Srb); - PIO_STACK_LOCATION irpStack; - - // - // This indicates that the request is being retried. So we need to: - // 1. Update old path's and new path's request count - // 2. If the old path was supposed to be removed, check if there are - // no more requests are outstanding, and if yes, remove the path - // - - irpStack = IoGetCurrentIrpStackLocation(irp); - oldPath = irpStack->Parameters.Others.Argument3; - - if (oldPath) { - - NT_ASSERT(oldPath->FailOverSig == DSM_FOG_SIG); - - if (DsmpDecrementCounters(oldPath, Srb)) { - - // - // If there are no requests on a path that is supposed to be removed, - // remove it now. - // - if (oldPath->State == DSM_FG_PENDING_REMOVE) { - KIRQL irql; - - NT_ASSERT(oldPath->Count == 0); - - // - // We need to acquire the DsmContextLock in Exclusive mode since - // we are removing a path from the Failover Group list. - // - irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); - lockInExclusiveMode = TRUE; - - RemoveEntryList(&oldPath->ListEntry); - InterlockedDecrement((LONG volatile*)&dsmContext->NumberStaleFOGroups); - - ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmLBGetPath (DsmIds %p): Removing FOGroup %p with path %p.\n", - DsmList, - oldPath, - oldPath->PathId)); - - DsmpFreePool(oldPath); - } - } - - irpStack->Parameters.Others.Argument3 = failGroup; - - DsmpIncrementCounters(failGroup, Srb); - } - } - - } else { - - *Status = STATUS_NO_SUCH_DEVICE; - - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmLBGetPath (DsmIds %p): Failed to get FO group in LBGetPath.\n", - DsmList)); - - - } - -__Exit_DsmLBGetPath: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmLBGetPath (DsmIds %p): Exiting function returning path %p for request %p.\n", - DsmList, - newPath, - Srb)); - - return newPath; -} - -_Success_(return == DSM_PATH_SET) -ULONG -DsmCategorizeRequest( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PVOID CurrentPath, - _Outptr_result_maybenull_ OUT PVOID *PathId, - _Out_ OUT NTSTATUS *Status - ) -/*++ - -Routine Description: - - This routine is called when a request is received other than a read/write. - It will determine the best path to which the request is to be sent. - - In order to support clusters, reserve and release need to be handled - via SrbControl. - -Arguments: - - DsmContext - Context value given to the multipath driver during - registration. - DsmIds - List of our DSM IDs. - Irp - The Irp containing Srb. - Srb - The current non-read/write Srb. - CurrentPath - The last path that was returned for this multi-path group. - PathId - Placeholder for the PathID - Status - Storage to place NTSTATUS of the call. - -Return Value: - - DSM_PATH_SET - Indicates PathID is valid. - DSM_ERROR - Couldn't get a path. - ---*/ -{ - ULONG dsmStatus; - NTSTATUS status = STATUS_UNSUCCESSFUL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmCategorizeRequest (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // Determine whether this is a special-case request. - // - if (DsmpReservationCommand(Irp, Srb)) { - - dsmStatus = DSM_WILL_HANDLE; - goto __Exit_DsmCategorizeRequest; - } - - - // - // If this is a mpio pass through or a mpio pass through direct request, - // pick the path that corresponds to the pathId specified. - // - if (DsmpMpioPassThroughPathCommand(Irp)) { - - *PathId = DsmpGetPathIdFromPassThroughPath(DsmContext, - DsmIds, - Irp, - &status); - } else { - - // - // For requests other than reservation-handling and pass through, punt - // it back to the bus-driver. Need to get a path for the request first, - // so call the Load-Balance function. - // - *PathId = DsmLBGetPath(DsmContext, - Srb, - DsmIds, - CurrentPath, - &status); - } - - if (NT_SUCCESS(status)) { - - if (!*PathId) { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmCategorizeRequest (DsmIds %p): DSM_PATH_SET didn't return a path.\n", - DsmIds)); - } - - // - // Indicate that the path is updated, and mpctl should handle the request. - // - dsmStatus = DSM_PATH_SET; - - } else { - - // - // Indicate the error back to mpctl. - // - dsmStatus = DSM_ERROR; - - // - // Mark-up the Srb to show that a failure has occurred. - // This value is really only for this DSM to know what to do - // in the InterpretError routine - Fatal Error. - // It could be something more meaningful. - // - if (Srb) { - Srb->SrbStatus = SRB_STATUS_NO_DEVICE; - } - - *PathId = NULL; - } - - // - // Pass back status info to mpctl. - // - *Status = status; - -__Exit_DsmCategorizeRequest: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmCategorizeRequest (DsmIds %p): Exiting function with categorization %x.\n", - DsmIds, - dsmStatus)); - - return dsmStatus; -} - - -NTSTATUS -DsmBroadcastRequest( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ) -/*++ - -Routine Description: - - This routine is called when the DSM has indicated that Srb should be - sent to the device down all paths. The DSM will update IoStatus - information and status, but not complete the request. - - Currently MSDSM doesn't have a need for this. - -Arguments: - - DsmIds - The collection of DSM IDs that pertain to the MPDisk. - Irp - Irp containing SRB. - Srb - Scsi request block - Event - DSM sets this once all sub-requests have completed and - the original request's IoStatus has been setup. - -Return Value: - - NTSTATUS of the operation. - ---*/ -{ - NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; - - UNREFERENCED_PARAMETER(DsmContext); - UNREFERENCED_PARAMETER(Srb); - UNREFERENCED_PARAMETER(Irp); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmBroadcastRequest (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // Currently nothing is handled via Broadcast. Just set the event to - // free up the request handling in the bus-driver. - // - NT_ASSERT(NT_SUCCESS(status)); - KeSetEvent(Event, 0, FALSE); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmBroadcastReqeust (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmSrbDeviceControl( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ) -/*++ - -Routine Description: - - This routine is called when the DSM has indicated that it wants to handle - it internally (via returning DSM_WILL_HANDLE in CategorizeRequest). - - It should set IoStatus (Status and Information) and the Event, but not - complete the request. - -Arguments: - - DsmContext - The DSM's context - DsmIds - The collection of DSM IDs that pertain to the MPDISK. - Irp - Irp containing SRB. - Srb - Scsi request block - Event - Event to be set when the DSM is finished if DsmHandled is TRUE - -Return Value: - - NTSTATUS of the request. - ---*/ -{ - PDSM_CONTEXT dsmContext = DsmContext; - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); - NTSTATUS status; - UCHAR opCode = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmSrbDeviceControl (DsmIds %p): Entering function.\n", - DsmIds)); - - if (!DsmIds || !DsmIds->Count) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_IOCTL, - "DsmSrbDeviceControl (DsmIds %p): No DsmIds passed in.\n", - DsmIds)); - - status = STATUS_NO_SUCH_DEVICE; - goto __Exit_DsmSrbDeviceControl; - } - - if (irpStack->MajorFunction == IRP_MJ_SCSI) { - - // - // Determine the operation. - // - PCDB cdb = SrbGetCdb(Srb); - if (cdb) { - opCode = cdb->AsByte[0]; - } - - if (opCode == SCSIOP_PERSISTENT_RESERVE_OUT) { - - status = DsmpPersistentReserveOut(dsmContext, - DsmIds, - Irp, - Srb, - Event); - - } else if (opCode == SCSIOP_PERSISTENT_RESERVE_IN) { - - status = DsmpPersistentReserveIn(dsmContext, - DsmIds, - Irp, - Srb, - Event); - - } else { - - // - // Should never be here. - // - DSM_ASSERT(FALSE); - status = STATUS_INVALID_DEVICE_REQUEST; - } - } else { - // - // Should never be here. - // - DSM_ASSERT(irpStack->MajorFunction == IRP_MJ_SCSI); - status = STATUS_INVALID_DEVICE_REQUEST; - } - -__Exit_DsmSrbDeviceControl: - if (status != STATUS_PENDING) { - - // - // Set-up the Irp status for mpio's completion of the request. - // If it was IRP_MJ_SCSI, one of the helper routines set Srb->SrbStatus - // already. - // - if ((irpStack->MajorFunction == IRP_MJ_SCSI) && - (Srb != NULL) && - (Srb->SrbStatus == SRB_STATUS_PENDING)) { - - Srb->SrbStatus = SRB_STATUS_ERROR; - } - - Irp->IoStatus.Status = status; - - // - // Set the event to free up the request handling in the bus-driver. - // - KeSetEvent(Event, 0, FALSE); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmSrbDeviceControl (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -VOID -DsmSetCompletion( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _Inout_ IN OUT PDSM_COMPLETION_INFO DsmCompletion - ) -/*++ - -Routine Description: - - This routine is called before the actual submission of a request, - but after the categorisation of the I/O. This will be called only - for those requests not handled by the DSM directly: - Read/Write - Other requests not handled by SrbControl or Broadcast - -Arguments: - - DsmContext - The DSM's context. - DsmId - Identifer that was indicated when the request was - categorized (or be LBGetPath) - Irp - Irp containing Srb. - Srb - The request - DsmCompletion - Completion info structure to be filled out by DSM. - -Return Value: - - None - ---*/ -{ - PDSM_CONTEXT dsmContext = DsmContext; - PDSM_DEVICE_INFO deviceInfo = DsmId; - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); - PDSM_FAILOVER_GROUP failGroup = deviceInfo->FailGroup; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmSetCompletion (DevInfo %p): Entering function.\n", - DsmId)); - - // - // Save off the path that was selected to service this request in Argument3. - // - irpStack->Parameters.Others.Argument3 = failGroup; - - DsmpIncrementCounters(failGroup, Srb); - - if (!dsmContext->DisableStatsGathering) { - - // - // Indicate one more request on this device down this path. - // - InterlockedIncrement(&deviceInfo->NumberOfRequestsInProgress); - } - - // - // Update the passed-in struct with our routine and context values. - // - DsmCompletion->DsmCompletionRoutine = DsmpRequestComplete; - DsmCompletion->DsmContext = DsmContext; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmSetCompletion (DevInfo %p): Exiting function.\n", - DsmId)); - - return; -} - - -ULONG -DsmInterpretError( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _Inout_ IN OUT NTSTATUS *Status, - _Out_ OUT PBOOLEAN Retry, - _Out_ OUT PLONG RetryInterval, - ... - ) -/*++ - -Routine Description: - - This routine is invoked by MPIO if Status is other than SUCCESS. - A few NTSTATUS and SRB_STATUS values indicate a fatal error. - Also checked are unit attentions, for which a retry is requested. - -Arguments: - - DsmContext - The DSM's context. - DsmId - Identifers returned from DMS_INQUIRE_DRIVER. - Srb - The Srb with an error. - Status - NTSTATUS of the operation. Can be updated. - Retry - Allows the DSM to indicate whether to retry the IO. - RetryInterval - Lets DSM specify (in seconds) when this specific I/O - should be retried. Use MAXLONG to use the default - retry interval. Use zero to retry immediately. - -Return Value: - - DSM_FATAL_ERROR indicates a fatal error. - ---*/ -{ - // - // The requests that will be encountered can be divided into four categories: - // 1. The request that has failed. - // 2. Subsequent requests that were sent down the failing path that will - // complete with failure. - // 3. Requests that were already submitted to LBGetPath() just before InterpretError() - // was called for the failed request (but have yet to have the LB policy - // algo run). - // 4. Requests that come into the Dispatch() routine after the failed request - // has been processed by InterpretError(). - // - // For the failed request: - // ======================= - // 1. Find a standby path to make active/optimized. - // 2. Send STPG asynchronously as a scsi pass through via IRP_MJ_SCSI (this - // way it can be sent at DISPATCH_IRQL) after setting a completion routine. - // 3. Save the devInfo corresponding to the standby path for the failing devInfo. - // 4. Return FATAL to MPIO so that new IO are queued. - // 5. In the completion routine, update the new states for the devInfos. Then - // clear the saved (previously) standby devInfo for the failing devInfo. - // - // For the subsequent request that will fail (since it was sent on the failing path): - // ================================================================================== - // 1. If a standby devInfo has been saved off, it indicates that an STPG was - // already sent, so no need to send another one. - // 2. Return FATAL to MPIO so that this request gets queued. - // - // For the requests that were already submitted to LBGetPath() during this time: - // ============================================================================= - // 1. If there is no active path, check if a standby devInfo has been saved - // away. If it has, return this path. Such requests will fail with check - // condition saying path used is in standby. - // 2. In InterpretError() retry (since the error indicates that request - // completed before STPG completed) without decrementing the remaining - // retries count. - // - // For new requests that come into Dispatch() after above processing: - // ================================================================== - // We don't need to worry about such requests, since MPIO will queue them - // automatically. - // - - PDSM_DEVICE_INFO deviceInfo = DsmId; - ULONG errorMask = 0; - PVOID senseData = SrbGetSenseInfoBuffer(Srb); - UCHAR senseDataLength = SrbGetSenseInfoBufferLength(Srb); - BOOLEAN failover = FALSE; - BOOLEAN retry = FALSE; - BOOLEAN handled = FALSE; - BOOLEAN sendTPG = FALSE; - BOOLEAN tpgException = FALSE; - BOOLEAN devInfoException = FALSE; - PCDB cdb = SrbGetCdb(Srb); - UCHAR opCode = 0; - UCHAR scsiStatus = SrbGetScsiStatus(Srb); - BOOLEAN validSense = FALSE; - UCHAR senseKey = 0; - UCHAR addSenseCode = 0; - UCHAR addSenseCodeQualifier = 0; - - if (cdb) { - opCode = cdb->AsByte[0]; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Entering function.\n", - DsmId)); - - *RetryInterval = MAXLONG; - - if ((scsiStatus == SCSISTAT_RESERVATION_CONFLICT) || - (*Status == STATUS_DEVICE_BUSY)) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Srb %p. Either busy or res. conflict (%x %x).\n", - DsmId, - Srb, - scsiStatus, - *Status)); - } - - // - // Go ahead and get the sense data if it's valid. - // - if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { - - NT_ASSERT(senseData != NULL); - - validSense = ScsiGetSenseKeyAndCodes(senseData, - senseDataLength, - SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, - &senseKey, - &addSenseCode, - &addSenseCodeQualifier); - } - - // - // Sense data relating to logical block provisioning should be failed - // immediately back to the class layer for handling. - // - if (validSense) { - if (senseKey == SCSI_SENSE_NOT_READY && - addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && - addSenseCodeQualifier == SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Temporary resource exhaustion. Fail Srb %p.\n", - DsmId, - Srb)); - - handled = TRUE; - - } else if (senseKey == SCSI_SENSE_DATA_PROTECT && - addSenseCode == SCSI_ADSENSE_WRITE_PROTECT && - addSenseCodeQualifier == SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Permanent resource exhaustion. Fail Srb %p.\n", - DsmId, - Srb)); - - handled = TRUE; - - } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && - addSenseCode == SCSI_ADSENSE_LB_PROVISIONING && - addSenseCodeQualifier == SCSI_SENSEQ_SOFT_THRESHOLD_REACHED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Soft threshold reached. Fail Srb %p.\n", - DsmId, - Srb)); - - handled = TRUE; - - } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && - addSenseCode == SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED && - addSenseCodeQualifier == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Inquiry data changed. Fail Srb %p.\n", - DsmId, - Srb)); - - handled = TRUE; - } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && - addSenseCode == SCSI_ADSENSE_PARAMETERS_CHANGED && - addSenseCodeQualifier == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Capacity data changed. Fail Srb %p.\n", - DsmId, - Srb)); - - handled = TRUE; - } - } - - if (handled) { - return errorMask; - } - - // - // Check the NT Status first. - // Several are clearly failover conditions. - // - switch (*Status) { - case STATUS_DEVICE_NOT_CONNECTED: - case STATUS_DEVICE_DOES_NOT_EXIST: - case STATUS_NO_SUCH_DEVICE: - case STATUS_DELETE_PENDING: { - - // - // The port pdo has either been removed or is - // very broken. A fail-over is necessary. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Will initiate fail over. Status %x. Opcode %x.\n", - DsmId, - *Status, - opCode)); - - handled = TRUE; - failover = TRUE; - break; - } - - case STATUS_IO_DEVICE_ERROR: { - - if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { - - if (validSense) { - - // - // See if it's a unit attention. - // - if (senseKey == SCSI_SENSE_UNIT_ATTENTION) { - - switch (addSenseCode) { - - case SCSI_ADSENSE_PARAMETERS_CHANGED: { - - switch (addSenseCodeQualifier) { - - case SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED: - case SPC3_SCSI_SENSEQ_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): TPG states have changed. Requesting retry on Srb %p. Will send asyn RTPG.\n", - DsmId, - Srb)); - - // - // Retry but after sending RTPG, which will update the path states. - // - sendTPG = TRUE; - retry = TRUE; - handled = TRUE; - errorMask = DSM_RETRY_DONT_DECREMENT; - - if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED) { - - // - // Worth retrying on the same path. - // - devInfoException = TRUE; - NT_ASSERT(!tpgException); - } - - break; - } - - - case SPC3_SCSI_SENSEQ_RESERVATIONS_RELEASED: { - - // - // This request needs to be immediately retried down the same path. - // - retry = TRUE; - *RetryInterval = 0; - handled = TRUE; - InterlockedExchangePointer(&(deviceInfo->Group->PathToBeUsed), deviceInfo->FailGroup); - break; - } - - case SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED: - case SPC3_SCSI_SENSEQ_RESERVATIONS_PREEMPTED: - case SPC3_SCSI_SENSEQ_REGISTRATIONS_PREEMPTED: - case SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Failing request. STATUS_IO_DEVICE_ERROR (params changed). SrbStatus (%x) Scsi (%x) AddQual (%u).\n", - DsmId, - Srb->SrbStatus, - scsiStatus, - addSenseCodeQualifier)); - - // - // Just fail these back. - // - handled = TRUE; - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): UNIT_ATTENTION for params changed. ASCQ %x. Asking for retry on Srb %p.\n", - DsmId, - addSenseCodeQualifier, - Srb)); - - // - // Indicate that a retry is necessary. - // - retry = TRUE; - handled = TRUE; - - break; - } - } - - break; - } - - - case SPC3_SCSI_ADSENSE_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR: { - - if (addSenseCodeQualifier == 0x00) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): UNIT_ATTENTION (commands cleared by another initiator). Fail back to upper level. Srb %p.\n", - DsmId, - Srb)); - - // - // Commands cleared by another Initiator - // - handled = TRUE; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): UNIT_ATTENTION (commands cleared by another initiator). ASCQ %x. Asking for retry on Srb %p.\n", - DsmId, - addSenseCodeQualifier, - Srb)); - - // - // Indicate that a retry is necessary. - // - retry = TRUE; - handled = TRUE; - } - - - break; - } - - case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: { - - if (addSenseCodeQualifier == SCSI_SENSEQ_VOLUME_SET_MODIFIED || - addSenseCodeQualifier == SCSI_SENSEQ_REPORTED_LUNS_DATA_CHANGED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): VolumeSet/LunsData changed. Fail Srb %p.\n", - DsmId, - Srb)); - - // - // Fail back to upper layers. - // - handled = TRUE; - - break; - - } else { - - // - // Fall through to default case (ie. retry the request) - // - } - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): UNIT_ATTENTION. ASC %x, ASCQ %x. Asking for retry on Srb %p.\n", - DsmId, - addSenseCode, - addSenseCodeQualifier, - Srb)); - - // - // Indicate that a retry is necessary. - // - retry = TRUE; - handled = TRUE; - - break; - } - } - } else if (senseKey == SCSI_SENSE_NOT_READY) { - - if (addSenseCode == SCSI_ADSENSE_LUN_NOT_READY) { - - if (scsiStatus == SCSISTAT_CHECK_CONDITION) { - - switch (addSenseCodeQualifier) { - - // - // See if failure is due to device's current TPG state. - // - // If the failure is PORT_IN_STANDBY_STATE, we leave DSM_RETRY_DONT_DECREMENT unset if no active path exists, - // because otherwise MPIO will not be able to find a better path, and it will get into an infinite loop - // of trying and failing the command on a Standby path. See WCxeTfs:89150 - // - case SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION: - case SPC3_SCSI_SENSEQ_TARGET_PORT_IN_UNAVAILABLE_STATE: - - errorMask = DSM_RETRY_DONT_DECREMENT; - - case SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE: - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): TPG-transition/TPG-SB/TPG-UA. ASCQ %x. Will send down async RTPG. Asking for retry on Srb %p.\n", - DsmId, - addSenseCodeQualifier, - Srb)); - - // - // Indicate that a retry is necessary but without decrementing the remaining - // retries count. However, we may need to send down an STPG/RTPG also. - // And we must set PTBU to a path that is in a different TPG. - // - sendTPG = TRUE; - tpgException = TRUE; - NT_ASSERT(!devInfoException); - retry = TRUE; - handled = TRUE; - - if ((addSenseCodeQualifier == SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE) && - DsmIsReadWrite(opCode)) { - - PDSM_CONTEXT context = (PDSM_CONTEXT) deviceInfo->DsmContext; - KIRQL oldIrql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); - BOOLEAN activePathExists = ( NULL != DsmpGetAnyActivePath(deviceInfo->Group, FALSE, NULL, 0) ); - ExReleaseSpinLockExclusive(&(context->DsmContextLock), oldIrql); - - if (activePathExists) { - errorMask = DSM_RETRY_DONT_DECREMENT; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Not decrementing error counter, as an active path exists in group %p and opcode %x is r/w\n", - DsmId, - deviceInfo->Group, - opCode)); - } - } - - break; - - case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Manual intervention required. Asking for retry on Srb %p.\n", - DsmId, - Srb)); - - // - // This may be caused by NDU of controller firmware. It does not - // necessarily indicate that the device won't be ready via other path(s). - // Worth retrying instead of immediately failing back. - // - retry = TRUE; - handled = TRUE; - - break; - } - } - } - } - } - - } else if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): BUS_RESET. Failing back Srb %p.\n", - DsmId, - Srb)); - - // - // Upper layers will retry in this case. If we retry here it will - // have a multiplicative effect which may result in a very long - // IO completion time if the device persistently times out. - // - retry = FALSE; - handled = TRUE; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Failing request. STATUS_IO_DEVICE_ERROR. SrbStatus (%x) ScsiStatus (%x).\n", - DsmId, - Srb->SrbStatus, - scsiStatus)); - } - - break; - } - - case STATUS_BUFFER_OVERFLOW: { - - if (DsmIsReadWrite(opCode)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): BUFFER_OVERFLOW: Retry.\n", - DsmId)); - - // - // Retry these, as this condition might indicate a torn write. - // - retry = TRUE; - handled = TRUE; - } - - break; - } - - case STATUS_DEVICE_BUSY: { - - // - // See if it's a check condition for TPG states in transition. - // - if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && - scsiStatus == SCSISTAT_CHECK_CONDITION) { - - if (validSense) { - - if (senseKey == SCSI_SENSE_NOT_READY && - addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && - addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): TPG transition. Will send down async RTPG. Asking for retry on Srb %p.\n", - DsmId, - Srb)); - - // - // Indicate that a retry is necessary but without decrementing the remaining - // retries count. However, we may need to send down an STPG/RTPG also. - // And we must set PTBU to a path that is in a different TPG. - // - sendTPG = TRUE; - tpgException = TRUE; - NT_ASSERT(!devInfoException); - retry = TRUE; - handled = TRUE; - errorMask = DSM_RETRY_DONT_DECREMENT; - } - - } - } - - break; - } - - case STATUS_DEVICE_NOT_READY: { - - if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && - scsiStatus == SCSISTAT_CHECK_CONDITION) { - - if (validSense) { - if (senseKey == SCSI_SENSE_NOT_READY && - addSenseCode == SCSI_ADSENSE_LUN_NOT_READY) { - - switch (addSenseCodeQualifier) { - - case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Manual intervention required. Asking for retry on Srb %p.\n", - DsmId, - Srb)); - - // - // This may be caused by NDU of controller firmware. It does not - // necessarily indicate that the device won't be ready via other path(s). - // Worth retrying instead of immediately failing back. - // - retry = TRUE; - handled = TRUE; - - break; - } - - case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: { - // - // This indicates a logical block provisioning temporary resource exhaustion - // condition and therefore we must allow the class layer to handle it. - // - retry = FALSE; - handled = TRUE; - - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Unhandled AddQual %x.\n", - DsmId, - addSenseCodeQualifier)); - - break; - } - } - } - } - } - } - - - default: { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Unhandled status code %x.\n", - DsmId, - *Status)); - - break; - } - } - - if (!handled) { - - // - // The NTSTATUS didn't indicate a fail-over condition, but - // check various srb status for failover-class error. - // - switch (Srb->SrbStatus) { - case SRB_STATUS_SELECTION_TIMEOUT: - case SRB_STATUS_INVALID_LUN: - case SRB_STATUS_INVALID_TARGET_ID: - case SRB_STATUS_NO_DEVICE: - case SRB_STATUS_NO_HBA: - case SRB_STATUS_INVALID_PATH_ID: { - - // - // All of these are fatal. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): SrbStatus 0x%x. Will initiate fail over.\n", - DsmId, - Srb->SrbStatus)); - - failover = TRUE; - break; - } - - - default: { - - if ((scsiStatus == SCSISTAT_CHECK_CONDITION) && - (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { - - if (validSense) { - - switch (senseKey) { - - case SCSI_SENSE_NO_SENSE: { - - if (addSenseCode == SCSI_ADSENSE_NO_SENSE && - addSenseCodeQualifier == SCSI_SENSEQ_CAUSE_NOT_REPORTABLE) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): CheckCondition with no sense info. Will initiate fail over.\n", - DsmId)); - - // - // This could be a transient error generated - // in response to potentially a hardware fault. - // Worth trying another path. - // - failover = TRUE; - handled = TRUE; - } - - break; - } - - case SCSI_SENSE_ILLEGAL_REQUEST: { - - if (addSenseCode == SCSI_ADSENSE_INVALID_LUN) { - - if (addSenseCodeQualifier == 0x00) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Invalid LUN. Will initiate fail over.\n", - DsmId)); - - // - // LUN may still exist on other path(s). - // Worth a failover. - // - failover = TRUE; - handled = TRUE; - } - } - - break; - } - - case SCSI_SENSE_HARDWARE_ERROR: { - - if (addSenseCode == SPC3_SCSI_ADSENSE_LOGICAL_UNIT_COMMAND_FAILED) { - - if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_SET_TARGET_PORT_GROUPS_FAILED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): STPG failed. Will initiate fail over.\n", - DsmId)); - - // - // If an STPG failed, treat as FATAL and get another - // path set to A/O via another STPG. - // - failover = TRUE; - handled = TRUE; - } - } else if ((addSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addSenseCodeQualifier == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) || - (addSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addSenseCodeQualifier == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) { - - // - // Could potentially indicate a dropped FC packet. Retry (along another - // path, based on the LB policy). - // - retry = TRUE; - handled = TRUE; - } - - break; - } - - default: { - - break; - } - } - } - } - - if (!handled) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Unhandled SRB Status 0x%x. Sense data %x|%x|%x.\n", - DsmId, - Srb->SrbStatus, - validSense ? senseKey : 0xFF, - validSense ? addSenseCode : 0xFF, - validSense ? addSenseCodeQualifier : 0xFF)); - } - - break; - } - } - } - - if (failover) { - ULONG SpecialHandlingFlag = 0; - - // - // If ALUA is supported, then it is possible that we may need to send - // down an STPG so build an IRP and fill in the SRB for STPG and send it down. - // - if (!DsmpIsSymmetricAccess(deviceInfo)) { - - DsmpSetLBForPathFailingALUA(DsmContext, deviceInfo, TRUE, SpecialHandlingFlag); - - } else { - - // - // If device doesn't support ALUA, we just need to update - // states without sending down any commands (STPG) - // - DsmpSetLBForPathFailing(DsmContext, deviceInfo, TRUE, SpecialHandlingFlag); - } - - errorMask = DSM_FATAL_ERROR; - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmInterpretError(DevInfo %p): Device changed to state %d\n", - deviceInfo, - deviceInfo->State)); - -#if DBG - { - ULONG inx; - PDSM_GROUP_ENTRY group = deviceInfo->Group; - PDSM_DEVICE_INFO tempDevInfo; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Device %p in group %p being marked as failed. NTStatus 0x%x.\n", - DsmId, - deviceInfo, - group, - *Status)); - - for (inx = 0; inx < group->NumberDevices; inx++) { - - tempDevInfo = group->DeviceList[inx]; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Device %p at %d. State %d.\n", - DsmId, - tempDevInfo, - inx, - tempDevInfo->State)); - } - } -#endif // DBG - } - - if (retry) { - - if (sendTPG) { - - // - // If ALUA is supported, send down STPG/RTPG as appropriate. - // - if (!DsmpIsSymmetricAccess(deviceInfo)) { - - DsmpSetPathForIoRetryALUA(DsmContext, deviceInfo, tpgException, devInfoException); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_RW, - "DsmInterpretError(DevInfo %p): SRB request %p will be retried. PTBU set to %p.\n", - deviceInfo, - Srb, - deviceInfo->Group->PathToBeUsed)); - } - } - } - - - *Retry = retry; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmInterpretError (DevInfo %p): Exiting function returning errorMask %x.\n", - DsmId, - errorMask)); - - return errorMask; -} - -BOOLEAN -DsmIsAddressTypeSupported( - _In_ IN PVOID DsmContext, - _In_ IN ULONG AddressType - ) -/*++ - -Routine Description: - - This routine is called when MPIO wants to know if the DSM supports a - particular storage address type. - - This routine must be provided for DSMs of DsmType6 or higher. - -Arguments: - - DsmContext - Context value passed to DsmInitialize() - AddressType - The storage address type being queried. - -Return Value: - - TRUE - If the DSM supports the given storage address type. - FALSE - If the DSM does not support the given storage address type. - ---*/ -{ - UNREFERENCED_PARAMETER(DsmContext); - - if (AddressType == STORAGE_ADDRESS_TYPE_BTL8) - { - return TRUE; - } - - return FALSE; -} - -NTSTATUS -DsmDeviceNotUsed( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId - ) -/*++ - -Routine Description: - - This routine indicates that the device represented by DsmId will not be - initialized completely by MPIO. - The DSM_ID list passed to other functions will no longer contain DsmId, - so internal structures should be updated accordingly. - - This routine must be provided for DSMs of DsmType6 or higher. - -Arguments: - - DsmContext - Context value given to the multipath driver during registration. - DsmId - Value referring to the uninitialized device. - -Return Value: - - NTSTATUS of the operation. - ---*/ -{ - PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)DsmId; - - DSM_ASSERT(deviceInfo->Group != NULL); - DSM_ASSERT(deviceInfo->Group->GroupSig == DSM_GROUP_SIG); - - // - // Undo anything we did to build up the device in DsmInquire(). - // - DsmRemoveDevice((PDSM_CONTEXT)DsmContext, DsmId, deviceInfo->FailGroup); - - return STATUS_SUCCESS; -} - -NTSTATUS -DsmUnload( - _In_ IN PVOID DsmContext - ) -/*++ - -Routine Description: - - This routine is called when the main module requires the DSM to be unloaded - (ie. prior to the main module unload). - -Arguments: - - DsmContext - Context value passed to DsmInitialize() - -Return Value: - - STATUS_SUCCESS; - ---*/ - -{ - PVOID tempAddress = DsmContext; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmUnload (DsmCtxt %p): Entering function.\n", - DsmContext)); - - DsmpFreeDSMResources((PDSM_CONTEXT) DsmContext); - - if (gMPIOControlObjectRefd) { - - ObDereferenceObject(gMPIOControlObject); - gMPIOControlObjectRefd = FALSE; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmUnload (DsmCtxt %p): Exiting function.\n", - tempAddress)); - - // - // Stop the tracing subsystem. - // - WPP_CLEANUP(gDsmDriverObject); - - return STATUS_SUCCESS; -} - diff --git a/tests/projects/wdk/kmdf/msdsm/msdsm.h b/tests/projects/wdk/kmdf/msdsm/msdsm.h deleted file mode 100644 index e1ddeb52d..000000000 --- a/tests/projects/wdk/kmdf/msdsm/msdsm.h +++ /dev/null @@ -1,1403 +0,0 @@ -/*++ - -Copyright (C) 2004-2010 Microsoft Corporation - -Module Name: - - msdsm.h - -Abstract: - - Header for the Microsoft Device Specific Module (DSM). - -Environment: - - kernel mode only - -Notes: - ---*/ - -#ifndef _MSDSM_H_ -#define _MSDSM_H_ - -// -// Maximum number of paths per device supported by the DSM. -// This is a limit currently set by MPIO itself and needs to be updated if MPIO -// supports more paths-per-device in the future. -// -#define DSM_MAX_PATHS 32 - -// -// MPIO control object's well known symbolic name -// -#define DSM_MPIO_CONTROL_OBJECT_SYMLINK L"\\DosDevices\\MPIOControl" - -// -// Location of System class node in the registry -// -#define DSM_SYSTEM_CLASS_GUID_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\{4D36E97D-E325-11CE-BFC1-08002BE10318}" - -// -// Values used for matching and figuring out the DriverVersion -// -#define DSM_INF_PATH L"InfPath" -#define DSM_MSDSM_INF_PATH L"msdsm.inf" -#define DSM_DRIVER_VERSION L"DriverVersion" -#define DSM_DRIVER_VERSION_FIELD_DELIMITER L'.' -#define DSM_BUFFER_MAXCOUNT 64 - -// -// MSDSM's display name. -// -#define DSM_FRIENDLY_NAME L"Microsoft DSM" - -// -// Name of the value for the supported devices in the registry, found in the -// DSM's Services' Parameters key -// -#define DSM_SUPPORTED_DEVICELIST_VALUE_NAME L"DsmSupportedDeviceList" - -// -// Value used to determine if per-IO statistics gathering needs to be turned OFF -// -#define DSM_DISABLE_STATISTICS L"DsmDisableStatistics" - -// -// Names of the values in the registry for whether to use the same path for -// sequential IOs when employing Least Blocks load balance policy, as well -// as its size. -// -#define DSM_USE_CACHE_FOR_LEAST_BLOCKS L"DsmUseCacheForLeastBlocks" -#define DSM_CACHE_SIZE_FOR_LEAST_BLOCKS L"DsmCacheSizeForLeastBlocks" - -// -// Name of the value in the registry for the maximum request retry time during ALUA -// state transitions. This value is found in the DSM's Services' Parameters key, and -// applies only to Persistent Reservation commands. -// -#define DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME L"DsmMaximumStateTransitionTime" - - -// -// Default max amount of time (in seconds) that a PR failing with retry-able UA will be retried -// -#define DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME 3 - -// -// Macro to translate seconds to ticks. Each system tick is 10^(-7) seconds. -// -#define DSM_SECONDS_TO_TICKS(_Seconds) ((_Seconds) * 10000000) - -// -// Size of the buffer allocated to retrieve device serial number. -// This is as defined by SPC-3 spec. The identifier with the biggest size is -// SCSI name type (0x8). -// -#define DSM_SERIAL_NUMBER_BUFFER_SIZE 255 - -// -// Number of LB Policies that are supported by this driver. -// -#define DSM_NUMBER_OF_LB_POLICIES 6 - -// -// Size of the buffer passed to read in Persistent Reserve keys. -// -#define DSM_READ_PERSISTENT_KEYS_BUFFER_SIZE 4096 - -// -// The default threshold for sequential IO for the Least Blocks load balance -// policy is 1MB. -// -#define DSM_LEAST_BLOCKS_DEFAULT_THRESHOLD 0x00100000 - -// -// Initialization data structure that needs to be filled in for MPIO -// -DSM_INIT_DATA gDsmInitData; - -// -// Macro used to round of a number to the nearest 8 byte aligned one. -// -#ifdef AlignOn8Bytes -#undef AlignOn8Bytes -#endif -#define AlignOn8Bytes(x) (((x) + 7) & ~7) - -// -// Macro for determining minimum of two numbers -// -#ifdef MIN -#undef MIN -#endif -#define MIN(a, b) ((ULONGLONG)(a) < (ULONGLONG)(b) ? (a) : (b)) - -// -// Macro used to convert a 4 byte array to a ULONG (where byte 0 MSB, byte 3 LSB) -// -#define GetUlongFrom4ByteArray(UCharArray, ULongValue) \ - ((UNALIGNED UCHAR *)&(ULongValue))[3] = ((UNALIGNED UCHAR *)(UCharArray))[0]; \ - ((UNALIGNED UCHAR *)&(ULongValue))[2] = ((UNALIGNED UCHAR *)(UCharArray))[1]; \ - ((UNALIGNED UCHAR *)&(ULongValue))[1] = ((UNALIGNED UCHAR *)(UCharArray))[2]; \ - ((UNALIGNED UCHAR *)&(ULongValue))[0] = ((UNALIGNED UCHAR *)(UCharArray))[3]; - -// -// Macro used to convert a ULONG into a 4 byte array (as big-endian) -// -#define Get4ByteArrayFromUlong(ULongValue, UCharArray) \ - ((UNALIGNED UCHAR *)(UCharArray))[3] = ((UNALIGNED UCHAR *)&(ULongValue))[0]; \ - ((UNALIGNED UCHAR *)(UCharArray))[2] = ((UNALIGNED UCHAR *)&(ULongValue))[1]; \ - ((UNALIGNED UCHAR *)(UCharArray))[1] = ((UNALIGNED UCHAR *)&(ULongValue))[2]; \ - ((UNALIGNED UCHAR *)(UCharArray))[0] = ((UNALIGNED UCHAR *)&(ULongValue))[3]; - -// -// Macro to check if passed in opcode is a read, write -// -#define DsmIsReadRequest(_Opcode) (_Opcode == SCSIOP_READ || _Opcode == SCSIOP_READ16) -#define DsmIsWriteRequest(_Opcode) (_Opcode == SCSIOP_WRITE || _Opcode == SCSIOP_WRITE16) -#define DsmIsReadWrite(_Opcode) (_Opcode == SCSIOP_READ || _Opcode == SCSIOP_READ16 || \ - _Opcode == SCSIOP_WRITE || _Opcode == SCSIOP_WRITE16) - -#define DsmIsReadCapacity( _Opcode ) (_Opcode == SCSIOP_READ_CAPACITY || _Opcode == SCSIOP_READ_CAPACITY16) - - -// -// Macro to find the number of bytes consumed by the array -// -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - - -// -// Signature used to identify various structures. -// Used solely for debugging purposes. -// -#define DSM_DEVICE_SIG 0xAAAAAAAA -#define DSM_GROUP_SIG 0x55555555 -#define DSM_FOG_SIG 0x88888888 -#define DSM_TARGET_PORT_GROUP_SIG 0x33333333 -#define DSM_TARGET_PORT_SIG 0xCCCCCCCC -#define DSM_CONTROLLER_SIG 0xEEEEEEEE - -#define WNULL (L'\0') -#define WNULL_SIZE (sizeof(WNULL)) - -#if DBG - -// -// NT_ASSERT wrapper. -// -#define DSM_ASSERT(exp) if (DoAssert) { \ - NT_ASSERT(exp); \ - } - -#else // DBG - -#define DSM_ASSERT(exp) - -#endif // DBG - -#define DSM_PARAMETER_PATH_W L"MSDSM\\Parameters" - -// -// Pool Tags used in memory allocation -// -#define DSM_TAG_GENERIC '00ZZ' -#define DSM_TAG_PASS_THRU '10ZZ' -#define DSM_TAG_GROUP_ENTRY '20ZZ' -#define DSM_TAG_FO_GROUP '30ZZ' -#define DSM_TAG_DSM_CONTEXT '40ZZ' -#define DSM_TAG_DEV_INFO '50ZZ' -#define DSM_TAG_SERIAL_NUM '60ZZ' -#define DSM_TAG_CTRL_INFO '70ZZ' -#define DSM_TAG_SUPPORTED_DEV '80ZZ' -#define DSM_TAG_REG_PATH '90ZZ' -#define DSM_TAG_FOG_DEV_ENTRY 'A0ZZ' -#define DSM_TAG_DEV_ID 'B0ZZ' -#define DSM_TAG_DEV_NAME 'C0ZZ' -#define DSM_TAG_LB_POLICY 'D0ZZ' -#define DSM_TAG_PR_KEYS 'E0ZZ' -#define DSM_TAG_RESERVED_DEVICE 'F0ZZ' -#define DSM_TAG_BIN_TO_ASCII '01ZZ' -#define DSM_TAG_TARGET_PORT_LIST_ENTRY '11ZZ' -#define DSM_TAG_TARGET_PORT_GROUP_ENTRY '21ZZ' -#define DSM_TAG_RELATIVE_TARGET_PORT_ID '31ZZ' -#define DSM_TAG_TARGET_PORT_GROUPS '41ZZ' -#define DSM_TAG_CONTROLLER_LIST_ENTRY '51ZZ' -#define DSM_TAG_CONTROLLER_INFO '61ZZ' -#define DSM_TAG_IO_STATUS_BLOCK '71ZZ' -#define DSM_TAG_DEVICE_ID_LIST '81ZZ' -#define DSM_TAG_TP_DEVICE_LIST_ENTRY '91ZZ' -#define DSM_TAG_RETRY_RESERVE 'A1ZZ' -#define DSM_TAG_WORKITEM 'B1ZZ' -#define DSM_TAG_SCSI_ADDRESS 'C1ZZ' -#define DSM_TAG_FAIL_DEVINFO_LIST_ENTRY 'D1ZZ' -#define DSM_TAG_TPG_COMPLETION_CONTEXT 'E1ZZ' -#define DSM_TAG_SCSI_REQUEST_BLOCK 'F1ZZ' -#define DSM_TAG_SCSI_SENSE_INFO '02ZZ' -#define DSM_TAG_SPT_DATA_BUFFER '12ZZ' -#define DSM_TAG_REG_KEY_RELATED '22ZZ' -#define DSM_TAG_DEV_HARDWARE_ID '32ZZ' -#define DSM_TAG_REG_VALUE_RELATED '42ZZ' -#define DSM_TAG_ZOMBIEGROUP_ENTRY '52ZZ' -#define DSM_TAG_PERSISTENT_RESERVATION '62ZZ' - -// -// Parameters subkey name under HKLM\System\CCS\Services\MSDSM -// -#define DSM_SERVICE_PARAMETERS L"Parameters" - -// -// Load Balance settings are persisted in the registry under this key -// -#define DSM_LOAD_BALANCE_SETTINGS L"DsmLoadBalanceSettings" - -// -// Load Balance settings on a VID/PID basis are persistented in the registry -// under this key -// -#define DSM_TARGETS_LOAD_BALANCE_SETTING L"DsmTargetsLoadBalanceSetting" - -// -// Values persisted per device: -// 1. Load Balance Policy -// 2. Preferred Path -// 3. Whether LB policy has been explicitly set -// -#define DSM_LOAD_BALANCE_POLICY L"DsmLoadBalancePolicy" -#define DSM_PREFERRED_PATH L"DsmPreferredPath" -#define DSM_POLICY_EXPLICITLY_SET L"DsmLoadBalancePolicyExplicitlySet" - -// -// Prefix for subkey created for each path -// -#define DSM_PATH L"DSMPath" - -// -// Values persisted per path: -// 1. Whether primary -// 2. Whether optimized -// 3. Path weight. -// -// Primary Optimized State -//==================================== -// True True Active-Optimized -// True False Active-Unoptimized -// False True StandBy -// False False Unavailable -// -#define DSM_PRIMARY_PATH L"DsmPrimaryPath" -#define DSM_OPTIMIZED_PATH L"DsmOptimizedPath" -#define DSM_PATH_WEIGHT L"DsmPathWeight" - -// -// Indicates that device doesn't support ALUA. -// -#define DSM_DEVINFO_ALUA_NOT_SUPPORTED 0 - -// -// Implies that device supports implicit ALUA transistions. -// -#define DSM_DEVINFO_ALUA_IMPLICIT 1 - -// -// Implies that device supports explicit ALUA state transitions. -// -#define DSM_DEVINFO_ALUA_EXPLICIT 2 - -// -// Type of device identifier (VPD 0x83) -// -typedef enum _DSM_DEVID_TYPE { - DSM_DEVID_SERIAL_NUMBER = 1, - DSM_DEVID_RELATIVE_TARGET_PORT, - DSM_DEVID_TARGET_PORT_GROUP -} DSM_DEVID_TYPE, *PDSM_DEVID_TYPE; - -#define _DSM_TERNARY_BOOLEAN UCHAR -typedef _DSM_TERNARY_BOOLEAN DSM_TERNARY_BOOLEAN, *PDSM_TERNARY_BOOLEAN; -#define DSM_TERNARY_UNKNOWN 0 -#define DSM_TERNARY_TRUE 1 -#define DSM_TERNARY_FALSE 2 - -// -// Macro to determine if _Id2 is more preferred than _Id1 to build a device's -// serial number. -// -#define DsmpIsPreferredDeviceId(_Id1, _Id2) (((_Id2) == StorageIdTypeScsiNameString) || \ - ((_Id2) == StorageIdTypeFCPHName && (_Id1) != StorageIdTypeScsiNameString) || \ - ((_Id2) == StorageIdTypeEUI64 && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName) || \ - ((_Id2) == StorageIdTypeVendorId && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName && (_Id1) != StorageIdTypeEUI64) || \ - ((_Id2) == StorageIdTypeVendorSpecific && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName && (_Id1) != StorageIdTypeEUI64 && (_Id1) != StorageIdTypeVendorId)) - -// -// Device State -// -typedef enum _DSM_DEVICE_STATE { - - // - // If ALUA is not supported, this state indicates that the device is active - // and a request can be sent to the device. - // If ALUA is supported, then this state indicates optimizied device-path - // pair for the device. - // - DSM_DEV_ACTIVE_OPTIMIZED = 0, - - // - // If ALUA is not supported, this state is not used. - // If ALUA is supported, then this state indicates active but unoptimized - // device-path pairing for the device. Can be used in in case no - // active/optimized path is available to service the IO. - // - DSM_DEV_ACTIVE_UNOPTIMIZED, - - // - // If ALUA is not supported, this state indicates that the device is in - // standby state. A request can be sent to the device in this state. - // If ALUA is supported, then this state indicates standby device-path - // pairing and only certain requests can be handled in this state. - // - DSM_DEV_STANDBY, - - // - // If ALUA is not supported, this state is not used. - // If ALUA is supported, then this state indicates that the device-path pairing - // is not active and incapable of handling any requests. - // - DSM_DEV_UNAVAILABLE, - - // - // If ALUA is not supported, this state is not used. - // If ALUA is supported, then this state indicates that the device-path pairing - // (actually its TPG) is in a transitioning state. - // - DSM_DEV_TRANSITIONING = 15, - - // - // Initial state when devInfo is created. - // - DSM_DEV_NOT_USED_STATE = 16, - - // - // Indicates that the state was undetermined (this is applicable only for - // a deviceInfo's DesiredState or if the device instance's path was not - // determined). - // - DSM_DEV_UNDETERMINED, - - // - // Indicates that a request sent down previously failed with a fatal error - // - DSM_DEV_FAILED, - - // - // Indicates that InvalidatePath has been called - // - DSM_DEV_INVALIDATED, - - // - // This indicates the device is about to be removed. No new request - // should be sent to the device. - // - DSM_DEV_REMOVE_PENDING, - - // - // This indicates the device has been removed. - // - DSM_DEV_REMOVED - -} DSM_DEVICE_STATE, *PDSM_DEVICE_STATE; - -// -// Device states supported -// -#define DSM_STATE_ACTIVE_OPTIMIZED_SUPPORTED 0 -#define DSM_STATE_STANDBY_SUPPORTED 1 -#define DSM_STATE_ACTIVE_UNOPTIMIZED_SUPPORTED 2 -#define DSM_STATE_UNAVAILABLE_SUPPORTED 4 - - -// -// Macro to determine if devInfo is in a failure state. -// -#define DsmpIsDeviceFailedState(_State) ((_State) > DSM_DEV_NOT_USED_STATE) - -// -// Macro to determine if devInfo was initialized. -// -#define DsmpIsDeviceInitialized(_DeviceInfo) ((_DeviceInfo)->Initialized) - -// -// Macro to determine if device is "usable" (ie. IsPathActive was successfully called). -// -#define DsmpIsDeviceUsable(_DeviceInfo) ((_DeviceInfo)->Usable) - -// -// Macro to determine if devInfo was used to send down registration. -// It the devInfo's group is not reserved, then the devInfo doesn't need to have -// had a register go down it. -// It the group is reserved, then the devInfo MUST have had a register go down it -// for it to be used. -// -#define DsmpIsDeviceUsablePR(_DeviceInfo) (!(_DeviceInfo)->Group->PRKeyValid || (_DeviceInfo)->PRKeyRegistered) - - -// -// Macro to determine if _State2 is a more preferred state than _State1. -// -#define DsmpIsBetterDeviceState(_State1, _State2) (((_State1) == DSM_DEV_STANDBY && (_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED) || \ - ((_State1) == DSM_DEV_UNAVAILABLE && \ - ((_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED || (_State2) == DSM_DEV_STANDBY)) || \ - ((_State1) == DSM_DEV_TRANSITIONING && \ - ((_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED || (_State2) == DSM_DEV_STANDBY) || (_State2) == DSM_DEV_UNAVAILABLE)) - -// -// Macro to determine if passed in _State is active. -// -#define DsmpIsDeviceStateActive(_State) ((_State) == DSM_DEV_ACTIVE_OPTIMIZED || (_State) == DSM_DEV_ACTIVE_UNOPTIMIZED) - -// -// Macro to determine if symmetric access to the storage -// -#define DsmpIsSymmetricAccess(_DeviceInfo) ((_DeviceInfo)->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED || \ - ((_DeviceInfo)->ALUASupport == DSM_DEVINFO_ALUA_IMPLICIT && \ - (_DeviceInfo)->Group->Symmetric)) - -// -// Multi-path Group State -// -typedef enum _DSM_GROUP_STATE { - - // - // This indicates that the device is in working state. - // - DSM_GP_NORMAL = 1, - - // - // This indicates that there is a pending reservation failover - // - DSM_GP_PENDING, - - // - // This indicates that the device has lost all its paths - // - DSM_GP_FAILED - -} DSM_GROUP_STATE, *PDSM_GROUP_STATE; - -// -// Fail-Over Group State -// -typedef enum _DSM_FAILOVER_GROUP_STATE { - - // - // This indicates that the path is in working state. - // - DSM_FG_NORMAL = 1, - - // - // This indicates the path which had failed earlier - // is back to working state now. - // - DSM_FG_FAILBACK, - - // - // This indicates the path is about to be removed - // - DSM_FG_PENDING_REMOVE, - - // - // This indicates the path has failed. - // - DSM_FG_FAILED - -} DSM_FAILOVER_GROUP_STATE, *PDSM_FAILOVER_GROUP_STATE; - -#define DsmpIsPathFailedState(_State) ((_State) >= DSM_FG_PENDING_REMOVE) - -// -// DSM Context is the global driver context that gets passed to each of the DSM -// entry points. -// -// The DSM Context will maintain a list of all DeviceInfos (device-path pairing). -// It will maintain a list of Group entries. Each entry in the Group list will -// represent a LUN's different instances down different paths (i.e. DeviceInfos). -// Each entry in the Group will maintain a list of target port groups. -// Each entry in the target port group list will maintain a list of target -// ports that make up the target port group. Every deviceInfo that isn't -// in a failure state will be in the same state as the Asymmetric Access -// State of the target port group. -// There will be a list of Fail Over Group entries, where each entry represents -// the list of devices that fail over as a group (i.e. devices on the same path). -// There will also be a list of controller entries, representing the controllers -// on all storages connected to the system. -// -typedef struct _DSM_CONTEXT { - - // - // Used to synchronize access to the SupportedDevices list. - // - KSPIN_LOCK SupportedDevicesListLock; - - // - // List of supported devices - added into the INF. - // - UNICODE_STRING SupportedDevices; - - // - // Used to synchronize access to the elements in this structure. - // - EX_SPIN_LOCK DsmContextLock; - - // - // Flag cached that indicates if statistics don't need to be gathered - // - BOOLEAN DisableStatsGathering; - - UCHAR Reserved[3]; - - // - // Number of devices currently found. - // - ULONG NumberDevices; - - // - // List of devices. - // - LIST_ENTRY DeviceList; - - // - // Number of multi-path groups. - // - ULONG NumberGroups; - - // - // List of multi-path groups. - // - LIST_ENTRY GroupList; - - // - // Number of fail-over groups. - // - ULONG NumberFOGroups; - - // - // List of fail-over groups. - // - LIST_ENTRY FailGroupList; - - // - // Number of controllers. - // - ULONG NumberControllers; - - // - // List of controllers - // - LIST_ENTRY ControllerList; - - // - // Number of stale fail-over groups - // - ULONG NumberStaleFOGroups; - - // - // List of stale fail-over groups maintained for paths for which all devices - // have gotten removed but for which there is still outstanding IO-statistics - // - LIST_ENTRY StaleFailGroupList; - - // - // Context value passed to the DSM from MPIO. - // - PVOID MPIOContext; - - - // - // Look-aside list of completion routine context structures. - // - NPAGED_LOOKASIDE_LIST CompletionContextList; - -} DSM_CONTEXT, *PDSM_CONTEXT; - -// -// Statistics structure. Used by the device and path routines. -// -typedef struct _DSM_STATS { - - ULONG NumberReads; - ULONG NumberWrites; - ULONGLONG BytesRead; - ULONGLONG BytesWritten; - -} DSM_STATS, *PDSM_STATS; - - -// -// Information about each device that is supported by the DSM. -// -typedef struct _DSM_DEVICE_INFO { - - // - // To link to the next device info structure in the list - // - LIST_ENTRY ListEntry; - - // - // The device SIG. Used for debug. - // - ULONG DeviceSig; - - // - // Back-pointer to the DSM_CONTEXT. - // - PVOID DsmContext; - - // - // The underlying port driver PDO. - // - PDEVICE_OBJECT PortPdo; - - // - // The port FDO to which PortPdo is attached. - // - PDEVICE_OBJECT PortFdo; - - // - // The DeviceObject to which I/Os generated by the DSM should - // be sent. This is given to us by MPIO. - // - PDEVICE_OBJECT TargetObject; - - // - // The multi-path group to which this device belongs. - // - struct _DSM_GROUP_ENTRY *Group; - - // - // The fail-over group to which this device belongs. - // - struct _DSM_FAILOVER_GROUP *FailGroup; - - // - // The controller through which this device showed up. - // - struct _DSM_CONTROLLER_LIST_ENTRY *Controller; - - // - // The Target Port Group that this device belongs to. - // - struct _DSM_TARGET_PORT_GROUP_ENTRY *TargetPortGroup; - - // - // The Target Port that this device was exposed via. - // - struct _DSM_TARGET_PORT_LIST_ENTRY *TargetPort; - - // - // The current state of this device: ACTIVE_O, ACTIVE_U, STANDBY, UNAVAILABLE, etc. - // - DSM_DEVICE_STATE State; - - // - // Previous state of this device. Updated whenever this deviceInfo makes a - // state transition. - // - DSM_DEVICE_STATE PreviousState; - - // - // The desired state of this device: based on PrimaryPath and OptimizedPath - // specified in the registry. - // - DSM_DEVICE_STATE DesiredState; - - // - // The ALUA state of the TPG immediately after a ReportTPG is issued. - // - DSM_DEVICE_STATE ALUAState; - - // - // Holds state information temporarily while applying LB policy. Used in case - // changes need to be reverted in case of failure to apply the policy. - // - DSM_DEVICE_STATE TempPreviousStateForLB; - - // - // This is to save off the last known non-failed state. - // In case of an error down this deviceInfo, it is marked to be in Failed state. - // However, if no remove comes down for this device and a PathVerify down this - // deviceInfo succeeds, we need to put the deviceInfo back into a usable state. - // - DSM_DEVICE_STATE LastKnownGoodState; - - // - // This counter indicates that this deviceInfo is being used and a remove - // must thus wait until the counter falls to 0. - // - LONG BlockRemove; - - - // - // This indicates whether this device has handled a register/register_ignore_existing request, - // irrespective of the actual status of the operation. - // - BOOLEAN RegisterServiced; - - // - // This flag is set when a register/register_ignore_existing succeeds down this device-path pair. - // - BOOLEAN PRKeyRegistered; - - // - // Indicates whether the serial number was embedded in the device - // descriptor, or it was allocated. - // - BOOLEAN SerialNumberAllocated; - - // - // Flag to indicate that SetDeviceInfo has been called (and succeeded) on this device - // - BOOLEAN Initialized; - - // - // Flag to indicate that IsPathActive has been called (and succeeded) on this device. - // - BOOLEAN Usable; - - // - // Flag to indicate if IALUAE was disabled (via mode select) - // - BOOLEAN ImplicitDisabled; - - // - // Flag to indicate that RTPG has already been sent down in Inquire, so - // PathVerify can ignore sending down one more if it is called during - // device initialization. - // - BOOLEAN IgnorePathVerify; - - // - // Bit map indicating whether (and what kind) of ALUA support. - // - UCHAR ALUASupport; - - // - // Weight assigned to this path by management application. This is used - // when doing Load Balancing based on weighted paths. - // - ULONG PathWeight; - - // - // Number of requests outstanding on this device. - // - LONG NumberOfRequestsInProgress; - - // - // I/O, Fail-Over statistics. - // - DSM_STATS DeviceStats; - - // - // The device's serial number. - // - PSTR SerialNumber; - - // - // The scsi address of the port pdo. - // - PSCSI_ADDRESS ScsiAddress; - - - // - // Kernel structure that describes this device. Passed in to Inquire. - // - // NOTE: Descriptor should be the LAST field in this structure - // - STORAGE_DEVICE_DESCRIPTOR Descriptor; - -} DSM_DEVICE_INFO, *PDSM_DEVICE_INFO; - -typedef enum _DSM_DEFAULT_LB_POLICY_TYPE { - DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY = 0, // DSM assigned based on LUN access capability - DSM_DEFAULT_LB_POLICY_DSM_WIDE, // Admin has set a DSM-wide default policy - DSM_DEFAULT_LB_POLICY_VID_PID, // Admin has set a default policy for LUN's VID/PID - DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT // Admin has explicitly set the policy on the LUN -} DSM_DEFAULT_LB_POLICY_TYPE, *PDSM_DEFAULT_LB_POLICY_TYPE; - -typedef ULONG DSM_LOAD_BALANCE_TYPE, *PDSM_LOAD_BALANCE_TYPE; - - -// -// Information about multi-path groups: The same device found via multiple paths -// are put under one group. Each group will have it's own Load Balance policy -// settings. In other words, Load Balance policy settings are on per-device basis. -// -typedef struct _DSM_GROUP_ENTRY { - - // - // To link to the next entry in the multi-path group. - // - LIST_ENTRY ListEntry; - - // - // Group signature. Used for debug. - // - ULONG GroupSig; - - // - // Ordinal of creation. Never decremented. - // - ULONG GroupNumber; - - // - // State of the group. - // - DSM_GROUP_STATE State; - - // - // Number of devices in the multi-path group. - // - ULONG NumberDevices; - - // - // Array of devices belonging to this group. - // - PDSM_DEVICE_INFO DeviceList[DSM_MAX_PATHS]; - - // - // Max time to retry failed PR requests - // - ULONG MaxPRRetryTimeDuringStateTransition; - - // - // Number of target port groups that this device is accessible via. - // - ULONG NumberTargetPortGroups; - - // - // Array of the target port groups that this LUN belongs in. - // - struct _DSM_TARGET_PORT_GROUP_ENTRY *TargetPortGroupList[DSM_MAX_PATHS]; - - // - // Key used in Persistent Reserve\Release. This key is provided to the DSM - // by Cluster service. If cluster service has provided the key PRKeyValid - // is set to TRUE. PRKeyValid is set to FALSE otherwise. - // PRServiceAction, PRType and PRScope are the service action, type and - // scope associated with the PR registration. - // - UCHAR PersistentReservationRegisteredKey[8]; - UCHAR PRServiceAction; - UCHAR PRType; - UCHAR PRScope; - UCHAR PRKeyValid; - - - // - // Flag used to denote that LU access is symmetric down all paths - // - BOOLEAN Symmetric; - - // - // Flag to indicate whether or not to use same path for sequential IO - // when employing Least Blocks load balance policy. - // - BOOLEAN UseCacheForLeastBlocks; - - // - // Flag used to indicate if a throttle request succeeded. - // - ULONG Throttled; - - // - // Counter to track the number of RTPG in flight. - // - ULONG InFlightRTPG; - - // - // A bitmask of which devices are currently reserved. - // - ULONG ReservationList; - - // - // Which type of Load Balancing is being performed. - // - DSM_LOAD_BALANCE_TYPE LoadBalanceType; - - // - // Indicates how the Load Balancing policy was selected. - // - DSM_DEFAULT_LB_POLICY_TYPE LBPolicySelection; - - // - // The path to use when possible - if in F.O. Only, if failover had taken - // place and this path comes back online, failback to this path will take - // place. - // - ULONGLONG PreferredPath; - - // - // The path to choose when Round Robin Load Balance policy is in use - // - PVOID PathToBeUsed; - - // - // Size of cache set by Admin. Used in case of handling sequential - // IO in Least Blocks policy. - // - ULONGLONG CacheSizeForLeastBlocks; - - // - // The HardwareId (VID/PID) of the LUN - // - PWSTR HardwareId; - - // - // The registry key under which Load Balance Policy settings - // are stored in the registry for this Device Group. - // - PWSTR RegistryKeyName; - - // - // Number of failing deviceInfos - // - ULONG NumberFailingDevInfos; - - // - // To link the list of failed A/O devInfos and the corresponding non-A/O - // devInfos that are temporarily being used to service IO until STPG can - // properly update the device states. This is applicable only for ALUA - // devices. - // - LIST_ENTRY FailingDevInfoList; - - // - // General Purpose Event. - // - KEVENT Event; - -} DSM_GROUP_ENTRY, *PDSM_GROUP_ENTRY; - -// -// The collection of devices on one path. These fail-over as a unit. -// A path is considered an I_T nexus, i.e. Initiator port to Target (controller) port. -// -typedef struct _DSM_FAILOVER_GROUP { - - // - // To link to the next entry in the failover group - // - LIST_ENTRY ListEntry; - - // - // Signature. Used for debug. - // - ULONG FailOverSig; - - // - // State of the Path. - // - DSM_FAILOVER_GROUP_STATE State; - - // - // The pathId corresponding to this FOG. It may or may not be - // the same as what MPIO gave us as the default value. - // - PVOID PathId; - - // - // The default pathId (port FDO). - // - PDEVICE_OBJECT MPIOPath; - - // - // Last LBA - // - ULONGLONG LastLba; - - // - // Cumulative outstanding IO (in terms of size) - // - ULONGLONG OutstandingBytesOfIO; - - // - // Count of inflight IOs. This will be used in LQD load balance policy. - // - volatile LONG NumberOfRequestsInFlight; - - // - // Number of devices in this FOG. - // - ULONG Count; - - // - // List of devices that will over together. - // - LIST_ENTRY FOG_DeviceList; - - // - // List of zombie groups (in case a device is removed before the failover - // processing begins). - // - LIST_ENTRY ZombieGroupList; - -} DSM_FAILOVER_GROUP, *PDSM_FAILOVER_GROUP; - - -// -// Information about a target port group entry for a given LUN. -// Note: This is not a global list of all TPGs that are built. It is local to a Group entry. -// -typedef struct _DSM_TARGET_PORT_GROUP_ENTRY { - - // - // Signature. Used for debug. - // - ULONG TargetPortGroupSig; - - // - // The asymmetric access state for this target port group: - // ACTIVE_O, ACTIVE_U, STANDBY or UNAVAILABLE - // - DSM_DEVICE_STATE AsymmetricAccessState; - - // - // Flag to indicate if this is the preferred target port group. - // - BOOLEAN Preferred; - - // - // Supported access states - // - BOOLEAN ActiveOptimizedSupported; - BOOLEAN ActiveUnoptimizedSupported; - BOOLEAN StandBySupported; - BOOLEAN UnavailableSupported; - - // - // Indicates if the device reports asymmetric state as being under transition. - // - BOOLEAN TransitioningSupported; - - // - // Flag to indicate if this has been returned in any subsequent RTPG after - // it is initially built. (If this flag is not set after parsing the RTPG - // information, it indicates that this TPG entry is stale and should be - // deleted). - // - BOOLEAN Traversed; - - UCHAR Reserved; - - // - // The target group identifier - // - USHORT Identifier; - - // - // Status code - // - UCHAR StatusCode; - - // - // Vendor unique - // - UCHAR VendorUnique; - - // - // Backpointer to owning group - // - PDSM_GROUP_ENTRY Group; - - // - // Number of target ports that make up this group - // - ULONG NumberTargetPorts; - - // - // Linked list of target ports that make up this target port group. - // - LIST_ENTRY TargetPortList; - -} DSM_TARGET_PORT_GROUP_ENTRY, *PDSM_TARGET_PORT_GROUP_ENTRY; - - -// -// Information about each target port list entry for a given target port group. -// Note: this is not a global list of all TPs. It is local to a given TPG entry. -// -typedef struct _DSM_TARGET_PORT_LIST_ENTRY { - - // - // Link - // - LIST_ENTRY ListEntry; - - // - // Signature. Used for debug. - // - ULONG TargetPortSig; - - // - // Relative target port identifier - // - ULONG Identifier; - - // - // Backpointer to owning target port group - // - PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup; - - // - // Number of device instances exposed via this target port - // - ULONG Count; - - // - // List of device instances exposed via this target port - // - LIST_ENTRY TP_DeviceList; - -} DSM_TARGET_PORT_LIST_ENTRY, *PDSM_TARGET_PORT_LIST_ENTRY; - -// -// Information about each controller entry -// -typedef struct _DSM_CONTROLLER_LIST_ENTRY { - - // - // To link to the next contoller entry. - // - LIST_ENTRY ListEntry; - - // - // It's signature. Used for debug. - // - ULONG ControllerSig; - - // - // Device object (this controller's PDO). - // - PDEVICE_OBJECT DeviceObject; - - // - // Port FDO through which this controller object was exposed. - // - PDEVICE_OBJECT PortObject; - - // - // Identifier. - // - _Field_size_(IdLength) PUCHAR Identifier; - - // - // Identifier length. - // - ULONG IdLength; - - // - // Identifier code set. - // - STORAGE_IDENTIFIER_CODE_SET IdCodeSet; - - // - // Controller's SCSI address. - // - PSCSI_ADDRESS ScsiAddress; - - // - // Number of references to this entry. - // - UCHAR RefCount; - - // - // Flag to indicate whether this is a fake entry built for storage that do - // NOT have controllers - // - BOOLEAN IsFakeController; - - UCHAR Reserved[2]; - -} DSM_CONTROLLER_LIST_ENTRY, *PDSM_CONTROLLER_LIST_ENTRY; - -// -// Generic linked list of devices -// -typedef struct _DSM_DEVICELIST_ENTRY { - - // - // To link to the next device info structure in the list - // - LIST_ENTRY ListEntry; - - // - // Representation of device-path pair - // - PDSM_DEVICE_INFO DeviceInfo; - -} DSM_DEVICELIST_ENTRY, *PDSM_DEVICELIST_ENTRY; - -// -// Zombie Group List Entry -// -typedef struct _DSM_ZOMBIEGROUP_ENTRY { - - // - // To link to the next zombie group structure in the list - // - LIST_ENTRY ListEntry; - - // - // Pointer to actual group entry - // - PDSM_GROUP_ENTRY Group; - - // - // Flag to indicate that the failover thread has processed this entry. - // - BOOLEAN Processed; - -} DSM_ZOMBIEGROUP_ENTRY, *PDSM_ZOMBIEGROUP_ENTRY; - -// -// Linked list of devices that will failover as a group -// -typedef DSM_DEVICELIST_ENTRY DSM_FOG_DEVICELIST_ENTRY, *PDSM_FOG_DEVICELIST_ENTRY; - -// -// Linked list of the same device being exposed off of a particular target port -// (possibly because the controller is connected to multiple HBAs). -// -typedef DSM_DEVICELIST_ENTRY DSM_TARGET_PORT_DEVICELIST_ENTRY, *PDSM_TARGET_PORT_DEVICELIST_ENTRY; - -// -// Information about each failing devInfo and its corresponding devInfo -// being used temporarily to service requests until STPG can update new -// device states. -// -typedef struct _DSM_FAIL_PATH_PROCESSING_LIST_ENTRY { - - // - // To link to the next device info structure in the list - // - LIST_ENTRY ListEntry; - - // - // Representation of the failing device-path pair - // - PDSM_DEVICE_INFO FailingDeviceInfo; - - // - // Representation of the new candidate device-path pair that will take over - // processing of requests - // - PDSM_DEVICE_INFO TempDeviceInfo; - -} DSM_FAIL_PATH_PROCESSING_LIST_ENTRY, *PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY; - -// -// Completion context structure. -// -typedef struct _DSM_COMPLETION_CONTEXT { - - // - // The device that handled the request. - // - PDSM_DEVICE_INFO DeviceInfo; - - // - // The global context. - // - PDSM_CONTEXT DsmContext; - - // - // These are used to store control code, pointer to KEVENT, etc. - // - PVOID RequestUnique1; - - ULONG_PTR RequestUnique2; - -#if DBG - // - // Request time-stamp. - // - LARGE_INTEGER TickCount; -#endif - -} DSM_COMPLETION_CONTEXT, *PDSM_COMPLETION_CONTEXT; - -// -// Completion context structure for report/set target port groups. -// -typedef struct _DSM_TPG_COMPLETION_CONTEXT { - - PDSM_COMPLETION_CONTEXT CompletionContext; - - PSCSI_REQUEST_BLOCK Srb; - - PVOID SenseInfoBuffer; - - ULONG NumberRetries; - - UCHAR SenseInfoBufferLength; - -} DSM_TPG_COMPLETION_CONTEXT, *PDSM_TPG_COMPLETION_CONTEXT; - -// -// Version number used to determine whice version of MPIO_DSM_Path to use. -// -#define DSM_WMI_VERSION_1 1 -#define DSM_WMI_VERSION_2 2 - -// -// Version of MPIO_DSM_Path that is currently supported by this DSM. -// -#define DSM_WMI_VERSION DSM_WMI_VERSION_2 - -// -// This struct is used to save Load Balance Policy Settings in the registry -// -typedef struct _DSM_LOAD_BALANCE_POLICY_SETTINGS { - - WCHAR RegistryKeyName[256]; - ULONG LoadBalancePolicy; - ULONG PathCount; - MPIO_DSM_Path_V2 DsmPath[1]; - -} DSM_LOAD_BALANCE_POLICY_SETTINGS, *PDSM_LOAD_BALANCE_POLICY_SETTINGS; - -// -// This structure is used to pass in information used by the workitem -// to failover reservations down another path. -// -typedef struct _DSM_RETRY_RESERVE { - - PDSM_COMPLETION_CONTEXT CompletionContext; - - PIRP Irp; - - PKEVENT Event; - -} DSM_RETRY_RESERVE, *PDSM_RETRY_RESERVE; - -// -// This structure defines the workitem that will be used to handle reservation -// failover. -// -typedef struct _DSM_WORKITEM { - - // - // Work item that should be freed by the worker routine - // - PIO_WORKITEM WorkItem; - - // - // Context to be passed to worker routine - // - PVOID Context; - -} DSM_WORKITEM, *PDSM_WORKITEM; - -#endif // _MSDSM_H - - diff --git a/tests/projects/wdk/kmdf/msdsm/msdsm.mof b/tests/projects/wdk/kmdf/msdsm/msdsm.mof deleted file mode 100644 index 53ee61392..000000000 --- a/tests/projects/wdk/kmdf/msdsm/msdsm.mof +++ /dev/null @@ -1,82 +0,0 @@ -// -// Copyright (C) 2004 Microsoft Corporation -// -// -// Microsoft DSM's internal classes -// - -// -// Perf class. -// -[WMI, - guid("{a34d03ec-6b0b-46a1-9178-82525f41133f}")] -class MSDSM_DEVICEPATH_PERF -{ - [WmiDataId(1), - Description("Path Identifier.") : amended - ] uint64 PathId; - - [WmiDataId(2), - Description("Number of Read Requests.") : amended - ] uint32 NumberReads; - - [WmiDataId(3), - Description("Number of Write Requests.") : amended - ] uint32 NumberWrites; - - [WmiDataId(4), - Description("Total Bytes Read.") : amended - ] uint64 BytesRead; - - [WmiDataId(5), - Description("Total Bytes Written.") : amended - ] uint64 BytesWritten; -}; - -[WMI, - Dynamic, - Provider("WmiProv"), - Description("Retrieve MSDSM Performance Information.") : amended, - Locale("MS\\0x409"), - guid("{875b8871-4889-4114-93f6-cd064c001cea}")] -class MSDSM_DEVICE_PERF -{ - [key, read] - string InstanceName; - [read] boolean Active; - - [WmiDataId(1), - read, - Description("Number of paths.") : amended - ] uint32 NumberPaths; - - [WmiDataId(2), - read, - Description("Array of Performance Information per path for the device.") : amended, - WmiSizeIs("NumberPaths") - ] MSDSM_DEVICEPATH_PERF PerfInfo[]; -}; - -// -// Methods -// Clear perf counters. -// -[Dynamic, - Provider("WMIProv"), - WMI, - Description("MSDSM WMI Methods") : amended, - guid("{04517f7e-92bb-4ebe-aed0-54339fa5f544}"), - locale("MS\\0x409") -] -class MSDSM_WMI_METHODS -{ - - [key, read] - string InstanceName; - [read] boolean Active; - - [WmiMethodId(1), - Implemented, - Description("Clear path performance counters for the device.") : amended - ] void MSDsmClearCounters(); -}; diff --git a/tests/projects/wdk/kmdf/msdsm/msdsm.rc b/tests/projects/wdk/kmdf/msdsm/msdsm.rc deleted file mode 100644 index 743640342..000000000 --- a/tests/projects/wdk/kmdf/msdsm/msdsm.rc +++ /dev/null @@ -1,24 +0,0 @@ -//+------------------------------------------------------------------------- -// -// Microsoft Windows -// -// Copyright (C) Microsoft Corporation, 2004 -// -// File: msdsm.rc -// -//-------------------------------------------------------------------------- - -#include - -#include - -#define VER_FILETYPE VFT_DRV -#define VER_FILESUBTYPE VFT2_DRV_SYSTEM -#define VER_FILEDESCRIPTION_STR "Microsoft Device Specific Module" -#define VER_INTERNALNAME_STR "msdsm.sys" -#define VER_ORIGINALFILENAME_STR "msdsm.sys" - -#include "common.ver" - -MofResourceName MOFDATA msdsm.bmf -DsmMofResourceName MOFDATA msdsmdsm.bmf diff --git a/tests/projects/wdk/kmdf/msdsm/msdsmdsm.mof b/tests/projects/wdk/kmdf/msdsm/msdsmdsm.mof deleted file mode 100644 index f85eea270..000000000 --- a/tests/projects/wdk/kmdf/msdsm/msdsmdsm.mof +++ /dev/null @@ -1,141 +0,0 @@ -// -// Copyright (C) 2004 Microsoft Corporation -// -// Microsoft DSM's DSM-specific classes -// - -// -// Class used for retrieving and setting MSDSM-wide default load balance policy. -// -[WMI, - Dynamic, - Provider("WmiProv"), - Description("MSDSM-wide default load balance policies.") : amended, - Locale("MS\\0x409"), - guid("{c81b5681-f3ca-4c98-9325-707d0d62ffc4}")] -class MSDSM_DEFAULT_LOAD_BALANCE_POLICY -{ - [key, read] - string InstanceName; - [read] boolean Active; - - [WmiDataId(1), - read, write, - Description("Load Balance Policy to be applied to devices controlled by MSDSM.") : amended - ] uint32 LoadBalancePolicy; - - [WmiDataId(2), - read, - Description("Reserved.") : amended - ] uint32 Reserved; - - // - // Preferred path. - // - [WmiDataId(3), - read, write, - Description("Preferred Path.") : amended - ] uint64 PreferredPath; -}; - -// -// Embedded class that describes a target and the default load balance policy -// of its LUNs. -// -[WMI, - guid("{ddb00a72-0fab-418b-a89e-97370ae293a4}")] -class MSDSM_TARGET_DEFAULT_POLICY_INFO -{ - // - // VID-PID string as an 8 + 16 character concatenated string. - // Spaces should be used to make the VID 8 chars and the PID 16 chars. - // - [WmiDataId(1), - MaxLen(31), - Description("Concatenated VendorID (8 characters) and ProductID (16 characters).") : amended - ] string HardwareId; - - // - // The default load balance policy to be applied to LUNs from the target - // whose hardware id matches the VID/PID above. - // NOTE: Setting this to 0 will act as removal of default setting for this - // target. - // - [WmiDataId(2)] uint32 LoadBalancePolicy; - - // - // Used for alignment reasons. - // - [WmiDataId(3)] uint32 Reserved; - - // - // Preferred path. - // - [WmiDataId(4)] uint64 PreferredPath; -}; - -// -// Class used for retrieving and setting target-level default load balance policy. -// -[WMI, - Dynamic, - Provider("WmiProv"), - Description("Target-level default load balance policies.") : amended, - Locale("MS\\0x409"), - guid("{5ccbcd91-1b56-4327-a2f3-0960335f8846}")] -class MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY -{ - [key, read] - string InstanceName; - [read] boolean Active; - - [WmiDataId(1), - read, write, - Description("Number of targets specified.") : amended - ] uint32 NumberDevices; - - [WmiDataId(2), - read, - Description("Reserved.") : amended - ] uint32 Reserved; - - [WmiDataId(3), - read, write, - MaxLen(31), - Description("Array of target hardware identifiers with policy and preferred path information.") : amended, - WmiSizeIs("NumberDevices") - ] MSDSM_TARGET_DEFAULT_POLICY_INFO TargetDefaultPolicyInfo[]; -}; - -// -// Supported devices list class. -// -[WMI, - Dynamic, - Provider("WmiProv"), - Description("Retrieve MSDSM's supported devices list.") : amended, - Locale("MS\\0x409"), - guid("{c362d67c-371e-44d8-8bba-044619e4f245}")] -class MSDSM_SUPPORTED_DEVICES_LIST -{ - [key, read] - string InstanceName; - [read] boolean Active; - - [WmiDataId(1), - read, - Description("Number of supported devices.") : amended - ] uint32 NumberDevices; - - [WmiDataId(2), - read, - Description("Reserved.") : amended - ] uint32 Reserved; - - [WmiDataId(3), - read, - MaxLen(31), - Description("Array of device hardware identifiers.") : amended, - WmiSizeIs("NumberDevices") - ] string DeviceId[]; -}; diff --git a/tests/projects/wdk/kmdf/msdsm/precomp.h b/tests/projects/wdk/kmdf/msdsm/precomp.h deleted file mode 100644 index bca8595db..000000000 --- a/tests/projects/wdk/kmdf/msdsm/precomp.h +++ /dev/null @@ -1,34 +0,0 @@ - -/*++ - -Copyright (c) 2004 Microsoft Corporation - -Module Name: - - precomp.h - -Abstract: - - Precompiled header file for Microsoft Device Specific Module (DSM). - -Revision History: - ---*/ - -#pragma once - -#define DEBUG_MAIN_SOURCE 1 - -#include -#include - -#include "dsm.h" -#include "mpiodisk.h" -#include "msdsm.h" -#include "prototypes.h" -#include "trace.h" -#include "srbhelper.h" - -#include -#include - diff --git a/tests/projects/wdk/kmdf/msdsm/precompsrc.c b/tests/projects/wdk/kmdf/msdsm/precompsrc.c deleted file mode 100644 index 5944cf515..000000000 --- a/tests/projects/wdk/kmdf/msdsm/precompsrc.c +++ /dev/null @@ -1 +0,0 @@ -#include "precomp.h" \ No newline at end of file diff --git a/tests/projects/wdk/kmdf/msdsm/prototypes.h b/tests/projects/wdk/kmdf/msdsm/prototypes.h deleted file mode 100644 index ebcf42029..000000000 --- a/tests/projects/wdk/kmdf/msdsm/prototypes.h +++ /dev/null @@ -1,1436 +0,0 @@ - -/*++ - -Copyright (C) 2004 Microsoft Corporation - -Module Name: - - prototypes.h - -Abstract: - - Contains function prototypes for all the functions defined - by Microsoft Device Specific Module (DSM). - -Environment: - - kernel mode only - -Notes: - ---*/ - -#pragma warning (disable:4214) // bit field usage -#pragma warning (disable:4200) // zero-sized array - -#ifndef _PROTOTYPES_H_ -#define _PROTOTYPES_H_ - -#define DSM_VENDOR_ID_LEN 8 -#define DSM_PRODUCT_ID_LEN 16 -#define DSM_VENDPROD_ID_LEN 24 - -// -// In accordance with SPC-3 specs -// -#define SPC3_TARGET_PORT_GROUPS_HEADER_SIZE 4 - -typedef struct _SPC3_CDB_REPORT_TARGET_PORT_GROUPS { - UCHAR OperationCode; - UCHAR ServiceAction : 5; - UCHAR Reserved1 : 3; - UCHAR Reserved2[4]; - UCHAR AllocationLength[4]; - UCHAR Reserved3; - UCHAR Control; -} SPC3_CDB_REPORT_TARGET_PORT_GROUPS, *PSPC3_CDB_REPORT_TARGET_PORT_GROUPS; - -typedef struct _SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR { - UCHAR AsymmetricAccessState : 4; - UCHAR Reserved : 3; - UCHAR Preferred : 1; - UCHAR ActiveOptimizedSupported : 1; - UCHAR ActiveUnoptimizedSupported : 1; - UCHAR StandbySupported : 1; - UCHAR UnavailableSupported : 1; - UCHAR Reserved2 : 3; - UCHAR TransitioningSupported : 1; - USHORT TPG_Identifier; - UCHAR Reserved3; - UCHAR StatusCode; - UCHAR VendorUnique; - UCHAR NumberTargetPorts; - ULONG TargetPortIds[0]; -} SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR, *PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR; - -typedef struct _SPC3_CDB_SET_TARGET_PORT_GROUPS { - UCHAR OperationCode; - UCHAR ServiceAction : 5; - UCHAR Reserved1 : 3; - UCHAR Reserved2[4]; - UCHAR ParameterListLength[4]; - UCHAR Reserved3; - UCHAR Control; -} SPC3_CDB_SET_TARGET_PORT_GROUPS, *PSPC3_CDB_SET_TARGET_PORT_GROUPS; - -typedef struct _SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR { - UCHAR AsymmetricAccessState : 4; - UCHAR Reserved1 : 4; - UCHAR Reserved2; - USHORT TPG_Identifier; -} SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR, *PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR; - -typedef struct _SPC3_CONTROL_EXTENSION_MODE_PAGE { - UCHAR PageCode : 6; - UCHAR SubpageFormat : 1; - UCHAR ParametersSavable : 1; - UCHAR SubpageCode; - UCHAR PageLength[2]; - UCHAR ImplicitALUAEnable : 1; - UCHAR ScsiPrecendence : 1; - UCHAR TimestampChangeable : 1; - UCHAR Reserved1 : 5; - UCHAR InitialPriority : 4; - UCHAR Reserved2 : 4; - UCHAR Reserved3[26]; -} SPC3_CONTROL_EXTENSION_MODE_PAGE, *PSPC3_CONTROL_EXTENSION_MODE_PAGE; - -#define SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS 0xA3 -#define SPC3_SCSIOP_SET_TARGET_PORT_GROUPS 0xA4 -#define SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS 0xA -#define SPC3_RESERVATION_ACTION_REPORT_CAPABILITIES 0x2 - -#define SPC3_SCSI_ADSENSE_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR 0x2F -#define SPC3_SCSI_ADSENSE_LOGICAL_UNIT_COMMAND_FAILED 0x67 - -#define SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED 0x1 -#define SPC3_SCSI_SENSEQ_RESERVATIONS_PREEMPTED 0x3 -#define SPC3_SCSI_SENSEQ_RESERVATIONS_RELEASED 0x4 -#define SPC3_SCSI_SENSEQ_REGISTRATIONS_PREEMPTED 0x5 -#define SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED 0x6 -#define SPC3_SCSI_SENSEQ_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x7 -#define SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED 0x9 -#define SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION 0xA -#define SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE 0xB -#define SPC3_SCSI_SENSEQ_TARGET_PORT_IN_UNAVAILABLE_STATE 0xC - -#define SPC3_SCSI_SENSEQ_SET_TARGET_PORT_GROUPS_FAILED 0xA - -#define SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT 10 -#define SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT 10 - - -// -// Function prototypes for functions intrface.c -// - -DRIVER_INITIALIZE DriverEntry; -DRIVER_UNLOAD DsmDriverUnload; - -NTSTATUS -DsmInquire ( - _In_ IN PVOID DsmContext, - _In_ IN PDEVICE_OBJECT TargetDevice, - _In_ IN PDEVICE_OBJECT PortObject, - _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, - _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, - _Out_ OUT PVOID *DsmIdentifier - ); - -BOOLEAN -DsmCompareDevices( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId1, - _In_ IN PVOID DsmId2 - ); - -NTSTATUS -DsmGetControllerInfo( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN ULONG Flags, - _Inout_ IN OUT PCONTROLLER_INFO *ControllerInfo - ); - -NTSTATUS -DsmSetDeviceInfo( - _In_ IN PVOID DsmContext, - _In_ IN PDEVICE_OBJECT TargetObject, - _In_ IN PVOID DsmId, - _Inout_ IN OUT PVOID *PathId - ); - -BOOLEAN -DsmIsPathActive( - _In_ IN PVOID DsmContext, - _In_ IN PVOID PathId, - _In_ IN PVOID DsmId - ); - -NTSTATUS -DsmPathVerify( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PVOID PathId - ); - -NTSTATUS -DsmInvalidatePath( - _In_ IN PVOID DsmContext, - _In_ IN ULONG ErrorMask, - _In_ IN PVOID PathId, - _Inout_ IN OUT PVOID *NewPathId - ); - -NTSTATUS -DsmMoveDevice( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PVOID MPIOPath, - _In_ IN PVOID SuggestedPath, - _In_ IN ULONG Flags - ); - -NTSTATUS -DsmRemovePending( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId - ); - -NTSTATUS -DsmRemoveDevice( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PVOID PathId - ); - -NTSTATUS -DsmRemovePath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PVOID PathId - ); - -NTSTATUS -DsmSrbDeviceControl( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ); - -PVOID -DsmLBGetPath( - _In_ IN PVOID DsmContext, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PDSM_IDS DsmList, - _In_ IN PVOID CurrentPath, - _Out_ OUT NTSTATUS *Status - ); - -ULONG -DsmInterpretError( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _Inout_ IN OUT NTSTATUS *Status, - _Out_ OUT PBOOLEAN Retry, - _Out_ OUT PLONG RetryInterval, - ... - ); - -NTSTATUS -DsmUnload( - _In_ IN PVOID DsmContext - ); - -VOID -DsmSetCompletion( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _Inout_ IN OUT PDSM_COMPLETION_INFO DsmCompletion - ); - -_Success_(return == DSM_PATH_SET) -ULONG -DsmCategorizeRequest( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PVOID CurrentPath, - _Outptr_result_maybenull_ OUT PVOID *PathId, - _Out_ OUT NTSTATUS *Status - ); - -NTSTATUS -DsmBroadcastRequest( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ); - -BOOLEAN -DsmIsAddressTypeSupported( - _In_ IN PVOID DsmContext, - _In_ IN ULONG AddressType - ); - -NTSTATUS -DsmDeviceNotUsed( - _In_ IN PVOID DsmContext, - _In_ IN PVOID DsmId - ); - - -// -// Function prototypes for functions in dsmmain.c -// - -VOID -DsmpFreeDSMResources( - _In_ IN PDSM_CONTEXT DsmContext - ); - -PDSM_GROUP_ENTRY -DsmpFindDevice( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN BOOLEAN AcquireDSMLockExclusive - ); - -PDSM_GROUP_ENTRY -DsmpBuildGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - -NTSTATUS -DsmpParseTargetPortGroupsInformation( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, - _In_ IN ULONG TargetPortGroupsInfoLength - ); - -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpFindTargetPortGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, - _In_ IN ULONG TPGs_BufferLength - ); - -_Success_(return!=0) -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpUpdateTargetPortGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, - _In_ IN ULONG TPGs_BufferLength, - _Out_ OUT PULONG DescriptorSize - ); - -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpBuildTargetPortGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, - _In_ IN ULONG TPGs_BufferLength, - _Out_ OUT PULONG DescriptorSize - ); - -PDSM_TARGET_PORT_LIST_ENTRY -DsmpFindTargetPortListEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN ULONG RelativeTargetPortId - ); - -PDSM_TARGET_PORT_LIST_ENTRY -DsmpBuildTargetPortListEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN ULONG RelativeTargetPortId - ); - -PDSM_TARGET_PORT_GROUP_ENTRY -DsmpFindTargetPortGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PUSHORT TargetPortGroupId - ); - -PDSM_TARGET_PORT_LIST_ENTRY -DsmpFindTargetPort( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN PULONG TargetPortGroupId - ); - -NTSTATUS -DsmpAddDeviceEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - -PDSM_CONTROLLER_LIST_ENTRY -DsmpFindControllerEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDEVICE_OBJECT PortObject, - _In_ IN PSCSI_ADDRESS ScsiAddress, - _In_reads_(ControllerSerialNumberLength) IN PSTR ControllerSerialNumber, - _In_ IN SIZE_T ControllerSerialNumberLength, - _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, - _In_ IN BOOLEAN AcquireLock - ); - -_Ret_maybenull_ -_Must_inspect_result_ -_When_(return != NULL, __drv_allocatesMem(Mem)) -PDSM_CONTROLLER_LIST_ENTRY -DsmpBuildControllerEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_opt_ IN PDEVICE_OBJECT DeviceObject, - _In_ IN PDEVICE_OBJECT PortObject, - _In_ IN PSCSI_ADDRESS ScsiAddress, - _In_ IN PSTR ControllerSerialNumber, - _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, - _In_ IN BOOLEAN AcquireLock - ); - -VOID -DsmpFreeControllerEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ __drv_freesMem(Mem) IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry - ); - -BOOLEAN -DsmpIsDeviceBelongsToController( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry - ); - -PDSM_DEVICE_INFO -DsmpFindDevInfoFromGroupAndFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_FAILOVER_GROUP FOGroup - ); - -PDSM_FAILOVER_GROUP -DsmpFindFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PVOID PathId - ); - -PDSM_FAILOVER_GROUP -DsmpBuildFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PVOID *PathId - ); - -NTSTATUS -DsmpUpdateFOGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_FAILOVER_GROUP FailGroup, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - -VOID -DsmpRemoveDeviceFailGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_FAILOVER_GROUP FailGroup, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN BOOLEAN AcquireDSMLockExclusive - ); - -ULONG -DsmpRemoveDeviceEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - -VOID -DsmpRemoveDeviceFromTargetPortList( - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - -PDSM_FAILOVER_GROUP -DsmpSetNewPath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDevice - ); - -PDSM_FAILOVER_GROUP -DsmpSetNewPathUsingGroup( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY Group - ); - -VOID -DsmpRemoveZombieGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY ZombieGroup - ); - -NTSTATUS -DsmpUpdateTargetPortGroupDevicesStates( - _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, - _In_ IN DSM_DEVICE_STATE NewState - ); - -VOID -DsmpIncrementCounters( - _In_ PDSM_FAILOVER_GROUP FailGroup, - _In_ PSCSI_REQUEST_BLOCK Srb - ); - -BOOLEAN -DsmpDecrementCounters( - _In_ PDSM_FAILOVER_GROUP FailGroup, - _In_ PSCSI_REQUEST_BLOCK Srb - ); - -PDSM_FAILOVER_GROUP -DsmpGetPath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmList, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN ULONG SpecialHandlingFlag - ); - -PVOID -DsmpGetPathIdFromPassThroughPath( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmList, - _In_ IN PIRP Irp, - _Inout_ IN OUT NTSTATUS *Status - ); - -VOID -DsmpRemoveGroupEntry( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_GROUP_ENTRY GroupEntry, - _In_ IN BOOLEAN AcquireDSMLockExclusive - ); - -BOOLEAN -DsmpMpioPassThroughPathCommand( - _In_ IN PIRP Irp - ); - -BOOLEAN -DsmpReservationCommand( - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb - ); - -VOID -DsmpRequestComplete( - _In_ IN PVOID DsmId, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PVOID DsmContext - ); - -NTSTATUS -DsmpRegisterPersistentReservationKeys( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN BOOLEAN Register - ); - - -BOOLEAN -DsmpShouldRetryPassThroughRequest( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ); - -BOOLEAN -DsmpShouldRetryPersistentReserveCommand( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ); - -BOOLEAN -DsmpShouldRetryTPGRequest( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ); - -BOOLEAN -DsmpIsDeviceRemoved( - _In_ IN PVOID SenseData, - _In_ IN UCHAR SenseDataSize - ); - -PDSM_DEVICE_INFO -DsmpGetActivePathToBeUsed( - _In_ PDSM_GROUP_ENTRY Group, - _In_ BOOLEAN Symmetric, - _In_ IN ULONG SpecialHandlingFlag - ); - -PDSM_DEVICE_INFO -DsmpGetAnyActivePath( - _In_ PDSM_GROUP_ENTRY Group, - _In_ BOOLEAN Exception, - _In_opt_ PDSM_DEVICE_INFO DeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ); - -PDSM_DEVICE_INFO -DsmpFindStandbyPathToActivate( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN ULONG SpecialHandlingFlag - ); - -PDSM_DEVICE_INFO -DsmpFindStandbyPathToActivateALUA( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PBOOLEAN SendTPG, - _In_ IN ULONG SpecialHandlingFlag - ); - -PDSM_DEVICE_INFO -DsmpFindStandbyPathInAlternateTpgALUA( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForDsmPolicyAdjustment( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ); - -NTSTATUS -DsmpSetLBForVidPidPolicyAdjustment( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PWSTR TargetHardwareId, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ); - -NTSTATUS -DsmpSetNewDefaultLBPolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_opt_ IN PDSM_DEVICE_INFO NewDeviceInfo, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForPathArrival( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForPathArrivalALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForPathRemoval( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, - _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForPathRemovalALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, - _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForPathFailing( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, - _In_ IN BOOLEAN MarkDevInfoFailed, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetLBForPathFailingALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, - _In_ IN BOOLEAN MarkDevInfoFailed, - _In_ IN ULONG SpecialHandlingFlag - ); - -NTSTATUS -DsmpSetPathForIoRetryALUA( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, - _In_ IN BOOLEAN TPGException, - _In_ IN BOOLEAN DeviceInfoException - ); - -PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY -DsmpFindFailPathDevInfoEntry( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO FailingDevInfo - ); - -PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY -DsmpBuildFailPathDevInfoEntry( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_DEVICE_INFO FailingDevInfo, - _In_ IN PDSM_DEVICE_INFO AlternateDevInfo - ); - -IO_COMPLETION_ROUTINE DsmpPhase1ProcessPathFailingALUA; - -NTSTATUS -DsmpRemoveFailPathDevInfoEntry( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY FailPathDevInfoEntry - ); - -IO_COMPLETION_ROUTINE DsmpPhase2ProcessPathFailingALUA; - -NTSTATUS -DsmpPersistentReserveOut( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ); - -__inline -BOOLEAN -DsmpIsPersistentReservationKeyZeroKey( - _In_ ULONG KeyLength, - _In_reads_bytes_(KeyLength) PUCHAR Key - ) -{ - BOOLEAN zeroKey = FALSE; - - NT_ASSERT(KeyLength == 8); - - if ((KeyLength) == 8 && - (Key[0] == 0 && Key[1] == 0 && Key[2] == 0 && Key[3] == 0 && - Key[4] == 0 && Key[5] == 0 && Key[6] == 0 && Key[7] == 0)) { - - zeroKey = TRUE; - } - - return zeroKey; -} - - -NTSTATUS -DsmpPersistentReserveIn( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN PSCSI_REQUEST_BLOCK Srb, - _In_ IN PKEVENT Event - ); - -IO_COMPLETION_ROUTINE DsmpPersistentReserveCompletion; - - -// -// Function prototypes for functions in utils.c -// - -_Success_(return != NULL) -__drv_allocatesMem(Mem) -_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) -_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) -_When_(((PoolType&0x2))!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, - _Post_maybenull_ _Must_inspect_result_) -_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, - _Post_notnull_) -_When_((PoolType&NonPagedPoolMustSucceed)!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -_Post_writable_byte_size_(NumberOfBytes) -PVOID -DsmpAllocatePool( - _In_ _Strict_type_match_ IN POOL_TYPE PoolType, - _In_ IN SIZE_T NumberOfBytes, - _In_ IN ULONG Tag - ); - -_Success_(return != NULL) -_Post_maybenull_ -_Must_inspect_result_ -__drv_allocatesMem(Mem) -_Post_writable_byte_size_(*BytesAllocated) -_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) -_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) -_When_((PoolType&NonPagedPoolMustSucceed)!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -PVOID -DsmpAllocateAlignedPool( - _In_ IN POOL_TYPE PoolType, - _In_ IN SIZE_T NumberOfBytes, - _In_ IN ULONG AlignmentMask, - _In_ IN ULONG Tag, - _Out_ OUT SIZE_T *BytesAllocated - ); - -_IRQL_requires_max_(DISPATCH_LEVEL) -VOID -DsmpFreePool( - _In_opt_ __drv_freesMem(Mem) IN PVOID Block - ); - -NTSTATUS -DsmpGetStatsGatheringChoice( - _In_ IN PDSM_CONTEXT Context, - _Out_ OUT PULONG StatsGatherChoice - ); - -NTSTATUS -DsmpSetStatsGatheringChoice( - _In_ IN PDSM_CONTEXT Context, - _In_ IN ULONG StatsGatherChoice - ); - - -NTSTATUS -DsmpGetDeviceList( - _In_ IN PDSM_CONTEXT Context - ); - -_Success_(return==0) -NTSTATUS -DsmpGetStandardInquiryData( - _In_ IN PDEVICE_OBJECT DeviceObject, - _Out_ OUT PINQUIRYDATA InquiryData - ); - -BOOLEAN -DsmpCheckScsiCompliance( - _In_ IN PDEVICE_OBJECT DeviceObject, - _In_ IN PINQUIRYDATA InquiryData, - _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, - _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList - ); - -BOOLEAN -DsmpDeviceSupported( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PCSTR VendorId, - _In_ IN PCSTR ProductId - ); - -BOOLEAN -DsmpFindSupportedDevice( - _In_ IN PUNICODE_STRING DeviceName, - _In_ IN PUNICODE_STRING SupportedDevices - ); - -_Success_(return!=0) -PVOID -DsmpParseDeviceID ( - _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID, - _In_ IN DSM_DEVID_TYPE DeviceIdType, - _In_opt_ IN PULONG IdNumber, - _Out_opt_ PSTORAGE_IDENTIFIER_CODE_SET CodeSet, - _In_ IN BOOLEAN Legacy - ); - -PUCHAR -DsmpBinaryToAscii( - _In_reads_(Length) IN PUCHAR HexBuffer, - _In_ IN ULONG Length, - _Inout_ IN OUT PULONG UpdateLength, - _In_ IN BOOLEAN Legacy - ); - -PSTR -DsmpGetSerialNumber( - _In_ IN PDEVICE_OBJECT DeviceObject - ); - - -NTSTATUS -DsmpDisableImplicitStateTransition( - _In_ IN PDEVICE_OBJECT DeviceObject, - _Out_ OUT PBOOLEAN DisableImplicit - ); - -PWSTR -DsmpBuildHardwareId( - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - -PWSTR -DsmpBuildDeviceNameLegacyPage0x80( - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ); - - -PWSTR -DsmpBuildDeviceName( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_reads_(SerialNumberLength) IN PSTR SerialNumber, - _In_ IN SIZE_T SerialNumberLength - ); - -NTSTATUS -DsmpApplyDeviceNameCorrection( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy, - _In_ IN SIZE_T DeviceNameLegacyLen, - _In_reads_(DeviceNameLen) PWSTR DeviceName, - _In_ IN SIZE_T DeviceNameLen - ); - -NTSTATUS -DsmpQueryDeviceLBPolicyFromRegistry( - _In_ PDSM_DEVICE_INFO DeviceInfo, - _In_ PWSTR RegistryKeyName, - _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Inout_ PULONGLONG PreferredPath, - _Inout_ PUCHAR ExplicitlySet - ); - -NTSTATUS -DsmpQueryTargetLBPolicyFromRegistry( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Out_ OUT PULONGLONG PreferredPath - ); - -NTSTATUS -DsmpQueryDsmLBPolicyFromRegistry( - _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Out_ OUT PULONGLONG PreferredPath - ); - -NTSTATUS -DsmpSetDsmLBPolicyInRegistry( - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ); - -NTSTATUS -DsmpSetVidPidLBPolicyInRegistry( - _In_ IN PWSTR TargetHardwareId, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ); - -NTSTATUS -DsmpOpenLoadBalanceSettingsKey( - _In_ IN ACCESS_MASK AccessMask, - _Out_ OUT PHANDLE LoadBalanceSettingsKey - ); - -NTSTATUS -DsmpOpenTargetsLoadBalanceSettingKey( - _In_ IN ACCESS_MASK AccessMask, - _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey - ); - -NTSTATUS -DsmpOpenDsmServicesParametersKey( - _In_ IN ACCESS_MASK AccessMask, - _Out_ OUT PHANDLE ParametersSettingsKey - ); - -IO_COMPLETION_ROUTINE DsmpReportTargetPortGroupsSyncCompletion; - -_Success_(return==0) -NTSTATUS -DsmpReportTargetPortGroups( - _In_ PDEVICE_OBJECT DeviceObject, - _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo, - _Out_ PULONG TargetPortGroupsInfoLength - ); - -NTSTATUS -DsmpReportTargetPortGroupsAsync( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, - _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, - _In_ IN ULONG TargetPortGroupsInfoLength, - _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo - ); - -NTSTATUS -DsmpQueryLBPolicyForDevice( - _In_ IN PWSTR RegistryKeyName, - _In_ IN ULONGLONG PathId, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Out_ OUT PULONG PrimaryPath, - _Out_ OUT PULONG OptimizedPath, - _Out_ OUT PULONG PathWeight - ); - -VOID -DsmpGetDSMPathKeyName( - _In_ ULONGLONG DSMPathId, - _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName, - _In_ ULONG DsmPathKeyNameSize - ); - -UCHAR -DsmpGetAsciiForBinary( - _In_ UCHAR BinaryChar - ); - -NTSTATUS -DsmpGetDeviceIdList ( - _In_ IN PDEVICE_OBJECT DeviceObject, - _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor - ); - -NTSTATUS -DsmpSetTargetPortGroups( - _In_ IN PDEVICE_OBJECT DeviceObject, - _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, - _In_ IN ULONG TargetPortGroupsInfoLength - ); - -NTSTATUS -DsmpSetTargetPortGroupsAsync( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, - _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, - _In_ IN ULONG TargetPortGroupsInfoLength, - _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo - ); - -PDSM_LOAD_BALANCE_POLICY_SETTINGS -DsmpCopyLoadBalancePolicies( - _In_ IN PDSM_GROUP_ENTRY GroupEntry, - _In_ IN ULONG DsmWmiVersion, - _In_ IN PVOID SupportedLBPolicies - ); - -NTSTATUS -DsmpPersistLBSettings( - _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings - ); - -NTSTATUS -DsmpSetDeviceALUAState( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN DSM_DEVICE_STATE DevState - ); - -NTSTATUS -DsmpGetDeviceALUAState( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_opt_ IN PDSM_DEVICE_STATE DevState - ); - -NTSTATUS -DsmpAdjustDeviceStatesALUA( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ); - -PDSM_WORKITEM -DsmpAllocateWorkItem( - _In_ IN PDEVICE_OBJECT DeviceObject, - _In_ IN PVOID Context - ); - -VOID -DsmpFreeWorkItem( - _In_ IN PDSM_WORKITEM DsmWorkItem - ); - -VOID -DsmpFreeZombieGroupList( - _In_ IN PDSM_FAILOVER_GROUP FailGroup - ); - -NTSTATUS -DsmpRegCopyTree( - _In_ IN HANDLE SourceKey, - _In_ IN HANDLE DestKey - ); - -NTSTATUS -DsmpRegDeleteTree( - _In_ IN HANDLE KeyRoot - ); - -#if defined (_WIN64) -VOID -DsmpPassThroughPathTranslate32To64( - _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32, - _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64 - ); - -VOID -DsmpPassThroughPathTranslate64To32( - _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64, - _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32 - ); -#endif - -NTSTATUS -DsmpGetMaxPRRetryTime( - _In_ IN PDSM_CONTEXT Context, - _Out_ OUT PULONG RetryTime - ); - -NTSTATUS -DsmpQueryCacheInformationFromRegistry( - _In_ IN PDSM_CONTEXT DsmContext, - _Out_ OUT PBOOLEAN UseCacheForLeastBlocks, - _Out_ OUT PULONGLONG CacheSizeForLeastBlocks - ); - -BOOLEAN -DsmpConvertSharedSpinLockToExclusive( - _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock - ); - - -// -// Function prototypes for functions in wmi.c -// - -VOID -DsmpDsmWmiInitialize( - _In_ IN PDSM_WMILIB_CONTEXT WmiGlobalInfo, - _In_ IN PUNICODE_STRING RegistryPath - ); - -NTSTATUS -DsmGlobalQueryData( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG InstanceCount, - _Inout_ IN OUT PULONG InstanceLengthArray, - _In_ IN ULONG BufferAvail, - _Out_writes_to_(BufferAvail, *DataLength) OUT PUCHAR Buffer, - _Out_ OUT PULONG DataLength, - ... - ); - -NTSTATUS -DsmGlobalSetData( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG BufferAvail, - _In_reads_bytes_(BufferAvail) IN PUCHAR Buffer, - ... - ); - -VOID -DsmpWmiInitialize( - _In_ IN PDSM_WMILIB_CONTEXT WmiInfo, - _In_ IN PUNICODE_STRING RegistryPath - ); - -NTSTATUS -DsmQueryData( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG InstanceCount, - _Inout_ IN OUT PULONG InstanceLengthArray, - _In_ IN ULONG BufferAvail, - _When_(GuidIndex == 0 || GuidIndex == 7, _Pre_notnull_ _Const_) - _When_(!(GuidIndex == 0 || GuidIndex == 7), _Out_writes_to_(BufferAvail, *DataLength)) - OUT PUCHAR Buffer, - _Out_ OUT PULONG DataLength, - ... - ); - -NTSTATUS -DsmpQueryLoadBalancePolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG DsmWmiVersion, - _In_ IN ULONG InBufferSize, - _In_ IN PULONG OutBufferSize, - _Out_writes_bytes_(*OutBufferSize) OUT PVOID Buffer - ); - -NTSTATUS -DsmpQuerySupportedLBPolicies( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG BufferAvail, - _In_ IN ULONG DsmWmiVersion, - _Out_ OUT PULONG OutBufferSize, - _Out_writes_to_(BufferAvail, *OutBufferSize) OUT PUCHAR Buffer - ); - -NTSTATUS -DsmExecuteMethod( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG MethodId, - _In_ IN ULONG InBufferSize, - _In_ IN PULONG OutBufferSize, - _Inout_ IN OUT PUCHAR Buffer, - ... - ); - -NTSTATUS -DsmpClearLoadBalancePolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds - ); - -NTSTATUS -DsmpSetLoadBalancePolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG DsmWmiVersion, - _In_ IN ULONG InBufferSize, - _In_ IN PULONG OutBufferSize, - _In_ IN PVOID Buffer - ); - -NTSTATUS -DsmpValidateSetLBPolicyInput( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG DsmWmiVersion, - _In_ IN PVOID SetLoadBalancePolicyIN, - _In_ IN ULONG InBufferSize - ); - -VOID -DsmpSaveDeviceState( - _In_ IN PVOID SupportedLBPolicies, - _In_ IN ULONG DsmWmiVersion - ); - -VOID -DsmpRestorePreviousDeviceState( - _In_ IN PVOID SupportedLBPolicies, - _In_ IN ULONG DsmWmiVersion - ); - -VOID -DsmpUpdateDesiredStateAndWeight( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN ULONG DsmWmiVersion, - _In_ IN PVOID SupportedLBPolicies - ); - -NTSTATUS -DsmpQueryDevicePerf( - _In_ PDSM_CONTEXT DsmContext, - _In_ PDSM_IDS DsmIds, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ); - -NTSTATUS -DsmpClearPerfCounters( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds - ); - -NTSTATUS -DsmpQuerySupportedDevicesList( - _In_ PDSM_CONTEXT DsmContext, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ); - -NTSTATUS -DsmpQueryTargetsDefaultPolicy( - _In_ PDSM_CONTEXT DsmContext, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ); - -NTSTATUS -DsmpQueryDsmDefaultPolicy( - _In_ PDSM_CONTEXT DsmContext, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ); - - -// -// Function prototypes for functions in debug.c -// - -VOID -DsmpDebugPrint( - _In_ ULONG DebugPrintLevel, - _In_ PCCHAR DebugMessage, - ... - ); - -// -// SRB Helpers not found in srbhelper.h -// -_Success_(return != 0) -__drv_allocatesMem(mem) -_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) -_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) -_When_(((PoolType&0x2))!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, - _Post_maybenull_ _Must_inspect_result_) -_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, - _Post_notnull_ ) -__inline PSTORAGE_REQUEST_BLOCK_HEADER -SrbAllocateCopy( - _Inout_ PVOID Srb, - _In_ _Strict_type_match_ POOL_TYPE PoolType, - _In_ ULONG Tag - ) -/* - -Description: - This function returns an allocated copy of the given SRB. The memory is - allocated using DsmpAllocatePool(). - - ***It is up to the caller to free the memory returned by this function.*** - -Arguments: - Srb - A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK. - PoolType - The pool type to use. See documentation for ExAllocatePoolWithTag(). - Tag - The allocation tag to use. See documentation for ExAllocatePoolWithTag(). - -Returns: - NULL, if the copy could not be allocated; or - A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK that is - direct copy of the given SRB. - -*/ -{ - PSTORAGE_REQUEST_BLOCK srb = (PSTORAGE_REQUEST_BLOCK)Srb; - PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; - ULONG allocationSize = 0; - - if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) - { - allocationSize = srb->SrbLength; - NT_ASSERT(allocationSize >= (sizeof(STORAGE_REQUEST_BLOCK) + sizeof(STOR_ADDR_BTL8))); - } - else - { - allocationSize = SCSI_REQUEST_BLOCK_SIZE; - NT_ASSERT(allocationSize >= sizeof(SCSI_REQUEST_BLOCK)); - } - - #pragma warning(suppress: 28160 28118) // False-positive; PoolType is simply passed through - srbCopy = (PSTORAGE_REQUEST_BLOCK_HEADER)DsmpAllocatePool(PoolType, allocationSize, Tag); - if (srbCopy != NULL) - { - RtlCopyMemory(srbCopy, Srb, allocationSize); - } - - return srbCopy; -} - -__inline -BOOLEAN DsmpIsMPIOPassThroughEx( - ULONG ControlCode - ) -// -// Returns TRUE if the given passthrough IOCTL's control code indicates it is -// an "extended" passthrough. Returns FALSE otherwise. -// -{ - if (ControlCode == IOCTL_MPIO_PASS_THROUGH_PATH_EX || - ControlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT_EX) { - return TRUE; - } else { - return FALSE; - } -} - -__inline -UCHAR DsmpNtStatusToSrbStatus( - _In_ NTSTATUS Status - ) -/*++ - -Routine Description: - - Translate an NT status value into a SCSI Srb status code. - -Arguments: - - Status - Supplies the NT status code to translate. - -Return Value: - - SRB status code. - ---*/ -{ - switch (Status) { - - case STATUS_DEVICE_BUSY: - return SRB_STATUS_BUSY; - - case STATUS_INVALID_DEVICE_REQUEST: - return SRB_STATUS_BAD_FUNCTION; - - case STATUS_INSUFFICIENT_RESOURCES: - return SRB_STATUS_INTERNAL_ERROR; - - case STATUS_INVALID_PARAMETER: - return SRB_STATUS_INVALID_REQUEST; - - default: - if (NT_SUCCESS (Status)) { - return SRB_STATUS_SUCCESS; - } else { - return SRB_STATUS_ERROR; - } - } -} - - -#endif // _PROTOTYPES_H_ - diff --git a/tests/projects/wdk/kmdf/msdsm/trace.h b/tests/projects/wdk/kmdf/msdsm/trace.h deleted file mode 100644 index b9449cc8f..000000000 --- a/tests/projects/wdk/kmdf/msdsm/trace.h +++ /dev/null @@ -1,35 +0,0 @@ - -/*++ - -Copyright (C) 2004 Microsoft Corporation - -Module Name: - - trace.h - -Abstract: - - Header file included by the Microsoft Device Specific Module (DSM). - - This file contains Windows tracing related defines. - -Environment: - - kernel mode only - -Notes: - ---*/ - -// -// Set component ID for DbgPrintEx calls -// -#define DEBUG_COMP_ID DPFLTR_MSDSM_ID - -// -// Include header file and setup GUID for tracing -// -#include -#define WPP_GUID_MSDSM (DEDADFF5, F99F, 4600, B8C9, 2D4D9B806B5B) -#define WPP_CONTROL_GUIDS WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_MSDSM) - diff --git a/tests/projects/wdk/kmdf/msdsm/utils.c b/tests/projects/wdk/kmdf/msdsm/utils.c deleted file mode 100644 index 91d8f44ce..000000000 --- a/tests/projects/wdk/kmdf/msdsm/utils.c +++ /dev/null @@ -1,7946 +0,0 @@ - -/*++ - -Copyright (C) 2004-2010 Microsoft Corporation - -Module Name: - - utils.c - -Abstract: - - This driver is the Microsoft Device Specific Module (DSM). - It exports behaviours that mpio.sys will use to determine how to - multipath SPC-3 compliant devices. - - This file contains utility routines. - -Environment: - - kernel mode only - -Notes: - ---*/ - -#include "precomp.h" - -#ifdef DEBUG_USE_WPP -#include "utils.tmh" -#endif - -#pragma warning (disable:4305) - -extern BOOLEAN DoAssert; - -#ifdef ALLOC_PRAGMA - #pragma alloc_text(PAGE, DsmpBuildDeviceNameLegacyPage0x80) - #pragma alloc_text(PAGE, DsmpBuildDeviceName) - #pragma alloc_text(PAGE, DsmpApplyDeviceNameCorrection) - #pragma alloc_text(PAGE, DsmpOpenLoadBalanceSettingsKey) - #pragma alloc_text(PAGE, DsmpQueryLBPolicyForDevice) - #pragma alloc_text(PAGE, DsmpOpenTargetsLoadBalanceSettingKey) - #pragma alloc_text(PAGE, DsmpOpenDsmServicesParametersKey) -#endif - -_Success_(return != NULL) -__drv_allocatesMem(Mem) -_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) -_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) -_When_(((PoolType&0x2))!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, - _Post_maybenull_ _Must_inspect_result_) -_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, - _Post_notnull_ ) -_When_((PoolType&NonPagedPoolMustSucceed)!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -_Post_writable_byte_size_(NumberOfBytes) -PVOID -DsmpAllocatePool( - _In_ _Strict_type_match_ IN POOL_TYPE PoolType, - _In_ IN SIZE_T NumberOfBytes, - _In_ IN ULONG Tag - ) -/*+++ - -Routine Description : - - Allocates memory from the specified pool using the given tag. - If the allocation is successful, the entire buffer will be zeroed. - -Arguements: - - PoolType - Pool to allocate from (NonPaged, Paged, etc) - NumberOfBytes - Size of the buffer to allocate - Tag - Tag (DSM_TAG_XXX) to be used for this allocation. - These tags are defined in msdsm.h - -Return Value: - - Pointer to the buffer if allocation is successful - NULL otherwise - ---*/ -{ - PVOID Block = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpAllocatePool (Tag %u): Entering function.\n", - Tag)); - - #pragma warning(suppress: 28118) // False-positive; PoolType is simply passed through - Block = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); - if (Block) { - RtlZeroMemory(Block, NumberOfBytes); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpAllocatePool (Tag %u): Exiting function with allocated block %p.\n", - Tag, - Block)); - - return Block; -} - - -_Success_(return != NULL) -_Post_maybenull_ -_Must_inspect_result_ -__drv_allocatesMem(Mem) -_Post_writable_byte_size_(*BytesAllocated) -_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) -_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) -_When_((PoolType&NonPagedPoolMustSucceed)!=0, - __drv_reportError("Must succeed pool allocations are forbidden. " - "Allocation failures cause a system crash")) -PVOID -#pragma warning(suppress:28195) // Allocation is not guaranteed, caller needs to check return value -DsmpAllocateAlignedPool( - _In_ IN POOL_TYPE PoolType, - _In_ IN SIZE_T NumberOfBytes, - _In_ IN ULONG AlignmentMask, - _In_ IN ULONG Tag, - _Out_ OUT SIZE_T *BytesAllocated - ) -/*+++ - -Routine Description : - - Allocates memory from the specified pool using the given tag and alignment requirement. - If the allocation is successful, the entire buffer will be zeroed. - -Arguements: - - PoolType - Pool to allocate from (NonPaged, Paged, etc) - NumberOfBytes - Size of the buffer to allocate - AlignmentMask - Alignment requirement specified by the device - Tag - Tag (DSM_TAG_XXX) to be used for this allocation. - These tags are defined in msdsm.h - BytesAllocated - Returns the number of bytes allocated, if the routine was successful - -Return Value: - - Pointer to the buffer if allocation is successful - NULL otherwise - ---*/ -{ - PVOID Block = NULL; - UINT_PTR align64 = (UINT_PTR)AlignmentMask; - ULONG totalSize = (ULONG)NumberOfBytes; - NTSTATUS status = STATUS_SUCCESS; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpAllocateAlignedPool (Tag %u): Entering function.\n", - Tag)); - - if (BytesAllocated == NULL) { - - status = STATUS_INVALID_PARAMETER; - goto __Exit; - } - - *BytesAllocated = 0; - - if (AlignmentMask) { - - status = RtlULongAdd((ULONG)NumberOfBytes, AlignmentMask, &totalSize); - } - - if (NT_SUCCESS(status)) { - - #pragma warning(suppress: 6014 28118) // Block isn't leaked, this function is marked as an allocator; PoolType is simply passed through - Block = ExAllocatePoolWithTag(PoolType, totalSize, Tag); - - if (Block != NULL) { - - if (AlignmentMask) { - - Block = (PVOID)(((UINT_PTR)Block + align64) & ~align64); - } - } else { - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - -__Exit: - - if (NT_SUCCESS(status)) { - - RtlZeroMemory(Block, totalSize); - *BytesAllocated = totalSize; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpAllocateAlignedPool (Tag %u): Exiting function with allocated block %p.\n", - Tag, - Block)); - - return Block; -} - - -_IRQL_requires_max_(DISPATCH_LEVEL) -VOID -DsmpFreePool( - _In_opt_ __drv_freesMem(Mem) IN PVOID Block - ) -/*+++ - -Routine Description : - - Frees the block passed in. - -Arguements: - - Block - pointer to the memory to free. - -Return Value: - - Nothing - ---*/ -{ - PVOID tempAddress = Block; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFreePool (Block %p): Entering function.\n", - Block)); - - if (Block) { - - ExFreePool(Block); - Block = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFreePool (Block %p): Exiting function.\n", - tempAddress)); - - return; -} - - -NTSTATUS -DsmpGetStatsGatheringChoice( - _In_ IN PDSM_CONTEXT Context, - _Out_ OUT PULONG StatsGatherChoice - ) -/*++ - -Routine Description: - - This routine is used to determine if the Admin wants statitics to be collected - on every IO. It queries the the services key for the value under - "msdsm\Parameters\DsmDisableStatistics" - -Arguments: - - Context - The DSM Context value. - StatsGatherChoice - Returns the choice of whether or not to gather statistics - -Return Value: - - Status of the RtlQueryRegistryValues call. - ---*/ -{ - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - WCHAR registryKeyName[56] = {0}; - NTSTATUS status = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetStatsGatherChoice (DsmCtxt %p): Entering function.\n", - Context)); - - if (!StatsGatherChoice) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_INIT, - "DsmpGetStatsGatherChoice (DsmCtxt %p): Invalid parameter - StatsGatherChoice is NULL.\n", - Context)); - - goto __Exit_DsmpGetStatsGatherChoice; - } - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - // - // Build the key value name that we want as the base of the query. - // - RtlStringCbPrintfW(registryKeyName, - sizeof(registryKeyName), - DSM_PARAMETER_PATH_W); - - // - // The query table has two entries. One for the supporteddeviceList and - // the second which is the 'NULL' terminator. - // - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_DISABLE_STATISTICS; - queryTable[0].EntryContext = StatsGatherChoice; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, - registryKeyName, - queryTable, - registryKeyName, - NULL); - -__Exit_DsmpGetStatsGatherChoice: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n", - Context, - status)); - - return status; -} - - -NTSTATUS -DsmpSetStatsGatheringChoice( - _In_ IN PDSM_CONTEXT Context, - _In_ IN ULONG StatsGatherChoice - ) -/*++ - -Routine Description: - - This routine is used to set the value that indicates whether statistics will - be gathered on every IO. It updates the services key for the value under - "msdsm\Parameters\DsmDisableStatistics" - -Arguments: - - Context - The DSM Context value. - StatsGatherChoice - Value indicating whether to gather statistics (TRUE) or not (FALSE) - -Return Value: - - Status of the RtlWriteRegistryValue call. - ---*/ -{ - WCHAR registryKeyName[56] = {0}; - NTSTATUS status = STATUS_SUCCESS; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetStatsGatherChoice (DsmCtxt %p): Entering function.\n", - Context)); - - // - // Build the key value name that we want as the base of the query. - // - RtlStringCbPrintfW(registryKeyName, - sizeof(registryKeyName), - DSM_PARAMETER_PATH_W); - - - status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES, - registryKeyName, - DSM_DISABLE_STATISTICS, - REG_DWORD, - &StatsGatherChoice, - sizeof(ULONG)); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n", - Context, - status)); - - return status; -} - - - -NTSTATUS -DsmpGetDeviceList( - _In_ IN PDSM_CONTEXT Context - ) -/*++ - -Routine Description: - - This routine is used to build the supported device list by querying the services - key for the values under "msdsm\Parameters\DsmSupportedDeviceList" - -Arguments: - - Context - The DSM Context value. It contains storage for the multi_sz string that may - be built. - -Return Value: - - Status of the RtlQueryRegistryValues call. - ---*/ -{ - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - WCHAR registryKeyName[56] = {0}; - UNICODE_STRING inquiryStrings; - WCHAR defaultIDs[] = { L"\0" }; - NTSTATUS status; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetDeviceList (DsmCtxt %p): Entering function.\n", - Context)); - - RtlZeroMemory(queryTable, sizeof(queryTable)); - RtlInitUnicodeString(&inquiryStrings, NULL); - - // - // Build the key value name that we want as the base of the query. - // - RtlStringCbPrintfW(registryKeyName, - sizeof(registryKeyName), - DSM_PARAMETER_PATH_W); - - // - // The query table has two entries. One for the supporteddeviceList and - // the second which is the 'NULL' terminator. - // - // Indicate that there is NO call-back routine, and to give back the MULTI_SZ as - // one blob, as opposed to individual unicode strings. - // - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND | RTL_QUERY_REGISTRY_TYPECHECK; - - // - // The value to query. - // - queryTable[0].Name = DSM_SUPPORTED_DEVICELIST_VALUE_NAME; - - // - // Where to put the strings. Note that we need to use an empty unicode_string - // for the query or else RtlQueryRegistryValues will only fill in enough - // entries as specified by the size of the unicode string's buffer, which - // is why we can't use Context->SupportedDevices directly in the call. - // - queryTable[0].EntryContext = &inquiryStrings; - queryTable[0].DefaultType = (REG_MULTI_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_MULTI_SZ; - queryTable[0].DefaultData = defaultIDs; - queryTable[0].DefaultLength = sizeof(defaultIDs); - - status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, - registryKeyName, - queryTable, - registryKeyName, - NULL); - - // - // If we successfully queried for the supported device list, we need to delete - // our cached list and update it with this new one. - // - if (NT_SUCCESS(status)) { - - KIRQL oldIrql; - PWCHAR tempBuffer = NULL; - - tempBuffer = DsmpAllocatePool(NonPagedPoolNx, inquiryStrings.MaximumLength, DSM_TAG_REG_VALUE_RELATED); - - // - // This is a "best effort" operation. If we are unable to allocate a - // buffer for the strings, we just continue using our old cached list. - // We do NOT fall back to using inquiryStrings's buffer as we want to - // be able to work with the supported devices list at raised IRQL. - // - if (tempBuffer) { - - RtlCopyMemory(tempBuffer, inquiryStrings.Buffer, inquiryStrings.Length); - - KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql); - DsmpFreePool(Context->SupportedDevices.Buffer); - Context->SupportedDevices.Buffer = tempBuffer; - Context->SupportedDevices.Length = inquiryStrings.Length; - Context->SupportedDevices.MaximumLength = inquiryStrings.MaximumLength; - KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetDeviceList (DsmCtxt %p): Failed to allocate supported device list's buffer.\n", - Context)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - - ExFreePool(inquiryStrings.Buffer); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetDeviceList (DsmCtxt %p): Exiting function with status %x.\n", - Context, - status)); - - return status; -} - - -_Success_(return==0) -NTSTATUS -DsmpGetStandardInquiryData( - _In_ IN PDEVICE_OBJECT DeviceObject, - _Out_ OUT PINQUIRYDATA InquiryData - ) -/*++ - -Routine Description: - - Helper routine to send an inquiry with EVPD cleared to get the standard inquiry data. - -Arguments: - - DeviceObject - The port PDO to which the command should be sent. - InquiryData - Pointer to inquiry data that will be returned to caller. - -Return Value: - - STATUS_SUCCESS or failure NTSTATUS code. - ---*/ -{ - PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; - PCDB cdb; - IO_STATUS_BLOCK ioStatus; - ULONG length; - NTSTATUS status = STATUS_SUCCESS; - PINQUIRYDATA inquiryData; - PSENSE_DATA senseData; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetStandardInquiryData (DevObj %p): Entering function.\n", - DeviceObject)); - - if (InquiryData == NULL) { - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpGetStandardInquiryData; - } - - // - // Build a standard inquiry command. - // - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - passThrough = DsmpAllocatePool(NonPagedPoolNx, - length, - DSM_TAG_PASS_THRU); - if (!passThrough) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetStandardInquiryData (DevObj %p): Failed to allocate mem for passthrough.\n", - DeviceObject)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpGetStandardInquiryData; - } - -__Retry_Request: - - // - // Build the cdb for SCSI-3 standard inquiry. - // - cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; - cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY; - cdb->CDB6INQUIRY3.EnableVitalProductData = 0; - cdb->CDB6INQUIRY3.AllocationLength = sizeof(INQUIRYDATA); - - passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); - passThrough->ScsiPassThrough.CdbLength = 6; - passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; - passThrough->ScsiPassThrough.DataIn = 1; - passThrough->ScsiPassThrough.DataTransferLength = sizeof(INQUIRYDATA); - passThrough->ScsiPassThrough.TimeOutValue = 20; - passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); - passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); - - DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, - DeviceObject, - passThrough, - passThrough, - length, - length, - FALSE, - &ioStatus); - - status = ioStatus.Status; - senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); - - if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { - - // - // Get the returned data. - // - inquiryData = (PINQUIRYDATA)(passThrough->DataBuffer); - - RtlCopyMemory(InquiryData, inquiryData, sizeof(INQUIRYDATA)); - - } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && - (NT_SUCCESS(ioStatus.Status)) && - (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { - - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - // - // Retry the request - // - RtlZeroMemory(passThrough, length); - goto __Retry_Request; - - } else { - - // Failed to get inquiry data - // Here it is possible that status is success, but scsi status is not. - // If so, set status to unsuccessful. - if (NT_SUCCESS(status)){ - status = STATUS_UNSUCCESSFUL; - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetStandardInquiryData (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n", - DeviceObject, - status, - passThrough->ScsiPassThrough.ScsiStatus)); - } - -__Exit_DsmpGetStandardInquiryData: - - // - // Free the passthrough + data buffer. - // - if (passThrough) { - DsmpFreePool(passThrough); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetStandardInquiryData (DevObj %p): Exiting function with status %x.\n", - DeviceObject, - status)); - - return status; -} - - -BOOLEAN -DsmpCheckScsiCompliance( - _In_ IN PDEVICE_OBJECT TargetObject, - _In_ IN PINQUIRYDATA InquiryData, - _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, - _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList - ) -/*++ - -Routine Description: - - Helper routine to determine if the device is SPC-3 compliant. - -Arguments: - - DeviceObject - The port PDO that we're determining compliance for. - InquiryData - Pointer to its inquiry data. - Descriptor - Pointer to its VPD page 0x80 data - DeviceIdList - Pointer to its VPD page 0x83 data - -Return Value: - - TRUE if compliant, else FALSE. - ---*/ -{ - BOOLEAN supported = FALSE /* TRUE */; - UCHAR deviceType; - UCHAR qualifier; - - UNREFERENCED_PARAMETER(DeviceIdList); - UNREFERENCED_PARAMETER(Descriptor); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpCheckScsiCompliance (DevObj %p): Entering function.\n", - TargetObject)); - - deviceType = InquiryData->DeviceType & 0x1F; - qualifier = (InquiryData->DeviceTypeQualifier >> 0x5) & 0x7; - - if ((deviceType | qualifier) == 0x7F) { - - supported = FALSE; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpCheckScsiCompliance (DevObj %p): Exiting function with Supported = %u.\n", - TargetObject, - supported)); - - return supported; -} - - -BOOLEAN -DsmpDeviceSupported( - _In_ IN PDSM_CONTEXT Context, - _In_ IN PCSTR VendorId, - _In_ IN PCSTR ProductId - ) -/*++ - -Routine Description: - - This routine determines whether the device is supported by traversing the SupportedDevice - list and comparing to the VendorId/ProductId values passed in. - -Arguments: - - Context - Context value given to the multipath driver during registration. - VendorId - Pointer to the inquiry data VendorId. - ProductId - Pointer to the inquiry data ProductId. - -Return Value: - - TRUE - If VendorId/ProductId is found. - ---*/ -{ - UNICODE_STRING deviceName; - UNICODE_STRING productName; - ANSI_STRING ansiVendor; - ANSI_STRING ansiProduct; - NTSTATUS status; - BOOLEAN supported = FALSE; - KIRQL oldIrql; - UNICODE_STRING tempStrings; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpDeviceSupported (DsmCtxt %p): Entering function.\n", - Context)); - - KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql); - - RtlInitUnicodeString(&tempStrings, NULL); - tempStrings.Buffer = DsmpAllocatePool(NonPagedPoolNx, Context->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED); - - if (tempStrings.Buffer) { - - RtlCopyMemory(tempStrings.Buffer, Context->SupportedDevices.Buffer, Context->SupportedDevices.Length); - tempStrings.Length = Context->SupportedDevices.Length; - tempStrings.MaximumLength = Context->SupportedDevices.MaximumLength; - - } else { - - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate temporary list (error %x).\n", - Context, - status)); - - KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); - - goto __Exit_DsmpDeviceSupported; - } - - KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); - - // - // The SupportedDevice list was built in DriverEntry from the services key. - // - if (tempStrings.MaximumLength == 0) { - - // - // List is empty. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDeviceSupported (DsmCtxt %p): No supported Device in the list.\n", - Context)); - - goto __Exit_DsmpDeviceSupported; - } - - RtlInitUnicodeString(&productName, NULL); - - // - // Convert the inquiry fields into ansi strings. - // - RtlInitAnsiString(&ansiVendor, VendorId); - RtlInitAnsiString(&ansiProduct, ProductId); - - // - // Allocate the deviceName buffer. Needs to be 8+16 plus NULL. - // (productId length + vendorId length + NULL). - // - deviceName.MaximumLength = 25 * sizeof(WCHAR); - deviceName.Buffer = DsmpAllocatePool(PagedPool, deviceName.MaximumLength, DSM_TAG_SUPPORTED_DEV); - - if (deviceName.Buffer) { - - // - // Convert the vendorId to unicode. - // - status = RtlAnsiStringToUnicodeString(&deviceName, &ansiVendor, FALSE); - if (NT_SUCCESS(status)) { - - // - // Convert the productId to unicode. - // - status = RtlAnsiStringToUnicodeString(&productName, &ansiProduct, TRUE); - - if (NT_SUCCESS(status)) { - - // - // 'cat' them. - // - status = RtlAppendUnicodeStringToString(&deviceName, &productName); - - if (NT_SUCCESS(status)) { - - // - // Run the list of supported devices that was captured from the registry - // and see if this one is in the list. - // - supported = DsmpFindSupportedDevice(&deviceName, - &tempStrings); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDeviceSupported (DsmCtxt %p): Failed to append product name. Status %x.\n", - Context, - status)); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDeviceSupported (DsmCtxt %p): Failed to convert ansi vendor string to unicode. Status %x\n", - Context, - status)); - } - - DsmpFreePool(deviceName.Buffer); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate device name buffer.\n", - Context)); - } - -__Exit_DsmpDeviceSupported: - - if (tempStrings.Buffer) { - DsmpFreePool(tempStrings.Buffer); - } - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpDeviceSupported (DsmCtxt %p): Exiting function with supported = %u.\n", - Context, - supported)); - - return supported; -} - - -BOOLEAN -DsmpFindSupportedDevice( - _In_ IN PUNICODE_STRING DeviceName, - _In_ IN PUNICODE_STRING SupportedDevices - ) -/*++ - -Routine Description: - - This routine compares the two unicode strings for a match. - -Arguments: - - DeviceName - String built from the current device's inquiry data. - SupportedDevices - MULTI_SZ of devices that are supported. - -Return Value: - - TRUE - If VendorId/ProductId is found. - ---*/ -{ - PWSTR devices = SupportedDevices->Buffer; - ULONG bufferLengthLeft = SupportedDevices->MaximumLength / sizeof(WCHAR); - UNICODE_STRING unicodeString; - USHORT originalLength = DeviceName->Length; - LONG compare; - BOOLEAN supported = FALSE; - WCHAR tempString[32]; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFindSupportedDevice (DevName %ws): Entering function.\n", - DeviceName->Buffer)); - - // - // 'devices' is the current buffer in the MULTI_SZ built from - // the registry. - // - while (devices[0]) { - - RtlZeroMemory(tempString, sizeof(tempString)); - - if (!NT_SUCCESS(RtlStringCchCopyNW(tempString, sizeof(tempString) / sizeof(tempString[0]), devices, bufferLengthLeft))) { - - tempString[(sizeof(tempString) / sizeof(tempString)) - 1] = L'\0'; - } - - // - // Make the current entry into a unicode string. - // - RtlInitUnicodeString(&unicodeString, tempString); - - // - // Compare this one with the current device. - // However, for storages that make up the product id on-the-fly, MPIO - // allows for matching based just on substring (product-id-prefix so to - // speak). - // - if (unicodeString.Length < DeviceName->Length) { - DeviceName->Length = unicodeString.Length; - } - - compare = RtlCompareUnicodeStrings(unicodeString.Buffer, - unicodeString.Length / sizeof(WCHAR), - DeviceName->Buffer, - DeviceName->Length / sizeof(WCHAR), - TRUE); - - DeviceName->Length = originalLength; - - if (compare == 0) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpFindSupportedDevice (DevName %ws): Device support found in the registry.\n", - DeviceName->Buffer)); - - supported = TRUE; - break; - } - - // - // Advance to next entry in the MULTI_SZ. - // - devices += (unicodeString.MaximumLength / sizeof(WCHAR)); - - bufferLengthLeft -= (unicodeString.MaximumLength / sizeof(WCHAR)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpFindSupportedDevice (DevName %ws): Exiting function with Supported = %u.\n", - DeviceName->Buffer, - supported)); - - return supported; -} - -_Success_(return!=0) -PVOID -DsmpParseDeviceID( - _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID, - _In_ IN DSM_DEVID_TYPE DeviceIdType, - _In_opt_ IN PULONG IdNumber, - _Out_opt_ OUT PSTORAGE_IDENTIFIER_CODE_SET CodeSet, - _In_ IN BOOLEAN Legacy - ) -/*++ - -Routine Description: - - This routine builds a serial number string based on the information - in the VPD page 0x83 data if serial number is requested, else it - returns the appropriate identifier requested. - - Caller must free the buffer. - -Arguments: - - DeviceIdList - VPD Page 0x83 information. - DeviceIdType - Type of identifier that the DeviceID is being parsed for - IdNumber - If there are multiple identifiers of type DeviceIdType, this parameter - determines which among them to actually return. - IMPORTANT: This number is one-based (not zero-based). - CodeSet - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. This - returns the code set that was used when building the serial number. - Legacy - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. If the - code set of the identifier is StorageIdCodeSetBinary, this determines - whether to use the legacy method of binary to ascii conversion. - -Return Value: - - Requested Device identifier. - ---*/ -{ - PSTORAGE_IDENTIFIER identifier; - STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; // Preload with a bogus value. - STORAGE_IDENTIFIER_TYPE type = 0xF; - STORAGE_ASSOCIATION_TYPE association = 0xF; - ULONG numberIds; - ULONG i; - ULONG identifierSize = 0; - PUCHAR bytes = NULL; - PVOID buffer = NULL; - BOOLEAN done = FALSE; - ULONG idNumber = MAXULONG; - ULONG matches = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpParseDeviceID (DevIdDesc %p): Entering function - IdType %x.\n", - DeviceID, - DeviceIdType)); - - if (IdNumber) { - idNumber = *IdNumber; - } - - // - // Get the number of encapsulated identifiers. - // - numberIds = DeviceID->NumberOfIdentifiers; - - if (idNumber != MAXULONG && idNumber > numberIds) { - goto __Exit_DsmpParseDeviceID; - } - - // - // Get a pointer to the first one. - // - identifier = (PSTORAGE_IDENTIFIER)(DeviceID->Identifiers); - - for (i = 0; i < numberIds && !done; i++) { - - switch (DeviceIdType) { - - case DSM_DEVID_SERIAL_NUMBER: { - - // - // The way this works is that we will go through all the identifiers - // Order of preference will be LUN-associated over Target-associated. - // Further, upon same association, preference will be based on type as - // follows: 0x8, 0x3, 0x2, 0x1, 0x0. - // So an existing identifier will be discarded if a better one is found. - // If two identifiers have the same type, we will prefer the one will - // the larger length. - // - - // - // 1. Ensure that the association is for either the LUN or target. (If neither, ignore id). - // 2. If association is with target, don't it consider if current candidate has assocation with LUN. - // 3. If considering this identifier, order of preference is 8 > 3 > 2 > 1 > 0. - // 4. If this id type is same as current candidate, consider it only if it is of greater length. - // - if (((identifier->Association == StorageIdAssocDevice) || - (identifier->Association == 0x2 && association != StorageIdAssocDevice)) && - ((type == identifier->Type && identifierSize < identifier->IdentifierSize) || - (type != identifier->Type && DsmpIsPreferredDeviceId(type, identifier->Type)))) { - - // - // Get a pointer to the id itself. - // - bytes = identifier->Identifier; - - // - // The id's size. - // - identifierSize = identifier->IdentifierSize; - - // - // Get the type, code set, and association. - // - type = identifier->Type; - codeSet = identifier->CodeSet; - association = identifier->Association; - - matches++; - } - - break; - } - - case DSM_DEVID_RELATIVE_TARGET_PORT: { - - // - // Ensure that the association is for the target port. - // - if (identifier->Association != StorageIdAssocPort) { - - if ((i + 1) < numberIds) { - identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); - } - - continue; - } - - if (identifier->Type == StorageIdTypePortRelative) { - - // - // Get a pointer to the id itself. - // - bytes = identifier->Identifier; - - // - // The id's size. - // - identifierSize = identifier->IdentifierSize; - - type = identifier->Type; - codeSet = identifier->CodeSet; - association = identifier->Association; - - matches++; - } - - break; - } - - case DSM_DEVID_TARGET_PORT_GROUP: { - - // - // Ensure that the association is for the target port. - // - if (identifier->Association != StorageIdAssocPort) { - - if ((i + 1) < numberIds) { - identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); - } - - continue; - } - - if (identifier->Type == 0x5) { - - // - // Get a pointer to the id itself. - // - bytes = identifier->Identifier; - - // - // Move this by two bytes because first two bytes are reservered - // - bytes += sizeof(USHORT); - - // - // The id's size. Reduce the size by 2 bytes (to account - // for the reservered bytes) - // - identifierSize = identifier->IdentifierSize - sizeof(USHORT); - - type = identifier->Type; - codeSet = identifier->CodeSet; - association = identifier->Association; - - matches++; - } - - break; - } - - default: break; - } - - - if (idNumber != MAXULONG && idNumber == matches) { - done = TRUE; - } - - // - // Advance to the next identifier in the buffer. - // - if ((i + 1) < numberIds) { - identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); - } - } - - if (idNumber != MAXULONG && idNumber > matches) { - goto __Exit_DsmpParseDeviceID; - } - - if (DeviceIdType == DSM_DEVID_SERIAL_NUMBER) { - - if (type != StorageIdTypeScsiNameString && - type != StorageIdTypeFCPHName && - type != StorageIdTypeEUI64 && - type != StorageIdTypeVendorId && - type != StorageIdTypeVendorSpecific) { - - DSM_ASSERT(FALSE); - bytes = NULL; - identifierSize = 0; - type = association = 0xF; - codeSet = StorageIdCodeSetReserved; - } - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpParseDeviceID (DevIdDesc %p): IdentifierSize = %u, Type = %u, Association = %u, CodeSet = %u.\n", - DeviceID, - identifierSize, - type, - association, - codeSet)); - - if (!bytes) { - goto __Exit_DsmpParseDeviceID; - } - - if (codeSet == StorageIdCodeSetBinary) { - - // - // Need to convert to ascii. - // - buffer = DsmpBinaryToAscii(bytes, - identifierSize, - &identifierSize, - Legacy); - - } else { - - if (identifierSize) { - // - // Allocate a buffer that is the size of the data, plus one for NULL. - // - buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize + 1, DSM_TAG_DEV_ID); - DSM_ASSERT(buffer); - - if (buffer) { - - // - // Copy over the id. - // - RtlCopyMemory(buffer, bytes, identifierSize); - } - } - } - - if (CodeSet) { - *CodeSet = codeSet; - } - - } else { - - if (identifierSize) { - - DSM_ASSERT((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) || - (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT))); - - _Analysis_assume_((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) || - (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT))); - - buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize, DSM_TAG_DEV_ID); - - if (buffer) { - - if (DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT) { - - GetUlongFrom4ByteArray(bytes, *((PULONG)buffer)); - - } else if (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP) { - - *((PUSHORT)buffer) = (bytes[0] << 8) | (bytes[1]); - } - } - } - } - -__Exit_DsmpParseDeviceID: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpParseDeviceID (DevIdDesc %p): Exiting function with buffer %p.\n", - DeviceID, - buffer)); - - return buffer; -} - - -PUCHAR -DsmpBinaryToAscii( - _In_reads_(Length) IN PUCHAR HexBuffer, - _In_ IN ULONG Length, - _Inout_ IN OUT PULONG UpdateLength, - _In_ IN BOOLEAN Legacy - ) -/*++ - -Routine Description: - - This routine will convert HexBuffer into an ascii NULL-terminated string. - - Note: This routine will allocate memory for storing the ascii string. It is - the responsibility of the caller to free this buffer. - -Arguments: - - HexBuffer - Pointer to the binary data. - Length - Length, in bytes, of HexBuffer. - UpdateLength - Storage to place the actual length of the returned string. - Legacy - Use the legacy method for the conversion. - -Return Value: - - Serial Number string, or NULL if an error occurred. - ---*/ -{ - static UCHAR IntegerTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - ULONG i; - ULONG j; - ULONG actualLength; - PUCHAR buffer = NULL; - UCHAR highWord; - UCHAR lowWord; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBinaryToAscii (HexBuff %p): Entering function.\n", - HexBuffer)); - - if (Length == 0) { - *UpdateLength = 0; - goto __Exit_DsmpBinaryToAscii; - } - - if (Legacy) { - // - // Do a pre-test on the buffer to determine the length actually needed. - // - for (i = 0, actualLength = 0; i < Length; i++) { - - if (HexBuffer[i] < 0x10) { - actualLength++; - } else { - actualLength += 2; - } - } - - // - // Add room for a terminating NULL. - // - actualLength++; - } else { - // - // We need one character for each nibble, plus one for the terminating NULL. - // - actualLength = (Length * 2) + 1; - } - - // - // Allocate the buffer. - // - buffer = DsmpAllocatePool(NonPagedPoolNx, - actualLength, - DSM_TAG_BIN_TO_ASCII); - if (!buffer) { - *UpdateLength = 0; - goto __Exit_DsmpBinaryToAscii; - } - - for (i = 0, j = 0; i < Length && j < actualLength; i++) { - - if (Legacy && (HexBuffer[i] < 0x10)) { - - // - // If legacy is mentioned and it's 0x0F or less, - // just convert the entire byte. - // - buffer[j++] = IntegerTable[HexBuffer[i]]; - } else { - - // - // Split out each nibble from the binary byte. - // - highWord = HexBuffer[i] >> 4; - lowWord = HexBuffer[i] & 0x0F; - - // - // Using the lookup table, convert and stuff into - // the ascii buffer. - // - buffer[j++] = IntegerTable[highWord]; - buffer[j++] = IntegerTable[lowWord]; - } - } - - // - // Update the caller's length field. - // - *UpdateLength = actualLength; - -__Exit_DsmpBinaryToAscii: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBinaryToAscii (HexBuff %p): Exiting function with buffer %s.\n", - HexBuffer, - (const char*) buffer)); - - return buffer; -} - - -PSTR -DsmpGetSerialNumber( - _In_ IN PDEVICE_OBJECT DeviceObject - ) -/*++ - -Routine Description: - - Helper routine to send an inquiry with EVPD set to get the serial number page. - Used if the serial number is not embedded in the device descriptor (this device probably - doesn't support VPD page 0x00). - - Note: This routine will allocate memory for storing the serial number. It is - the responsibility of the caller to free this buffer. - -Arguments: - - DeviceObject - The port PDO to which the command should be sent. - -Return Value: - - The serial number (null-terminated string) or NULL if the call fails. - ---*/ -{ - PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; - PVPD_SERIAL_NUMBER_PAGE serialPage; - PCDB cdb; - PSTR serialNumber = NULL; - IO_STATUS_BLOCK ioStatus; - ULONG length; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetSerialNumber (DevObj %p): Entering function.\n", - DeviceObject)); - - // - // Build an inquiry command with EVPD and pagecode of 0x80 (serial number). - // - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - passThrough = DsmpAllocatePool(NonPagedPoolNx, - length, - DSM_TAG_PASS_THRU); - if (!passThrough) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for passthrough.\n", - DeviceObject)); - - goto __Exit_DsmpGetSerialNumber; - } - - // - // Build the cdb. - // - cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; - cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; - cdb->CDB6INQUIRY.Reserved1 = 1; - cdb->CDB6INQUIRY.PageCode = VPD_SERIAL_NUMBER; - cdb->CDB6INQUIRY.AllocationLength = DSM_SERIAL_NUMBER_BUFFER_SIZE; - - passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); - passThrough->ScsiPassThrough.CdbLength = 6; - passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; - passThrough->ScsiPassThrough.DataIn = 1; - passThrough->ScsiPassThrough.DataTransferLength = DSM_SERIAL_NUMBER_BUFFER_SIZE; - passThrough->ScsiPassThrough.TimeOutValue = 20; - passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); - passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); - - DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, - DeviceObject, - passThrough, - passThrough, - length, - length, - FALSE, - &ioStatus); - if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && - (NT_SUCCESS(ioStatus.Status))) { - - ULONG inx; - - // - // Get the returned data. - // - serialPage = (PVPD_SERIAL_NUMBER_PAGE)(passThrough->DataBuffer); - - // - // Allocate a buffer to hold just the serial number plus a null terminator - // - serialNumber = DsmpAllocatePool(NonPagedPoolNx, - serialPage->PageLength + 1, - DSM_TAG_SERIAL_NUM); - if (serialNumber) { - - // - // Copy it over. - // - RtlCopyMemory(serialNumber, serialPage->SerialNumber, serialPage->PageLength); - - // - // Some devices return binary data for the serial number. - // Convert to a more ascii-ish format so that other routines don't have a problem. - // - for (inx = 0; inx < serialPage->PageLength; inx++) { - if (serialNumber[inx] == '\0') { - serialNumber[inx] = ' '; - } - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for serialnumber.\n", - DeviceObject)); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetSerialNumber (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n", - DeviceObject, - ioStatus.Status, - passThrough->ScsiPassThrough.ScsiStatus)); - } - -__Exit_DsmpGetSerialNumber: - - // - // Free the passthrough + data buffer. - // - if (passThrough) { - - DsmpFreePool(passThrough); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetSerialNumber (DevObj %p): Exiting function with serial number %s.\n", - DeviceObject, - (const char*)serialNumber)); - - // - // Return the sn. - // - return serialNumber; -} - - -NTSTATUS -DsmpDisableImplicitStateTransition( - _In_ IN PDEVICE_OBJECT TargetDevice, - _Out_ OUT PBOOLEAN DisableImplicit - ) -/*++ - -Routine Description: - - Send down request to disable implicit ALUA state transition. - The function first sends down a mode sense to get the control extension mode - sense data. It then clears the IALUAE bit and sends down a mode select. - -Arguements: - - TargetDevice - Device object that will be target of this command. - DisableImplicit - Flag returned to the caller to indicate whether or not - implicit transitions are disabled. - -Return Value : - - STATUS_SUCCESS if the command succeeds. - Appropriate NTSTATUS code on failure - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; - PCDB cdb; - IO_STATUS_BLOCK ioStatus; - ULONG length; - PSPC3_CONTROL_EXTENSION_MODE_PAGE controlExtensionPage = NULL; - PSENSE_DATA senseData = NULL; - BOOLEAN implicitDisabled = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): Entering function.\n", - TargetDevice)); - - // - // First build the mode sense command to get the control extension parameters. - // - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - passThrough = DsmpAllocatePool(NonPagedPoolNx, - length, - DSM_TAG_PASS_THRU); - if (!passThrough) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): Failed to allocate mem for passthrough.\n", - TargetDevice)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpDisableImplicitStateTransition; - } - -__Retry_ModeSense: - - passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); - passThrough->ScsiPassThrough.CdbLength = 6; - passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; - passThrough->ScsiPassThrough.DataIn = 1; - passThrough->ScsiPassThrough.DataTransferLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); - passThrough->ScsiPassThrough.TimeOutValue = 20; - passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); - passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); - - // - // Build the cdb for mode sense. - // - cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; - cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; - cdb->MODE_SENSE.Dbd = 1; - cdb->MODE_SENSE.PageCode = 0xA; - cdb->MODE_SENSE.SubPageCode = 0x01; - cdb->MODE_SENSE.AllocationLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); - - DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, - TargetDevice, - passThrough, - passThrough, - length, - length, - FALSE, - &ioStatus); - - status = ioStatus.Status; - senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); - - if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { - - controlExtensionPage = (PSPC3_CONTROL_EXTENSION_MODE_PAGE)(passThrough->DataBuffer); - - if (controlExtensionPage->ImplicitALUAEnable) { - - controlExtensionPage->ImplicitALUAEnable = 0; - -__Retry_ModeSelect: - - RtlZeroMemory(passThrough->SenseInfoBuffer, passThrough->ScsiPassThrough.SenseInfoLength); - - passThrough->ScsiPassThrough.DataIn = 0; - - // - // Build the cdb for mode select. - // - RtlZeroMemory(cdb, 6); - cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; - cdb->MODE_SELECT.SPBit = 0; - cdb->MODE_SELECT.PFBit = 1; - cdb->MODE_SELECT.ParameterListLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); - - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, - TargetDevice, - passThrough, - passThrough, - length, - length, - FALSE, - &ioStatus); - - status = ioStatus.Status; - senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); - - if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { - - implicitDisabled = TRUE; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions turned off successfully.\n", - TargetDevice)); - - } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && - (NT_SUCCESS(status)) && - (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { - - // - // Retry the request - // - goto __Retry_ModeSelect; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): ModeSelect failed - NTStatus 0x%x, ScsiStatus 0x%x.\n", - TargetDevice, - status, - passThrough->ScsiPassThrough.ScsiStatus)); - } - } else { - - implicitDisabled = TRUE; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions already turned OFF.\n", - TargetDevice)); - } - } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && - (NT_SUCCESS(status)) && - (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { - - length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); - - // - // Retry the request - // - RtlZeroMemory(passThrough, length); - goto __Retry_ModeSense; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): ModeSense failed - NTStatus 0x%x, ScsiStatus 0x%x.\n", - TargetDevice, - status, - passThrough->ScsiPassThrough.ScsiStatus)); - } - -__Exit_DsmpDisableImplicitStateTransition: - - // - // Free the passthrough + data buffer. - // - if (passThrough) { - DsmpFreePool(passThrough); - } - - // - // Return whether IALUAE is set to 0. - // - if (DisableImplicit) { - - *DisableImplicit = implicitDisabled; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpDisableImplicitStateTransition (DevObj %p): Exiting function with status %x.\n", - TargetDevice, - status)); - - return status; -} - - -PWSTR -DsmpBuildHardwareId( - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - Construct a string concatinating VendorId with ProductId. - -Arguements: - - DeviceInfo - Device Extension - -Return Value : - - NULL terminated hardware id if it was built successfully. - NULL in case of failure. - ---*/ -{ - PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; - PWSTR hardwareId = NULL; - SIZE_T vendorIDLength = 0; - SIZE_T productIDLength = 0; - PCSZ vendorIdOffset; - PCSZ productIdOffset; - SIZE_T sizeNeeded; - NTSTATUS status = STATUS_SUCCESS; - ANSI_STRING ansiString; - UNICODE_STRING unicodeString; - ULONG offset; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): Entering function.\n", - DeviceInfo)); - - deviceDescriptor = &(DeviceInfo->Descriptor); - - // - // Save the vendorid and productid offset in Device Descriptor - // - offset = deviceDescriptor->ProductIdOffset; - if ((offset != 0) && (offset != MAXULONG)) { - - productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - offset = deviceDescriptor->VendorIdOffset; - if ((offset != 0) && (offset != MAXULONG)) { - - vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - if (!vendorIDLength || !productIDLength) { - - status = STATUS_UNSUCCESSFUL; - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): Invalid vendor and/or product id.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildHardwareId; - } - - sizeNeeded = vendorIDLength + productIDLength; - hardwareId = DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_HARDWARE_ID); - if (!hardwareId) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): Failed to allocate memory for device name.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpBuildHardwareId; - } - - // - // Build the NULL terminated hardwareId whose format is : - // - // VendorIdProductId - // - vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->VendorIdOffset); - RtlInitAnsiString(&ansiString, vendorIdOffset); - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT)vendorIDLength; - unicodeString.Buffer = hardwareId; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): Failed to convert vendor id to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildHardwareId; - } - - productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset); - RtlInitAnsiString(&ansiString, productIdOffset); - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT)productIDLength; - unicodeString.Buffer = hardwareId + strlen(((PCHAR)deviceDescriptor) + offset); - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): Failed to convert product id to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildHardwareId; - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): HardwareId is %ws.\n", - DeviceInfo, - hardwareId)); - -__Exit_DsmpBuildHardwareId: - - if (hardwareId && !NT_SUCCESS(status)) { - DsmpFreePool(hardwareId); - hardwareId = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildHardwareId (DevInfo %p): Exiting function with deviceName %ws.\n", - DeviceInfo, - hardwareId)); - - return hardwareId; -} - - -PWSTR -DsmpBuildDeviceNameLegacyPage0x80( - _In_ IN PDSM_DEVICE_INFO DeviceInfo - ) -/*++ - -Routine Description: - - Construct a string from VendorId, ProductId, and SerialNumber (page 0x80 - info) of the device. - -Arguements: - - DeviceInfo - Device Extension - -Return Value : - - STATUS_SUCCESS if the device name was built successfully. - - Appropriate NTSTATUS code on failure - ---*/ -{ - PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; - PWCHAR deviceName = NULL; - PWCHAR tmpPtr; - PWCHAR vendorID = NULL; - PWCHAR productID = NULL; - PWCHAR serialID = NULL; - ANSI_STRING ansiString; - UNICODE_STRING unicodeString; - UNICODE_STRING unicodeDeviceName; - SIZE_T vendorIDLength = 0; - SIZE_T productIDLength = 0; - SIZE_T serialIDLength = 0; - ULONG offset; - SIZE_T sizeNeeded; - NTSTATUS status = STATUS_SUCCESS; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Entering function.\n", - DeviceInfo)); - - deviceDescriptor = &(DeviceInfo->Descriptor); - - // - // Save the vendorid, productid, and serialnumber offset - // in Device Descriptor - // - offset = deviceDescriptor->VendorIdOffset; - if ((offset != 0) && (offset != MAXULONG)) { - - vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - offset = deviceDescriptor->ProductIdOffset; - if ((offset != 0) && (offset != MAXULONG)) { - - productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - offset = deviceDescriptor->SerialNumberOffset; - if ((offset != 0) && (offset != MAXULONG)) { - - serialIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - // - // Allocate buffers to use to convert the IDs from ANSI to Unicode and - // eventually build the device name. - // - if (vendorIDLength > 0) { - vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME); - if (!vendorID) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for vendor ID.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (productIDLength > 0) { - productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME); - if (!productID) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for product ID.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (serialIDLength > 0) { - serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME); - if (!serialID) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for serial ID.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - sizeNeeded = vendorIDLength + productIDLength + serialIDLength; - if (sizeNeeded > 0) { - - // - // Account for the terminating NULL if serial id is empty. - // - - sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE); - - deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME); - if (!deviceName) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for device name.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } else { - - status = STATUS_UNSUCCESSFUL; - } - - if (!NT_SUCCESS(status)) { - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - - // - // Build the NULL terminated device name whose format is : - // - // VendorId_ProductId_SerialNumber - // - - unicodeDeviceName.Length = 0; - unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded; - unicodeDeviceName.Buffer = deviceName; - - if (vendorIDLength) { - - PCSZ vendorIdOffset; - - vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + - deviceDescriptor->VendorIdOffset); - - RtlInitAnsiString(&ansiString, vendorIdOffset); - - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT) vendorIDLength; - unicodeString.Buffer = vendorID; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert vendor id to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - - // - // If there are spaces in the id, set NULL at the first space. - // - tmpPtr = wcschr(vendorID, L' '); - if (tmpPtr != NULL) { - *tmpPtr = WNULL; - } - - status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate vendor ID to device name.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - - RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); - } - - if (productIDLength) { - - PCSZ productIdOffset; - - productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + - deviceDescriptor->ProductIdOffset); - - RtlInitAnsiString(&ansiString, productIdOffset); - - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT) productIDLength; - unicodeString.Buffer = productID; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert product id to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - - // - // If there are spaces in the id, set NULL at the first space. - // - tmpPtr = wcschr(productID, L' '); - if (tmpPtr != NULL) { - *tmpPtr = WNULL; - } - - status = RtlUnicodeStringCatString(&unicodeDeviceName, productID); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate product ID to device name.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - - RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); - } - - // - // Serial number - // - if (serialIDLength) { - - PCSZ serialNumberOffset; - - serialNumberOffset = (PCSZ)((PUCHAR)deviceDescriptor + - deviceDescriptor->SerialNumberOffset); - - RtlInitAnsiString(&ansiString, serialNumberOffset); - - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT) serialIDLength; - unicodeString.Buffer = serialID; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert serial number to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - - // - // If there are spaces in the id, set NULL at the first space. - // - tmpPtr = wcschr(serialID, L' '); - if (tmpPtr != NULL) { - *tmpPtr = WNULL; - } - - status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate serial number to device name.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; - } - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Device Name is %ws.\n", - DeviceInfo, - deviceName)); - -__Exit_DsmpBuildDeviceNameLegacyPage0x80: - - if (vendorID) { - DsmpFreePool(vendorID); - } - - if (productID) { - DsmpFreePool(productID); - } - - if (serialID) { - DsmpFreePool(serialID); - } - - if (deviceName && !NT_SUCCESS(status)) { - DsmpFreePool(deviceName); - deviceName = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Exiting function with deviceName %ws.\n", - DeviceInfo, - deviceName)); - - return deviceName; -} - - - -PWSTR -DsmpBuildDeviceName( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_reads_(SerialNumberLength) IN PSTR SerialNumber, - _In_ IN SIZE_T SerialNumberLength - ) -/*++ - -Routine Description: - - Construct a string from VendorId, ProductId, and SerialNumber (page 0x83 - identifiers) of the device. - -Arguements: - - DeviceInfo - Device Extension - SerialNumber - Device serial number built from appropriate page 0x83 identifier - SerialNumberLength - Length (in chars) of the passed in serial number buffer - -Return Value : - - Device name if it was built successfully. - NULL in case of failure. - ---*/ -{ - PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; - PWCHAR deviceName = NULL; - PWCHAR tmpPtr; - PWCHAR vendorID = NULL; - PWCHAR productID = NULL; - PWCHAR serialID = NULL; - ANSI_STRING ansiString; - UNICODE_STRING unicodeString; - UNICODE_STRING unicodeDeviceName; - SIZE_T vendorIDLength = 0; - SIZE_T productIDLength = 0; - SIZE_T serialIDLength = 0; - ULONG offset; - SIZE_T sizeNeeded; - NTSTATUS status = STATUS_SUCCESS; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Entering function.\n", - DeviceInfo)); - - deviceDescriptor = &(DeviceInfo->Descriptor); - - // - // Save the vendorid, productid, and serialnumber offset - // in Device Descriptor - // - offset = deviceDescriptor->VendorIdOffset; - if ((offset != 0) && (offset != MAXULONG)) { - - vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - offset = deviceDescriptor->ProductIdOffset; - if ((offset != 0) && (offset != -1)) { - - productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; - } - - if (SerialNumber) { - - serialIDLength = (SerialNumberLength * sizeof(WCHAR)) + WNULL_SIZE; - } - - // - // Allocate buffers to use to convert the IDs from ANSI to Unicode and - // eventually build the device name. - // - if (vendorIDLength > 0) { - vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME); - if (!vendorID) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for vendor ID.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (productIDLength > 0) { - productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME); - if (!productID) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for product ID.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (serialIDLength > 0) { - serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME); - if (!serialID) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for serial ID.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - sizeNeeded = vendorIDLength + productIDLength + serialIDLength; - if (sizeNeeded > 0) { - - // - // Account for the terminating NULL if serial id is empty. - // - - sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE); - - deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME); - if (!deviceName) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for device name.\n", - DeviceInfo)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } else { - - status = STATUS_UNSUCCESSFUL; - } - - if (!NT_SUCCESS(status)) { - goto __Exit_DsmpBuildDeviceName; - } - - // - // Build the NULL terminated device name whose format is : - // - // VendorId_ProductId_SerialNumber - // - - unicodeDeviceName.Length = 0; - unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded; - unicodeDeviceName.Buffer = deviceName; - - if (vendorIDLength) { - - PCSZ vendorIdOffset; - - vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + - deviceDescriptor->VendorIdOffset); - - RtlInitAnsiString(&ansiString, vendorIdOffset); - - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT) vendorIDLength; - unicodeString.Buffer = vendorID; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to convert vendor id to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceName; - } - - // - // If there are spaces in the id, set NULL at the first space. - // - tmpPtr = wcschr(vendorID, L' '); - if (tmpPtr != NULL) { - *tmpPtr = WNULL; - } - - status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate vendor ID to device name.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceName; - } - - RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); - } - - if (productIDLength) { - - PCSZ productIdOffset; - - productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + - deviceDescriptor->ProductIdOffset); - - RtlInitAnsiString(&ansiString, productIdOffset); - - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT) productIDLength; - unicodeString.Buffer = productID; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to convert product id to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceName; - } - - // - // If there are spaces in the id, set NULL at the first space. - // - tmpPtr = wcschr(productID, L' '); - if (tmpPtr != NULL) { - *tmpPtr = WNULL; - } - - status = RtlUnicodeStringCatString(&unicodeDeviceName, productID); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate product ID to device name.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceName; - } - - RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); - } - - // - // Serial number - // - if (serialIDLength) { - - PSTR serialNumberOffset; - - serialNumberOffset = SerialNumber; - - RtlInitAnsiString(&ansiString, serialNumberOffset); - - unicodeString.Length = 0; - unicodeString.MaximumLength = (USHORT) serialIDLength; - unicodeString.Buffer = serialID; - - status = RtlAnsiStringToUnicodeString(&unicodeString, - &ansiString, - FALSE); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to convert serial number to unicode string.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceName; - } - - // - // If there are spaces in the id, set NULL at the first space. - // - tmpPtr = wcschr(serialID, L' '); - if (tmpPtr != NULL) { - *tmpPtr = WNULL; - } - - status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate serial number to device name.\n", - DeviceInfo)); - - goto __Exit_DsmpBuildDeviceName; - } - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Device Name is %ws.\n", - DeviceInfo, - deviceName)); - -__Exit_DsmpBuildDeviceName: - - if (vendorID) { - DsmpFreePool(vendorID); - } - - if (productID) { - DsmpFreePool(productID); - } - - if (serialID) { - DsmpFreePool(serialID); - } - - if (deviceName && !NT_SUCCESS(status)) { - DsmpFreePool(deviceName); - deviceName = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpBuildDeviceName (DevInfo %p): Exiting function with deviceName %ws.\n", - DeviceInfo, - deviceName)); - - return deviceName; -} - - -NTSTATUS -DsmpApplyDeviceNameCorrection( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy, - _In_ IN SIZE_T DeviceNameLegacyLen, - _In_reads_(DeviceNameLen) PWSTR DeviceName, - _In_ IN SIZE_T DeviceNameLen - ) -/*++ - -Routine Description: - - If the registry has a key name built with a legacy device name, this - function updates the key name with the current device name. - -Arguements: - - DeviceInfo - Device instance - DeviceNameLegacy - Device name built using legacy methods. - DeviceNameLegacyLen - Number of chars (including NULL) of the DeviceNameLegacy buffer. - DeviceName - Device name built using current methods. - DeviceNameLen - Number of chars (including NULL) of the DeviceName buffer. - -Return Value : - - STATUS_SUCCESS if the device's key was updated successfully. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE lbSettingsKey = NULL; - HANDLE deviceKeyLegacy = NULL; - HANDLE deviceKey = NULL; - OBJECT_ATTRIBUTES objectAttributes; - NTSTATUS status; - UNICODE_STRING deviceNameLegacy; - UNICODE_STRING deviceName; - - PAGED_CODE(); - - UNREFERENCED_PARAMETER(DeviceNameLen); - UNREFERENCED_PARAMETER(DeviceNameLegacyLen); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Entering function.\n", - DeviceInfo)); - - // - // First open LoadBalanceSettings key under the service key. - // - status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to open LB Settings key. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpApplyDeviceNameCorrection; - } - - RtlInitUnicodeString(&deviceNameLegacy, DeviceNameLegacy); - - InitializeObjectAttributes(&objectAttributes, - &deviceNameLegacy, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - lbSettingsKey, - (PSECURITY_DESCRIPTOR) NULL); - - // - // Open the old device key under DsmLoadBalanceSettings key. - // The name of this key is the one built using legacy methods - either a - // serial number from VPD page 0x80 or an aliased serial number from VPD - // page 0x83. - // - status = ZwOpenKey(&deviceKeyLegacy, - KEY_ALL_ACCESS, - &objectAttributes); - - if (NT_SUCCESS(status)) { - - ULONG disposition; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name exists.\n", - DeviceInfo)); - - RtlInitUnicodeString(&deviceName, DeviceName); - - InitializeObjectAttributes(&objectAttributes, - &deviceName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - lbSettingsKey, - (PSECURITY_DESCRIPTOR) NULL); - - // - // Since the old name key exists, create one with the new name. - // - status = ZwCreateKey(&deviceKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - &disposition); - - if (NT_SUCCESS(status)) { - - // - // The new key shouldn't exist if the old one does. - // If it does, it indicates a error occured the previous time - // this was tried, so just copy over the old subtree anyways now. - // - DSM_ASSERT(disposition == REG_CREATED_NEW_KEY); - - // - // Copy over the entire subtree of the old key over to the new key. - // - status = DsmpRegCopyTree(deviceKeyLegacy, deviceKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to copy over the old device key's subtree. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpApplyDeviceNameCorrection; - } - - // - // Delete the old key name. - // - status = DsmpRegDeleteTree(deviceKeyLegacy); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to delete the old device key's subtree. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpApplyDeviceNameCorrection; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to create the new device key. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpApplyDeviceNameCorrection; - } - - } else if (status == STATUS_INVALID_HANDLE || - status == STATUS_OBJECT_NAME_NOT_FOUND) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name does not exist.\n", - DeviceInfo)); - - status = STATUS_SUCCESS; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to query key with old device name. Status %x\n", - DeviceInfo, - status)); - } - -__Exit_DsmpApplyDeviceNameCorrection: - - if (deviceKey) { - ZwClose(deviceKey); - } - - if (deviceKeyLegacy) { - ZwClose(deviceKeyLegacy); - } - - if (lbSettingsKey) { - ZwClose(lbSettingsKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpApplyDeviceNameCorrection (DevInfo %p): Exiting function with status %x\n", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryDeviceLBPolicyFromRegistry( - _In_ PDSM_DEVICE_INFO DeviceInfo, - _In_ PWSTR RegistryKeyName, - _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Inout_ PULONGLONG PreferredPath, - _Inout_ PUCHAR ExplicitlySet - ) -/*++ - -Routine Description: - - Query the saved load balance policy and preferred path for this device from - the registry. - Also returns whether this setting was explicitly set via WMI call to SetLBPolicy, - (as opposed to the settings being made based on defaults determined through the - storage's ALUA capabilities). - -Arguements: - - DeviceInfo - The instance of the LUN through a paricular path - RegistryKeyName - DeviceName representing this LUN - LoadBalanceType - Type of LB policy. - PreferredPath - The preferred path for the device. - ExplicitlySet - Flag reflecting if LB policy was explicitly set. - -Return Value : - - STATUS_SUCCESS if we were able to successfully query the registry for the info. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE lbSettingsKey = NULL; - HANDLE deviceKey = NULL; - UNICODE_STRING subKeyName; - OBJECT_ATTRIBUTES objectAttributes; - NTSTATUS status; - UNICODE_STRING keyValueName; - ULONG length; - struct _explicitSet { - KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; - UCHAR Data; - } explicitSet; - struct _preferredPath { - KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; - ULONGLONG Data; - } preferredPath; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Entering function.\n", - DeviceInfo)); - - // - // Query the Load Balance settings for the given device from the registry. - // First open LoadBalanceSettings key under the service key. - // - status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to open LB Settings key. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry; - } - - RtlInitUnicodeString(&subKeyName, RegistryKeyName); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - lbSettingsKey, - (PSECURITY_DESCRIPTOR) NULL); - - // - // Create or Open the device key under DsmLoadBalanceSettings key. - // The name of this key is the one built in DsmpBuildDeviceName - // - status = ZwCreateKey(&deviceKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (NT_SUCCESS(status)) { - - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | - RTL_QUERY_REGISTRY_REQUIRED | - RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; - queryTable[0].EntryContext = LoadBalanceType; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - deviceKey, - queryTable, - deviceKey, - NULL); - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n", - DeviceInfo, - *LoadBalanceType)); - - } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { - - // - // The device key must have been newly created. - // Set the default load balance policy for this device - // - - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_LOAD_BALANCE_POLICY, - REG_DWORD, - LoadBalanceType, - sizeof(ULONG)); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write LB policy. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry; - } - } - - if (NT_SUCCESS(status)) { - - RtlInitUnicodeString(&keyValueName, DSM_POLICY_EXPLICITLY_SET); - status = ZwQueryValueKey(deviceKey, - &keyValueName, - KeyValuePartialInformation, - &explicitSet, - sizeof(explicitSet), - &length); - - if (NT_SUCCESS(status)) { - - NT_ASSERT(explicitSet.KeyValueInfo.DataLength == sizeof(UCHAR)); - - *ExplicitlySet = *((UCHAR UNALIGNED *)&(explicitSet.KeyValueInfo.Data)); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): ExplicitlySet is %!bool!.\n", - DeviceInfo, - *ExplicitlySet)); - - } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { - - *ExplicitlySet = FALSE; - - // - // The device key must have been newly created. - // Set ExplicitlySet to 0 to indicate that the default was used. - // - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_POLICY_EXPLICITLY_SET, - REG_BINARY, - ExplicitlySet, - sizeof(UCHAR)); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write ExplicitlySet. Status %x.\n", - DeviceInfo, - status)); - } - } - - if (NT_SUCCESS(status)) { - - RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); - status = ZwQueryValueKey(deviceKey, - &keyValueName, - KeyValuePartialInformation, - &preferredPath, - sizeof(preferredPath), - &length); - - if (NT_SUCCESS(status)) { - - NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); - - *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n", - DeviceInfo, - *PreferredPath)); - - } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { - - *PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - - // - // The device key must have been newly created. - // Set a bogus preferred path as default. - // - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_PREFERRED_PATH, - REG_BINARY, - PreferredPath, - sizeof(ULONGLONG)); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write PreferredPath. Status %x.\n", - DeviceInfo, - status)); - } - } - } - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to create LB policy registry key. Status %x.\n", - DeviceInfo, - status)); - - deviceKey = NULL; - } - -__Exit_DsmpQueryDeviceLBPolicyFromRegistry: - - if (deviceKey) { - ZwClose(deviceKey); - } - - if (lbSettingsKey) { - ZwClose(lbSettingsKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryTargetLBPolicyFromRegistry( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Out_ OUT PULONGLONG PreferredPath - ) -/*++ - -Routine Description: - - Query the load balance policy for the VID/PID of the passed in device from - the registry if it has been set. - -Arguements: - - DeviceInfo - Device's whose VID/PID we need to compare against. - LoadBalanceType - Type of LB policy. - PreferredPath - The preferred path for the device. - -Return Value : - - STATUS_SUCCESS if we were able to successfully query the registry for the info. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE targetsLBSettingKey = NULL; - HANDLE targetKey = NULL; - UNICODE_STRING subKeyName; - OBJECT_ATTRIBUTES objectAttributes; - NTSTATUS status = STATUS_INVALID_PARAMETER; - UNICODE_STRING keyValueName; - ULONG length; - struct _preferredPath { - KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; - ULONGLONG Data; - } preferredPath; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Entering function.\n", - DeviceInfo)); - - if (!LoadBalanceType || !PreferredPath) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Invalid parameter.\n", - DeviceInfo)); - - goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; - } - - if (!DeviceInfo->Group->HardwareId) { - - status = STATUS_UNSUCCESSFUL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Couldn't build hardware id for passed in device.\n", - DeviceInfo)); - - goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; - } - - // - // Query the Load Balance settings for the given target from the registry. - // First open TargetsLoadBalanceSetting key under the service key. - // - status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open Targets LB Setting key. Status %x.\n", - DeviceInfo, - status)); - - goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; - } - - RtlInitUnicodeString(&subKeyName, DeviceInfo->Group->HardwareId); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - targetsLBSettingKey, - (PSECURITY_DESCRIPTOR) NULL); - - // - // Open the VID/PID key under DsmTargetsLoadBalanceSetting key. - // - status = ZwOpenKey(&targetKey, KEY_ALL_ACCESS, &objectAttributes); - - if (NT_SUCCESS(status)) { - - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | - RTL_QUERY_REGISTRY_REQUIRED | - RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; - queryTable[0].EntryContext = LoadBalanceType; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - targetKey, - queryTable, - targetKey, - NULL); - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n", - DeviceInfo, - *LoadBalanceType)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query LB Policy - error %x.\n", - DeviceInfo, - status)); - } - - if (NT_SUCCESS(status)) { - - RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); - status = ZwQueryValueKey(targetKey, - &keyValueName, - KeyValuePartialInformation, - &preferredPath, - sizeof(preferredPath), - &length); - - if (NT_SUCCESS(status)) { - - NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); - - *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n", - DeviceInfo, - *PreferredPath)); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query PreferredPath. Status %x.\n", - DeviceInfo, - status)); - } - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open LB policy registry key. Status %x.\n", - DeviceInfo, - status)); - - targetKey = NULL; - } - -__Exit_DsmpQueryTargetLBPolicyFromRegistry: - - if (targetKey) { - ZwClose(targetKey); - } - - if (targetsLBSettingKey) { - ZwClose(targetsLBSettingKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryDsmLBPolicyFromRegistry( - _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Out_ OUT PULONGLONG PreferredPath - ) -/*++ - -Routine Description: - - Query the overall load balance policy for MSDSM controlled devices from - the registry if it has been set. - -Arguements: - - LoadBalanceType - Type of LB policy. - PreferredPath - The preferred path for the device. - -Return Value : - - STATUS_SUCCESS if we were able to successfully query the registry for the info. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE parametersKey = NULL; - NTSTATUS status = STATUS_INVALID_PARAMETER; - UNICODE_STRING keyValueName; - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - ULONG length; - struct _preferredPath { - KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; - ULONGLONG Data; - } preferredPath; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: Entering function.\n")); - - if (!LoadBalanceType || !PreferredPath) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: Invalid parameter.\n")); - - goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; - } - - // - // Query the overall default Load Balance settings for MSDSM from the registry. - // First open the Parameters key under the service key. - // - status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, ¶metersKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: Failed to open Parameters key. Status %x.\n", - status)); - - goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; - } - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | - RTL_QUERY_REGISTRY_REQUIRED | - RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; - queryTable[0].EntryContext = LoadBalanceType; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - parametersKey, - queryTable, - parametersKey, - NULL); - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: LB Policy is %d.\n", - *LoadBalanceType)); - - } else { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: Failed to query LB policy. Status %x.\n", - status)); - - goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; - } - - if (NT_SUCCESS(status)) { - - RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); - status = ZwQueryValueKey(parametersKey, - &keyValueName, - KeyValuePartialInformation, - &preferredPath, - sizeof(preferredPath), - &length); - - if (NT_SUCCESS(status)) { - - NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); - - *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: PreferredPath is %I64x.\n", - *PreferredPath)); - - } else { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: Failed to query PreferredPath. Status %x.\n", - status)); - } - } - -__Exit_DsmpQueryDsmLBPolicyFromRegistry: - - if (parametersKey) { - ZwClose(parametersKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryDsmLBPolicyFromRegistry: Exiting function with status %x.\n", - status)); - - return status; -} - - -NTSTATUS -DsmpSetDsmLBPolicyInRegistry( - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ) -/*++ - -Routine Description: - - Set the overall load balance policy for MSDSM controlled devices in - the registry. - Note: If the policy specified is 0, remove the currently set values - for policy and preferred path. - -Arguements: - - LoadBalanceType - Type of LB policy. - PreferredPath - The preferred path for devices controlled by DSM. - -Return Value : - - STATUS_SUCCESS if we were able to successfully set the info in the registry. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE parametersKey = NULL; - NTSTATUS status; - UNICODE_STRING lbPolicyValueName; - UNICODE_STRING preferredPathValueName; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetDsmLBPolicyInRegistry: Entering function.\n")); - - // - // First open the Parameters key under the service key. - // - status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, ¶metersKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetDsmLBPolicyInRegistry: Failed to open Parameters key. Status %x.\n", - status)); - - goto __Exit_DsmpSetDsmLBPolicyInRegistry; - } - - RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY); - RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH); - - // - // If the LB policy is specified as 0, we need to delete the values. - // - if (LoadBalanceType < DSM_LB_FAILOVER) { - - status = ZwDeleteValueKey(parametersKey, &preferredPathValueName); - - if (NT_SUCCESS(status) || status == STATUS_OBJECT_NAME_NOT_FOUND) { - - status = ZwDeleteValueKey(parametersKey, &lbPolicyValueName); - } - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetDsmLBPolicyInRegistry: Failed to delete either preferredPath or lbPolicy. Status %x.\n", - status)); - } - } else { - - status = ZwSetValueKey(parametersKey, - &lbPolicyValueName, - 0, - REG_DWORD, - &LoadBalanceType, - sizeof(ULONG)); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetDsmLBPolicyInRegistry: Failed to set LB policy in registry. Status %x.\n", - status)); - - goto __Exit_DsmpSetDsmLBPolicyInRegistry; - } - - status = ZwSetValueKey(parametersKey, - &preferredPathValueName, - 0, - REG_BINARY, - &PreferredPath, - sizeof(ULONGLONG)); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetDsmLBPolicyInRegistry: Failed to set preferred path in registry. Status %x.\n", - status)); - } - } - -__Exit_DsmpSetDsmLBPolicyInRegistry: - - if (parametersKey) { - ZwClose(parametersKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetDsmLBPolicyInRegistry: Exiting function with status %x.\n", - status)); - - return status; -} - - -NTSTATUS -DsmpSetVidPidLBPolicyInRegistry( - _In_ IN PWSTR TargetHardwareId, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _In_ IN ULONGLONG PreferredPath - ) -/*++ - -Routine Description: - - Set the default load balance policy for MSDSM controlled devices for - a particular target VID/PID in the registry. - Note: If the policy specified is 0, remove the subkey that matches - the passed in TargetHardwareId. - -Arguements: - - TargetHardwareId - The VID/PID for which a default LB policy is being set. - LoadBalanceType - Type of LB policy. - PreferredPath - The preferred path for devices controlled by DSM. - -Return Value : - - STATUS_SUCCESS if we were able to successfully set the info in the registry. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE targetsLBSettingKey = NULL; - HANDLE targetSubKey = NULL; - NTSTATUS status; - UNICODE_STRING vidPidKeyName; - UNICODE_STRING lbPolicyValueName; - UNICODE_STRING preferredPathValueName; - OBJECT_ATTRIBUTES objectAttributes; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Entering function.\n", - TargetHardwareId)); - - // - // First open the DsmTargetsLoadBalanceSetting key under the service's parameters key. - // - status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open Targets Policy settings key. Status %x.\n", - TargetHardwareId, - status)); - - goto __Exit_DsmpSetVidPidLBPolicyInRegistry; - } - - RtlInitUnicodeString(&vidPidKeyName, TargetHardwareId); - InitializeObjectAttributes(&objectAttributes, - &vidPidKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - targetsLBSettingKey, - (PSECURITY_DESCRIPTOR) NULL); - - // - // If the LB policy is specified as 0, we need to delete the values. - // - if (LoadBalanceType < DSM_LB_FAILOVER) { - - // - // Open the VID/PID key under DsmTargetsLoadBalanceSetting key. - // - status = ZwOpenKey(&targetSubKey, KEY_ALL_ACCESS, &objectAttributes); - - if (NT_SUCCESS(status)) { - - status = ZwDeleteKey(targetSubKey); - } - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to either open or delete. Status %x.\n", - TargetHardwareId, - status)); - } - } else { - - RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY); - RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH); - - status = ZwCreateKey(&targetSubKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open/create key in registry. Status %x.\n", - TargetHardwareId, - status)); - - goto __Exit_DsmpSetVidPidLBPolicyInRegistry; - } - - status = ZwSetValueKey(targetSubKey, - &lbPolicyValueName, - 0, - REG_DWORD, - &LoadBalanceType, - sizeof(ULONG)); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set LB policy in registry. Status %x.\n", - TargetHardwareId, - status)); - - goto __Exit_DsmpSetVidPidLBPolicyInRegistry; - } - - status = ZwSetValueKey(targetSubKey, - &preferredPathValueName, - 0, - REG_BINARY, - &PreferredPath, - sizeof(ULONGLONG)); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set preferred path in registry. Status %x.\n", - TargetHardwareId, - status)); - } - } - -__Exit_DsmpSetVidPidLBPolicyInRegistry: - - if (targetSubKey) { - ZwClose(targetSubKey); - } - - if (targetsLBSettingKey) { - ZwClose(targetsLBSettingKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpSetVidPidLBPolicyInRegistry (%ws): Exiting function with status %x.\n", - TargetHardwareId, - status)); - - return status; -} - - -NTSTATUS -DsmpOpenLoadBalanceSettingsKey( - _In_ IN ACCESS_MASK AccessMask, - _Out_ OUT PHANDLE LoadBalanceSettingsKey - ) -/*++ - -Routine Description: - - Open the device key in the registry. - - NOTE: It is the responsibility of the caller to close the returned handle. - -Arguements: - - AccessMask - Requested access with which to open key - LoadBalanceSettingsKey - handle of the key that is returned to the caller - -Return Value : - - STATUS_SUCCESS if we were able to successfully open the registry key. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE serviceKey = NULL; - HANDLE parametersKey = NULL; - PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING parametersKeyName; - UNICODE_STRING subKeyName; - NTSTATUS status = STATUS_UNSUCCESSFUL; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Entering function.\n", - registryPath)); - - *LoadBalanceSettingsKey = NULL; - - // - // First check if registry path is available for msdsm. - // - if (!registryPath->Buffer) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Registry Path not set.\n", - registryPath)); - - goto __Exit_DsmpOpenLoadBalanceSettingsKey; - } - - // - // Open the service key first - // - InitializeObjectAttributes(&objectAttributes, - registryPath, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - NULL, - NULL); - - status = ZwOpenKey(&serviceKey, - AccessMask, - &objectAttributes); - if (NT_SUCCESS(status)) { - - // - // Open Parameters key under the Service key - // - RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - ¶metersKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - serviceKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(¶metersKey, - AccessMask, - &objectAttributes); - - if (NT_SUCCESS(status)) { - - // - // Open LoadBalanceSettings key under the Parameters key - // - RtlInitUnicodeString(&subKeyName, DSM_LOAD_BALANCE_SETTINGS); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - parametersKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwCreateKey(LoadBalanceSettingsKey, - AccessMask, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open/create LBSettings key. Status %x.\n", - registryPath, - status)); - - *LoadBalanceSettingsKey = NULL; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open parameters key. Status %x.\n", - registryPath, - status)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open service key %ws. Status %x.\n", - registryPath, - registryPath->Buffer, - status)); - } - -__Exit_DsmpOpenLoadBalanceSettingsKey: - - if (parametersKey) { - ZwClose(parametersKey); - } - - if (serviceKey) { - ZwClose(serviceKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Exiting function with status %x.\n", - registryPath, - status)); - - return status; -} - - -NTSTATUS -DsmpOpenTargetsLoadBalanceSettingKey( - _In_ IN ACCESS_MASK AccessMask, - _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey - ) -/*++ - -Routine Description: - - Open the target key in the registry. - - NOTE: It is the responsibility of the caller to close the returned handle. - -Arguements: - - AccessMask - Requested access with which to open key - LoadBalanceSettingsKey - handle of the key that is returned to the caller - -Return Value : - - STATUS_SUCCESS if we were able to successfully open the registry key. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE serviceKey = NULL; - HANDLE parametersKey = NULL; - PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING parametersKeyName; - UNICODE_STRING subKeyName; - NTSTATUS status = STATUS_UNSUCCESSFUL; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Entering function.\n", - registryPath)); - - if (!TargetsLoadBalanceSettingKey) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Invalid parameter.\n", - registryPath)); - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey; - } - - *TargetsLoadBalanceSettingKey = NULL; - - // - // First check if registry path is available for msdsm. - // - if (!registryPath->Buffer) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Registry Path not set.\n", - registryPath)); - - goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey; - } - - // - // Open the service key first - // - InitializeObjectAttributes(&objectAttributes, - registryPath, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - NULL, - NULL); - - status = ZwOpenKey(&serviceKey, - AccessMask, - &objectAttributes); - if (NT_SUCCESS(status)) { - - // - // Open Parameters key under the Service key - // - RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - ¶metersKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - serviceKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(¶metersKey, - AccessMask, - &objectAttributes); - - if (NT_SUCCESS(status)) { - - // - // Open LoadBalanceSettings key under the Parameters key - // - RtlInitUnicodeString(&subKeyName, DSM_TARGETS_LOAD_BALANCE_SETTING); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - parametersKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwCreateKey(TargetsLoadBalanceSettingKey, - AccessMask, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open/create TargetsLBSetting key. Status %x.\n", - registryPath, - status)); - - *TargetsLoadBalanceSettingKey = NULL; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open parameters key. Status %x.\n", - registryPath, - status)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open service key %ws. Status %x.\n", - registryPath, - registryPath->Buffer, - status)); - } - -__Exit_DsmpOpenTargetsLoadBalanceSettingKey: - - if (parametersKey) { - ZwClose(parametersKey); - } - - if (serviceKey) { - ZwClose(serviceKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Exiting function with status %x.\n", - registryPath, - status)); - - return status; -} - - -NTSTATUS -DsmpOpenDsmServicesParametersKey( - _In_ IN ACCESS_MASK AccessMask, - _Out_ OUT PHANDLE ParametersKey - ) -/*++ - -Routine Description: - - Open the DSM's Parameters key in the registry. - - NOTE: It is the responsibility of the caller to close the returned handle. - -Arguements: - - AccessMask - Requested access with which to open key - ParametersKey - handle of the key that is returned to the caller - -Return Value : - - STATUS_SUCCESS if we were able to successfully open the registry key. - - Appropriate NTSTATUS code on failure - ---*/ -{ - HANDLE serviceKey = NULL; - PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING parametersKeyName; - NTSTATUS status = STATUS_UNSUCCESSFUL; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpOpenDsmServicesParametersKey (RegPath %p): Entering function.\n", - registryPath)); - - if (!ParametersKey) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenDsmServicesParametersKey (RegPath %p): Invalid parameter.\n", - registryPath)); - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpOpenDsmServicesParametersKey; - } - - *ParametersKey = NULL; - - // - // First check if registry path is available for msdsm. - // - if (!registryPath->Buffer) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenDsmServicesParametersKey (RegPath %p): Registry Path not set.\n", - registryPath)); - - goto __Exit_DsmpOpenDsmServicesParametersKey; - } - - // - // Open the service key first - // - InitializeObjectAttributes(&objectAttributes, - registryPath, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - NULL, - NULL); - - status = ZwOpenKey(&serviceKey, - AccessMask, - &objectAttributes); - if (NT_SUCCESS(status)) { - - // - // Open Parameters key under the Service key - // - RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - ¶metersKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - serviceKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(ParametersKey, - AccessMask, - &objectAttributes); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open parameters key. Status %x.\n", - registryPath, - status)); - - *ParametersKey = NULL; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open service key %ws. Status %x.\n", - registryPath, - registryPath->Buffer, - status)); - } - -__Exit_DsmpOpenDsmServicesParametersKey: - - if (serviceKey) { - ZwClose(serviceKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpOpenDsmServicesParametersKey (RegPath %p): Exiting function with status %x.\n", - registryPath, - status)); - - return status; -} - -NTSTATUS -DsmpReportTargetPortGroupsSyncCompletion( - IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp, - IN PVOID Context - ) -{ - UNREFERENCED_PARAMETER(DeviceObject); - UNREFERENCED_PARAMETER(Context); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroupsSyncCompletion: IRP %p, Context %p\n", - Irp, Context)); - - KeSetEvent(Irp->UserEvent, 0, FALSE); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - -_Success_(return==0) -NTSTATUS -DsmpReportTargetPortGroups( - _In_ PDEVICE_OBJECT DeviceObject, - _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo, - _Out_ PULONG TargetPortGroupsInfoLength - ) -/*++ - -Routine Description: - - Helper routine to send down ReportTargetPortGroups request synchronously. - Used if device supports ALUA. - - Note: This routine will allocate memory for the TPG info. It is the - responsibility of the caller to free this buffer, but only if the function - returns STATUS_SUCCESS. - -Arguments: - - DeviceObject - The port PDO to which the command should be sent. - TargetPortGroupsInfo - buffer containing the returned data. - TargetPortGroupsInfoLength - size of the returned buffer. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb; - NTSTATUS status = STATUS_SUCCESS; - PIRP irp = NULL; - PMDL mdl = NULL; - PSCSI_REQUEST_BLOCK srb = NULL; - PSENSE_DATA_EX senseInfoBuffer = NULL; - UCHAR senseInfoBufferLength = 0; - KEVENT completionEvent; - ULONG targetPortGroupsInfoLength = 0; - PUCHAR targetPortGroupsInfo = NULL; - PIO_STACK_LOCATION irpStack = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Entering function.\n", - DeviceObject)); - - if (TargetPortGroupsInfoLength == NULL || - TargetPortGroupsInfo == NULL) { - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpReportTargetPortGroups; - } - - *TargetPortGroupsInfoLength = 0; - *TargetPortGroupsInfo = NULL; - - senseInfoBuffer = (PSENSE_DATA_EX)DsmpAllocatePool(NonPagedPoolNx, - SENSE_BUFFER_SIZE_EX, - DSM_TAG_SCSI_SENSE_INFO); - if (senseInfoBuffer != NULL) { - - senseInfoBufferLength = SENSE_BUFFER_SIZE_EX; - - srb = (PSCSI_REQUEST_BLOCK)DsmpAllocatePool(NonPagedPoolNx, - sizeof(SCSI_REQUEST_BLOCK), - DSM_TAG_SCSI_REQUEST_BLOCK); - if (srb != NULL) { - - SrbSetSenseInfoBufferLength(srb, senseInfoBufferLength); - SrbSetSenseInfoBuffer(srb, senseInfoBuffer); - - // - // Take care of worst case scenario, which is: - // 1. 4-byte header (for allocation length) - // 2. 32 8-byte descriptors (for TPGs) - // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) - // - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + - DSM_MAX_PATHS * sizeof(ULONG))); - - targetPortGroupsInfo = (PUCHAR)DsmpAllocatePool(NonPagedPoolNx, - targetPortGroupsInfoLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo == NULL) { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate TPG info.\n", - DeviceObject)); - goto __Exit_DsmpReportTargetPortGroups; - } - - } else { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate SRB.\n", - DeviceObject)); - goto __Exit_DsmpReportTargetPortGroups; - } - - } else { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate Sense Info Buffer.\n", - DeviceObject)); - goto __Exit_DsmpReportTargetPortGroups; - } - - irp = IoAllocateIrp(DeviceObject->StackSize + 1, FALSE); - if (irp == NULL) { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate IRP.\n", - DeviceObject)); - goto __Exit_DsmpReportTargetPortGroups; - } - - mdl = IoAllocateMdl(targetPortGroupsInfo, - targetPortGroupsInfoLength, - FALSE, - FALSE, - irp); - - if (mdl == NULL) { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate MDL.\n", - DeviceObject)); - goto __Exit_DsmpReportTargetPortGroups; - } - - MmBuildMdlForNonPagedPool(mdl); - -__Retry_DsmpReportTargetPortGroups: - - irp->MdlAddress = mdl; - - // - // Set up SRB for execute scsi request. Save SRB address in next stack - // for the port driver. - // - irpStack = IoGetNextIrpStackLocation(irp); - irpStack->MajorFunction = IRP_MJ_SCSI; - irpStack->MinorFunction = IRP_MN_SCSI_CLASS; - irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb; - irpStack->DeviceObject = DeviceObject; - - // - // Set the completion event and the completion routine. - // - KeInitializeEvent(&completionEvent, NotificationEvent, FALSE); - irp->UserEvent = &completionEvent; - IoSetCompletionRoutine(irp, - DsmpReportTargetPortGroupsSyncCompletion, - srb, - TRUE, - TRUE, - TRUE); - - srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - srb->Length = sizeof(SCSI_REQUEST_BLOCK); - - SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS)); - cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb); - cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS; - cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; - REVERSE_BYTES(&(cdb->AllocationLength), &targetPortGroupsInfoLength); - - SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT); - SrbSetDataTransferLength(srb, targetPortGroupsInfoLength); - SrbSetDataBuffer(srb, targetPortGroupsInfo); - srb->SrbStatus = 0; - SrbSetScsiStatus(srb, 0); - SrbSetNextSrb(srb, NULL); - SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | - SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | - SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); - SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); - SrbSetOriginalRequest(srb, irp); - - ObReferenceObject(DeviceObject); - - // - // Finally, send the IRP down and wait for its completion. - // - status = IoCallDriver(DeviceObject, irp); - - if (status == STATUS_PENDING) { - KeWaitForSingleObject(&completionEvent, - Executive, - KernelMode, - FALSE, - NULL); - status = irp->IoStatus.Status; - } - - ObDereferenceObject(DeviceObject); - - if ((status == STATUS_BUFFER_OVERFLOW) || - (NT_SUCCESS(status) && (SrbGetScsiStatus(srb) == SCSISTAT_GOOD))) { - - // - // The first 4 bytes of the returned data are the Returned Data Length - // field of the RTPG header. - // - ULONG returnedDataLength = 0; - REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo); - - status = STATUS_SUCCESS; - if (returnedDataLength > SrbGetDataTransferLength(srb)) { - - status = STATUS_BUFFER_OVERFLOW; - } - } - - if (NT_SUCCESS(status) && SrbGetScsiStatus(srb) == SCSISTAT_GOOD) { - - // - // RTPG was successful so return the TPG info to the caller. - // - - // - // The first 4 bytes of the returned data are the Returned Data Length - // field of the RTPG header. We need to return this value plus the header size. - // - ULONG returnedDataLength = 0; - REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo); - *TargetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength; - - *TargetPortGroupsInfo = targetPortGroupsInfo; - - } else if (SrbGetScsiStatus(srb) == SCSISTAT_CHECK_CONDITION) { - - if (DsmpShouldRetryTPGRequest(senseInfoBuffer, senseInfoBufferLength)) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Retrying request.\n", - DeviceObject)); - - IoReuseIrp(irp, STATUS_SUCCESS); - - RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); - - goto __Retry_DsmpReportTargetPortGroups; - } - - if (DsmpIsDeviceRemoved(senseInfoBuffer, senseInfoBufferLength)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Device not available.\n", - DeviceObject)); - - // - // Sense key was illegal request. SPC 6.25 says response to TPG should follow Test Unit Ready responses - // - status = STATUS_NO_SUCH_DEVICE; - - } - - // RTPG was unsuccessful - // Here it is possible that status is success, but scsi status is not. - // and there was no RTPG retry. If so, set status to unsuccessful. - if (NT_SUCCESS(status)) { - status = STATUS_UNSUCCESSFUL; - } - - // - // TPG resulted HW to respond with Check Condition but Sense Key indicates it is not for retry or illegal request - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): TPG returned Check Condition, NTStatus 0x%x, ScsiStatus 0x%x.\n", - DeviceObject, - status, - SrbGetScsiStatus(srb))); - } else { - - // RTPG was unsuccessful - // Here it is possible that status is success, but scsi status is not. - // If so, set status to unsuccessful. - if (NT_SUCCESS(status)) { - status = STATUS_UNSUCCESSFUL; - } - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n", - DeviceObject, - status, - SrbGetScsiStatus(srb))); - } - -__Exit_DsmpReportTargetPortGroups: - - // - // The port driver may have allocated its own sense buffer so we need to - // make sure we free that here. - // - if (srb != NULL && - SrbGetSrbFlags(srb) & SRB_FLAGS_PORT_DRIVER_ALLOCSENSE && - SrbGetSrbFlags(srb) & SRB_FLAGS_FREE_SENSE_BUFFER && - SrbGetSenseInfoBuffer(srb) != NULL) { - DsmpFreePool(SrbGetSenseInfoBuffer(srb)); - } - - if (senseInfoBuffer) { - DsmpFreePool(senseInfoBuffer); - } - - if (srb) { - DsmpFreePool(srb); - } - - if (irp) { - if (irp->MdlAddress) { - IoFreeMdl(irp->MdlAddress); - } - IoFreeIrp(irp); - } - - if (!NT_SUCCESS(status) && targetPortGroupsInfo) { - DsmpFreePool(targetPortGroupsInfo); - *TargetPortGroupsInfoLength = 0; - *TargetPortGroupsInfo = NULL; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpReportTargetPortGroups (DevObj %p): Exiting function with status %x.\n", - DeviceObject, - status)); - - return status; -} - -NTSTATUS -DsmpReportTargetPortGroupsAsync( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, - _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, - _In_ IN ULONG TargetPortGroupsInfoLength, - _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo - ) -/*++ - -Routine Description: - - Helper routine to send down ReportTargetPortGroups request asynchronously. - Used if device supports ALUA. - - NOTE: Caller needs to free Irp, system buffer, and passThrough buffer. - -Arguments: - - DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to. - CompletionRoutine - completion routine passed in by the caller. - CompletionContext - context to be passed to be completion routine. - TargetPortGroupsInfoLength - size of the returned buffer. - TargetPortGroupsInfo - preallocated (by caller) buffer that'll contain the returned data. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext; - PSCSI_REQUEST_BLOCK srb = NULL; - PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb; - NTSTATUS status; - PIRP irp = NULL; - PIO_STACK_LOCATION irpStack; - PMDL mdl = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpReportTargetPortGroupsAsync (DevInfo %p): Entering function.\n", - DeviceInfo)); - - srb = tpgCompletionContext->Srb; - - SrbZeroSrb(srb); - - // - // Allocate an irp. - // - irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE); - if (!irp) { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n", - DeviceInfo)); - goto __Exit_DsmpReportTargetPortGroupsAsync; - } - - mdl = IoAllocateMdl(TargetPortGroupsInfo, - TargetPortGroupsInfoLength, - FALSE, - FALSE, - irp); - if (!mdl) { - - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n", - DeviceInfo)); - goto __Exit_DsmpReportTargetPortGroupsAsync; - } - - MmBuildMdlForNonPagedPool(irp->MdlAddress); - - // - // It is possible that if an implicit access state transition took place, - // each I_T nexus will return UA for asymmetric access state changed. So - // set the number of retries to be one more than the total number of paths. - // Worst case scenario is the it is sent down each path once (assuming every - // is a different I_T nexus) and then one more for a retry one one of the - // paths. - // - tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1; - - // - // Set-up the completion routine. - // - IoSetCompletionRoutine(irp, - CompletionRoutine, - (PVOID)CompletionContext, - TRUE, - TRUE, - TRUE); - - // - // Get the recipient's irpstack location. - // - irpStack = IoGetNextIrpStackLocation(irp); - - irpStack->Parameters.Scsi.Srb = srb; - irpStack->DeviceObject = DeviceInfo->TargetObject; - - // - // Set the major function code to IRP_MJ_SCSI. - // - irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; - - // - // Set the minor function, or many requests will get kicked by by port. - // - irpStack->MinorFunction = IRP_MN_SCSI_CLASS; - - srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - srb->Length = sizeof(SCSI_REQUEST_BLOCK); - - SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS)); - cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb); - cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS; - cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; - Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->AllocationLength); - - SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT); - SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer); - SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength); - SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength); - SrbSetDataBuffer(srb, TargetPortGroupsInfo); - srb->SrbStatus = 0; - SrbSetScsiStatus(srb, 0); - SrbSetNextSrb(srb, NULL); - SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | - SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | - SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); - SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); - SrbSetOriginalRequest(srb, irp); - - irp->UserBuffer = TargetPortGroupsInfo; - irp->Tail.Overlay.Thread = PsGetCurrentThread(); - - // - // Send the IRP asynchronously - // - DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext, - DeviceInfo->TargetObject, - irp, - (PVOID)DeviceInfo, - DSM_CALL_COMPLETION_ON_MPIO_ERROR); - - // - // We know that the completion routine will always be called. - // - status = STATUS_PENDING; - -__Exit_DsmpReportTargetPortGroupsAsync: - - if (status != STATUS_PENDING) { - - // - // This indicates Irp was never sent down to stack (completion routine was never called). - // We need to clean up. - // - if (irp) { - - if (irp->MdlAddress) { - IoFreeMdl(irp->MdlAddress); - } - - IoFreeIrp(irp); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpReportTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x\n.", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryLBPolicyForDevice( - _In_ IN PWSTR RegistryKeyName, - _In_ IN ULONGLONG PathId, - _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, - _Out_ OUT PULONG PrimaryPath, - _Out_ OUT PULONG OptimizedPath, - _Out_ OUT PULONG PathWeight - ) -/*++ - -Routine Description: - - This routine opens the device's registry subkey, builds the path subkey from - the passed in PathId, then queries that subkey for the value of PrimaryPath, - OptimizedPath and PathWeight. - -Arguments: - - RegistryKeyName - The device's registry subkey name. - PathId - The pathId for this instance of the device. - LoadBalanceType - The current load balance policy. - PrimaryPath - Output of the queried PrimaryPath value. - OptimizedPath - Output of the queried OptimizedPath value. - PathWeight - Output of the queried PathWeight value. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - HANDLE lbSettingsKey = NULL; - HANDLE deviceKey = NULL; - HANDLE dsmPathKey = NULL; - UNICODE_STRING subKeyName; - WCHAR dsmPathName[128] = {0}; - OBJECT_ATTRIBUTES objectAttributes; - NTSTATUS status; - NTSTATUS pathWeightQueryStatus = STATUS_SUCCESS; - - PAGED_CODE(); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Entering function.\n", - RegistryKeyName)); - - // - // Query PrimaryPath and PathWeight for the given path. - // These values are stored under DsmPath#Suffix key for - // this path. If this key doesn't exist create it and - // create PrimaryPath and PathWeight values - use the - // values passed in PrimaryPath and PathWeight in this case. - // - status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open LB Settings key. Status %x.\n", - RegistryKeyName, - status)); - - goto __Exit_DsmpQueryLBPolicyForDevice; - } - - RtlInitUnicodeString(&subKeyName, RegistryKeyName); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - lbSettingsKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); - if (NT_SUCCESS(status)) { - - // - // Create or open DsmPath#Suffix key for this path - // - DsmpGetDSMPathKeyName(PathId, dsmPathName, 128); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Will query %ws for PrimaryPath, OptimizedPath and PathWeight.\n", - RegistryKeyName, - dsmPathName)); - - RtlInitUnicodeString(&subKeyName, dsmPathName); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - deviceKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwCreateKey(&dsmPathKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (NT_SUCCESS(status)) { - - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - - // - // Query the Path Weight value. - // - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | - RTL_QUERY_REGISTRY_REQUIRED | - RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_PATH_WEIGHT; - queryTable[0].EntryContext = PathWeight; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - pathWeightQueryStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - dsmPathKey, - queryTable, - dsmPathKey, - NULL); - - if (!NT_SUCCESS(pathWeightQueryStatus)) { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PathWeight. Status %x.\n", - RegistryKeyName, - pathWeightQueryStatus)); - } - - // - // Query the Primary Path value. - // - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | - RTL_QUERY_REGISTRY_REQUIRED | - RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_PRIMARY_PATH; - queryTable[0].EntryContext = PrimaryPath; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - dsmPathKey, - queryTable, - dsmPathKey, - NULL); - if (NT_SUCCESS(status)) { - - // - // Query the Optimized Path value. - // - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | - RTL_QUERY_REGISTRY_REQUIRED | - RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_OPTIMIZED_PATH; - queryTable[0].EntryContext = OptimizedPath; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - dsmPathKey, - queryTable, - dsmPathKey, - NULL); - if (!NT_SUCCESS(status)) { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query OptimizedPath. Status %x.\n", - RegistryKeyName, - status)); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PrimaryPath. Status %x.\n", - RegistryKeyName, - status)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to create DSM Path key %ws. Status %x.\n", - RegistryKeyName, - dsmPathName, - status)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open key. Status %x.\n", - RegistryKeyName, - status)); - } - - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): PrimaryPath %d, OptmizedPath %d, PathWeight %d.\n", - RegistryKeyName, - *PrimaryPath, - *OptimizedPath, - *PathWeight)); - } - -__Exit_DsmpQueryLBPolicyForDevice: - - if (dsmPathKey) { - ZwClose(dsmPathKey); - } - - if (deviceKey) { - ZwClose(deviceKey); - } - - if (lbSettingsKey) { - ZwClose(lbSettingsKey); - } - - // - // If the load balance policy is Weighted Paths and we failed to read in - // the path weight value, we need to return the failure status from the - // path weight value query. - // - if (LoadBalanceType == DSM_LB_WEIGHTED_PATHS && !NT_SUCCESS(pathWeightQueryStatus)) { - status = pathWeightQueryStatus; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryLBPolicyForDevice (DevName %ws): Exiting function with status %x.\n", - RegistryKeyName, - status)); - - return status; -} - - -VOID -DsmpGetDSMPathKeyName( - _In_ ULONGLONG DSMPathId, - _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName, - _In_ ULONG DsmPathKeyNameSize - ) -/*++ - -Routine Description: - - This routine builds the string that corresponds to the device's Path subkey - name in the registry. - -Arguments: - - DSMPathId - The pathId of this instance of the device. - DsmPathKeyName - Output buffer in which the subkey name for path is returned. - DsmPathKeyNameSize - size of the output buffer in WCHARs. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - PWCHAR pathPtr; - SIZE_T wcharsLeft; - SIZE_T size; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetDSMPathKeyName (PathId %I64x): Entering function.\n", - DSMPathId)); - - // - // This routine will build a name for a given DSM Path. - // The name is of the format DsmPath#Suffix, where Suffix - // is derived from the PathId - // - pathPtr = DsmPathKeyName; - - wcharsLeft = DsmPathKeyNameSize; - - size = wcslen(DSM_PATH); - - if (size < wcharsLeft) { - - // - // First copy the string DsmPath# - // - if (NT_SUCCESS(RtlStringCchCopyNW(pathPtr, wcharsLeft, DSM_PATH, wcslen(DSM_PATH)))) { - - wcharsLeft -= size; - pathPtr += size; - - if (wcharsLeft > 2) { - - RtlStringCchCatW(pathPtr, wcharsLeft, L"#"); - wcharsLeft--; - pathPtr++; - - // - // Each nibble in the path id would need 1 WCHAR - // upon conversion to WCHAR string. So we'll need - // 2 WCHARs for each byte. Include the NULL char also - // - size = (sizeof(PVOID) + 1) * 2; - if (size <= wcharsLeft) { - - PVOID pathId; - PUCHAR pathIdPtr; - ULONG inx; - UCHAR tmpChar; - - // - // Convert the ULONGLONG path id to a string and - // append that to DsmPath# - // - pathId = (PVOID) DSMPathId; - - pathIdPtr = (PUCHAR) &pathId; - - for (inx = 0; inx < sizeof(PVOID); inx++) { - - tmpChar = (*pathIdPtr & 0xF0) >> 4; - *pathPtr++ = DsmpGetAsciiForBinary(tmpChar); - - tmpChar = (*pathIdPtr & 0x0F); - *pathPtr++ = DsmpGetAsciiForBinary(tmpChar); - - pathIdPtr++; - } - - *pathPtr = WNULL; - } - } - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetDSMPathKeyName (PathId %I64x): Exiting function.\n", - DSMPathId)); - - return; -} - - -UCHAR -DsmpGetAsciiForBinary( - _In_ UCHAR BinaryChar - ) -/*++ - -Routine Description: - - This routine converts the passed in binary value into ASCII equivalent. - -Arguments: - - BinaryChar - The binary value that needs to be converted. - -Return Value: - - Corresponding ASCII value. - ---*/ -{ - UCHAR outChar = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetAsciiForBinary (BinaryChar %d): Entering function.\n", - BinaryChar)); - - // - // Convert a binary nibble into an ASCII character. - // - if ((BinaryChar >= 0) && (BinaryChar <= 9)) { - outChar = BinaryChar + '0'; - } else { - outChar = BinaryChar + 'A' - 10; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetAsciiForBinary (BinaryChar %d): Exiting function with outChar %c.\n", - BinaryChar, - outChar)); - - return outChar; -} - - -NTSTATUS -DsmpGetDeviceIdList( - _In_ IN PDEVICE_OBJECT DeviceObject, - _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor - ) -/*++ - -Routine Description: - - This routine will perform a query for the StorageDeviceIdProperty and will - allocate a non-paged buffer to store the data in. - IMPORTANT: It is the responsibility of the caller to ensure that this buffer is freed. - -Arguments: - - DeviceObject - the device to query - Descriptor - a location to store a pointer to the buffer we allocate - -Return Value: - - status. - ---*/ -{ - STORAGE_PROPERTY_QUERY query; - PIO_STATUS_BLOCK ioStatus = NULL; - PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL; - ULONG length; - NTSTATUS status = STATUS_UNSUCCESSFUL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetDeviceIdList (DevObj %p): Entering function.\n", - DeviceObject)); - - if (!DeviceObject) { - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpGetDeviceIdList; - } - - // - // Poison the passed in descriptor. - // - *Descriptor = NULL; - - // - // Setup the query buffer. - // - query.PropertyId = StorageDeviceIdProperty; - query.QueryType = PropertyStandardQuery; - query.AdditionalParameters[0] = 0; - - ioStatus = DsmpAllocatePool(NonPagedPoolNx, sizeof(IO_STATUS_BLOCK), DSM_TAG_IO_STATUS_BLOCK); - - if (!ioStatus) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetDeviceIdList (DevObj %p): Failed to allocate an IO_STATUS_BLOCK.\n", - DeviceObject)); - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpGetDeviceIdList; - } - - ioStatus->Status = 0; - ioStatus->Information = 0; - - // - // On the first call, just need to get the length of the descriptor. - // - descriptor = (PVOID)&query; - DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY, - DeviceObject, - &query, - &query, - sizeof(STORAGE_PROPERTY_QUERY), - sizeof(STORAGE_DESCRIPTOR_HEADER), - FALSE, - ioStatus); - - status = ioStatus->Status; - - if(!NT_SUCCESS(status)) { - - descriptor = NULL; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetDeviceIdList (DevObj %p): Query failed (%x) on attempt 1.\n", - DeviceObject, - ioStatus->Status)); - - goto __Exit_DsmpGetDeviceIdList; - } - - NT_ASSERT(descriptor->Size); - if (descriptor->Size == 0) { - status = STATUS_UNSUCCESSFUL; - goto __Exit_DsmpGetDeviceIdList; - } - - // - // This time we know how much data there is so we can - // allocate a buffer of the correct size - // - length = descriptor->Size; - - descriptor = DsmpAllocatePool(NonPagedPoolNx, length, DSM_TAG_DEVICE_ID_LIST); - - if(!descriptor) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetDeviceIdList (DevObj %p): Couldn't allocate descriptor of %ld.\n", - DeviceObject, - length)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpGetDeviceIdList; - } - - // - // setup the query again. - // - query.PropertyId = StorageDeviceIdProperty; - query.QueryType = PropertyStandardQuery; - query.AdditionalParameters[0] = 0; - - // - // copy the input to the new outputbuffer - // - RtlCopyMemory(descriptor, - &query, - sizeof(STORAGE_PROPERTY_QUERY)); - - DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY, - DeviceObject, - descriptor, - descriptor, - sizeof(STORAGE_PROPERTY_QUERY), - length, - 0, - ioStatus); - - status = ioStatus->Status; - - if(!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpGetDeviceIdList (DevObj %p): Query Failed (%x) on attempt 2.\n", - DeviceObject, - ioStatus->Status)); - - goto __Exit_DsmpGetDeviceIdList; - } - -__Exit_DsmpGetDeviceIdList: - - if (ioStatus) { - DsmpFreePool(ioStatus); - } - - if (!NT_SUCCESS(status)) { - - if (descriptor) { - DsmpFreePool(descriptor); - } - - } else { - *Descriptor = descriptor; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetDeviceIdList (DevObj %p): Exiting function with status %x.\n", - DeviceObject, - status)); - - return status; -} - - -NTSTATUS -DsmpSetTargetPortGroups( - _In_ IN PDEVICE_OBJECT DeviceObject, - _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, - _In_ IN ULONG TargetPortGroupsInfoLength - ) -/*++ - -Routine Description: - - Helper routine to send down SetTargetPortGroups request. - -Arguments: - - DeviceObject - The port PDO to which the command should be sent. - TargetPortGroupsInfo - buffer containing the TPG data. - TargetPortGroupsInfoLength - size of the TPG buffer. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER passThrough; - PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb; - IO_STATUS_BLOCK ioStatus; - ULONG alignmentMask = DeviceObject->AlignmentRequirement; - PUCHAR dataBuffer = NULL; - SIZE_T allocatedLength = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpSetTargetPortGroups (DevObj %p): Entering function.\n", - DeviceObject)); - - NT_ASSERT(TargetPortGroupsInfoLength && TargetPortGroupsInfo); - - // - // Build request. - // - RtlZeroMemory(&passThrough, sizeof(passThrough)); - - dataBuffer = DsmpAllocateAlignedPool(NonPagedPoolNx, - TargetPortGroupsInfoLength, - alignmentMask, - DSM_TAG_PASS_THRU, - &allocatedLength); - if (!dataBuffer) { - - status = STATUS_INSUFFICIENT_RESOURCES; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetTargetPortGroups (DevObj %p): Failed to allocate mem for passthrough's databuffer.\n", - DeviceObject)); - goto __Exit_DsmpSetTargetPortGroups; - } - -__Retry_Request: - - // - // Build the cdb. - // - cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)passThrough.ScsiPassThroughDirect.Cdb; - - cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS; - cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; - Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength); - - passThrough.ScsiPassThroughDirect.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); - passThrough.ScsiPassThroughDirect.CdbLength = 12; - passThrough.ScsiPassThroughDirect.SenseInfoLength = SPTWB_SENSE_LENGTH; - passThrough.ScsiPassThroughDirect.DataIn = 0; - passThrough.ScsiPassThroughDirect.DataTransferLength = TargetPortGroupsInfoLength; - passThrough.ScsiPassThroughDirect.TimeOutValue = 20; - passThrough.ScsiPassThroughDirect.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseInfoBuffer); - passThrough.ScsiPassThroughDirect.DataBuffer = dataBuffer; - RtlCopyMemory(dataBuffer, - TargetPortGroupsInfo, - TargetPortGroupsInfoLength); - - DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT, - DeviceObject, - &passThrough, - &passThrough, - sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), - sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), - FALSE, - &ioStatus); - - if ((passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_GOOD) && - (NT_SUCCESS(ioStatus.Status))) { - - status = STATUS_SUCCESS; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetTargetPortGroups (DevObj %p): STPG succeeded.\n", - DeviceObject)); - - } else if (NT_SUCCESS(ioStatus.Status) && - passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_CHECK_CONDITION && - DsmpShouldRetryTPGRequest((PSENSE_DATA)&passThrough.SenseInfoBuffer, passThrough.ScsiPassThroughDirect.SenseInfoLength)) { - - // - // Retry the request - // - RtlZeroMemory(dataBuffer, TargetPortGroupsInfoLength); - goto __Retry_Request; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetTargetPortGroups (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n", - DeviceObject, - ioStatus.Status, - passThrough.ScsiPassThroughDirect.ScsiStatus)); - - status = ioStatus.Status; - } - -__Exit_DsmpSetTargetPortGroups: - - // - // Free the passthrough + data buffer. - // - if (dataBuffer) { - DsmpFreePool(dataBuffer); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpSetTargetPortGroups (DevObj %p): Exiting function with status %x.\n", - DeviceObject, - status)); - - return status; -} - - -NTSTATUS -DsmpSetTargetPortGroupsAsync( - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, - _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, - _In_ IN ULONG TargetPortGroupsInfoLength, - _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo - ) -/*++ - -Routine Description: - - Helper routine to send down SetTargetPortGroups request asynchronously. - - IMPORTANT: Caller needs to free the IRP and allocated system buffer. - -Arguments: - - DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to. - CompletionRoutine - completion routine provided by the caller. - CompletionContext - context passed into the completion routine. - TargetPortGroupsInfoLength - size of the TPG buffer. - TargetPortGroupsInfo - buffer containing the TPG data. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext; - PSCSI_REQUEST_BLOCK srb; - PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb; - NTSTATUS status; - PIRP irp = NULL; - PIO_STACK_LOCATION irpStack; - PMDL mdl = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetTargetPortGroupsAsync (DevInfo %p): Entering function.\n", - DeviceInfo)); - - srb = tpgCompletionContext->Srb; - - SrbZeroSrb(srb); - - // - // Allocate an irp. - // - irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE); - if (!irp) { - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n", - DeviceInfo)); - goto __Exit_DsmpSetTargetPortGroupsAsync; - } - - mdl = IoAllocateMdl(TargetPortGroupsInfo, - TargetPortGroupsInfoLength, - FALSE, - FALSE, - irp); - if (!mdl) { - - status = STATUS_INSUFFICIENT_RESOURCES; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_RW, - "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n", - DeviceInfo)); - goto __Exit_DsmpSetTargetPortGroupsAsync; - } - - MmBuildMdlForNonPagedPool(irp->MdlAddress); - - // - // It is possible that an implicit state transition may have occurred which - // will cause every I_T nexus to return an UA (for asymmetric access state - // changed). So set the number of retries to number of paths (worst case of - // every path being a separate I_T nexus) plus one for a retry down one of - // paths. - // - tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1; - - // - // Set-up the completion routine. - // - IoSetCompletionRoutine(irp, - CompletionRoutine, - (PVOID)CompletionContext, - TRUE, - TRUE, - TRUE); - - // - // Get the recipient's irpstack location. - // - irpStack = IoGetNextIrpStackLocation(irp); - - irpStack->Parameters.Scsi.Srb = srb; - irpStack->DeviceObject = DeviceInfo->TargetObject; - - // - // Set the major function code to IRP_MJ_SCSI. - // - irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; - - // - // Set the minor function, or many requests will get kicked by by port. - // - irpStack->MinorFunction = IRP_MN_SCSI_CLASS; - - srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - srb->Length = sizeof(SCSI_REQUEST_BLOCK); - - SrbSetCdbLength(srb, sizeof(SPC3_CDB_SET_TARGET_PORT_GROUPS)); - cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)SrbGetCdb(srb); - cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS; - cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; - Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength); - - SrbSetTimeOutValue(srb, SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT); - SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer); - SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength); - SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength); - SrbSetDataBuffer(srb, TargetPortGroupsInfo); - srb->SrbStatus = 0; - SrbSetScsiStatus(srb, 0); - SrbSetNextSrb(srb, NULL); - SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | - SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | - SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); - SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); - SrbSetOriginalRequest(srb, irp); - - irp->UserBuffer = TargetPortGroupsInfo; - irp->Tail.Overlay.Thread = PsGetCurrentThread(); - - // - // Send the IRP asynchronously - // - DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext, - DeviceInfo->TargetObject, - irp, - DeviceInfo, - DSM_CALL_COMPLETION_ON_MPIO_ERROR); - - // - // We know that the completion routine will always be called. - // - status = STATUS_PENDING; - - -__Exit_DsmpSetTargetPortGroupsAsync: - - if (status != STATUS_PENDING) { - - // - // This indicates Irp was never sent down to stack (completion routine was never called). - // We need to clean up. - // - if (irp) { - - if (irp->MdlAddress) { - IoFreeMdl(irp->MdlAddress); - } - - IoFreeIrp(irp); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_RW, - "DsmpSetTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x.\n", - DeviceInfo, - status)); - - return status; -} - - -PDSM_LOAD_BALANCE_POLICY_SETTINGS -DsmpCopyLoadBalancePolicies( - _In_ IN PDSM_GROUP_ENTRY GroupEntry, - _In_ IN ULONG DsmWmiVersion, - _In_ IN PVOID SupportedLBPolicies - ) -/*+++ - -Routine Description: - - This routine copies the LB Policies that needs to be persisted in registry. - This is done because registry routines can be called at PASSIVE IRQL only. - So a spinlock cannot be held while accessing registry. So hold a spinlock, - save the values in a temp buffer, release spinlock, and save data to registry - from the temp buffer. - - NOTE: This routine MUST be called with DSM_CONTEXT lock held. - -Arguements: - - GroupEntry - Group entry - DsmWmiVersion - version of the MPIO_DSM_Path class to use - SupportedLBPolicies - LB policy for the group - - Return Value: - - Pointer to LOAD_BALANCE_POLICY_SETTINGS if successful. Else, NULL ---*/ -{ - PDSM_LOAD_BALANCE_POLICY_SETTINGS lbSettings = NULL; - ULONG sizeNeeded; - ULONG inx; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpCopyLoadBalancePolicies (Group %p): Entering function.\n", - GroupEntry)); - - if (((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount == 0) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpCopyLoadBalancePolicies (Group %p): No paths specified in Set LB policies.\n", - GroupEntry)); - - goto __Exit_DsmpCopyLoadBalancePolicies; - } - - sizeNeeded = sizeof(DSM_LOAD_BALANCE_POLICY_SETTINGS) + - ((((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount - 1) * sizeof(MPIO_DSM_Path_V2));; - - lbSettings = DsmpAllocatePool(NonPagedPoolNx, - sizeNeeded, - DSM_TAG_LB_POLICY); - - if (!lbSettings) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpCopyLoadBalancePolicies (Group %p): Failed to allocate memory for LBSettings.\n", - GroupEntry)); - goto __Exit_DsmpCopyLoadBalancePolicies; - } - - // - // Copy the registry key name used to store the LB policies. - // - RtlStringCchCopyNW(lbSettings->RegistryKeyName, - sizeof(lbSettings->RegistryKeyName) / sizeof(lbSettings->RegistryKeyName[0]), - GroupEntry->RegistryKeyName, - ((sizeof(lbSettings->RegistryKeyName) - sizeof(WCHAR))/sizeof(WCHAR))); - - // - // Copy the Load Balance settings for this group - // - lbSettings->LoadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->LoadBalancePolicy; - - lbSettings->PathCount = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; - - for (inx = 0; inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; inx++) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - RtlCopyMemory(&(lbSettings->DsmPath[inx]), - &(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]), - sizeof(MPIO_DSM_Path)); - - // - // DSM_WMI_VERSION_1 supports only active and standby states - // - (lbSettings->DsmPath[inx]).OptimizedPath = TRUE; - - } else { - - RtlCopyMemory(&(lbSettings->DsmPath[inx]), - &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]), - sizeof(MPIO_DSM_Path_V2)); - } - } - -__Exit_DsmpCopyLoadBalancePolicies: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpCopyLoadBalancePolicies (Group %p): Exiting function with lbSettings %p.\n", - GroupEntry, - lbSettings)); - - return lbSettings; -} - - -NTSTATUS -DsmpPersistLBSettings( - _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings - ) -/*+++ - -Routine Description: - - This routine will save the Load Balance settings from LoadBalanceSettings - to registry. - - NOTE: This routine MUST be called at PASSIVE IRQL - - The format of the registry tree is : - - Services\MSDSM\LoadBalanceSettings -> - - DeviceName -> LoadBalancePolicy REG_DWORD - - DsmPath#Suffix -> PrimaryPath REG_DWORD - OptimizedPath REG_DWORD - PathWeight REG_DWORD - - The device name is the one built in DsmpBuildDeviceName - - The Suffix in DsmPath#Suffix is built from the PathId. It is built in - the routine DsmpGetDSMPathKeyName. - -Arguements: - - LoadBalanceSettings - Load Balance settings to be persisted in registry - -Return Value: - - STATUS_SUCCESS if the data could be successfully stored in the registry - Appropriate NT Status code on failure. ---*/ -{ - PMPIO_DSM_Path_V2 dsmPath; - HANDLE lbSettingsKey = NULL; - HANDLE deviceKey = NULL; - HANDLE dsmPathKey = NULL; - UNICODE_STRING subKeyName; - WCHAR dsmPathName[128]; - OBJECT_ATTRIBUTES objectAttributes; - NTSTATUS status; - ULONG inx; - PMPIO_DSM_Path_V2 preferredPath = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Entering function.\n", - LoadBalanceSettings->RegistryKeyName)); - - // - // First open LoadBalanceSettings key under the Service key - // - status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to open LB Settings key. Status %x.\n", - LoadBalanceSettings->RegistryKeyName, - status)); - - goto __Exit_DsmpPersistLBSettings; - } - - // - // Now open the key under which the LB settings for the given device is stored - // - RtlInitUnicodeString(&subKeyName, LoadBalanceSettings->RegistryKeyName); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - lbSettingsKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); - - if (NT_SUCCESS(status)) { - - // - // Remove all LB policy information as we are going to rewrite it. - // We do this in case there is stale information about a path that - // no longer exists - // - status = DsmpRegDeleteTree(deviceKey); - - if (NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Deleted key along with its subkeys.\n", - LoadBalanceSettings->RegistryKeyName)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to delete key. Status %x\n", - LoadBalanceSettings->RegistryKeyName, - status)); - - } - - ZwClose(deviceKey); - deviceKey = NULL; - - } - - status = ZwCreateKey(&deviceKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (NT_SUCCESS(status)) { - - PDSM_DEVICE_INFO devInfo; - - for (inx = 0; inx < LoadBalanceSettings->PathCount; inx++) { - - dsmPath = &(LoadBalanceSettings->DsmPath[inx]); - - if (dsmPath->DsmPathId == 0) { - - continue; - } - - RtlZeroMemory(dsmPathName, sizeof(dsmPathName)); - - // - // Get the sub key name under which the LB settings for - // the given path is stored. - // - DsmpGetDSMPathKeyName(dsmPath->DsmPathId, dsmPathName, 128); - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Will open subkey %ws.\n", - LoadBalanceSettings->RegistryKeyName, - dsmPathName)); - - RtlInitUnicodeString(&subKeyName, dsmPathName); - - RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - deviceKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwCreateKey(&dsmPathKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (NT_SUCCESS(status)) { - - if (dsmPath->PreferredPath) { - - preferredPath = dsmPath; - } - - devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; - - // - // Save PrimaryPath, PathWeight and OptimizedPath values for this path - // - if (devInfo->DesiredState != DSM_DEV_UNDETERMINED) { - - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - dsmPathKey, - DSM_PRIMARY_PATH, - REG_DWORD, - &(dsmPath->PrimaryPath), - sizeof(ULONG)); - - if (NT_SUCCESS(status)) { - - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - dsmPathKey, - DSM_OPTIMIZED_PATH, - REG_DWORD, - &(dsmPath->OptimizedPath), - sizeof(ULONG)); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to save OptimizedPath. Status %x.\n", - LoadBalanceSettings->RegistryKeyName, - status)); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to save Primary Path. Status %x.\n", - LoadBalanceSettings->RegistryKeyName, - status)); - } - } - - if (NT_SUCCESS(status)) { - - if (LoadBalanceSettings->LoadBalancePolicy == DSM_LB_WEIGHTED_PATHS) { - - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - dsmPathKey, - DSM_PATH_WEIGHT, - REG_DWORD, - &(dsmPath->PathWeight), - sizeof(ULONG)); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to save PathWeight. Status %x.\n", - LoadBalanceSettings->RegistryKeyName, - status)); - } - } - } - - ZwClose(dsmPathKey); - dsmPathKey = NULL; - } else { - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to open DSM Path key. Status %x.\n", - LoadBalanceSettings->RegistryKeyName, - status)); - } - - if (!NT_SUCCESS(status)) { - break; - } - } - - if (NT_SUCCESS(status)) { - - // - // Save the new Load Balance Policy value, - // - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_LOAD_BALANCE_POLICY, - REG_DWORD, - &(LoadBalanceSettings->LoadBalancePolicy), - sizeof(ULONG)); - if (NT_SUCCESS(status)) { - - UCHAR explicitlySet = TRUE; - - // - // Write out that the policy has been explicitly set - // - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_POLICY_EXPLICITLY_SET, - REG_BINARY, - &explicitlySet, - sizeof(UCHAR)); - - if (NT_SUCCESS(status)) { - - // - // If FailOver-Only policy, set the PreferredPath, if specified - // - if (preferredPath) { - - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_PREFERRED_PATH, - REG_BINARY, - &(preferredPath->DsmPathId), - sizeof(ULONGLONG)); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (ES).\n", - LoadBalanceSettings->RegistryKeyName)); - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (LBP).\n", - LoadBalanceSettings->RegistryKeyName)); - } - } - } - -__Exit_DsmpPersistLBSettings: - - if (dsmPathKey) { - ZwClose(dsmPathKey); - } - - if (deviceKey) { - ZwClose(deviceKey); - } - - if (lbSettingsKey) { - ZwClose(lbSettingsKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpPersistLBSettings (DevName %ws): Exiting function with status %x.\n", - LoadBalanceSettings->RegistryKeyName, - status)); - - return status; -} - - -NTSTATUS -DsmpSetDeviceALUAState( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_ IN DSM_DEVICE_STATE DevState - ) -/*++ - -Routine Description: - - Helper routine to build the STPG info and send it down to modify the passed in - devInfo's state. - -Arguments: - - DsmContext - DSM context. - DeviceInfo - DevInfo whose state needs to be changed. - DevState - New state to be set. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength; - PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; - NTSTATUS status; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpSetDeviceALUAState (DevInfo %p): Entering function.\n", - DeviceInfo)); - - // - // Send down SetTPG to set the appropriate access state - // (The TPG block will contain the header and a SetTPG descriptor). - // - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); - - targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, - targetPortGroupsInfoLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo) { - - tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); - tpgDescriptor->AsymmetricAccessState = DevState; - REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &DeviceInfo->TargetPortGroup->Identifier); - - status = DsmpSetTargetPortGroups(DeviceInfo->TargetObject, - targetPortGroupsInfo, - targetPortGroupsInfoLength); - - if (NT_SUCCESS(status)) { - - // - // An explicit transition may cause changes to some other TPGs. - // So we need to query for the states of all the TPGs and update - // our internal list and its elements. - // - status = DsmpGetDeviceALUAState(DsmContext, - DeviceInfo, - NULL); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetDeviceALUAState (DevInfo %p): Failed to SetTPG with %x.\n", - DeviceInfo, - status)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpSetDeviceALUAState (DevInfo %p): Failed to allocate TPG.\n", - DeviceInfo)); - status = STATUS_INSUFFICIENT_RESOURCES; - } - - if (targetPortGroupsInfo) { - DsmpFreePool(targetPortGroupsInfo); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpSetDeviceALUAState (DevInfo %p): Exiting function with status %x\n", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpAdjustDeviceStatesALUA( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo, - _In_ IN ULONG SpecialHandlingFlag - ) -/*++ - -Routine Description: - - Helper routine to build the adjust every device state in the group taking - the following into consideration: - 1. PreferredActiveDeviceInfo - 2. DeviceInfo's TPG state - 3. Preferred Path - 4. LB Policy - -Arguments: - - Group - Pseudo-LUN whose path states need to be adjusted. - PreferredActiveDeviceInfo - DevInfo whose state needs to preferrably made - A/O, if possible. This parameter is optional. - - SpecialHandlingFlag - Flags to indicate any special handling requirement - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - ULONG index; - PDSM_DEVICE_INFO deviceInfo; - PDSM_DEVICE_INFO activeDevice = NULL; - DSM_DEVICE_STATE devState; - NTSTATUS status = STATUS_SUCCESS; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): Entering function with preferred active devInfo %p.\n", - Group, - PreferredActiveDeviceInfo)); - - // - // Ensure that: - // 1. All devices match their ALUA state. - // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it. - // 3. For FOO there must be only one A/O device. Preferably the preferred path. - // - for (index = 0; index < DSM_MAX_PATHS; index++) { - - deviceInfo = Group->DeviceList[index]; - - if (deviceInfo) { - - devState = deviceInfo->State; - - if (!DsmpIsDeviceFailedState(deviceInfo->State) && - DsmpIsDeviceInitialized(deviceInfo) && - DsmpIsDeviceUsable(deviceInfo) && - DsmpIsDeviceUsablePR(deviceInfo)) { - - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = deviceInfo->ALUAState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (its ALUA state).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - - if (deviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { - - // - // In FOO and RRWS, we need to mask states. - // - switch (Group->LoadBalanceType) { - case DSM_LB_FAILOVER: { - - // - // Cache the first available devInfo that is in A/O - // - if (!activeDevice) { - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p choosen as the active device.\n", - Group, - activeDevice)); - - break; - } - - // - // Check if this deviceInfo is the preferred path. If yes, - // mask the active device's state and make this the new - // active device. - // - if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)deviceInfo->FailGroup->PathId)) { - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || - activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): Previous active devInfo %p transitioning from %u to %u.\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (preferred path).\n", - Group, - activeDevice)); - - break; - } - - // - // If active device's desired state is not A/O but this - // deviceInfo's is, then mask the active device's state - // and make this one the new active device. - // - if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { - - // - // The exception though is if the current active device - // is the preferred path - // - if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) { - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || - deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active DI is PrefPath).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - } else { - - // - // If this is the devInfo that is preferred to be A/O, make it such - // - if (PreferredActiveDeviceInfo && - PreferredActiveDeviceInfo == deviceInfo) { - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (found a preferred active DI).\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active DI (preferred).\n", - Group, - activeDevice)); - } else { - - // - // Check if this devInfo desires to be in A/O, since the currently - // active one doesn't want to be. - // - if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { - - // - // This deviceInfo's desire is also not to be in A/O, - // so just leave the current one active. - // - if (devState == DSM_DEV_ACTIVE_OPTIMIZED) { - - // - // Exception is if we're processing the device whose state before - // RTPG was sent was already A/O, it is best to leave this device - // in A/O state. - // - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || - activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Found a DI that was previously active.\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (previously A/O).\n", - Group, - activeDevice)); - } else { - - // - // This device wasn't in A/O state before, so just leave - // the currently selected active device as is. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = deviceInfo->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active device already exists).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - } - } else { - - // - // Current devInfo wants (or doesn't) mind being in - // A/O, whereas the current active device doesn't, so - // mask the active device and make this devInfo the - // active device. - // - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Current DI prefers being A/O.\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (desired state).\n", - Group, - activeDevice)); - } - } - } - } else { - - // - // The single overriding factor is always the preferred path. - // Everything else is secondary, so first check if the currently - // active device can even be overridden by another one. - // - if (Group->PreferredPath != (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) { - - // - // It can't be overridden, so we're done. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || - deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (current active DI is PrefPath).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - } else { - - // - // Active device's desired state is A/O but it isn't the preferred - // path. Check if this devInfo is preferred as A/O. - // - if (PreferredActiveDeviceInfo && - PreferredActiveDeviceInfo == deviceInfo) { - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || - activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. New DI is preferred active.\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (this DI is preferred active).\n", - Group, - activeDevice)); - } else { - - // - // Active device's desired state is A/O but it isn't the - // preferred path. Check if this devInfo's desired state - // is also A/O. If yes, we'll need to make certain decisions. - // - if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { - - // - // Since this device doesn't desire to be in - // A/O and we already have an active device, just - // mask its state. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = deviceInfo->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (prefers being in non-A/O).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - } else { - - // - // Active device is in A/O and this device desires to be in - // A/O too. Make this the new active device only if its state - // before the RTPG was already A/O. - // - if (devState == DSM_DEV_ACTIVE_OPTIMIZED) { - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || - activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI was already in A/O previously).\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is new active device (since it was in A/O previously too).\n", - Group, - activeDevice)); - } else { - - // - // Just leave the currently active one alone. - // - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || - deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (leave current active DI alone).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - } - } - } - } - } - break; - } - - case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { - - // - // At least one path needs to be in A/O state, so - // cache the first available devInfo that is in A/O - // - if (!activeDevice) { - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): We have atleast one A/O DevInfo %p.\n", - Group, - activeDevice)); - - break; - } - - // - // Check if this device is preferred to be in A/O - // - if (PreferredActiveDeviceInfo && - PreferredActiveDeviceInfo == deviceInfo) { - - // - // If the currently active device, doesn't desire to be in - // A/O state, mask its state. - // - if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desired state non-A/O).\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - } - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (preferred active DI).\n", - Group, - activeDevice)); - } else { - - // - // If this device's desired state is specified and not A/O, - // mask its path state. - // - if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { - - deviceInfo->PreviousState = deviceInfo->State; - deviceInfo->State = deviceInfo->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desires to be in non-A/O).\n", - Group, - deviceInfo, - deviceInfo->PreviousState, - deviceInfo->State)); - } else { - - // - // Since this devInfo desires to be in A/O, we are assured - // of at least one path in A/O. So check to see if the - // currently active device doesn't desire to be in A/O. - // - if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && - activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { - - activeDevice->PreviousState = activeDevice->State; - activeDevice->State = activeDevice->DesiredState; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI desires to be in A/O).\n", - Group, - activeDevice, - activeDevice->PreviousState, - activeDevice->State)); - - activeDevice = deviceInfo; - - TracePrint((TRACE_LEVEL_INFORMATION, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active DI (desires to be in A/O).\n", - Group, - activeDevice)); - } - } - } - - break; - } - - default: { - - // - // For RR, LQD and WP, paths must be in the same - // state as their corresponding TPG. Preferably - // all should be A/O. - // - if (deviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { - DSM_ASSERT(deviceInfo->State == deviceInfo->ALUAState); - } - - break; - } - } - } - } - } - } - - // - // There may have been a change to the device states. - // DsmpGetPath() will pick these changes for RR, RRWS and LQD. - // However, it won't for FOO and WP, so update PTBU if needed. - // - if (Group->LoadBalanceType == DSM_LB_FAILOVER || - Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) { - - deviceInfo = DsmpGetActivePathToBeUsed(Group, FALSE, SpecialHandlingFlag); - - if (deviceInfo) { - - InterlockedExchangePointer(&(Group->PathToBeUsed), deviceInfo->FailGroup); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpAdjustDeviceStatesALUA (Group %p): Exiting function with status %x\n", - Group, - status)); - - return status; -} - - -PDSM_WORKITEM -DsmpAllocateWorkItem( - _In_ IN PDEVICE_OBJECT DeviceObject, - _In_ IN PVOID Context - ) -/*++ - -Routine Description: - - Allocates a work item to handle reservation failover. - -Arguments: - - DeviceObject - Target device. - Context - Workitem context - -Return Value: - - Allocated workitem or NULL (if low memory). - ---*/ -{ - PDSM_WORKITEM dsmWorkItem = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpAllocateWorkItem (DevObj %p): Entering function.\n", - DeviceObject)); - - dsmWorkItem = DsmpAllocatePool(NonPagedPoolNx, - sizeof(DSM_WORKITEM), - DSM_TAG_WORKITEM); - if (dsmWorkItem != NULL) { - - dsmWorkItem->WorkItem = IoAllocateWorkItem(DeviceObject); - if (dsmWorkItem->WorkItem != NULL) { - - dsmWorkItem->Context = Context; - } else { - - DsmpFreePool(dsmWorkItem); - dsmWorkItem = NULL; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpAllocateWorkItem (DevObj %p): Exiting function. dsmWorkItem %p.\n", - DeviceObject, - dsmWorkItem)); - - return dsmWorkItem; -} - - -VOID -DsmpFreeWorkItem( - _In_ IN PDSM_WORKITEM DsmWorkItem - ) -{ - PVOID temp = DsmWorkItem; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpFreeWorkItem (WorkItem %p): Entering function.\n", - DsmWorkItem)); - - if (DsmWorkItem != NULL) { - - if (DsmWorkItem->WorkItem != NULL) { - IoFreeWorkItem(DsmWorkItem->WorkItem); - } - - DsmpFreePool(DsmWorkItem); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_IOCTL, - "DsmpFreeWorkItem (WorkItem %p): Exiting function.\n", - temp)); - - return; -} - - -VOID -DsmpFreeZombieGroupList( - _In_ IN PDSM_FAILOVER_GROUP FailGroup - ) -{ - PLIST_ENTRY zombieEntry = NULL; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFreeZombieGroupList (FailGroup %p): Entering function.\n", - FailGroup)); - - while (!IsListEmpty(&FailGroup->ZombieGroupList)) { - - zombieEntry = RemoveHeadList(&FailGroup->ZombieGroupList); - - if (zombieEntry) { - - DsmpFreePool(zombieEntry); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpFreeZombieGroupList (FailGroup %p): Exiting function.\n", - FailGroup)); -} - - -NTSTATUS -DsmpGetDeviceALUAState( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_DEVICE_INFO DeviceInfo, - _In_opt_ IN PDSM_DEVICE_STATE DevState - ) -/*++ - -Routine Description: - - Helper routine to build the RTPG info and send it down to retrieve the - devInfo's current state. - -Arguments: - - DsmContext - DSM context. - DeviceInfo - DevInfo whose state needs to be changed. - DevState - Current state of passed in DeviceInfo. - -Return Value: - - STATUS_SUCCESS or appropriate failure code. - ---*/ -{ - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength = 0; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; - KIRQL irql; - NTSTATUS status; - ULONG index; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetDeviceALUAState (DevInfo %p): Entering function.\n", - DeviceInfo)); - - status = DsmpReportTargetPortGroups(DeviceInfo->TargetObject, - &targetPortGroupsInfo, - &targetPortGroupsInfoLength); - - - if (NT_SUCCESS(status) && targetPortGroupsInfo != NULL) { - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - status = DsmpParseTargetPortGroupsInformation(DsmContext, - DeviceInfo->Group, - targetPortGroupsInfo, - targetPortGroupsInfoLength); - - for (index = 0; index < DSM_MAX_PATHS; index++) { - - targetPortGroup = DeviceInfo->Group->TargetPortGroupList[index]; - - if (targetPortGroup) { - - DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - if (DevState) { - - *DevState = DeviceInfo->State; - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_GENERAL, - "DsmpGetDeviceALUAState (DevInfo %p): ReportTPG failed with %x.\n", - DeviceInfo, - status)); - } - - if (targetPortGroupsInfo) { - - DsmpFreePool(targetPortGroupsInfo); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_GENERAL, - "DsmpGetDeviceALUAState (DevInfo %p): Exiting function with status %x\n", - DeviceInfo, - status)); - - return status; -} - - -NTSTATUS -DsmpRegCopyTree( - _In_ IN HANDLE SourceKey, - _In_ IN HANDLE DestKey - ) -/*++ - -Routine Description: - - Copies a reg subtree from source key to destination key. - This routine will first copy over all the key's values, and then - copy the subkeys, each time recursively handling the subkey's - values and its subtree. - -Arguments: - - SourceKey - Handle to the root of the subtree to copy over. - DestKey - Handle to the root of the new tree. - -Return Value: - - STATUS_SUCCESS upon successfully coping over the tree. - Appropriate NT error code in case of failure. - ---*/ -{ - ULONG numValues = 0; - ULONG numSubKeys = 0; - ULONG lengthOfValueName = 0; - ULONG lengthOfValueData = 0; - ULONG lengthOfKeyName = 0; - LPWSTR valueBuf = NULL; - BYTE *valueDataBuf = NULL; - ULONG valueDataType; - ULONG titleIndex; - HANDLE srcSubKey = NULL; - HANDLE destSubKey = NULL; - LPWSTR subKey = NULL; - NTSTATUS status; - PKEY_FULL_INFORMATION keyFullInfo = NULL; - ULONG length = sizeof(KEY_FULL_INFORMATION); - ULONG index = 0; - PKEY_VALUE_FULL_INFORMATION keyValueFullInfo = NULL; - PKEY_BASIC_INFORMATION keyBasicInfo = NULL; - OBJECT_ATTRIBUTES objectAttributes; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Entering function.\n", - SourceKey)); - - if (!SourceKey || !DestKey) { - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpRegCopyTree; - } - - // - // Query the source key for information about number of subkeys, number of values, etc. - // - do { - if (keyFullInfo) { - - DsmpFreePool(keyFullInfo); - } - - keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); - - if (!keyFullInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key full info.\n", - SourceKey)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegCopyTree; - } - - status = ZwQueryKey(SourceKey, - KeyFullInformation, - keyFullInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to query key. Status %x.\n", - SourceKey, - status)); - - goto __Exit_DsmpRegCopyTree; - } - - numSubKeys = keyFullInfo->SubKeys; - numValues = keyFullInfo->Values; - lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR); - lengthOfValueName = keyFullInfo->MaxValueNameLen + sizeof(WCHAR); - lengthOfValueData = keyFullInfo->MaxValueDataLen + sizeof(WCHAR); - - // - // Allocate a buffer for the name of the value - // - valueBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - lengthOfValueName, - DSM_TAG_REG_KEY_RELATED); - if (!valueBuf) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value name.\n", - SourceKey)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegCopyTree; - } - - // - // Allocate a buffer for the value data - // - valueDataBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - lengthOfValueData, - DSM_TAG_REG_KEY_RELATED); - - if (!valueDataBuf) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value's data.\n", - SourceKey)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegCopyTree; - } - - // - // First enumerate all of the values - // - status = STATUS_SUCCESS; - for (index = 0; index < numValues && NT_SUCCESS(status); index++) { - - UNICODE_STRING valueName; - - length = sizeof(KEY_VALUE_FULL_INFORMATION); - - do { - - if (keyValueFullInfo) { - - DsmpFreePool(keyValueFullInfo); - } - - keyValueFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); - - if (!keyValueFullInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value full info.\n", - SourceKey)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegCopyTree; - } - - // - // Get the information of the index'th value - // - status = ZwEnumerateValueKey(SourceKey, - index, - KeyValueFullInformation, - keyValueFullInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to enumerate key's value information. Status %x.\n", - SourceKey, - status)); - - goto __Exit_DsmpRegCopyTree; - } - - // - // Capture the data type, data value, and value name. - // - titleIndex = keyValueFullInfo->TitleIndex; - valueDataType = keyValueFullInfo->Type; - - RtlZeroMemory(valueDataBuf, lengthOfValueData); - RtlCopyMemory(valueDataBuf, - (PUCHAR)keyValueFullInfo + keyValueFullInfo->DataOffset, - keyValueFullInfo->DataLength); - - RtlZeroMemory(valueBuf, lengthOfValueName); - RtlStringCbCopyNW(valueBuf, lengthOfValueName, keyValueFullInfo->Name, keyValueFullInfo->NameLength); - RtlInitUnicodeString(&valueName, valueBuf); - - // - // Copy the value over to the new key - // - status = ZwSetValueKey(DestKey, - &valueName, - titleIndex, - valueDataType, - valueDataBuf, - keyValueFullInfo->DataLength); - } - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate set new key's value information. Status %x.\n", - SourceKey, - status)); - - goto __Exit_DsmpRegCopyTree; - } - - // - // Allocate buffer for subkey name - // - subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - lengthOfKeyName, - DSM_TAG_REG_KEY_RELATED); - - if(!subKey) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for sub key name.\n", - SourceKey)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegCopyTree; - } - - // - // Now Enumerate all of the subkeys - // - length = sizeof(KEY_BASIC_INFORMATION); - for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) { - - UNICODE_STRING subKeyName; - - do { - if (keyBasicInfo) { - - DsmpFreePool(keyBasicInfo); - } - - keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - length, - DSM_TAG_REG_KEY_RELATED); - - if (!keyBasicInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key basic info.\n", - SourceKey)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegCopyTree; - } - - // - // Enumerate the index'th subkey - // - status = ZwEnumerateKey(SourceKey, - index, - KeyBasicInformation, - keyBasicInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to enumerate sub key's info. Status %x.\n", - SourceKey, - status)); - - goto __Exit_DsmpRegCopyTree; - } - - RtlZeroMemory(subKey, lengthOfKeyName); - RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength); - RtlInitUnicodeString(&subKeyName, subKey); - - // - // Open a handle to the the subkey on the old device. - // - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - SourceKey, - (PSECURITY_DESCRIPTOR) NULL); - - if (srcSubKey) { - ZwClose(srcSubKey); - srcSubKey = NULL; - } - - status = ZwOpenKey(&srcSubKey, - KEY_ALL_ACCESS, - &objectAttributes); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to open reg key %ws. Status %x.\n", - SourceKey, - subKey, - status)); - - goto __Exit_DsmpRegCopyTree; - } - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - DestKey, - (PSECURITY_DESCRIPTOR) NULL); - - if (destSubKey) { - ZwClose(destSubKey); - destSubKey = NULL; - } - - // - // Create the subkey on the new device. - // - status = ZwCreateKey(&destSubKey, - KEY_ALL_ACCESS, - &objectAttributes, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - NULL); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Failed to create reg key %ws. Status %x.\n", - SourceKey, - subKey, - status)); - - goto __Exit_DsmpRegCopyTree; - } - - // - // That's it. We've got everything we need (ie. handles to the two new - // subtrees' roots. Call recursively. - // - status = DsmpRegCopyTree(srcSubKey, destSubKey); - } - -__Exit_DsmpRegCopyTree: - - if (keyFullInfo) { - DsmpFreePool(keyFullInfo); - } - - if (valueBuf) { - DsmpFreePool(valueBuf); - } - - if (valueDataBuf) { - DsmpFreePool(valueDataBuf); - } - - if (keyValueFullInfo) { - DsmpFreePool(keyValueFullInfo); - } - - if (subKey) { - DsmpFreePool(subKey); - } - - if (keyBasicInfo) { - DsmpFreePool(keyBasicInfo); - } - - if (srcSubKey) { - ZwClose(srcSubKey); - } - - if (destSubKey) { - ZwClose(destSubKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRegCopyTree (SrcKey %p): Exiting function with status %x.\n", - SourceKey, - status)); - - return status; -} - - -NTSTATUS -DsmpRegDeleteTree( - _In_ IN HANDLE KeyRoot - ) -/*++ -Routine Description: - - This routine is a recursive worker that enumerates the subkeys - of a given key, applies itself to each one, then deletes itself. - -Arguments: - - KeyRoot - Supplies a handle to the root of subtree to be deleted. - -Return Value: - - STATUS_SUCCESS - upon successful deletion of subtree. - Appropriate NT error code upon failure. - ---*/ -{ - NTSTATUS status; - PKEY_FULL_INFORMATION keyFullInfo = NULL; - ULONG length = sizeof(KEY_FULL_INFORMATION); - ULONG numSubKeys; - ULONG lengthOfKeyName; - LPWSTR subKey = NULL; - PKEY_BASIC_INFORMATION keyBasicInfo = NULL; - ULONG index = 0; - HANDLE srcSubKey = NULL; - OBJECT_ATTRIBUTES objectAttributes; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Entering function.\n", - KeyRoot)); - - if (!KeyRoot) { - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpRegDeleteTree; - } - - // - // Query the source key for information about number of subkeys and max - // length needed for subkey name. - // - do { - if (keyFullInfo) { - - DsmpFreePool(keyFullInfo); - } - - keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); - - if (!keyFullInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key full info.\n", - KeyRoot)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegDeleteTree; - } - - status = ZwQueryKey(KeyRoot, - KeyFullInformation, - keyFullInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Failed to query key. Status %x.\n", - KeyRoot, - status)); - - goto __Exit_DsmpRegDeleteTree; - } - - numSubKeys = keyFullInfo->SubKeys; - lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR); - - if (numSubKeys) { - - // - // Allocate buffer for subkey name - // - subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - lengthOfKeyName, - DSM_TAG_REG_KEY_RELATED); - - if(!subKey) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for sub key.\n", - KeyRoot)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegDeleteTree; - } - - // - // Now Enumerate all of the subkeys - // - index = numSubKeys - 1; - length = sizeof(KEY_BASIC_INFORMATION); - do { - - UNICODE_STRING subKeyName; - - do { - if (keyBasicInfo) { - - DsmpFreePool(keyBasicInfo); - } - - keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - length, - DSM_TAG_REG_KEY_RELATED); - - if (!keyBasicInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key basic info.\n", - KeyRoot)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpRegDeleteTree; - } - - // - // Enumerate the index'th subkey - // - status = ZwEnumerateKey(KeyRoot, - index, - KeyBasicInformation, - keyBasicInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (NT_SUCCESS(status)) { - - RtlZeroMemory(subKey, lengthOfKeyName); - RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength); - RtlInitUnicodeString(&subKeyName, subKey); - - // - // Open a handle to the the current root's subkey. - // - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - KeyRoot, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(&srcSubKey, - KEY_ALL_ACCESS, - &objectAttributes); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Failed to open key %ws. Status %x.\n", - KeyRoot, - subKey, - status)); - - goto __Exit_DsmpRegDeleteTree; - } - - // - // Delete this key's subtree (recursively). - // - status = DsmpRegDeleteTree(srcSubKey); - - ZwClose(srcSubKey); - srcSubKey = NULL; - } - - index--; - - } while (status != STATUS_NO_MORE_ENTRIES && (LONG)index >= 0); - - if (status == STATUS_NO_MORE_ENTRIES) { - - status = STATUS_SUCCESS; - } - } - - ZwDeleteKey(KeyRoot); - -__Exit_DsmpRegDeleteTree: - - if (srcSubKey) { - ZwClose(srcSubKey); - } - - if (keyFullInfo) { - DsmpFreePool(keyFullInfo); - } - - if (subKey) { - DsmpFreePool(subKey); - } - - if (keyBasicInfo) { - DsmpFreePool(keyBasicInfo); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpRegDeleteTree (SrcKey %p): Exiting function with status %x.\n", - KeyRoot, - status)); - - return status; -} - - -#if defined (_WIN64) -VOID -DsmpPassThroughPathTranslate32To64( - _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32, - _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64 - ) -/*++ - -Routine Description: - - On WIN64, the SCSI_PASS_THROUGH field of the MPIO_PASS_THROUGH_PATH structure - sent down by a 32-bit application must be marshaled into a 64-bit version - of the structure. This function performs that marshaling. - -Arguments: - - MpioPassThroughPath32 - Supplies a pointer to a 32-bit MPIO_PASS_THROUGH_PATH - struct. - - MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH - structure, into which we'll copy the marshaled - 32-bit data. - -Return Value: - - None. - ---*/ -{ - // - // Copy the first set of fields out of the 32-bit structure. These - // fields all line up between the 32 & 64 bit versions. - // - // Note that we do NOT adjust the length in the SrbControl. This is to - // allow the calling routine to compare the length of the actual - // control area against the offsets embedded within. If we adjusted the - // length then requests with the sense area backed against the control - // area would be rejected because the 64-bit control area is 4 bytes - // longer. - // - RtlCopyMemory(MpioPassThroughPath64, - MpioPassThroughPath32, - FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); - - // - // Copy over the CDB. - // - RtlCopyMemory(MpioPassThroughPath64->PassThrough.Cdb, - MpioPassThroughPath32->PassThrough.Cdb, - 16 * sizeof(UCHAR) - ); - - // - // Copy over the rest of the fields of the structure. - // - MpioPassThroughPath64->Version = MpioPassThroughPath32->Version; - MpioPassThroughPath64->Length = MpioPassThroughPath32->Length; - MpioPassThroughPath64->Flags = MpioPassThroughPath32->Flags; - MpioPassThroughPath64->PortNumber = MpioPassThroughPath32->PortNumber; - MpioPassThroughPath64->MpioPathId = MpioPassThroughPath32->MpioPathId; - - // - // Copy the fields that follow the ULONG_PTR. - // - MpioPassThroughPath64->PassThrough.DataBufferOffset = (ULONG_PTR)MpioPassThroughPath32->PassThrough.DataBufferOffset; - MpioPassThroughPath64->PassThrough.SenseInfoOffset = MpioPassThroughPath32->PassThrough.SenseInfoOffset; - - return; -} - - -VOID -DsmpPassThroughPathTranslate64To32( - _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64, - _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32 - ) -/*++ - -Routine Description: - - On WIN64, the SCSI_PASS_THROUGH field of MPIO_PASS_THROUGH_PATH structure - sent down by a 32-bit application must be marshaled into a 64-bit version - of the structure. This function marshals a 64-bit version of the structure - back into a 32-bit version. - -Arguments: - - MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH - struct. - - MpioPassThroughPath32 - Supplies the address of a pointer to a 32-bit - MPIO_PASS_THROUGH_PATH structure, into which we'll - copy the marshaled 64-bit data. - -Return Value: - - None. - ---*/ -{ - // - // Copy back the fields through the data offsets. - // - RtlCopyMemory(MpioPassThroughPath32, - MpioPassThroughPath64, - FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); - - - // - // Copy over the CDB. - // - RtlCopyMemory(MpioPassThroughPath32->PassThrough.Cdb, - MpioPassThroughPath64->PassThrough.Cdb, - 16 * sizeof(UCHAR) - ); - - // - // Copy over the rest of the fields of the structure. - // - MpioPassThroughPath32->Version = MpioPassThroughPath64->Version; - MpioPassThroughPath32->Length = MpioPassThroughPath64->Length; - MpioPassThroughPath32->Flags = MpioPassThroughPath64->Flags; - MpioPassThroughPath32->PortNumber = MpioPassThroughPath64->PortNumber; - MpioPassThroughPath32->MpioPathId = MpioPassThroughPath64->MpioPathId; - - return; -} -#endif - - -NTSTATUS -DsmpGetMaxPRRetryTime( - _In_ IN PDSM_CONTEXT Context, - _Out_ OUT PULONG RetryTime - ) -/*++ - -Routine Description: - - This routine is used to get the max time period for which a PR request failing - with a retry-able unit attention should be retried before failing back to MSCS. - The value is determined by querying the value found at - "msdsm\Parameters\DsmMaximumStateTransitionTime" - -Arguments: - - Context - The DSM Context value. - RetryTime - The output parameter that will receive the value to be used. - -Return Value: - - Status of the RtlQueryRegistryValues call. - ---*/ -{ - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - WCHAR registryKeyName[56] = {0}; - NTSTATUS status; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetMaxPRRetryTime (DsmCtxt %p): Entering function.\n", - Context)); - - NT_ASSERT(RetryTime); - *RetryTime = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME; - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - // - // Build the key value name that we want as the base of the query. - // - RtlStringCbPrintfW(registryKeyName, - sizeof(registryKeyName), - DSM_PARAMETER_PATH_W); - - // - // The query table has two entries. One for the state transition time and - // the second which is the 'NULL' terminator. - // - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME; - queryTable[0].EntryContext = RetryTime; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, - registryKeyName, - queryTable, - registryKeyName, - NULL); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpGetMaxPRRetryTime (DsmCtxt %p): Exiting function with status %x.\n", - Context, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryCacheInformationFromRegistry( - _In_ IN PDSM_CONTEXT DsmContext, - _Out_ OUT PBOOLEAN UseCacheForLeastBlocks, - _Out_ OUT PULONGLONG CacheSizeForLeastBlocks - ) -/*++ - -Routine Description: - - This routine is used to get the information about whether sequential IO - should use the same path when employing Least Blocks policy. - It also queries the size of cache set by the administrator. - The value is determined by querying the value found at - "msdsm\Parameters\DsmUseCacheForLeastBlocks" and - "msdsm\Parameters\DsmCacheSizeForLeastBlocks" - -Arguments: - - Context - The DSM Context value. - UseCacheForLeastBlocks - Returns the flag that indicates whether or not to - use same path for sequential IO when LB policy - is Least Blocks. - CacheSizeForLeastBlocks - Returns the size of the cache (in bytes) set by - the Admin to indicate the amount of sequential - data that should be use the same path when LB - policy is Least Blocks. - -Return Value: - - Status of the RtlQueryRegistryValues call. - ---*/ -{ - RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; - WCHAR registryKeyName[56] = {0}; - HANDLE parametersKey = NULL; - UNICODE_STRING keyValueName; - NTSTATUS status; - struct _cacheSizeForLeastBlocks { - KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; - ULONGLONG Data; - } cacheSizeForLeastBlocks; - ULONG length = 0; - BOOLEAN useCacheForLeastBlocksDefault = FALSE; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Entering function.\n", - DsmContext)); - - NT_ASSERT(UseCacheForLeastBlocks); - NT_ASSERT(CacheSizeForLeastBlocks); - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - // - // Build the key value name that we want as the base of the query. - // - RtlStringCbPrintfW(registryKeyName, - sizeof(registryKeyName), - DSM_PARAMETER_PATH_W); - - // - // The query table has two entries. One for whether to use cache, and - // and the second which is the 'NULL' terminator. - // - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_USE_CACHE_FOR_LEAST_BLOCKS; - queryTable[0].EntryContext = UseCacheForLeastBlocks; - queryTable[0].DefaultType = (REG_BINARY << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_BINARY; - queryTable[0].DefaultLength = sizeof(BOOLEAN); - queryTable[0].DefaultData = &useCacheForLeastBlocksDefault; - - status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, - registryKeyName, - queryTable, - registryKeyName, - NULL); - - if (NT_SUCCESS(status)) { - - status = DsmpOpenDsmServicesParametersKey(KEY_QUERY_VALUE, ¶metersKey); - - if (NT_SUCCESS(status)) { - - RtlInitUnicodeString(&keyValueName, DSM_CACHE_SIZE_FOR_LEAST_BLOCKS); - - status = ZwQueryValueKey(parametersKey, - &keyValueName, - KeyValuePartialInformation, - &cacheSizeForLeastBlocks, - sizeof(cacheSizeForLeastBlocks), - &length); - - if (NT_SUCCESS(status)) { - - NT_ASSERT(cacheSizeForLeastBlocks.KeyValueInfo.DataLength == sizeof(ULONGLONG)); - *CacheSizeForLeastBlocks = *((ULONGLONG UNALIGNED *)&(cacheSizeForLeastBlocks.KeyValueInfo.Data)); - } - } - - if (parametersKey) { - ZwClose(parametersKey); - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_PNP, - "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Exiting function with status %x.\n", - DsmContext, - status)); - - return status; -} - -BOOLEAN -DsmpConvertSharedSpinLockToExclusive( - _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock - ) -/*++ - -Routine Description: - - This routine is a wrapper around ExTryConvertSharedSpinLockExclusive() that - guarantees the given EX_SPIN_LOCK will be acquired in Exclusive mode once - this function returns. - - It's possible the lock may be released and re-acquired within this function - so the caller should be very careful about the use of this function. - - N.B. The caller MUST have acquired the given lock in Shared mode before - calling this function. - -Arguments: - - SpinLock - The EX_SPIN_LOCK to convert from Shared to Exclusive mode. - -Return Value: - - Status of the ExTryConvertSharedSpinLockExclusive() call. This function - will always return with the lock acquired in Exclusive mode. The FALSE is - returned, then the lock had to be released and re-acquired. - ---*/ -{ - BOOLEAN converted = FALSE; - - converted = (BOOLEAN)ExTryConvertSharedSpinLockExclusive(SpinLock); - - // - // If the conversion attempt failed, then we should release the lock from - // Shared mode and try to pick it back up in Exclusive mode to guarantee - // this function will always return with the lock in Exclusive mode. - // - if (converted == FALSE) { - ExReleaseSpinLockSharedFromDpcLevel(SpinLock); - ExAcquireSpinLockExclusiveAtDpcLevel(SpinLock); - } - - return converted; -} - - diff --git a/tests/projects/wdk/kmdf/msdsm/wmi.c b/tests/projects/wdk/kmdf/msdsm/wmi.c deleted file mode 100644 index 0ee882192..000000000 --- a/tests/projects/wdk/kmdf/msdsm/wmi.c +++ /dev/null @@ -1,3822 +0,0 @@ - -/*++ - -Copyright (C) 2004-2010 Microsoft Corporation - -Module Name: - - wmi.c - -Abstract: - - This driver is the Microsoft Device Specific Module (DSM). - It exports behaviours that mpio.sys will use to determine how to - multipath SPC-3 compliant devices. - - This file contains WMI related functions. - -Environment: - - kernel mode only - -Notes: - ---*/ - - - -#include "precomp.h" -#include "msdsmwmi.h" -#include "msdsmdsm.h" - -#ifdef DEBUG_USE_WPP -#include "wmi.tmh" -#endif - -#pragma warning (disable:4305) - -extern BOOLEAN DoAssert; - -#define USE_BINARY_MOF_RESOURCE - -#define DSM_INVALID_LOAD_BALANCE_POLICY STATUS_INVALID_PARAMETER -#define DSM_UNSUPPORTED_VERSION STATUS_NOT_SUPPORTED - -// -// Max length for each of the DeviceId strings (supported device list) -// NOTE: This must be kept in sync with msdsmdsm.mof -// -#define MSDSM_MAX_DEVICE_ID_LENGTH 31 -#define MSDSM_MAX_DEVICE_ID_SIZE (MSDSM_MAX_DEVICE_ID_LENGTH * sizeof(WCHAR)) - -// -// List of supported DSM-centric guids -// -GUID MSDSM_SUPPORTED_DEVICES_LISTGUID = MSDSM_SUPPORTED_DEVICES_LISTGuid; -GUID MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID = MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGuid; -GUID MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID = MSDSM_DEFAULT_LOAD_BALANCE_POLICYGuid; - -// -// Symbolic names for the DSM-centric guid indexes -// -#define MSDSM_SUPPORTED_DEVICES_LISTGUID_Index 0 -#define MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index 1 -#define MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index 2 - -WMIGUIDREGINFO MSDsmGuidList[] = { - { - &MSDSM_SUPPORTED_DEVICES_LISTGUID, - 1, - 0 - }, - - { - &MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID, - 1, - 0 - }, - - { - &MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID, - 1, - 0 - } -}; - -#define MSDsmGuidCount (sizeof(MSDsmGuidList) / sizeof(WMIGUIDREGINFO)) - -// -// List of supported Device-centric guids -// -GUID DSM_LBOperationsGUID = DSM_LB_OperationsGuid; -GUID DSM_QueryLBPolicyGUID = DSM_QueryLBPolicyGuid; -GUID DSM_QuerySupportedLBPoliciesGUID = DSM_QuerySupportedLBPoliciesGuid; -GUID DSM_QueryDsmUniqueIdGUID = DSM_QueryUniqueIdGuid; -GUID DSM_QueryLBPolicyV2GUID = DSM_QueryLBPolicy_V2Guid; -GUID DSM_QuerySupportedLBPoliciesV2GUID = DSM_QuerySupportedLBPolicies_V2Guid; -GUID MSDSM_DEVICE_PERFGUID = MSDSM_DEVICE_PERFGuid; -GUID MSDSM_WMI_METHODSGUID = MSDSM_WMI_METHODSGuid; - -// -// Symbolic names for the Device-centric guid indexes -// -#define DSM_LBOperationsGUID_Index 0 -#define DSM_QueryLBPolicyGUID_Index 1 -#define DSM_QuerySupportedLBPoliciesGUID_Index 2 -#define DSM_QueryDsmUniqueIdGUID_Index 3 -#define DSM_QueryLBPolicyV2GUID_Index 4 -#define DSM_QuerySupportedLBPoliciesV2GUID_Index 5 -#define MSDSM_DEVICE_PERFGuidIndex 6 -#define MSDSM_WMI_METHODSGuidIndex 7 - -WMIGUIDREGINFO DsmGuidList[] = { - { - &DSM_LBOperationsGUID, - 1, - 0 - }, - - { - &DSM_QueryLBPolicyGUID, - 1, - 0 - }, - - { - &DSM_QuerySupportedLBPoliciesGUID, - 1, - 0 - }, - - { - &DSM_QueryDsmUniqueIdGUID, - 1, - 0 - }, - - { - &DSM_QueryLBPolicyV2GUID, - 1, - 0 - }, - - { - &DSM_QuerySupportedLBPoliciesV2GUID, - 1, - 0 - }, - - { - &MSDSM_DEVICE_PERFGUID, - 1, - 0 - }, - - { - &MSDSM_WMI_METHODSGUID, - 1, - 0 - } -}; - -#define DsmGuidCount (sizeof(DsmGuidList) / sizeof(WMIGUIDREGINFO)) - -VOID -DsmpDsmWmiInitialize( - _In_ IN PDSM_WMILIB_CONTEXT WmiGlobalInfo, - _In_ IN PUNICODE_STRING RegistryPath - ) -/*++ - -Routine Description: - - This routine intializes the DSM-specific WmiGlobalInfo structure that is passed - back to MPIO during DriverEntry. - -Arguments: - - WmiGlobalInfo - WMI information structure to initialize. - RegistryPath - Registry path to the service key for this driver. - -Return Value: - - None - ---*/ -{ - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmpDsmWmiInitialize (RegPath %ws): Entering function.\n", - RegistryPath->Buffer)); - - RtlZeroMemory(WmiGlobalInfo, sizeof(DSM_WMILIB_CONTEXT)); - - // - // Build the mof resource name. This tells wmi via the busdriver, - // where to find the mof data. This is found in the .rc. - // - RtlInitUnicodeString(&WmiGlobalInfo->MofResourceName, L"DsmMofResourceName"); - - // - // This will jam in the entry points and guids for supported WMI - // operations. SetDataBlock, SetDataItem, ExecuteMethod and FunctionControl are - // currently not needed, so leave them set to zero. - // - WmiGlobalInfo->GuidCount = MSDsmGuidCount; - WmiGlobalInfo->GuidList = MSDsmGuidList; - - WmiGlobalInfo->QueryWmiDataBlockEx = DsmGlobalQueryData; - WmiGlobalInfo->SetWmiDataBlockEx = DsmGlobalSetData; - - // - // Allocate a buffer for the reg. path. - // - WmiGlobalInfo->RegistryPath.Buffer = DsmpAllocatePool(NonPagedPoolNx, - RegistryPath->MaximumLength, - DSM_TAG_REG_PATH); - if (WmiGlobalInfo->RegistryPath.Buffer) { - - // - // Set maximum length of the new string and copy it. - // - WmiGlobalInfo->RegistryPath.MaximumLength = RegistryPath->MaximumLength; - - RtlCopyUnicodeString(&WmiGlobalInfo->RegistryPath, RegistryPath); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_INIT, - "DsmpDsmWmiInitialize (RegPath %ws): Failed to allocate memory for Registry path in WmiGlobalInfo.\n", - RegistryPath->Buffer)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmpDsmWmiInitialize (RegPath %ws): Exiting function.\n", - RegistryPath->Buffer)); - - return; -} - - -NTSTATUS -DsmGlobalQueryData( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG InstanceCount, - _Inout_ IN OUT PULONG InstanceLengthArray, - _In_ IN ULONG BufferAvail, - _Out_writes_to_(BufferAvail, *DataLength) OUT PUCHAR Buffer, - _Out_ OUT PULONG DataLength, - ... - ) -/*++ - -Routine Description: - - This is the WMI query entry point for DSM-specific GUIDs. The index into the - GUID array is found and assuming the buffer is large enough, the data will be - copied over. - -Arguments: - - DsmContext - Global DSM Context - DsmIds - Dsm Ids - Irp - The WMI Irp - GuidIndex - Index into the WMIGUIDINFO array - InstanceIndex - Index of the data instance - InstanceCount - Number of instances - InstanceLengthArray - Array of ULONGs that indicate per-instance data lengths. - BufferAvail - Size of the buffer in which data is returned. - Buffer - Buffer in which the data is returned. - DataLength - Storage for the actual data length written. - -Return Value: - - STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to - to return all the available data. - STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry - in the reginfo array. - STATUS_SUCCESS - On success. - ---*/ -{ - NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; - UNREFERENCED_PARAMETER(DsmContext); - UNREFERENCED_PARAMETER(InstanceLengthArray); - UNREFERENCED_PARAMETER(InstanceCount); - UNREFERENCED_PARAMETER(InstanceIndex); - UNREFERENCED_PARAMETER(Irp); - UNREFERENCED_PARAMETER(DsmIds); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmGlobalQueryData (DsmContext %p): Entering function - GuidIndex %u.\n", - DsmContext, - GuidIndex)); - - // - // Check the GuidIndex - the index into the DsmGuildList array - to see - // whether this is a supported GUID or not. - // - switch(GuidIndex) { - - case MSDSM_SUPPORTED_DEVICES_LISTGUID_Index: { - - *DataLength = BufferAvail; - - status = DsmpQuerySupportedDevicesList(DsmContext, - BufferAvail, - DataLength, - Buffer); - - break; - } - - case MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { - - *DataLength = BufferAvail; - - status = DsmpQueryTargetsDefaultPolicy(DsmContext, - BufferAvail, - DataLength, - Buffer); - - break; - } - - case MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { - - *DataLength = BufferAvail; - - status = DsmpQueryDsmDefaultPolicy(DsmContext, - BufferAvail, - DataLength, - Buffer); - - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalQueryData (DsmContext %p): Unknown GuidIndex %d.\n", - DsmContext, - GuidIndex)); - - *DataLength = 0; - - break; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmGlobalQueryData (DsmContext %p): Exiting function with status 0x%x.\n", - DsmContext, - status)); - - return status; -} - - -NTSTATUS -DsmGlobalSetData( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG BufferAvail, - _In_reads_bytes_(BufferAvail) IN PUCHAR Buffer, - ... - ) -/*++ - -Routine Description: - - This is the WMI set entry point for DSM-specific GUIDs. The index into the - GUID array is found and the contents of the buffer are set to the passed in - instance index. - -Arguments: - - DsmContext - Global DSM Context - DsmIds - Dsm Ids - Irp - The WMI Irp - GuidIndex - Index into the WMIGUIDINFO array - InstanceIndex - Index of the data instance - BufferAvail - Size of the buffer in which data is returned. - Buffer - Buffer in which the data is returned. - -Return Value: - - STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to - to return all the available data. - STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry - in the reginfo array. - STATUS_SUCCESS - On success. - ---*/ -{ - NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; - ULONG dataLength; - PDSM_CONTEXT dsmContext = (PDSM_CONTEXT)DsmContext; - - UNREFERENCED_PARAMETER(DsmIds); - UNREFERENCED_PARAMETER(Irp); - UNREFERENCED_PARAMETER(InstanceIndex); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): Entering function - GuidIndex %u.\n", - DsmContext, - GuidIndex)); - - switch (GuidIndex) { - - case MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { - - PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY targetsPolicyInfo = (PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY)Buffer; - PMSDSM_TARGET_DEFAULT_POLICY_INFO targetPolicyInfo; - PWSTR vidpidIndex; - DSM_LOAD_BALANCE_TYPE loadBalancePolicy; - ULONGLONG preferredPath; - DWORD index; - NTSTATUS errorStatus = STATUS_SUCCESS; - - // - // Determine the correct buffer size. - // - dataLength = AlignOn8Bytes(FIELD_OFFSET(MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY, TargetDefaultPolicyInfo)); - - if (BufferAvail < dataLength) { - - status = STATUS_BUFFER_TOO_SMALL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size. Status %x\n", - DsmContext, - GuidIndex, - status)); - break; - } - - dataLength += targetsPolicyInfo->NumberDevices * sizeof(MSDSM_TARGET_DEFAULT_POLICY_INFO); - - if (BufferAvail < dataLength) { - - status = STATUS_BUFFER_TOO_SMALL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size for %u targets. Status %x\n", - DsmContext, - GuidIndex, - targetsPolicyInfo->NumberDevices, - status)); - break; - } - - targetPolicyInfo = targetsPolicyInfo->TargetDefaultPolicyInfo; - - for (index = 0; index < targetsPolicyInfo->NumberDevices; index++, targetPolicyInfo++) { - - size_t stringLength = 0; - - // - // First ensure that these values make sense. The VID/PID should be - // a string of 8+16 chars and the LB policy must be one that MSDSM - // supports. - // - // The WMI string is like a unicode string with the first USHORT - // containing the size. - // - vidpidIndex = targetPolicyInfo->HardwareId; - vidpidIndex++; - - if (!NT_SUCCESS(RtlStringCchLengthW(vidpidIndex, DSM_VENDPROD_ID_LEN + 1, &stringLength)) || (stringLength != DSM_VENDPROD_ID_LEN)) { - - errorStatus = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Ignoring incorrect VID/PID %ws. Status %x\n", - DsmContext, - GuidIndex, - vidpidIndex, - errorStatus)); - continue; - } - - if (targetPolicyInfo->LoadBalancePolicy >= DSM_LB_VENDOR_SPECIFIC) { - - errorStatus = STATUS_INVALID_PARAMETER; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Ignoring policy %u for %ws. Status %x\n", - DsmContext, - GuidIndex, - targetPolicyInfo->LoadBalancePolicy, - vidpidIndex, - errorStatus)); - continue; - } - - loadBalancePolicy = targetPolicyInfo->LoadBalancePolicy; - preferredPath = (ULONGLONG)((ULONG_PTR)targetPolicyInfo->PreferredPath); - - // - // Now update/create the key in the registry with the LB policy info. - // If the LB policy is specified as 0, delete the key. - // - status = DsmpSetVidPidLBPolicyInRegistry(vidpidIndex, loadBalancePolicy, preferredPath); - - // - // If above was successful, find the group that corresponds to this - // targetId and update its LB policy as well as the states of the paths - // - if (NT_SUCCESS(status)) { - - DsmpSetLBForVidPidPolicyAdjustment(dsmContext, vidpidIndex, loadBalancePolicy, preferredPath); - } else { - errorStatus = status; - } - } - - // - // If any error occurred, return the last error. - // - if (!NT_SUCCESS(errorStatus)) { - status = errorStatus; - } - - break; - } - - case MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { - - PMSDSM_DEFAULT_LOAD_BALANCE_POLICY dsmPolicyInfo = (PMSDSM_DEFAULT_LOAD_BALANCE_POLICY)Buffer; - DSM_LOAD_BALANCE_TYPE loadBalancePolicy; - ULONGLONG preferredPath; - - // - // Determine the correct buffer size. - // - dataLength = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); - - if (BufferAvail < dataLength) { - - status = STATUS_BUFFER_TOO_SMALL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size. Status %x\n", - DsmContext, - GuidIndex, - status)); - break; - } - - loadBalancePolicy = dsmPolicyInfo->LoadBalancePolicy; - preferredPath = (ULONGLONG)((ULONG_PTR)dsmPolicyInfo->PreferredPath); - - // - // First ensure that the values make sense. - // - if (loadBalancePolicy >= DSM_LB_VENDOR_SPECIFIC) { - - status = STATUS_INVALID_PARAMETER; - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Invalid policy %u specified. Status %x\n", - DsmContext, - GuidIndex, - loadBalancePolicy, - status)); - - } else { - - // - // Update/create the values in the registry with the LB policy info. - // If the LB policy is specified as 0, delete the values. - // - status = DsmpSetDsmLBPolicyInRegistry(loadBalancePolicy, preferredPath); - - // - // If above is successful, find the groups that haven't had their LB policy - // explicitly set or haven't had their policy set in accordance with target - // hardware id. For each of these, adjust the states of the paths as well. - // - if (NT_SUCCESS(status)) { - - DsmpSetLBForDsmPolicyAdjustment(dsmContext, loadBalancePolicy, preferredPath); - } - } - - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): Unknown GuidIndex %d.\n", - DsmContext, - GuidIndex)); - - break; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmGlobalSetData (DsmContext %p): Exiting function with status 0x%x.\n", - DsmContext, - status)); - - return status; -} - - -VOID -DsmpWmiInitialize( - _In_ IN PDSM_WMILIB_CONTEXT WmiInfo, - _In_ IN PUNICODE_STRING RegistryPath - ) -/*++ - -Routine Description: - - This routine intializes the Device-specific WmiInfo structure that is passed - back to MPIO during DriverEntry. - -Arguments: - - WmiInfo - WMI information structure to initialize. - RegistryPath - Registry path to the service key for this driver. - -Return Value: - - None - ---*/ -{ - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmpWmiInitialize (RegPath %ws): Entering function.\n", - RegistryPath->Buffer)); - - RtlZeroMemory(WmiInfo, sizeof(DSM_WMILIB_CONTEXT)); - - // - // Build the mof resource name. This tells wmi via the busdriver, - // where to find the mof data. This is found in the .rc. - // - RtlInitUnicodeString(&WmiInfo->MofResourceName, L"MofResourceName"); - - // - // This will jam in the entry points and guids for supported WMI - // operations. SetDataBlock, SetDataItem, and FunctionControl are - // currently not needed, so leave them set to zero. - // - WmiInfo->GuidCount = DsmGuidCount; - WmiInfo->GuidList = DsmGuidList; - - WmiInfo->QueryWmiDataBlockEx = DsmQueryData; - WmiInfo->ExecuteWmiMethodEx = DsmExecuteMethod; - - // - // Allocate a buffer for the reg. path. - // - WmiInfo->RegistryPath.Buffer = DsmpAllocatePool(NonPagedPoolNx, - RegistryPath->MaximumLength, - DSM_TAG_REG_PATH); - if (WmiInfo->RegistryPath.Buffer) { - - // - // Set maximum length of the new string and copy it. - // - WmiInfo->RegistryPath.MaximumLength = RegistryPath->MaximumLength; - - RtlCopyUnicodeString(&WmiInfo->RegistryPath, RegistryPath); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_INIT, - "DsmpWmiInitialize (RegPath %ws): Failed to allocate memory for Registry path in WMIInfo.\n", - RegistryPath->Buffer)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_INIT, - "DsmpWmiInitialize (RegPath %ws): Exiting function.\n", - RegistryPath->Buffer)); - - return; -} - - -NTSTATUS -DsmQueryData( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG InstanceCount, - _Inout_ IN OUT PULONG InstanceLengthArray, - _In_ IN ULONG BufferAvail, - _When_(GuidIndex == DSM_LBOperationsGUID_Index || GuidIndex == MSDSM_WMI_METHODSGuidIndex, _Pre_notnull_ _Const_) - _When_(!(GuidIndex == DSM_LBOperationsGUID_Index || GuidIndex == MSDSM_WMI_METHODSGuidIndex), _Out_writes_to_(BufferAvail, *DataLength)) - OUT PUCHAR Buffer, - _Out_ OUT PULONG DataLength, - ... - ) -/*++ - -Routine Description: - - This is the main WMI query entry point. The index into the GUID array is found - and assuming the buffer is large enough, the data will be copied over. - -Arguments: - - DsmContext - Global DSM Context - DsmIds - Dsm Ids - Irp - The WMI Irp - GuidIndex - Index into the WMIGUIDINFO array - InstanceIndex - Index of the data instance - InstanceCount - Number of instances - InstanceLengthArray - Array of ULONGs that indicate per-instance data lengths. - BufferAvail - Size of the buffer in which data is returned. - Buffer - Buffer in which the data is returned. - DataLength - Storage for the actual data length written. - -Return Value: - - STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to - to return all the available data. - STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry - in the reginfo array. - STATUS_SUCCESS - On success. - ---*/ -{ - ULONG sizeNeeded; - NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; - - UNREFERENCED_PARAMETER(DsmContext); - UNREFERENCED_PARAMETER(InstanceCount); - UNREFERENCED_PARAMETER(InstanceIndex); - UNREFERENCED_PARAMETER(Irp); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmQueryData (DsmIds %p): Entering function - GuidIndex %u.\n", - DsmIds, - GuidIndex)); - - // - // Check the GuidIndex - the index into the DsmGuildList array - to see - // whether this is a supported GUID or not. - // - switch(GuidIndex) { - - case DSM_LBOperationsGUID_Index: { - - // - // Even though this class only has methods, we need to respond - // to any queries for it since WMI expects that there is an actual - // instance of the class on which to execute the method - // - - sizeNeeded = sizeof(ULONG); - - *DataLength = sizeNeeded; - - if (BufferAvail >= sizeNeeded) { - - *InstanceLengthArray = sizeNeeded; - status = STATUS_SUCCESS; - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_WMI, - "DsmQueryData (DsmIds %p): Buffer too small in query data. Needed %d, Given %d.\n", - DsmIds, - sizeNeeded, - BufferAvail)); - - status = STATUS_BUFFER_TOO_SMALL; - } - - break; - } - - case DSM_QueryLBPolicyGUID_Index: - case DSM_QueryLBPolicyV2GUID_Index: { - - *DataLength = BufferAvail; - - status = DsmpQueryLoadBalancePolicy(DsmContext, - DsmIds, - ((GuidIndex == DSM_QueryLBPolicyGUID_Index) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2), - BufferAvail, - DataLength, - Buffer); - break; - } - - case DSM_QuerySupportedLBPoliciesGUID_Index: - case DSM_QuerySupportedLBPoliciesV2GUID_Index: { - - *DataLength = BufferAvail; - - status = DsmpQuerySupportedLBPolicies(DsmContext, - DsmIds, - BufferAvail, - ((GuidIndex == DSM_QuerySupportedLBPoliciesGUID_Index) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2), - DataLength, - Buffer); - break; - } - - case DSM_QueryDsmUniqueIdGUID_Index: { - - PDSM_QueryUniqueId dsmQueryUniqueId; - - *DataLength = sizeof(DSM_QueryUniqueId); - - if (BufferAvail >= sizeof(DSM_QueryUniqueId)) { - - dsmQueryUniqueId = (PDSM_QueryUniqueId) Buffer; - dsmQueryUniqueId->DsmUniqueId = (ULONGLONG)((ULONG_PTR)DsmContext); - status = STATUS_SUCCESS; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmQueryData (DsmIds %p): Buffersize %d too small for Query Unique Id.\n", - DsmIds, - BufferAvail)); - - status = STATUS_BUFFER_TOO_SMALL; - } - - break; - } - - case MSDSM_DEVICE_PERFGuidIndex: { - - *DataLength = BufferAvail; - - status = DsmpQueryDevicePerf(DsmContext, - DsmIds, - BufferAvail, - DataLength, - Buffer); - - break; - } - - case MSDSM_WMI_METHODSGuidIndex: { - - // - // Even though this class only has methods, we need to respond - // to any queries for it since WMI expects that there is an actual - // instance of the class on which to execute the method - // - - sizeNeeded = sizeof(ULONG); - - *DataLength = sizeNeeded; - - if (BufferAvail >= sizeNeeded) { - - *InstanceLengthArray = sizeNeeded; - status = STATUS_SUCCESS; - - } else { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_WMI, - "DsmQueryData (DsmIds %p): Buffer too small in query data. Needed %d, Given %d.\n", - DsmIds, - sizeNeeded, - BufferAvail)); - - status = STATUS_BUFFER_TOO_SMALL; - } - - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmQueryData (DsmIds %p): Unknown GuidIndex %d in DsmQueryData.\n", - DsmIds, - GuidIndex)); - - *DataLength = 0; - - break; - } - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmQueryData (DsmIds %p): Exiting function with status 0x%x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryLoadBalancePolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG DsmWmiVersion, - _In_ IN ULONG InBufferSize, - _In_ IN PULONG OutBufferSize, - _Out_writes_bytes_(*OutBufferSize) OUT PVOID Buffer - ) -/*+++ - -Routine Description: - - This routine returns the current Load Balance policy settings - for the given device. - -Arguements: - - DsmContext - Global DSM context - DsmIds - DSM Ids for the given device - DsmWmiVersion - version of the MPIO_DSM_Path class to use - InBufferSize - Size of the input buffer - OutBufferSize - Size of the output buffer - Buffer - Buffer in which the current Load Balance policy settings - is returned, if the buffer is big enough - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - PDSM_GROUP_ENTRY groupEntry; - PDSM_DEVICE_INFO devInfo; - PDSM_DEVICE_INFO rtpgDeviceInfo = NULL; - ULONG inx; - ULONG sizeNeeded; - NTSTATUS status = STATUS_SUCCESS; - KIRQL irql; - PDSM_Load_Balance_Policy_V2 supportedLBPolicies; - PMPIO_DSM_Path_V2 dsmPath; - PDSM_FAILOVER_GROUP foGroup; - ULONG SpecialHandlingFlag = 0; - - UNREFERENCED_PARAMETER(InBufferSize); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryLoadBalancePolicy (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // At least one device should be given - // - if (DsmIds->Count == 0) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryLoadBalancePolicy (DsmIds %p): No DSM Ids given in DsmpQueryLoadBalancePolicy.\n", - DsmIds)); - - *OutBufferSize = 0; - status = STATUS_INVALID_PARAMETER; - - goto __Exit_DsmpQueryLoadBalancePolicy; - } - - // - // Compute the size needed for returning LoadBalance policy information - // - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)); - sizeNeeded += (DsmIds->Count) * sizeof(MPIO_DSM_Path); - - } else { - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)); - sizeNeeded += (DsmIds->Count * sizeof(MPIO_DSM_Path_V2)); - } - - if (*OutBufferSize < sizeNeeded) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryLoadBalancePolicy (DsmIds %p): Output buffer too small for QueryLBPolicy.\n", - DsmIds)); - - *OutBufferSize = sizeNeeded; - status = STATUS_BUFFER_TOO_SMALL; - - goto __Exit_DsmpQueryLoadBalancePolicy; - } - - // - // Set the size of the data returned to user - // - *OutBufferSize = sizeNeeded; - - // - // Zero out the output buffer first - // - RtlZeroMemory(Buffer, sizeNeeded); - - devInfo = DsmIds->IdList[0]; - DSM_ASSERT(devInfo && devInfo->DeviceSig == DSM_DEVICE_SIG); - groupEntry = devInfo->Group; - - // - // Send down an RTPG to get the current state info if implicit transitions - // are supported, since the states may have changed from under us. - // Storages that support both implicit and explicit transitions that haven't - // allowed us to turn OFF their implicit transitions, may have also changed - // TPG states from under us. So do this for such storages also. - // - if (!DsmpIsSymmetricAccess(devInfo) && - devInfo->ALUASupport != DSM_DEVINFO_ALUA_EXPLICIT) { - - rtpgDeviceInfo = DsmpGetActivePathToBeUsed(groupEntry, FALSE, SpecialHandlingFlag); - - if (!rtpgDeviceInfo) { - - BOOLEAN sendTPG = FALSE; - - rtpgDeviceInfo = DsmpFindStandbyPathToActivateALUA(groupEntry, &sendTPG, SpecialHandlingFlag); - } - - if (rtpgDeviceInfo) { - - status = DsmpGetDeviceALUAState(DsmContext, rtpgDeviceInfo, NULL); - } - } - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // If an RTPG was sent down, update all the devInfo states. - // - if (NT_SUCCESS(status) && rtpgDeviceInfo) { - - DsmpAdjustDeviceStatesALUA(groupEntry, NULL, SpecialHandlingFlag); - } - - supportedLBPolicies = &(((PDSM_QueryLBPolicy_V2)Buffer)->LoadBalancePolicy); - supportedLBPolicies->Version = DSM_WMI_VERSION; - supportedLBPolicies->LoadBalancePolicy = groupEntry->LoadBalanceType; - supportedLBPolicies->DSMPathCount = DsmIds->Count; - dsmPath = supportedLBPolicies->DSM_Paths; - - // - // Indicate which path is active and which path(s) are standby paths - // - inx = 0; - while (inx < DsmIds->Count) { - - devInfo = (PDSM_DEVICE_INFO)DsmIds->IdList[inx]; - - dsmPath->PathWeight = devInfo->PathWeight; - dsmPath->Reserved = DSM_STATE_ACTIVE_OPTIMIZED_SUPPORTED; - - if (devInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - dsmPath->TargetPortGroup_State = DSM_DEV_NOT_USED_STATE; - } - - dsmPath->Reserved |= DSM_STATE_STANDBY_SUPPORTED; - - } else { - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - dsmPath->TargetPortGroup_State = devInfo->TargetPortGroup->AsymmetricAccessState; - dsmPath->TargetPortGroup_Preferred = devInfo->TargetPortGroup->Preferred; - dsmPath->TargetPortGroup_Identifier = devInfo->TargetPortGroup->Identifier; - - if (devInfo->TargetPort) { - - dsmPath->TargetPort_Identifier = devInfo->TargetPort->Identifier; - } - - if (groupEntry->Symmetric) { - - // - // For certain policies like FOO and RRWS, we need to be able to put - // path in standby. - // - dsmPath->Reserved |= DSM_STATE_STANDBY_SUPPORTED; - } - } - - dsmPath->Reserved |= devInfo->TargetPortGroup->ActiveUnoptimizedSupported ? DSM_STATE_ACTIVE_UNOPTIMIZED_SUPPORTED : 0; - dsmPath->Reserved |= devInfo->TargetPortGroup->StandBySupported ? DSM_STATE_STANDBY_SUPPORTED : 0; - dsmPath->Reserved |= devInfo->TargetPortGroup->UnavailableSupported ? DSM_STATE_UNAVAILABLE_SUPPORTED : 0; - } - - groupEntry = devInfo->Group; - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - dsmPath->SymmetricLUA = groupEntry->Symmetric; - dsmPath->ALUASupport = devInfo->ALUASupport; - - } - - if (DsmpIsDeviceFailedState(devInfo->State) || !DsmpIsDeviceInitialized(devInfo)) { - - dsmPath->PrimaryPath = FALSE; - dsmPath->DsmPathId = 0; - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - dsmPath->OptimizedPath = dsmPath->PreferredPath = FALSE; - dsmPath->FailedPath = TRUE; - } - - } else { - - foGroup = devInfo->FailGroup; - dsmPath->DsmPathId = (ULONGLONG)((ULONG_PTR)foGroup->PathId); - - if (DsmpIsDeviceStateActive(devInfo->State)) { - - dsmPath->PrimaryPath = TRUE; - } - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED || - devInfo->State == DSM_DEV_STANDBY) { - - dsmPath->OptimizedPath = TRUE; - } - - if (((ULONGLONG)((ULONG_PTR)(foGroup->PathId))) == (devInfo->Group->PreferredPath)) { - - dsmPath->PreferredPath = TRUE; - } - } - } - -#if DBG - if (!dsmPath->PrimaryPath && - !dsmPath->FailedPath) { - NT_ASSERT(groupEntry->LoadBalanceType != DSM_LB_ROUND_ROBIN && - groupEntry->LoadBalanceType != DSM_LB_WEIGHTED_PATHS && - groupEntry->LoadBalanceType != DSM_LB_DYN_LEAST_QUEUE_DEPTH && - groupEntry->LoadBalanceType != DSM_LB_LEAST_BLOCKS); - } -#endif - - dsmPath = DsmWmiVersion == DSM_WMI_VERSION_1 ? - (PVOID)((PUCHAR)dsmPath + sizeof(MPIO_DSM_Path)) : - (PVOID)((PUCHAR)dsmPath + sizeof(MPIO_DSM_Path_V2)); - - inx++; - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - -__Exit_DsmpQueryLoadBalancePolicy: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryLoadBalancePolicy (DsmIds %p): Exiting with status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpQuerySupportedLBPolicies( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG BufferAvail, - _In_ IN ULONG DsmWmiVersion, - _Out_ OUT PULONG OutBufferSize, - _Out_writes_to_(BufferAvail, *OutBufferSize) OUT PUCHAR Buffer - ) -/*+++ - -Routine Description: - - This routine returns the load balance policies supported by this DSM for the - given LUN (specified by the DsmIds). - -Arguements: - - DsmContext - Global DSM context - DsmIds - DSM Ids for the given device - BufferAvail - Size of buffer available. - DsmWmiVersion - Indicates which version of MPIO_DSMPath to use. - OutBufferSize - Size of the output buffer. - Buffer - Buffer in which the supported Load Balance policies are - returned, if the buffer is big enough. - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - PDSM_QuerySupportedLBPolicies_V2 supportedLBPolicies; - PDSM_Load_Balance_Policy_V2 dsmLBPolicy; - ULONG sizeNeeded; - ULONG policyCount; - ULONG inx; - NTSTATUS status = STATUS_SUCCESS; - BOOLEAN skipRR = FALSE; - PDSM_DEVICE_INFO devInfo = NULL; - PUCHAR endOfBuffer; - - UNREFERENCED_PARAMETER(DsmContext); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI,"DsmpQuerySupportedLBPolicies (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // At least one device should be given - // - if (DsmIds->Count == 0) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQuerySupportedLBPolicies (DsmIds %p): No DSM Ids given in DsmpQuerySupportedLBPolicies.\n", - DsmIds)); - - *OutBufferSize = 0; - status = STATUS_INVALID_PARAMETER; - - goto __Exit_DsmpQuerySupportedLBPolicies; - } - - devInfo = DsmIds->IdList[0]; - DSM_ASSERT(devInfo && devInfo->DeviceSig == DSM_DEVICE_SIG); - - policyCount = DSM_NUMBER_OF_LB_POLICIES; - - // - // Round Robin policy is not supported for arrays that are AAA. - // - if (!DsmpIsSymmetricAccess(devInfo)) { - - skipRR = TRUE; - policyCount--; - } - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_QuerySupportedLBPolicies, Supported_LB_Policies)); - sizeNeeded += policyCount * AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)); - - } else { - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_QuerySupportedLBPolicies_V2, Supported_LB_Policies)); - sizeNeeded += policyCount * AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)); - } - - // - // Set the size of the data returned to user or needed but not provided. - // - *OutBufferSize = sizeNeeded; - - if (sizeNeeded > BufferAvail) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQuerySupportedLBPolicies (Buffer %p): Output buffer too small. Size needed = %u.\n", - Buffer, - sizeNeeded)); - - status = STATUS_BUFFER_TOO_SMALL; - - goto __Exit_DsmpQuerySupportedLBPolicies; - } - - endOfBuffer = Buffer + sizeNeeded - 1; - - // - // Zero out the output buffer first - // - supportedLBPolicies = (PDSM_QuerySupportedLBPolicies_V2)Buffer; - RtlZeroMemory(Buffer, sizeNeeded); - - supportedLBPolicies->SupportedLBPoliciesCount = policyCount; - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - dsmLBPolicy = &(supportedLBPolicies->Supported_LB_Policies[0]); - - } else { - - dsmLBPolicy = (PVOID)&(((PDSM_QuerySupportedLBPolicies)supportedLBPolicies)->Supported_LB_Policies[0]); - } - - // - // All Load Balance policies are supported in Windows Server 2003 - // and above. - // - for (inx = 0; inx < DSM_NUMBER_OF_LB_POLICIES; inx++) { - - // - // Skip reporting Round Robin for AAA arrays. - // - if (((inx + 1) == DSM_LB_ROUND_ROBIN) && skipRR) { - - continue; - } - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - if ((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)) - 1 > endOfBuffer) { - - status = STATUS_BUFFER_TOO_SMALL; - break; - } - } else { - - if ((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)) - 1 > endOfBuffer) { - - status = STATUS_BUFFER_TOO_SMALL; - break; - } - } - - dsmLBPolicy->Version = DSM_WMI_VERSION; - - // - // The value set for LoadBalancePolicy is based on - // the #define for LB policies in LBPolicy.h - // - dsmLBPolicy->LoadBalancePolicy = inx + 1; - - // - // Point to the next DSM_Load_Balance_Policy area - // - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - dsmLBPolicy = (PDSM_Load_Balance_Policy_V2)((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths))); - - } else { - - dsmLBPolicy = (PVOID)((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths))); - } - } - -__Exit_DsmpQuerySupportedLBPolicies: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQuerySupportedLBPolicies (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmExecuteMethod( - _In_ IN PVOID DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN PIRP Irp, - _In_ IN ULONG GuidIndex, - _In_ IN ULONG InstanceIndex, - _In_ IN ULONG MethodId, - _In_ IN ULONG InBufferSize, - _In_ IN PULONG OutBufferSize, - _Inout_ IN OUT PUCHAR Buffer, - ... - ) -/*++ - -Routine Description: - - This routine handles the invocation of WMI methods defined in the DSM mof. - -Arguments: - - DsmContext - Global DSM context - DsmIds - DSM Ids - Irp - The WMI Irp - GuidIndex - Index into the WMIGUIDINFO array - InstanceIndex - Index value indicating for which instance data should be returned. - MethodId - Specifies which method to invoke. - InBufferSize - Buffer size, in bytes, of input parameter data. - OutBufferSize - Buffer size, in bytes, of output data. - Buffer - Buffer to which the data is read/written. - -Return Value: - - Status of the method, or STATUS_WMI_ITEMID_NOT_FOUND - ---*/ -{ - NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; - UNREFERENCED_PARAMETER(DsmContext); - UNREFERENCED_PARAMETER(InstanceIndex); - UNREFERENCED_PARAMETER(Irp); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmExecuteMethod (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // This should be the index for ExecMethod Index - // - if (GuidIndex == DSM_LBOperationsGUID_Index) { - - switch (MethodId) { - - case DsmSetLoadBalancePolicy: - case DsmSetLoadBalancePolicyALUA: { - - status = DsmpSetLoadBalancePolicy(DsmContext, - DsmIds, - (MethodId == DsmSetLoadBalancePolicy) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2, - InBufferSize, - OutBufferSize, - Buffer); - break; - } - - default: { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmExecuteMethod (DsmIds %p): Unknown MethodId %d in DsmExecuteMethod.\n", - DsmIds, - MethodId)); - - status = STATUS_WMI_ITEMID_NOT_FOUND; - - break; - } - } - } else if (GuidIndex == MSDSM_WMI_METHODSGuidIndex) { - - if (MethodId == MSDsmClearCounters) { - - status = DsmpClearPerfCounters(DsmContext, DsmIds); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmExecuteMethod (DsmIds %p): Unknown MethodId %d for GuidIndex %d in DsmExecuteMethod.\n", - DsmIds, - MethodId, - GuidIndex)); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmExecuteMethod (DsmIds %p): Unknown GuidIndex %d in DsmExecuteMethod.\n", - DsmIds, - GuidIndex)); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmExecuteMethod (DsmIds %p): Exiting function with status 0x%x.\n", - DsmIds, - status)); - - return status; -} - -NTSTATUS -DsmpClearLoadBalancePolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds - ) -/*++ - -Routine Description: - - This routine is called to clear the LUN-specific load balance policy for the given device. - - First, the routine will try to clear the "explicitly set" registry key for the device. If - this fails, the whole routine is aborted. - - If the registry key is successfully cleared, the following happens: - 1. Check to see if there is a target-wide load balance policy set for this device's VID/PID. - If yes, we set the device's load balance policy accordingly and return. - 2. Check to see if there is an MSDSM-wide load balance policy set. - If yes, we set the device's load balance policy accordingly and return. - 3. If steps 1 and 2 fall through, we set the device's load balance policy to RR, or RRWS if - ALUA is enabled. - -Arguements: - - DsmContext - Global DSM context - DsmIds - DSM Ids for the given device - -Return Value: - - Appropriate status indicating the error if the input is malformed or - if the function was unable to clear the load balance policy. - STATUS_SUCCESS on success - ---*/ - -{ - NTSTATUS status = STATUS_SUCCESS; - PDSM_DEVICE_INFO deviceInfo = NULL; - PDSM_GROUP_ENTRY group = NULL; - HANDLE lbSettingsKey = NULL; - HANDLE deviceKey = NULL; - UNICODE_STRING subKeyName; - OBJECT_ATTRIBUTES objectAttributes; - DSM_LOAD_BALANCE_TYPE loadBalanceType; - ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - ULONG devInfoIndex; - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpClearLoadBalancePolicy (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // There should be at least one device - // - if (DsmIds->Count == 0) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_WMI, - "DsmpClearLoadBalancePolicy (DsmIds %p): No DSM Ids given.\n", - DsmIds)); - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpClearLoadBalancePolicy; - } - - deviceInfo = (PDSM_DEVICE_INFO)DsmIds->IdList[0]; - group = deviceInfo->Group; - - // - // First open LoadBalanceSettings key under the Services key - // - status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpClearLoadBalancePolicy (DevName %ws): Failed to open LB Settings key. Status %x.\n", - group->RegistryKeyName, - status)); - - goto __Exit_DsmpClearLoadBalancePolicy; - } - - // - // Now open the key under which the LB settings for the given device is stored - // and clear the DsmLoadBalancePolicyExplicitlySet key. - // - RtlInitUnicodeString(&subKeyName, group->RegistryKeyName); - - InitializeObjectAttributes(&objectAttributes, - &subKeyName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - lbSettingsKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); - - if (NT_SUCCESS(status)) { - - UCHAR explicitlySet = FALSE; - - status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, - deviceKey, - DSM_POLICY_EXPLICITLY_SET, - REG_BINARY, - &explicitlySet, - sizeof(UCHAR)); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpClearLoadBalancePolicy (DevName %ws): Failed to clear DsmLoadBalancePolicyExplicitlySet key.\n", - group->RegistryKeyName)); - - goto __Exit_DsmpClearLoadBalancePolicy; - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpClearLoadBalancePolicy (DevName %ws): Failed to open device subkey.\n", - group->RegistryKeyName)); - - goto __Exit_DsmpClearLoadBalancePolicy; - } - - - - // - // Set the defaults. These will be used if no target-wide or MSDSM-wide - // load balance policies are set. - // - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; - loadBalanceType = DSM_LB_ROUND_ROBIN; - preferredPath = 0; - - // - // Check to see if target-wide (VID/PID) LB policy is set for this device. - // - status = DsmpQueryTargetLBPolicyFromRegistry(deviceInfo, - &loadBalanceType, - &preferredPath); - if (NT_SUCCESS(status)) { - - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; - - } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { - - // - // Since the policy hasn't been set for this VID/PID, check if - // overall MSDSM-wide policy has been set. - // - status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, - &preferredPath); - if (NT_SUCCESS(status)) { - - group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpClearLoadBalancePolicy (DevInfo %p): Failed to query Dsm overall LB policy from registry. Status %x.\n", - deviceInfo, - status)); - - NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); - status = STATUS_SUCCESS; - } - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_PNP, - "DsmpClearLoadBalancePolicy (DevInfo %p): Failed to query VID/PID LB policy from registry. Status %x.\n", - deviceInfo, - status)); - - NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); - status = STATUS_SUCCESS; - } - - - // - // If the storage is ALUA enabled and we specified Round Robin, change - // it to Round Robin with Subset instead. - // - if (!DsmpIsSymmetricAccess(deviceInfo) && loadBalanceType == DSM_LB_ROUND_ROBIN) { - - loadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; - } - - // - // Finally set the load balance policy and the preferred path. - // - group->LoadBalanceType = loadBalanceType; - group->PreferredPath = preferredPath; - - // - // Update the path states in accordance with the new policy. - // - for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { - - DsmpSetNewDefaultLBPolicy(DsmContext, - group->DeviceList[devInfoIndex], - group->LoadBalanceType, - SpecialHandlingFlag); - } - -__Exit_DsmpClearLoadBalancePolicy: - - if (deviceKey) { - ZwClose(deviceKey); - } - - if (lbSettingsKey) { - ZwClose(lbSettingsKey); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpClearLoadBalancePolicy (DsmIds %p): Exiting function with status 0x%x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpSetLoadBalancePolicy( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG DsmWmiVersion, - _In_ IN ULONG InBufferSize, - _In_ IN PULONG OutBufferSize, - _In_ IN PVOID Buffer - ) -/*++ - -Routine Description: - - This routine is called to set the load balance policy for the given device. - - If zero is passed in as the load balance policy, the LUN-specific load balance - policy will attempt to be cleared. See DsmpClearLoadBalancePolicy for more details. - -Arguements: - - DsmContext - Global DSM context - DsmIds - DSM Ids for the given device - DsmWmiVersion - version of the MPIO_DSM_Path class to use - InBufferSize - Size of the input buffer - OutBufferSize - Size of the output buffer - Buffer - Buffer for input\output data - -Return Value: - - STATUS_BUFFER_TOO_SMALL - If the input buffer is too small - Appropriate status indicating the error if the input is malformed. - STATUS_SUCCESS on success - ---*/ - -{ - PDsmSetLoadBalancePolicyALUA_IN setLoadBalancePolicyIN = (PDsmSetLoadBalancePolicyALUA_IN) Buffer; - PDsmSetLoadBalancePolicyALUA_OUT setLoadBalancePolicyOUT = (PDsmSetLoadBalancePolicyALUA_OUT) Buffer; - PVOID supportedLBPolicies; - PMPIO_DSM_Path_V2 dsmPath; - ULONG inx = 0; - ULONG jnx; - NTSTATUS status = STATUS_SUCCESS; - BOOLEAN lengthOkay = TRUE; - PDSM_DEVICE_INFO devInfo = NULL; - PDSM_DEVICE_INFO tempDevInfo = NULL; - PDSM_GROUP_ENTRY groupEntry; - PDSM_LOAD_BALANCE_POLICY_SETTINGS savedLBSettings = NULL; - KIRQL irql; - BOOLEAN optimized = TRUE; - BOOLEAN preferred = FALSE; - ULONG activePaths = 0; - ULONG activeTPGs = 0; - ULONG numberDevInfoChanged = 0; - ULONG numberPreferredPaths = 0; - DSM_LOAD_BALANCE_TYPE loadBalancePolicy; - BOOLEAN sendSTPG = FALSE; - ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - ULONG SpecialHandlingFlag = 0; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // There should be at least one device - // - if (DsmIds->Count == 0) { - - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): No DSM Ids given.\n", - DsmIds)); - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpSetLoadBalancePolicy; - } - - groupEntry = ((PDSM_DEVICE_INFO)DsmIds->IdList[0])->Group; - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - if (*OutBufferSize < sizeof(DsmSetLoadBalancePolicy_OUT)) { - - *OutBufferSize = sizeof(DsmSetLoadBalancePolicy_OUT); - lengthOkay = FALSE; - } - } else { - - if (*OutBufferSize < sizeof(DsmSetLoadBalancePolicyALUA_OUT)) { - - *OutBufferSize = sizeof(DsmSetLoadBalancePolicyALUA_OUT); - lengthOkay = FALSE; - } - } - - if (!lengthOkay) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Buffer too small for SetLBPolicy.\n", - DsmIds)); - - status = STATUS_BUFFER_TOO_SMALL; - goto __Exit_DsmpSetLoadBalancePolicy; - } - - *OutBufferSize = (DsmWmiVersion == DSM_WMI_VERSION_1) ? sizeof(DsmSetLoadBalancePolicy_OUT) : sizeof(DsmSetLoadBalancePolicyALUA_OUT); - - // - // If the user specified zero as the load balance policy, we need to clear the - // LUN-specific load balance policy. - // - if (setLoadBalancePolicyIN->LoadBalancePolicy.LoadBalancePolicy == 0) { - status = DsmpClearLoadBalancePolicy(DsmContext, DsmIds); - goto __Exit_DsmpSetLoadBalancePolicy; - } - - status = DsmpValidateSetLBPolicyInput(DsmContext, - DsmIds, - DsmWmiVersion, - Buffer, - InBufferSize); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Failed to validate input. Status %x.\n", - DsmIds, - status)); - - goto __Exit_DsmpSetLoadBalancePolicy; - } - - // - // At this point the Reserved field in each MPIO_DSM_Path should - // contain the respective Device Info - // - supportedLBPolicies = &(setLoadBalancePolicyIN->LoadBalancePolicy); - loadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->LoadBalancePolicy; - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Cache each DeviceInfo's current state. - // This will be used to rollback in case of errors. - // - DsmpSaveDeviceState(supportedLBPolicies, DsmWmiVersion); - - while (inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); - - optimized = TRUE; - preferred = FALSE; - - } else { - - dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); - - optimized = dsmPath->OptimizedPath ? TRUE : FALSE; - preferred = dsmPath->PreferredPath ? TRUE : FALSE; - - if (preferred && loadBalancePolicy == DSM_LB_FAILOVER) { - - preferredPath = dsmPath->DsmPathId; - - if (preferredPath != 0) { - - numberPreferredPaths++; - } - - if (numberPreferredPaths > 1) { - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - status = STATUS_INVALID_PARAMETER; - break; - } - } - } - - // - // Reserved field in MPIO_DSM_Path is set to DeviceInfo in - // DsmpValidateSetLBPolicyInput routine. - // - devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; - - if (!devInfo) { - - inx++; - continue; - - } else { - - if (!tempDevInfo) { - - tempDevInfo = devInfo; - - if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || - loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { - - InterlockedExchangePointer(&(groupEntry->PathToBeUsed), NULL); - } - } - } - - if (!DsmpIsDeviceFailedState(devInfo->State)) { - - if (devInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { - - activeTPGs++; - } - - if (dsmPath->PrimaryPath) { - - // - // Optimized flag decides between AO and AU - // - if (optimized) { - - // - // For implicit-only ALUA, state cannot be explicitly changed to A/O - // - if (!DsmpIsSymmetricAccess(devInfo) && devInfo->ALUASupport == DSM_DEVINFO_ALUA_IMPLICIT) { - - // - // While we can mask off acutal A/O to be A/U, there is no - // way to explicitly make non-A/O state A/O - // - if (devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Can't make non-AO path A/O for Implicit-only transitions.\n", - DsmIds)); - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - status = STATUS_INVALID_PARAMETER; - break; - } - } - - numberDevInfoChanged++; - - devInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; - activePaths++; - - // - // Check to see if the actual making of this path state A/O - // will require an STPG to be sent down. - // - if (devInfo->TargetPortGroup && - devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED) { - - sendSTPG = TRUE; - } - - if (loadBalancePolicy == DSM_LB_FAILOVER) { - - // - // Only ONE path can be specified as AO for FailOverOnly policy. - // - if (activePaths > 1) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): More than one AO node given for FO Only.\n", - DsmIds)); - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - status = STATUS_INVALID_PARAMETER; - break; - } - } - - if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || - loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { - - if (!groupEntry->PathToBeUsed) { - InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)devInfo->FailGroup); - } - } - } else { - - // - // This is an ActiveUnoptimized path - // - devInfo->State = DSM_DEV_ACTIVE_UNOPTIMIZED; - - // - // For LB policy RR, WP, LB and LQD, all paths must be in A/O - // state. However, this is not possible for ALUA storages. - // For these storages, A/U is allowable only if that is the - // access state that the TPG is in. - // - if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || - loadBalancePolicy == DSM_LB_WEIGHTED_PATHS || - loadBalancePolicy == DSM_LB_DYN_LEAST_QUEUE_DEPTH || - loadBalancePolicy == DSM_LB_LEAST_BLOCKS) { - - if (devInfo->TargetPortGroup && devInfo->ALUAState != DSM_DEV_ACTIVE_UNOPTIMIZED) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) specified in A/U state when TPG is in %u state (for LB %u).\n", - DsmIds, - inx, - devInfo->ALUAState, - loadBalancePolicy)); - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - status = STATUS_INVALID_PARAMETER; - break; - } - } - } - } else { - - if (optimized) { - - // - // This is a standby path - // - devInfo->State = DSM_DEV_STANDBY; - - } else { - - // - // This is unavailable path - // - devInfo->State = DSM_DEV_UNAVAILABLE; - } - - // - // For RR, LQD, LB and WP, all paths must be in A/O state for non-ALUA - // storage. For ALUA storage, the only time path states can be in - // S/B or U/A is if the TPG itself is in that state. - // - if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || - loadBalancePolicy == DSM_LB_WEIGHTED_PATHS || - loadBalancePolicy == DSM_LB_DYN_LEAST_QUEUE_DEPTH || - loadBalancePolicy == DSM_LB_LEAST_BLOCKS) { - - if ((!devInfo->TargetPortGroup) || - (devInfo->TargetPortGroup && devInfo->State != devInfo->ALUAState)) { - - // - // No paths can be in SB or UA unless its TPG is in that state. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) specified in non-active state for LB %u.\n", - DsmIds, - inx, - loadBalancePolicy)); - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - status = STATUS_INVALID_PARAMETER; - break; - } - } else if (loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { - - // - // It is okay to set a path to be in S/B or U/A state in RRWS - // if either the storage is non-ALUA, or if the storage is - // ALUA but the TPG is in A/O (where it can be masked) or the - // TPG is in the state that the path is being set to. - // - if ((devInfo->TargetPortGroup) && - (devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED && devInfo->State != devInfo->ALUAState)) { - - // - // No paths can be in SB or UA unless its TPG is in that state. - // - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) (in TPG state %u) can't be specified in non-active state for LB %u.\n", - DsmIds, - inx, - devInfo->ALUAState, - loadBalancePolicy)); - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - status = STATUS_INVALID_PARAMETER; - break; - } - } - } - } - - inx++; - } - - if (NT_SUCCESS(status)) { - - - // - // If we arrive here, that means DsmpValidateSetLBPolicyInput already returned success. - // The device info is found. - // - _Analysis_assume_(tempDevInfo != NULL); - - // - // There must be at least one AO path. Unless there are no A/O TPGs. - // eg. During a controller failover, it is possible that the TPG through - // the TPG through other controller is still in non-A/O state and the - // storage supports implicit transitions and is still in the midst of - // making the transition of the non-A/O TPG to A/O. During such windows - // the states for all paths will be non-A/O and there's nothing that can - // be done about it. This is not an error condition. - // - if (!activePaths) { - - if ((tempDevInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) || - (tempDevInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED && activeTPGs)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): No active node given for LB %u.\n", - DsmIds, - loadBalancePolicy)); - - // - // Roll back to DeviceState to the state it was before - // processing this SetLB policy request - // - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - - status = STATUS_INVALID_PARAMETER; - } - } - } - - if (NT_SUCCESS(status)) { - - // - // If we arrive here, that means DsmpValidateSetLBPolicyInput already returned success. - // The device info is found. - // - _Analysis_assume_(tempDevInfo != NULL); - - // - // If device supports explicit transitions, we need to send down an - // STPG to enforce A/O path selection if we need to make a path in a - // non-A/O TPG active/optimized. - // - if (tempDevInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT && sendSTPG) { - - PUCHAR targetPortGroupsInfo = NULL; - ULONG targetPortGroupsInfoLength = 0; - PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; - - // - // Build the target port groups info to set the new states. - // Send down an STPG for TPG descriptors for those devInfos' TPGs - // that need to be in AO state. If this causes side-effects in - // state transitions (these can't be considered implicit according - // to the spec), fake the devInfo states to what was selected. - // - targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + - activePaths * sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); - - targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, - targetPortGroupsInfoLength, - DSM_TAG_TARGET_PORT_GROUPS); - - if (targetPortGroupsInfo) { - - PDSM_DEVICE_INFO devInfoToUse = NULL; - - // - // Set the new asymmetric access states for the the devices' target port groups - // - tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); - - for (inx = 0, jnx = 0; - inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount; - inx++) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); - - } else { - - dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); - } - - devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; - - if (!devInfo) { - - continue; - } - - if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { - - tpgDescriptor->AsymmetricAccessState = devInfo->State; - REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &devInfo->TargetPortGroup->Identifier); - - tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)((PUCHAR)tpgDescriptor + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)); - - jnx++; - } - - if (devInfo->TempPreviousStateForLB == DSM_DEV_ACTIVE_OPTIMIZED) { - - devInfoToUse = devInfo; - } - } - - NT_ASSERT(jnx == numberDevInfoChanged); - NT_ASSERT(devInfoToUse); - - if (devInfoToUse) { - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - status = DsmpSetTargetPortGroups(devInfoToUse->TargetObject, - targetPortGroupsInfo, - targetPortGroupsInfoLength); - - if (NT_SUCCESS(status)) { - - DsmpFreePool(targetPortGroupsInfo); - targetPortGroupsInfo = NULL; - targetPortGroupsInfoLength = 0; - status = DsmpReportTargetPortGroups(devInfoToUse->TargetObject, - &targetPortGroupsInfo, - &targetPortGroupsInfoLength); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): STPG failed with status %x.\n", - DsmIds, - status)); - - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - } - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - } - - if (NT_SUCCESS(status)) { - - ULONG index; - PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; - - status = DsmpParseTargetPortGroupsInformation(DsmContext, - groupEntry, - targetPortGroupsInfo, - targetPortGroupsInfoLength); - - NT_ASSERT(NT_SUCCESS(status)); - - for (index = 0; index < DSM_MAX_PATHS; index++) { - - targetPortGroup = groupEntry->TargetPortGroupList[index]; - - if (targetPortGroup) { - - DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); - } - } - - // - // Update TPGs with new state - // - for (inx = 0; - inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount; - inx++) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); - - } else { - - dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); - } - - devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; - - if (devInfo) { - - // - // An explicit state transition can cause TPGs that were not specified - // in the parameter list to also change (this is not considered to be - // an implicit transition. It is SPC3 behavior and we must take - // this into consideration and update the devInfo states. - // This is an unfortunate side-effect in that the Admin may not get - // the paths to be in the exact states that he has set. - // - if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { - - if (devInfo->ALUAState == DSM_DEV_ACTIVE_UNOPTIMIZED || - devInfo->ALUAState == DSM_DEV_STANDBY || - devInfo->ALUAState == DSM_DEV_UNAVAILABLE) { - - // - // An A/O TPG's devInfos can be masked as A/U. - // However, the reverse the is not true (ie. we can't - // mark a non-A/O TPG's devInfo(s) to be in A/O state. - // - devInfo->State = devInfo->ALUAState; - } - } - - // - // The devInfo->State has already been set. Update its previous state. - // - devInfo->PreviousState = devInfo->TempPreviousStateForLB; - } - } - - NT_ASSERT(jnx == numberDevInfoChanged); - } - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Failed to allocate targetPortGroupsInfo.\n", - DsmIds)); - - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) { - - groupEntry->LoadBalanceType = loadBalancePolicy; - - if (loadBalancePolicy == DSM_LB_FAILOVER) { - - groupEntry->PreferredPath = preferredPath; - } - - savedLBSettings = DsmpCopyLoadBalancePolicies(groupEntry, - DsmWmiVersion, - supportedLBPolicies); - - } else { - - // - // Roll back to DeviceState to the state it was before - // processing this SetLB policy request - // - DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); - } - } - - if (NT_SUCCESS(status)) { - - // - // LUN's LB policy has been explicitly set by Admin - // - groupEntry->LBPolicySelection = DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT; - - // - // Update the states and if appropriate, the path weight - // - DsmpUpdateDesiredStateAndWeight(groupEntry, - DsmWmiVersion, - supportedLBPolicies); - - // - // Update the next path to be used for the group - // - devInfo = DsmpGetActivePathToBeUsed(groupEntry, - DsmpIsSymmetricAccess(tempDevInfo), - SpecialHandlingFlag); - if (devInfo != NULL) { - - InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)devInfo->FailGroup); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): After setting LB policy No FOG available for group %p\n", - DsmIds, - groupEntry)); - - InterlockedExchangePointer(&(groupEntry->PathToBeUsed), NULL); - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - if (NT_SUCCESS(status) && savedLBSettings) { - - DsmpPersistLBSettings(savedLBSettings); - - DsmpFreePool(savedLBSettings); - } - -__Exit_DsmpSetLoadBalancePolicy: - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - ((PDsmSetLoadBalancePolicy_OUT)setLoadBalancePolicyOUT)->Status = status; - - } else { - - setLoadBalancePolicyOUT->Status = status; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSetLoadBalancePolicy (DsmIds %p): Exiting function with status 0x%x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpValidateSetLBPolicyInput( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds, - _In_ IN ULONG DsmWmiVersion, - _In_ IN PVOID SetLoadBalancePolicyIN, - _In_ IN ULONG InBufferSize - ) -/*++ - -Routine Description: - - This routine validates the input buffer given for setting - Load Balance policy - -Arguements: - - DsmContext - DSM Global Context - DsmIds - DSM Ids for the given device - DsmWmiVersion - version of the MPIO_DSM_Path class to use - SetLoadBalancePolicyIN - Describes the load balance policy to be set - InBufferSize - Number of bytes in SetLoadBalancePolicyIN - -Return Value: - - STATUS_SUCCESS - if the input buffer is well formed - Appropriate error status if the input buffer is malformed. - ---*/ -{ - PDSM_Load_Balance_Policy_V2 supportedLBPolicies; - PMPIO_DSM_Path_V2 dsmPath0; - PMPIO_DSM_Path_V2 dsmPath1; - NTSTATUS status = STATUS_SUCCESS; - ULONG inx; - ULONG jnx; - ULONG sizeNeeded; - KIRQL irql; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // Validate the input buffer for setting Load Balance policy - // - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - sizeNeeded = FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths); - - } else { - - sizeNeeded = FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths); - } - - if (InBufferSize < sizeNeeded) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Insufficient buffer in SetLB. Expected %d, Given %d.\n", - DsmIds, - sizeNeeded, - InBufferSize)); - - status = STATUS_BUFFER_TOO_SMALL; - goto __Exit_DsmpValidateSetLBPolicyInput; - } - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - supportedLBPolicies = (PVOID)&(((PDsmSetLoadBalancePolicy_IN)SetLoadBalancePolicyIN)->LoadBalancePolicy); - - sizeNeeded += supportedLBPolicies->DSMPathCount * sizeof(MPIO_DSM_Path); - - } else { - - supportedLBPolicies = &(((PDsmSetLoadBalancePolicyALUA_IN)SetLoadBalancePolicyIN)->LoadBalancePolicy); - - sizeNeeded += supportedLBPolicies->DSMPathCount * sizeof(MPIO_DSM_Path_V2); - } - - if (InBufferSize < sizeNeeded) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Insufficient buffer in SetLB. Expected %d, Given %d.\n", - DsmIds, - sizeNeeded, - InBufferSize)); - - status = STATUS_BUFFER_TOO_SMALL; - goto __Exit_DsmpValidateSetLBPolicyInput; - } - - if (supportedLBPolicies->Version > DSM_WMI_VERSION) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): WMI Version mismatch. Expected %d, Given %d.\n", - DsmIds, - DSM_WMI_VERSION, - supportedLBPolicies->Version)); - - status = DSM_UNSUPPORTED_VERSION; - goto __Exit_DsmpValidateSetLBPolicyInput; - - } else if (supportedLBPolicies->Version < DSM_WMI_VERSION) { - - ULONG dsmWmiVersion = DSM_WMI_VERSION; - TracePrint((TRACE_LEVEL_WARNING, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Use of older management app (WMI-Version %x) with newer DSM (WMI-Version %x).\n", - DsmIds, - supportedLBPolicies->Version, - dsmWmiVersion)); - - NT_ASSERT(supportedLBPolicies->Version == DSM_WMI_VERSION); - } - - if ((supportedLBPolicies->LoadBalancePolicy < DSM_LB_FAILOVER) || - (supportedLBPolicies->LoadBalancePolicy > DSM_LB_LEAST_BLOCKS)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Invalid LB Policy %d.\n", - DsmIds, - supportedLBPolicies->LoadBalancePolicy)); - - status = DSM_INVALID_LOAD_BALANCE_POLICY; - goto __Exit_DsmpValidateSetLBPolicyInput; - } - - // - // It is expected that the user provide LB policy settings - // for all the paths and not just a subset of the paths. - // - if (supportedLBPolicies->DSMPathCount != DsmIds->Count) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Path Count %d not equal to DSM IDs count %d.\n", - DsmIds, - supportedLBPolicies->DSMPathCount, - DsmIds->Count)); - - status = STATUS_INVALID_PARAMETER; - goto __Exit_DsmpValidateSetLBPolicyInput; - } - - // - // Make sure user did not provide duplicate path ids - // - for (inx = 0; inx < supportedLBPolicies->DSMPathCount && NT_SUCCESS(status); inx++) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath0 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); - - } else { - - dsmPath0 = &(supportedLBPolicies->DSM_Paths[inx]); - } - - dsmPath0->Reserved = 0; - - for (jnx = 0; jnx < supportedLBPolicies->DSMPathCount; jnx++) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath1 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[jnx]); - - } else { - - dsmPath1 = &(supportedLBPolicies->DSM_Paths[jnx]); - } - - if ((inx != jnx) && - ((dsmPath0->DsmPathId == dsmPath1->DsmPathId) && (dsmPath1->DsmPathId != 0))) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Duplicate path id %I64x at %d and %d.\n", - DsmIds, - dsmPath0->DsmPathId, - inx, - jnx)); - - status = STATUS_INVALID_PARAMETER; - - break; - } - } - } - - if (NT_SUCCESS(status)) { - - PDSM_DEVICE_INFO devInfo; - PDSM_FAILOVER_GROUP foGroup; - PVOID pathId; - BOOLEAN foundPath; - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - // - // Make sure the user has provided path id corresponding - // to all the DSM IDs given to us. - // - for (inx = 0; inx < DsmIds->Count; inx++) { - - devInfo = DsmIds->IdList[inx]; - - if (!DsmpIsDeviceInitialized(devInfo)) { - - continue; - } - - foGroup = devInfo->FailGroup; - if (!foGroup) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): FO Group NULL for %p at index %d.\n", - DsmIds, - devInfo, - inx)); - - status = STATUS_INVALID_PARAMETER; - - break; - } - - foundPath = FALSE; - - for (jnx = 0; jnx < supportedLBPolicies->DSMPathCount; jnx++) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath0 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[jnx]); - - } else { - - dsmPath0 = &(supportedLBPolicies->DSM_Paths[jnx]); - } - - pathId = (PVOID) dsmPath0->DsmPathId; - if (foGroup->PathId == pathId) { - - // - // Found the device info corresponding to the given path. - // Use the reserved field in MPIO_DSM_Path to store - // the pointer to the device info. Device Info is used - // later on to set the load balance policy for the device. - // - foundPath = TRUE; - - dsmPath0->Reserved = (ULONG_PTR) devInfo; - - // - // If ALUA, RoundRobin is not an allowed LB policy since not all paths can - // be in A/O state. RRWS must be used instead. - // - if (supportedLBPolicies->LoadBalancePolicy == DSM_LB_ROUND_ROBIN && !DsmpIsSymmetricAccess(devInfo)) { - - status = DSM_INVALID_LOAD_BALANCE_POLICY; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Invalid LB policy for ALUA. Status %x.\n", - DsmIds, - status)); - } - - break; - } - } - - if (!foundPath) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Failed to find path %p for %p at index %d.\n", - DsmIds, - foGroup->PathId, - devInfo, - inx)); - - status = STATUS_INVALID_PARAMETER; - - break; - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - } - -__Exit_DsmpValidateSetLBPolicyInput: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpValidateSetLBPolicyInput (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -VOID -DsmpSaveDeviceState( - _In_ IN PVOID SupportedLBPolicies, - _In_ IN ULONG DsmWmiVersion - ) -/*+++ - -Routine Description: - - This routine saves the current Load Balance policy settings. - If there is any error while setting the new policy given - by the user, the saved values will be used to restore - the old state. - - Note: This routine MUST be called with DsmContextLock held in Exclusive mode. - -Arguements: - - SupportedLBPolicies - New Load Balance policy values - DsmWmiVersion - version of the MPIO_DSM_Path class to use - -Return Value: - - None ---*/ -{ - PDSM_DEVICE_INFO devInfo; - PMPIO_DSM_Path_V2 dsmPath; - ULONG inx; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSaveDeviceState (LBP %p): Entering function.\n", - SupportedLBPolicies)); - - inx = 0; - - while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); - - } else { - - dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); - } - - devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; - - if (devInfo) { - - devInfo->TempPreviousStateForLB = devInfo->State; - } - - inx++; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpSaveDeviceState (LBP %p): Exiting function.\n", - SupportedLBPolicies)); - - return; -} - - -VOID -DsmpRestorePreviousDeviceState( - _In_ IN PVOID SupportedLBPolicies, - _In_ IN ULONG DsmWmiVersion - ) -/*++ - -Routine Description: - - This routine restores the old Load Balance policy settings. - If there is any error while setting the new policy given - by the user, the old state is restored from the saved state. - - Note: This routine MUST be called with DsmContextLock held in Exclusive mode. - -Arguements: - - SupportedLBPolicies - New Load Balance policy values - DsmWmiVersion - version of the MPIO_DSM_Path class to use - -Return Value: - - None ---*/ -{ - PDSM_DEVICE_INFO devInfo; - PMPIO_DSM_Path_V2 dsmPath; - ULONG inx; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpRestorePreviousDeviceState (LBP %p): Entering function.\n", - SupportedLBPolicies)); - - inx = 0; - - while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); - - } else { - - dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); - } - - devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; - - if (devInfo) { - - devInfo->State = devInfo->TempPreviousStateForLB; - } - - inx++; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpRestorePreviousDeviceState (LBP %p): Exiting function.\n", - SupportedLBPolicies)); - - return; -} - - -VOID -DsmpUpdateDesiredStateAndWeight( - _In_ IN PDSM_GROUP_ENTRY Group, - _In_ IN ULONG DsmWmiVersion, - _In_ IN PVOID SupportedLBPolicies - ) -/*++ - -Routine Description: - - This routine updates the desired state and path weights - based on admin's LB selection. - - Note: This routine MUST be called with DsmContextLock held in Exclusive mode. - -Arguements: - - Group - The group entry correponding to the pseudo-LUN. - SupportedLBPolicies - New Load Balance policy values - DsmWmiVersion - version of the MPIO_DSM_Path class to use - -Return Value: - - None ---*/ -{ - PMPIO_DSM_Path_V2 dsmPath; - PDSM_DEVICE_INFO devInfo; - ULONG inx; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpUpdatedDesiredState (Group %p): Entering function.\n", - Group)); - - inx = 0; - while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { - - if (DsmWmiVersion == DSM_WMI_VERSION_1) { - - dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); - - } else { - - dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); - } - - devInfo = (PDSM_DEVICE_INFO) dsmPath->Reserved; - - if (!devInfo) { - - inx++; - continue; - } - - DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); - NT_ASSERT(devInfo->Group == Group); - - // - // We'll honor the chosen path for FOO for ALUA storage - // since we know for a fact that the Admin has chosen the path. - // We'll also honor path state in RRWS if it is different from TPG state - // as that too is an indication that it was explicitly selected. - // - if ((DsmpIsSymmetricAccess(devInfo)) || - (Group->LoadBalanceType == DSM_LB_FAILOVER) || - (!DsmpIsSymmetricAccess(devInfo) && Group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && devInfo->State != devInfo->ALUAState)) { - - // - // Check if this is the primary path or a standby path - // - if (dsmPath->PrimaryPath) { - - devInfo->DesiredState = DSM_DEV_ACTIVE_OPTIMIZED; - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - if (!dsmPath->OptimizedPath) { - - devInfo->DesiredState = DSM_DEV_ACTIVE_UNOPTIMIZED; - } - } - - } else { - - devInfo->DesiredState = DSM_DEV_STANDBY; - - if (DsmWmiVersion > DSM_WMI_VERSION_1) { - - if (!dsmPath->OptimizedPath) { - - devInfo->DesiredState = DSM_DEV_UNAVAILABLE; - } - } - } - } else { - - devInfo->DesiredState = DSM_DEV_UNDETERMINED; - } - - if (Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) { - - devInfo->PathWeight = dsmPath->PathWeight; - } - - inx++; - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpUpdatedDesiredState (Group %p): Exiting function.\n", - Group)); - - return; -} - - -NTSTATUS -DsmpQueryDevicePerf( - _In_ PDSM_CONTEXT DsmContext, - _In_ PDSM_IDS DsmIds, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ) -/*++ - -Routine Description: - - This routine returns the perf counters for each path for the - device that corresponds to the passed in DsmIds. - -Arguements: - - DsmContext - Global DSM context - DsmIds - DSM Ids for the given device - InBufferSize - Size of the input buffer - OutBufferSize - Size of the output buffer - Buffer - Buffer in which the current Load Balance policy settings - is returned, if the buffer is big enough - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PDSM_DEVICE_INFO devInfo; - ULONG sizeNeeded; - PMSDSM_DEVICE_PERF devicePerf; - ULONG i; - PMSDSM_DEVICEPATH_PERF pathPerf; - KIRQL irql; - - UNREFERENCED_PARAMETER(InBufferSize); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryDevicePerf (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // At least one device should be given - // - if (DsmIds->Count == 0) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryDevicePerf (DsmIds %p): No DSM Ids given.\n", - DsmIds)); - - *OutBufferSize = 0; - status = STATUS_INVALID_PARAMETER; - - goto __Exit_DsmpQueryDevicePerf; - } - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_DEVICE_PERF, PerfInfo)); - sizeNeeded += (DsmIds->Count * sizeof(MSDSM_DEVICEPATH_PERF)); - - if (*OutBufferSize < sizeNeeded) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryDevicePerf (DsmIds %p): Output buffer too small for QueryLBPolicy.\n", - DsmIds)); - - *OutBufferSize = sizeNeeded; - status = STATUS_BUFFER_TOO_SMALL; - - goto __Exit_DsmpQueryDevicePerf; - } - - // - // Zero out the output buffer first - // - RtlZeroMemory(Buffer, sizeNeeded); - -#if DBG - devInfo = DsmIds->IdList[0]; - DSM_ASSERT(devInfo); - DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); -#endif - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - devicePerf = (PMSDSM_DEVICE_PERF)Buffer; - devicePerf->NumberPaths = DsmIds->Count; - - // - // For each path, get the stats info - // - for (i = 0; i < DsmIds->Count; i++) { - - pathPerf = &devicePerf->PerfInfo[i]; - devInfo = DsmIds->IdList[i]; - - if (DsmpIsDeviceInitialized(devInfo)) { - - pathPerf->PathId = (ULONGLONG)((ULONG_PTR)((devInfo->FailGroup)->PathId)); - pathPerf->NumberReads = (devInfo->DeviceStats).NumberReads; - pathPerf->NumberWrites = (devInfo->DeviceStats).NumberWrites; - pathPerf->BytesRead = (devInfo->DeviceStats).BytesRead; - pathPerf->BytesWritten = (devInfo->DeviceStats).BytesWritten; - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - - *OutBufferSize = sizeNeeded; - -__Exit_DsmpQueryDevicePerf: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryDevicePerf (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpClearPerfCounters( - _In_ IN PDSM_CONTEXT DsmContext, - _In_ IN PDSM_IDS DsmIds - ) -/*++ - -Routine Description: - - This routine clears the perf counters for each path for the - device that corresponds to the passed in DsmIds. - -Arguements: - - DsmContext - Global DSM context - DsmIds - DSM Ids for the given device - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PDSM_DEVICE_INFO devInfo; - KIRQL irql; - ULONG i; - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpClearPerfCounters (DsmIds %p): Entering function.\n", - DsmIds)); - - // - // At least one device should be given - // - if (DsmIds->Count == 0) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpClearPerfCounters (DsmIds %p): No DSM Ids given.\n", - DsmIds)); - - status = STATUS_INVALID_PARAMETER; - - goto __Exit_DsmpClearPerfCounters; - } - - irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); - - for (i = 0; i < DsmIds->Count; i++) { - - devInfo = DsmIds->IdList[i]; - DSM_ASSERT(devInfo); - DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); - - if (devInfo) { - (devInfo->DeviceStats).BytesRead = 0; - (devInfo->DeviceStats).BytesWritten = 0; - (devInfo->DeviceStats).NumberReads = 0; - (devInfo->DeviceStats).NumberWrites = 0; - } - } - - ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); - -__Exit_DsmpClearPerfCounters: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpClearPerfCounters (DsmIds %p): Exiting function with status %x.\n", - DsmIds, - status)); - - return status; -} - - -NTSTATUS -DsmpQuerySupportedDevicesList( - _In_ PDSM_CONTEXT DsmContext, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ) -/*++ - -Routine Description: - - This routine returns the list of devices that are supported by MSDSM. - -Arguements: - - DsmContext - Global DSM context - InBufferSize - Size of the input buffer - OutBufferSize - Size of the output buffer - Buffer - Buffer in which the current Load Balance policy settings - is returned, if the buffer is big enough - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - NTSTATUS status; - ULONG sizeNeeded; - PMSDSM_SUPPORTED_DEVICES_LIST supportedDeviceIds; - PWSTR szIndex; - PWSTR deviceIdIndex; - ULONG numberDeviceIds = 0; - ULONG index = 0; - KIRQL oldIrql; - PWSTR tempBuffer = NULL; - - UNREFERENCED_PARAMETER(InBufferSize); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQuerySupportedDevicesList (DsmContext %p): Entering function.\n", - DsmContext)); - - // - // It is possible that manually changes to the registry weren't yet picked up, - // so query for the list in its current state. Failure to get this list is not - // fatal, so ignore errors. - // -#if DBG - status = DsmpGetDeviceList(DsmContext); - NT_ASSERT(NT_SUCCESS(status)); -#else - DsmpGetDeviceList(DsmContext); -#endif - - // - // Since it is possible that this list may change if a new device arrival - // gets processed at the same time as this query being processed, we need - // to protect it. - // - KeAcquireSpinLock(&DsmContext->SupportedDevicesListLock, &oldIrql); - - tempBuffer = DsmpAllocatePool(NonPagedPoolNx, DsmContext->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED); - - if (tempBuffer) { - - RtlCopyMemory(tempBuffer, DsmContext->SupportedDevices.Buffer, DsmContext->SupportedDevices.Length); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQuerySupportedDevicesList (DsmContext %p): Failed to allocate temporary list.\n", - DsmContext)); - - status = STATUS_INSUFFICIENT_RESOURCES; - KeReleaseSpinLock(&DsmContext->SupportedDevicesListLock, oldIrql); - - goto __Exit_DsmpQuerySupportedDevicesList; - } - - KeReleaseSpinLock(&DsmContext->SupportedDevicesListLock, oldIrql); - - status = STATUS_SUCCESS; - szIndex = tempBuffer; - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_SUPPORTED_DEVICES_LIST, DeviceId)); - - if (szIndex) { - - while (*szIndex) { - - szIndex += wcslen(szIndex) + 1; - numberDeviceIds++; - } - - sizeNeeded += numberDeviceIds * (MSDSM_MAX_DEVICE_ID_SIZE + sizeof(WNULL)); - } - - if (*OutBufferSize < sizeNeeded) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQuerySupportedDevicesList (DsmContext %p): Output buffer too small for QuerySupportedDevicesList.\n", - DsmContext)); - - *OutBufferSize = sizeNeeded; - status = STATUS_BUFFER_TOO_SMALL; - - goto __Exit_DsmpQuerySupportedDevicesList; - } - - // - // Zero out the output buffer first - // - RtlZeroMemory(Buffer, sizeNeeded); - - *OutBufferSize = sizeNeeded; - - supportedDeviceIds = (PMSDSM_SUPPORTED_DEVICES_LIST)Buffer; - supportedDeviceIds->NumberDevices = numberDeviceIds; - - for (index = 0, szIndex = tempBuffer, deviceIdIndex = supportedDeviceIds->DeviceId; - index < numberDeviceIds; - index++, szIndex += wcslen(szIndex) + 1, deviceIdIndex += MSDSM_MAX_DEVICE_ID_LENGTH) { - - *((PUSHORT)deviceIdIndex) = MSDSM_MAX_DEVICE_ID_SIZE; - deviceIdIndex++; - - RtlStringCchCopyW(deviceIdIndex, - MSDSM_MAX_DEVICE_ID_LENGTH - 1, - szIndex); - } - -__Exit_DsmpQuerySupportedDevicesList: - - if (tempBuffer) { - DsmpFreePool(tempBuffer); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQuerySupportedDevicesList (DsmContext %p): Exiting function with status %x.\n", - DsmContext, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryTargetsDefaultPolicy( - _In_ PDSM_CONTEXT DsmContext, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ) -/*++ - -Routine Description: - - This routine is used to build the target list (for which the override default LB policy - was explicitly set), by querying the services key for the subkeys under - "msdsm\Parameters\DsmTargetsLoadBalanceSetting" - -Arguements: - - Context - The DSM Context value. It contains storage for the target hardware ids and their - default policy info. - InBufferSize - Size of the input buffer - OutBufferSize - Size of the output buffer - Buffer - Buffer in which the current targets whose default policy settings is returned, if the buffer is big enough - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - ULONG sizeNeeded; - PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY targetsPolicyInfo = (PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY)Buffer; - PMSDSM_TARGET_DEFAULT_POLICY_INFO targetPolicyInfo; - HANDLE targetsLBSettingKey = NULL; - NTSTATUS status; - PKEY_FULL_INFORMATION keyFullInfo = NULL; - ULONG length = sizeof(KEY_FULL_INFORMATION); - ULONG numSubKeys = 0; - WCHAR vidPid[25] = {0}; - PKEY_BASIC_INFORMATION keyBasicInfo = NULL; - OBJECT_ATTRIBUTES objectAttributes; - HANDLE targetKey = NULL; - ULONG index = 0; - RTL_QUERY_REGISTRY_TABLE queryTable[2]; - DSM_LOAD_BALANCE_TYPE loadBalanceType; - ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - PWCHAR policyInfoIndex; - UNICODE_STRING keyValueName; - PKEY_VALUE_PARTIAL_INFORMATION keyValueInfo = NULL; - - UNREFERENCED_PARAMETER(InBufferSize); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Entering function.\n", - DsmContext)); - - status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to open Targets LB Setting key. Status %x.\n", - DsmContext, - status)); - - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - // - // Query for number of subkeys - // - do { - if (keyFullInfo) { - - DsmpFreePool(keyFullInfo); - } - - keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); - - if (!keyFullInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for key full info.\n", - DsmContext)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - status = ZwQueryKey(targetsLBSettingKey, - KeyFullInformation, - keyFullInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query key. Status %x.\n", - DsmContext, - status)); - - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - // - // Calculate total buffer size required - // - numSubKeys = keyFullInfo->SubKeys; - - sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY, TargetDefaultPolicyInfo)); - sizeNeeded += numSubKeys * sizeof(MSDSM_TARGET_DEFAULT_POLICY_INFO); - - if (*OutBufferSize < sizeNeeded) { - - *OutBufferSize = sizeNeeded; - status = STATUS_BUFFER_TOO_SMALL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Buffer insufficient. Status %x.\n", - DsmContext, - status)); - - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - *OutBufferSize = sizeNeeded; - RtlZeroMemory(Buffer, *OutBufferSize); - - targetsPolicyInfo->NumberDevices = numSubKeys; - targetPolicyInfo = targetsPolicyInfo->TargetDefaultPolicyInfo; - - // - // Now Enumerate all of the subkeys - // - for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) { - - UNICODE_STRING targetName; - - if (targetKey) { - ZwClose(targetKey); - targetKey = NULL; - } - - length = sizeof(KEY_BASIC_INFORMATION); - - do { - if (keyBasicInfo) { - - DsmpFreePool(keyBasicInfo); - } - - keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, - length, - DSM_TAG_REG_KEY_RELATED); - - if (!keyBasicInfo) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for key basic info.\n", - DsmContext)); - - status = STATUS_INSUFFICIENT_RESOURCES; - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - // - // Enumerate the index'th subkey - // - status = ZwEnumerateKey(targetsLBSettingKey, - index, - KeyBasicInformation, - keyBasicInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - // - // Ignore errors - this is a best case effort. - // - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to enumerate sub key's info. Status %x.\n", - DsmContext, - status)); - - status = STATUS_SUCCESS; - continue; - } - - RtlZeroMemory(vidPid, sizeof(vidPid)); - RtlStringCbCopyNW(vidPid, sizeof(vidPid), keyBasicInfo->Name, keyBasicInfo->NameLength); - RtlInitUnicodeString(&targetName, vidPid); - - // - // Open a handle to the the target subkey. - // - InitializeObjectAttributes(&objectAttributes, - &targetName, - (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), - targetsLBSettingKey, - (PSECURITY_DESCRIPTOR) NULL); - - status = ZwOpenKey(&targetKey, - KEY_ALL_ACCESS, - &objectAttributes); - - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to open reg key %ws. Status %x.\n", - DsmContext, - vidPid, - status)); - - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - RtlZeroMemory(queryTable, sizeof(queryTable)); - - queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; - queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; - queryTable[0].EntryContext = &loadBalanceType; - queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - targetKey, - queryTable, - targetKey, - NULL); - if (!NT_SUCCESS(status)) { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query LB Policy for %ws - error %x.\n", - DsmContext, - vidPid, - status)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): LB Policy for %ws is %d.\n", - DsmContext, - vidPid, - loadBalanceType)); - - RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); - - length = sizeof(KEY_VALUE_PARTIAL_INFORMATION); - - do { - DsmpFreePool(keyValueInfo); - keyValueInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); - if (!keyValueInfo) { - - status = STATUS_INSUFFICIENT_RESOURCES; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for keyValueInfo (PP). Status %x.\n", - DsmContext, - status)); - - goto __Exit_DsmpQueryTargetsDefaultPolicy; - } - - status = ZwQueryValueKey(targetKey, - &keyValueName, - KeyValuePartialInformation, - keyValueInfo, - length, - &length); - - } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); - - if (NT_SUCCESS(status)) { - - NT_ASSERT(keyValueInfo->DataLength == sizeof(ULONGLONG)); - - preferredPath = *((ULONGLONG UNALIGNED *)keyValueInfo->Data); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): PreferredPath for %ws is %I64x.\n", - DsmContext, - vidPid, - preferredPath)); - - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query PreferredPath for %ws. Status %x.\n", - DsmContext, - vidPid, - status)); - } - - // - // Copy over this target's policy info. - // - policyInfoIndex = targetPolicyInfo->HardwareId; - *((PUSHORT)policyInfoIndex) = MSDSM_MAX_DEVICE_ID_SIZE; - policyInfoIndex++; - RtlStringCchCopyW((PWSTR)policyInfoIndex, MSDSM_MAX_DEVICE_ID_LENGTH - 1, vidPid); - targetPolicyInfo->LoadBalancePolicy = loadBalanceType; - targetPolicyInfo->PreferredPath = preferredPath; - - targetPolicyInfo++; - } - } - -__Exit_DsmpQueryTargetsDefaultPolicy: - - if (targetKey) { - ZwClose(targetKey); - } - - if (targetsLBSettingKey) { - ZwClose(targetsLBSettingKey); - } - - if (keyBasicInfo) { - DsmpFreePool(keyBasicInfo); - } - - if (keyValueInfo) { - DsmpFreePool(keyValueInfo); - } - - if (keyFullInfo) { - DsmpFreePool(keyFullInfo); - } - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryTargetsDefaultPolicy (Context %p): Exiting function with status %x.\n", - DsmContext, - status)); - - return status; -} - - -NTSTATUS -DsmpQueryDsmDefaultPolicy( - _In_ PDSM_CONTEXT DsmContext, - _In_ ULONG InBufferSize, - _Inout_ PULONG OutBufferSize, - _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer - ) -/*++ - -Routine Description: - - This routine is used to return the override MSDSM-wide default LB policy - if it was explicitly set, by querying the services key at "msdsm\Parameters" - -Arguements: - - Context - The DSM Context value. It contains storage for the target hardware ids and their - default policy info. - InBufferSize - Size of the input buffer - OutBufferSize - Size of the output buffer - Buffer - Buffer in which the current MSDSM-wide default policy is returned, if the buffer - is big enough - -Return Value: - - STATUS_SUCCESS on success - Appropriate error code on error. - ---*/ -{ - PMSDSM_DEFAULT_LOAD_BALANCE_POLICY dsmPolicyInfo = (PMSDSM_DEFAULT_LOAD_BALANCE_POLICY)Buffer; - NTSTATUS status; - DSM_LOAD_BALANCE_TYPE loadBalanceType; - ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); - - UNREFERENCED_PARAMETER(InBufferSize); - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryDsmDefaultPolicy (Context %p): Entering function.\n", - DsmContext)); - - if (*OutBufferSize < sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY)) { - - *OutBufferSize = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); - status = STATUS_BUFFER_TOO_SMALL; - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryDsmDefaultPolicy (Context %p): Buffer insufficient. Status %x.\n", - DsmContext, - status)); - - goto __Exit_DsmpQueryDsmDefaultPolicy; - } - - *OutBufferSize = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); - RtlZeroMemory(Buffer, *OutBufferSize); - - status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, &preferredPath); - - if (NT_SUCCESS(status)) { - - dsmPolicyInfo->LoadBalancePolicy = loadBalanceType; - dsmPolicyInfo->PreferredPath = (ULONGLONG)((ULONG_PTR)preferredPath); - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryDsmDefaultPolicy (Context %p): LB policy = %u, Preferred path = %I64x.\n", - DsmContext, - dsmPolicyInfo->LoadBalancePolicy, - dsmPolicyInfo->PreferredPath)); - } else { - - TracePrint((TRACE_LEVEL_ERROR, - TRACE_FLAG_WMI, - "DsmpQueryDsmDefaultPolicy (Context %p): Query for MSDSM-wide policy, status %x.\n", - DsmContext, - status)); - } - -__Exit_DsmpQueryDsmDefaultPolicy: - - TracePrint((TRACE_LEVEL_VERBOSE, - TRACE_FLAG_WMI, - "DsmpQueryDsmDefaultPolicy (Context %p): Exiting function with status %x.\n", - DsmContext, - status)); - - return status; -} - diff --git a/tests/projects/wdk/kmdf/msdsm/xmake.lua b/tests/projects/wdk/kmdf/msdsm/xmake.lua deleted file mode 100644 index 31dfaf236..000000000 --- a/tests/projects/wdk/kmdf/msdsm/xmake.lua +++ /dev/null @@ -1,30 +0,0 @@ - --- add modes: debug and release -add_rules("mode.debug", "mode.release") - --- add target -target("msdsm") - - -- add rules - add_rules("wdk.kmdf.driver") - - -- add flags for rule: wdk.tracewpp - add_values("wdk.tracewpp.flags", "-func:TracePrint((LEVEL,FLAGS,MSG,...))") - - -- add files - add_files("*.c", {rule = "wdk.tracewpp"}) - add_files("*.rc", "*.inf") - add_files("*.mof|msdsm.mof") - - -- add file msdsm.mof and modify default wdk.mof.header for this file - add_files("msdsm.mof", {values = {wdk_mof_header = "msdsmwmi.h"}}) - - -- set precompiled header - set_pcheader("precomp.h") - - -- add links - add_links("mpio", "ucrt") - - -- set entry - add_ldflags("-entry:GsDriverEntry" .. (is_arch("x86") and "@8" or ""), {force = true}) - diff --git a/tests/projects/wdk/kmdf/perfcounters/kcs.c b/tests/projects/wdk/kmdf/perfcounters/kcs.c deleted file mode 100644 index 9996addcb..000000000 --- a/tests/projects/wdk/kmdf/perfcounters/kcs.c +++ /dev/null @@ -1,407 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved. - - THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY - KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR - PURPOSE. - -Module Name: - - kcs.c - -Abstract: - - This module contains sample code to demonstrate how to provide - counter data from a kernel driver. - -Environment: - - Kernel mode only. - ---*/ - - -#include -#include "kcs.h" -#include "kcsCounters.h" - -#pragma code_seg("PAGE") - -DRIVER_INITIALIZE DriverEntry; -DRIVER_UNLOAD KcsUnload; - -NTSTATUS -KcsAddGeometricInstance ( - _In_ PPCW_BUFFER Buffer, - _In_ PCWSTR Name, - _In_ ULONG MinimalValue, - _In_ ULONG Amplitude - ) - -/*++ - -Routine Description: - - This utility function adds instance to the callback buffer. - -Arguments: - - Buffer - Data will be returned in this buffer. - - Name - Name of instances to be added. - - MinimalValue - Minimum value of the wave. - - Amplitude - Amplitude of the wave. - -Return Value: - - NTSTATUS indicating if the function succeeded. - ---*/ - -{ - ULONG Index; - LARGE_INTEGER Timestamp; - UNICODE_STRING UnicodeName; - GEOMETRIC_WAVE_VALUES Values; - - PAGED_CODE(); - - KeQuerySystemTime(&Timestamp); - - Index = (Timestamp.QuadPart / 10000000) % 10; - - Values.Triangle = MinimalValue + Amplitude * abs(5 - Index) / 5; - Values.Square = MinimalValue + Amplitude * (Index < 5); - - RtlInitUnicodeString(&UnicodeName, Name); - - return KcsAddGeometricWave(Buffer, &UnicodeName, 0, &Values); -} - -NTSTATUS NTAPI -KcsGeometricWaveCallback ( - _In_ PCW_CALLBACK_TYPE Type, - _In_ PPCW_CALLBACK_INFORMATION Info, - _In_opt_ PVOID Context - ) - -/*++ - -Routine Description: - - This function returns the list of counter instances and counter data. - -Arguments: - - Type - Request type. - - Info - Buffer for returned data. - - Context - Not used. - -Return Value: - - NTSTATUS indicating if the function succeeded. - ---*/ - -{ - NTSTATUS Status; - UNICODE_STRING UnicodeName; - - UNREFERENCED_PARAMETER(Context); - - PAGED_CODE(); - - switch (Type) { - case PcwCallbackEnumerateInstances: - - // - // Instances are being enumerated, so we add them without values. - // - - RtlInitUnicodeString(&UnicodeName, L"Small Wave"); - Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, - &UnicodeName, - 0, - NULL); - if (!NT_SUCCESS(Status)) { - return Status; - } - - RtlInitUnicodeString(&UnicodeName, L"Medium Wave"); - Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, - &UnicodeName, - 0, - NULL); - if (!NT_SUCCESS(Status)) { - return Status; - } - - RtlInitUnicodeString(&UnicodeName, L"Large Wave"); - Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, - &UnicodeName, - 0, - NULL); - if (!NT_SUCCESS(Status)) { - return Status; - } - - break; - - case PcwCallbackCollectData: - - // - // Add values for 3 instances of Geometric Wave Counter Set. - // - - Status = KcsAddGeometricInstance(Info->CollectData.Buffer, - L"Small Wave", - 40, - 20); - if (!NT_SUCCESS(Status)) { - return Status; - } - - Status = KcsAddGeometricInstance(Info->CollectData.Buffer, - L"Medium Wave", - 30, - 40); - if (!NT_SUCCESS(Status)) { - return Status; - } - - Status = KcsAddGeometricInstance(Info->CollectData.Buffer, - L"Large Wave", - 20, - 60); - if (!NT_SUCCESS(Status)) { - return Status; - } - - break; - } - - return STATUS_SUCCESS; -} - -NTSTATUS -KcsAddTrignometricInstance ( - _In_ PPCW_BUFFER Buffer, - _In_ PCWSTR Name, - _In_ ULONG MinimalValue, - _In_ ULONG Amplitude - ) - -/*++ - -Routine Description: - - This utility function adds instance to the callback buffer. - -Arguments: - - Buffer - Data will be returned in this buffer. - - Name - Name of instances to be added. - - MinimalValue - Minimum value of the wave. - - Amplitude - Amplitude of the wave. - -Return Value: - - NTSTATUS indicating if the function succeeded. - ---*/ - -{ - double Angle; - KFLOATING_SAVE FloatSave; - NTSTATUS Status; - LARGE_INTEGER Timestamp; - UNICODE_STRING UnicodeName; - TRIGNOMETRIC_WAVE_VALUES Values; - - PAGED_CODE(); - - Status = KeSaveFloatingPointState(&FloatSave); - if (!NT_SUCCESS(Status)) { - return Status; - } - - KeQuerySystemTime(&Timestamp); - - Angle = (double)(Timestamp.QuadPart / 400000) * (22/7) / 180; - - Values.Constant = MinimalValue; - Values.Cosine = (ULONG)(MinimalValue + Amplitude * cos(Angle)); - Values.Sine = (ULONG)(MinimalValue + Amplitude * sin(Angle)); - - KeRestoreFloatingPointState(&FloatSave); - - // - // Add instance name & values to the caller's buffer. - // - - RtlInitUnicodeString(&UnicodeName, Name); - - return KcsAddTrignometricWave(Buffer, &UnicodeName, 0, &Values); -} - -NTSTATUS NTAPI -KcsTrignometricWaveCallback ( - _In_ PCW_CALLBACK_TYPE Type, - _In_ PPCW_CALLBACK_INFORMATION Info, - _In_opt_ PVOID Context - ) - -/*++ - -Routine Description: - - This function returns the list of counter instances and counter data. - -Arguments: - - Type - Request type. - - Info - Buffer for returned data. - - Context - Not used. - -Return Value: - - NTSTATUS indicating if the function succeeded. - ---*/ - -{ - NTSTATUS Status; - UNICODE_STRING UnicodeName; - - UNREFERENCED_PARAMETER(Context); - - PAGED_CODE(); - - switch (Type) { - case PcwCallbackEnumerateInstances: - RtlInitUnicodeString(&UnicodeName, L"default"); - Status = KcsAddTrignometricWave(Info->EnumerateInstances.Buffer, - &UnicodeName, - 0, - NULL); - if (!NT_SUCCESS(Status)) { - return Status; - } - - break; - - case PcwCallbackCollectData: - - // - // Add values for Single Instance of Trignometirc Wave Counter Set. - // - - return KcsAddTrignometricInstance(Info->CollectData.Buffer, - L"default", - 50, - 30); - } - - return STATUS_SUCCESS; -} - -VOID -KcsUnload ( - _In_ PDRIVER_OBJECT DriverObject - ) - -/*++ - -Routine Description: - - This function unregisters countersets - -Arguments: - - DriverObject - Not used. - -Return Value: - - None. - ---*/ - -{ - UNREFERENCED_PARAMETER(DriverObject); - - PAGED_CODE(); - - // - // Unregister Countersets. - // - - KcsUnregisterGeometricWave(); - KcsUnregisterTrignometricWave(); -} - -NTSTATUS -DriverEntry ( - _In_ PDRIVER_OBJECT DriverObject, - _In_ PUNICODE_STRING RegistryPath - ) - -/*++ - -Routine Description: - - This function registers countersets on initial loading of the driver. - -Arguments: - - DriverObject - Supplies the driver object of the driver being loaded. - - RegistryPath - Not used. - -Return Value: - - NTSTATUS indicating if driver was properly loaded. - ---*/ - -{ - NTSTATUS Status; - - UNREFERENCED_PARAMETER(RegistryPath); - - PAGED_CODE(); - - // - // Register Countersets. - // - - Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL); - if (!NT_SUCCESS(Status)) { - return Status; - } - - Status = KcsRegisterTrignometricWave(KcsTrignometricWaveCallback, NULL); - if (!NT_SUCCESS(Status)) { - KcsUnregisterTrignometricWave(); - return Status; - } - - // - // Success path - set up unload routine and return success. - // - - DriverObject->DriverUnload = KcsUnload; - - return STATUS_SUCCESS; -} - diff --git a/tests/projects/wdk/kmdf/perfcounters/kcs.h b/tests/projects/wdk/kmdf/perfcounters/kcs.h deleted file mode 100644 index f575b816d..000000000 --- a/tests/projects/wdk/kmdf/perfcounters/kcs.h +++ /dev/null @@ -1,34 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved. - - THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY - KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR - PURPOSE. - -Module Name: - - kcs.h - -Abstract: - - This module contains sample code to demonstrate how to provide - counter data from a kernel driver. - -Environment: - - Kernel mode only. - ---*/ - -typedef struct _GEOMETRIC_WAVE_VALUES { - ULONG Square; - ULONG Triangle; -} GEOMETRIC_WAVE_VALUES, *PGEOMETRIC_WAVE_VALUES; - -typedef struct _TRIGNOMETRIC_WAVE_VALUES { - ULONG Constant; - ULONG Cosine; - ULONG Sine; -} TRIGNOMETRIC_WAVE_VALUES, *PTRIGNOMETRIC_WAVE_VALUES; \ No newline at end of file diff --git a/tests/projects/wdk/kmdf/perfcounters/kcs.man b/tests/projects/wdk/kmdf/perfcounters/kcs.man deleted file mode 100644 index a5a16ffbd..000000000 --- a/tests/projects/wdk/kmdf/perfcounters/kcs.man +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/projects/wdk/kmdf/perfcounters/kcs.rc b/tests/projects/wdk/kmdf/perfcounters/kcs.rc deleted file mode 100644 index 7ebb6b97b..000000000 --- a/tests/projects/wdk/kmdf/perfcounters/kcs.rc +++ /dev/null @@ -1 +0,0 @@ -#include "kcsCounters.rc" diff --git a/tests/projects/wdk/kmdf/perfcounters/xmake.lua b/tests/projects/wdk/kmdf/perfcounters/xmake.lua deleted file mode 100644 index 17fd5d5cb..000000000 --- a/tests/projects/wdk/kmdf/perfcounters/xmake.lua +++ /dev/null @@ -1,19 +0,0 @@ - --- add modes: debug and release -add_rules("mode.debug", "mode.release") - --- add target -target("kcs") - - -- add rules - add_rules("wdk.kmdf.driver") - - -- add flags for rule: wdk.man - add_values("wdk.man.prefix", "Kcs") - add_values("wdk.man.resource", "kcsCounters.rc") - add_values("wdk.man.header", "kcsCounters.h") - add_values("wdk.man.counter_header", "kcsCounters_counters.h") - - -- add files - add_files("*.c", "*.rc", "*.man") - diff --git a/tests/projects/wdk/wdm/msdsm/SampleDSM.inf b/tests/projects/wdk/wdm/msdsm/SampleDSM.inf new file mode 100644 index 000000000..f632f6e50 Binary files /dev/null and b/tests/projects/wdk/wdm/msdsm/SampleDSM.inf differ diff --git a/tests/projects/wdk/wdm/msdsm/dsmmain.c b/tests/projects/wdk/wdm/msdsm/dsmmain.c new file mode 100644 index 000000000..bdf875184 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/dsmmain.c @@ -0,0 +1,9752 @@ +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + dsmmain.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM). + It exports behaviours that mpio.sys will use to determine how to + multipath SPC-3 conforming devices. + + This file contains routines that are internal to MSDSM. + +Environment: + + kernel mode only + +Notes: + +--*/ + +#include "precomp.h" + +#ifdef DEBUG_USE_WPP +#include "dsmmain.tmh" +#endif + +#pragma warning (disable:4305) + +extern BOOLEAN DoAssert; + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, DsmpRegisterPersistentReservationKeys) +#endif + +VOID +DsmpFreeDSMResources( + _In_ IN PDSM_CONTEXT DsmContext + ) +/*++ + +Routine Description: + + This routine will free the resources allocated by the DSM. This routine + should be called when the DSM is being unloaded. + +Arguements: + + DsmContext - DSM context given to MPIO during initialization + +Return Value: + + None +--*/ +{ + PDSM_WMILIB_CONTEXT wmiInfo; + PVOID tempAddress = (PVOID)DsmContext; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Entering function.\n", + DsmContext)); + + // + // First free the buffer allocated for storing the registry path. + // + wmiInfo = &gDsmInitData.DsmWmiInfo; + + if (wmiInfo->RegistryPath.Buffer) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Freeing wmiInfo's registry buffer.\n", + DsmContext)); + + DsmpFreePool(wmiInfo->RegistryPath.Buffer); + } + + if (DsmContext) { + PLIST_ENTRY entry; + PDSM_DEVICE_INFO deviceInfo; + PDSM_GROUP_ENTRY groupEntry; + PDSM_FAILOVER_GROUP failGroup; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry; + + ExDeleteNPagedLookasideList(&(DsmContext->CompletionContextList)); + + // + // Free up the devices (DeviceInfo) list. + // + while (!IsListEmpty(&DsmContext->DeviceList)) { + + entry = DsmContext->DeviceList.Flink; + + NT_ASSERT(entry); + + deviceInfo = CONTAINING_RECORD(entry, DSM_DEVICE_INFO, ListEntry); + + if (deviceInfo) { + + DsmpRemoveDeviceFailGroup(DsmContext, deviceInfo->FailGroup, deviceInfo, TRUE); + DsmpRemoveDeviceEntry(DsmContext, deviceInfo->Group, deviceInfo); + } + } + + NT_ASSERT(!DsmContext->NumberDevices && + !DsmContext->NumberFOGroups && + !DsmContext->NumberGroups); + + // + // By now, there should be no group entries left but play it safe and + // free up the GROUP list. + // + while (!IsListEmpty(&DsmContext->GroupList)) { + + entry = DsmContext->GroupList.Flink; + + NT_ASSERT(entry); + + groupEntry = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); + + if (groupEntry) { + + DsmpRemoveGroupEntry(DsmContext, groupEntry, TRUE); + + DsmpFreePool(groupEntry); + } + } + + // + // By now there should be no FOG entries left but we play it safe and + // free up the FOG list. + // + while (!IsListEmpty(&DsmContext->FailGroupList)) { + + entry = RemoveHeadList(&DsmContext->FailGroupList); + + if (entry) { + + failGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); + + if (failGroup) { + + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; + PLIST_ENTRY deviceEntry = NULL; + + while (!IsListEmpty(&failGroup->FOG_DeviceList)) { + + deviceEntry = RemoveHeadList(&failGroup->FOG_DeviceList); + + if (deviceEntry) { + + fogDeviceListEntry = CONTAINING_RECORD(deviceEntry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); + + if (!fogDeviceListEntry) { + continue; + } + + (fogDeviceListEntry->DeviceInfo)->FailGroup = NULL; + + DsmpFreePool(fogDeviceListEntry); + InterlockedDecrement((LONG volatile*)&failGroup->Count); + } + } + + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + InterlockedDecrement((LONG volatile*)&DsmContext->NumberFOGroups); + } + } + } + + // + // Free up the controller list. + // + while (!IsListEmpty(&DsmContext->ControllerList)) { + + entry = RemoveHeadList(&DsmContext->ControllerList); + + if (entry) { + + controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); + + if (controllerEntry) { + + DsmpFreeControllerEntry(DsmContext, controllerEntry); + + InterlockedDecrement((LONG volatile*)&DsmContext->NumberControllers); + } + } + } + + NT_ASSERT(!DsmContext->NumberControllers); + + // + // Free up the stale FOG list. + // + while (!IsListEmpty(&DsmContext->StaleFailGroupList)) { + + entry = RemoveHeadList(&DsmContext->StaleFailGroupList); + + if (entry) { + + failGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); + + if (failGroup) { + + InterlockedDecrement((LONG volatile*)&DsmContext->NumberStaleFOGroups); + NT_ASSERT(IsListEmpty(&failGroup->FOG_DeviceList)); + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + } + } + } + + // + // Free up the supported devices list buffer. + // + DsmpFreePool(DsmContext->SupportedDevices.Buffer); + + // + // It's the responsibility of the mpio bus driver to have already + // destroyed all devices and paths. As those functions free allocations + // for the objects, the only thing needed here is to free the DsmContext. + // + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Freeing the DsmContext.\n", + DsmContext)); + + DsmpFreePool(DsmContext); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Exiting function.\n", + tempAddress)); + + return; +} + + +PDSM_GROUP_ENTRY +DsmpFindDevice( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ) +/*++ + +Routine Description: + + This routine searches for a serial number match between DeviceInfo and + the rest of the devices currently being driven by this DSM. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DeviceInfo - The deviceInfo containing serial number for which to search. + AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively + +Return Value: + + The multi-path group entry in which the device resides. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + PLIST_ENTRY entry; + PDSM_GROUP_ENTRY groupEntry = NULL; + ULONG i; + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Entering function.\n", + DeviceInfo)); + + if (AcquireDSMLockExclusive) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + // + // Run through the DeviceInfo List + // + entry = DsmContext->DeviceList.Flink; + for (i = 0; i < DsmContext->NumberDevices; i++, entry = entry->Flink) { + + // + // Extract the deviceInfo structure. + // + deviceInfo = CONTAINING_RECORD(entry, DSM_DEVICE_INFO, ListEntry); + DSM_ASSERT(deviceInfo); + + if (deviceInfo) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Comparing with %p.\n", + DeviceInfo, + deviceInfo)); + + // + // Call the Serial Number compare routine. + // + if (DsmCompareDevices(DsmContext, + DeviceInfo, + deviceInfo)) { + + groupEntry = deviceInfo->Group; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Found matching multi-path group %p.\n", + DeviceInfo, + groupEntry)); + + break; + } + } + } + + if (AcquireDSMLockExclusive) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Exiting function with groupEntry %p.\n", + DeviceInfo, + groupEntry)); + + return groupEntry; +} + + +PDSM_GROUP_ENTRY +DsmpBuildGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This will allocate and partially initialise a multi-path group entry. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DeviceInfo - The first device to be added to the group. + +Return Value: + + The new group entry. + +--*/ +{ + PDSM_GROUP_ENTRY group; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildGroupEntry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Allocate the memory for the multi-path group. + // + group = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_GROUP_ENTRY), + DSM_TAG_GROUP_ENTRY); + + if (group) { + + InitializeListHead(&group->FailingDevInfoList); + group->GroupNumber = InterlockedIncrement((LONG volatile*)&DsmContext->NumberGroups); + group->GroupSig = DSM_GROUP_SIG; + group->State = DSM_GP_NORMAL; + + // + // Add it to the list of multi-path groups. + // + InsertTailList(&DsmContext->GroupList, &group->ListEntry); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildGroupEntry (DevInfo %p): Failed to allocate memory for the group.\n", + DeviceInfo)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildGroupEntry (DevInfo %p): Exiting function with group %p.\n", + DeviceInfo, + group)); + + return group; +} + + +NTSTATUS +DsmpParseTargetPortGroupsInformation( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ) +/*++ + +Routine Description: + + This will parse the information returned back from a previously + made call to ReportTargetPortGroups and build new TPG entries or + update old ones. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + Group - group entry + TargetPortGroupsInfo - Pointer to the ReportTPG returned buffer. + TargetPortGroupsInfoLength - length of the buffer. + +Return Value: + + STATUS_SUCCESS or appropriate error code. + +--*/ +{ + PUCHAR targetPortGroupsInfoIndex; + ULONG bytes = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; + ULONG descriptorSize = 0; + NTSTATUS status = STATUS_SUCCESS; + ULONG index; + DSM_DEVICE_STATE tpgState = DSM_DEV_NOT_USED_STATE; + ULONG bytesLeft; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Entering function.\n", + Group)); + + targetPortGroupsInfoIndex = TargetPortGroupsInfo + bytes; + bytesLeft = TargetPortGroupsInfoLength - bytes; + + while (bytes < TargetPortGroupsInfoLength && NT_SUCCESS(status)) { + + targetPortGroupEntry = DsmpFindTargetPortGroupEntry(DsmContext, + Group, + targetPortGroupsInfoIndex, + bytesLeft); + + if (targetPortGroupEntry) { + + targetPortGroupEntry = DsmpUpdateTargetPortGroupEntry(DsmContext, + targetPortGroupEntry, + targetPortGroupsInfoIndex, + bytesLeft, + &descriptorSize); + } else { + + targetPortGroupEntry = DsmpBuildTargetPortGroupEntry(DsmContext, + Group, + targetPortGroupsInfoIndex, + bytesLeft, + &descriptorSize); + + if (targetPortGroupEntry) { + + // + // Insert this TPG entry into array + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + if (!Group->TargetPortGroupList[index]) { + + Group->TargetPortGroupList[index] = targetPortGroupEntry; + InterlockedIncrement((LONG volatile*)&Group->NumberTargetPortGroups); + targetPortGroupEntry->Group = Group; + break; + } + } + + if (index == DSM_MAX_PATHS) { + + NT_ASSERT(index < DSM_MAX_PATHS); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Number of paths exceeded max supported.\n", + Group)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpParseTargetPortGroupsInformation; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Insufficient resources to build TPG.\n", + Group)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + // + // If this is the first TPG being parsed, save off its AA state. + // + if (tpgState == DSM_DEV_NOT_USED_STATE) { + + tpgState = targetPortGroupEntry->AsymmetricAccessState; + + } else { + + // + // Check if this TPG's AA state differs from the previous one's. + // Symmetric LU access means that TPG access states must be the + // same for TPGs. If this one is different, we know that the + // device supports Asymmetric LU access. + // + if (tpgState != targetPortGroupEntry->AsymmetricAccessState) { + + Group->Symmetric = FALSE; + } + } + } + + if (targetPortGroupEntry) { + + // + // Set the flag to indicate that we've encountered this TPG in the RTPG information. + // + targetPortGroupEntry->Traversed = TRUE; + } + + bytes += descriptorSize; + targetPortGroupsInfoIndex += descriptorSize; + bytesLeft -= descriptorSize; + } + + // + // Since we've gone through the entire information reported by back RTPG, it + // is now time to delete the stale entries. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroupEntry = Group->TargetPortGroupList[index]; + + if (targetPortGroupEntry) { + + if (targetPortGroupEntry->Traversed) { + + // + // Entry needs to continue to exist. Reset the flag and continue. + // + targetPortGroupEntry->Traversed = FALSE; + continue; + + } else { + + PLIST_ENTRY entry; + PLIST_ENTRY tempEntry; + PDSM_TARGET_PORT_LIST_ENTRY targetPort; + + // + // For this target port group, clean up all its target ports if + // the port doesn't expose any instance of this device. + // + for (entry = targetPortGroupEntry->TargetPortList.Flink; + entry != NULL && entry != &targetPortGroupEntry->TargetPortList; + entry = entry->Flink) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + + if (targetPort) { + + // + // If the TP doesn't expose this device, it is safe + // to delete it. + // + if (IsListEmpty(&targetPort->TP_DeviceList)) { + + tempEntry = entry; + entry = entry->Blink; + + RemoveEntryList(tempEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpParseTargetPortGroupsInformation (Group %p): Deleting empty target port %p from TPG %p list.\n", + Group, + targetPort, + targetPortGroupEntry)); + + DsmpFreePool(targetPort); + + InterlockedDecrement((LONG volatile*)&targetPortGroupEntry->NumberTargetPorts); + } + } + } + + // + // If the TPG doesn't have any TPs, it is safe to delete it. + // + if (!targetPortGroupEntry->NumberTargetPorts) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpParseTargetPortGroupsInformation (Group %p): Deleting target port group %p.\n", + Group, + targetPortGroupEntry)); + + DsmpFreePool(targetPortGroupEntry); + + InterlockedDecrement((LONG volatile*)&Group->NumberTargetPortGroups); + + Group->TargetPortGroupList[index] = NULL; + } + } + } + } + +__Exit_DsmpParseTargetPortGroupsInformation: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Exiting function with status %x\n", + Group, + status)); + + return status; +} + + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength + ) +/*++ + +Routine Description: + + This will search the group's TPG array to look for an identifier match. + + N.B: This routine must be called with DsmContextLock held in either Shared + or Exclusive mode. + +Arguments: + + DsmContext - DsmContext + Group - group entry + TargetPortGroupsDescriptor - Pointer to the TPG descriptor. + TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. + +Return Value: + + Pointer to the array element that matches, else NULL. + +--*/ +{ + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; + PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; + ULONG index; + BOOLEAN found = FALSE; + USHORT identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); + + UNREFERENCED_PARAMETER(TPGs_BufferLength); + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroupEntry (Group %p): Entering function.\n", + Group)); + + for (index = 0; index < DSM_MAX_PATHS && !found; index++) { + + targetPortGroup = Group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + if (targetPortGroup->Identifier == identifier) { + + found = TRUE; + } + } + } + + if (!found) { + targetPortGroup = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroupEntry (Group %p): Exiting function with targetPortGroup %p.\n", + Group, + targetPortGroup)); + + return targetPortGroup; +} + +_Success_(return!=0) +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpUpdateTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ) +/*++ + +Routine Description: + + This routine will update the target port group with information contained + in the passed in descriptor. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + TargetPortGroup - Pointer to the TPG entry to update. + TargetPortGroupsDescriptor - Pointer to the TPG descriptor. + TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. + DescriptorSize - return value of the size of the descriptor. + +Return Value: + + The updated target port group entry on success, NULL in case of failure. + +--*/ +{ + PLIST_ENTRY entry; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = TargetPortGroup; + PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; + ULONG numberTargetPorts = 0; + PULONG descriptorIndex; + ULONG index; + PDSM_TARGET_PORT_LIST_ENTRY listEntry; + NTSTATUS status = STATUS_SUCCESS; + ULONG identifier; + PLIST_ENTRY tempEntry = NULL; + ULONG delCount; + PUCHAR endOfBuffer = TargetPortGroupsDescriptor + TPGs_BufferLength - 1; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Entering function.\n", + TargetPortGroup)); + + if (DescriptorSize == NULL) { + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to null passed in DescriptorSize pointer\n", + TargetPortGroup, + status)); + + goto __Exit_DsmpUpdateTargetPortGroupEntry; + } + + *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + (targetPortGroup->NumberTargetPorts * sizeof(ULONG)); + + if (((PUCHAR)descriptor + sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to incorrect passed in TPG buffer size (%u).\n", + TargetPortGroup, + status, + TPGs_BufferLength)); + + goto __Exit_DsmpUpdateTargetPortGroupEntry; + } + + identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); + NT_ASSERT(targetPortGroup->Identifier == (USHORT)identifier); + NT_ASSERT(targetPortGroup->ActiveOptimizedSupported == (descriptor->ActiveOptimizedSupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->ActiveUnoptimizedSupported == (descriptor->ActiveUnoptimizedSupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->StandBySupported == (descriptor->StandbySupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->UnavailableSupported == (descriptor->UnavailableSupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->TransitioningSupported == (descriptor->TransitioningSupported) ? TRUE : FALSE); + DSM_ASSERT(targetPortGroup->VendorUnique == descriptor->VendorUnique); + + // + // It is possible that the asymmetric access state, status code and number of port + // may have changed + // + if ((targetPortGroup->AsymmetricAccessState) != (descriptor->AsymmetricAccessState & 0xF)) + { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Asymmetric access state has changed.\n", + TargetPortGroup)); + + + targetPortGroup->AsymmetricAccessState = descriptor->AsymmetricAccessState & 0xF; + } + + targetPortGroup->Preferred = (descriptor->Preferred) ? TRUE : FALSE; + + targetPortGroup->StatusCode = descriptor->StatusCode; + + numberTargetPorts = descriptor->NumberTargetPorts; + + NT_ASSERT(numberTargetPorts > 0); + + // + // Point to first target port identifier + // + descriptorIndex = descriptor->TargetPortIds; + + for (index = 0; index < numberTargetPorts && NT_SUCCESS(status); index++) { + + if (((PUCHAR)descriptorIndex + ((index + 1) * sizeof(ULONG)) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to incorrect TPG buffer size (%u) passed in.\n", + TargetPortGroup, + status, + TPGs_BufferLength)); + + goto __Exit_DsmpUpdateTargetPortGroupEntry; + } + + GetUlongFrom4ByteArray((PUCHAR)(&descriptorIndex[index]), identifier); + + listEntry = DsmpFindTargetPortListEntry(DsmContext, + targetPortGroup, + identifier); + + if (listEntry) { + + RemoveEntryList(&listEntry->ListEntry); + InsertHeadList(&targetPortGroup->TargetPortList, &listEntry->ListEntry); + + } else { + + listEntry = DsmpBuildTargetPortListEntry(DsmContext, + targetPortGroup, + identifier); + + if (listEntry) { + + InsertHeadList(&targetPortGroup->TargetPortList, &listEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Failed to allocate TargetPort (identifier %x).\n", + TargetPortGroup, + identifier)); + } + } + } + + // + // Ignore the status & carry on. Even if we weren't able to build TP entries + // for the new target ports, we are no worse off than before. + // + DSM_ASSERT(NT_SUCCESS(status)); + + *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + (numberTargetPorts * sizeof(ULONG)); + + for (index = 0, entry = targetPortGroup->TargetPortList.Flink; + index < numberTargetPorts; + index++, entry = entry->Flink); + + delCount = targetPortGroup->NumberTargetPorts - numberTargetPorts; + + for (index = 0; index < delCount; index++) { + + tempEntry = entry; + entry = entry->Flink; + + RemoveEntryList(tempEntry); + InterlockedDecrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); + + listEntry = CONTAINING_RECORD(tempEntry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + NT_ASSERT(listEntry); + + if (listEntry) { + + PLIST_ENTRY deviceEntry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device; + + while (!IsListEmpty(&listEntry->TP_DeviceList)) { + + deviceEntry = RemoveHeadList(&listEntry->TP_DeviceList); + InterlockedDecrement((LONG volatile*)&listEntry->Count); + + if (deviceEntry) { + + tp_device = CONTAINING_RECORD(deviceEntry, DSM_TARGET_PORT_DEVICELIST_ENTRY, ListEntry); + + if (tp_device) { + + if (tp_device->DeviceInfo) { + + tp_device->DeviceInfo->TargetPort = NULL; + } + + DsmpFreePool(tp_device); + } + } + } + + DsmpFreePool(listEntry); + } + } + +__Exit_DsmpUpdateTargetPortGroupEntry: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Exiting function.\n", + targetPortGroup)); + + return targetPortGroup; +} + + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpBuildTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ) +/*++ + +Routine Description: + + This will allocate and partially initialise a target port group entry. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + Group - The group that this newly going to be built TPG belongs to. + TargetPortGroupsDescriptor - Pointer to the TPG descriptor. + TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. + DescriptorSize - return value of the size of the descriptor. + +Return Value: + + The new target port group entry. + +--*/ +{ + PDSM_TARGET_PORT_GROUP_ENTRY entry = NULL; + PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; + ULONG numberTargetPorts = 0; + PULONG descriptorIndex; + ULONG index = 0; + PDSM_TARGET_PORT_LIST_ENTRY listEntry; + NTSTATUS status = STATUS_SUCCESS; + ULONG identifier; + PUCHAR endOfBuffer = TargetPortGroupsDescriptor + TPGs_BufferLength - 1; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Entering function.\n", + Group)); + + if (DescriptorSize == NULL) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to null passed in DescriptorSize pointer\n", + Group, + status)); + + goto __Exit_DsmpBuildTargetPortGroupEntry; + } + + *DescriptorSize = 0; + + if (((PUCHAR)descriptor + sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to incorrect passed in TPG buffer size (%u).\n", + Group, + status, + TPGs_BufferLength)); + + goto __Exit_DsmpBuildTargetPortGroupEntry; + } + + // + // Allocate the memory for the multi-path group. + // + entry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TARGET_PORT_GROUP_ENTRY), + DSM_TAG_TARGET_PORT_GROUP_ENTRY); + + if (entry) { + + entry->TargetPortGroupSig = DSM_TARGET_PORT_GROUP_SIG; + + // + // Target Port Group's access state + // + entry->AsymmetricAccessState = descriptor->AsymmetricAccessState & 0xF; + + // + // Target Port Group's supported states + // + entry->ActiveOptimizedSupported = (descriptor->ActiveOptimizedSupported) ? TRUE : FALSE; + entry->ActiveUnoptimizedSupported = (descriptor->ActiveUnoptimizedSupported) ? TRUE : FALSE; + entry->StandBySupported = (descriptor->StandbySupported) ? TRUE : FALSE; + entry->UnavailableSupported = (descriptor->UnavailableSupported) ? TRUE : FALSE; + + // + // Target Port Group's Preference and support for reporting transitioning + // + entry->Preferred = (descriptor->Preferred) ? TRUE : FALSE; + entry->TransitioningSupported = (descriptor->TransitioningSupported) ? TRUE : FALSE; + + // + // Target Port Group's identifier + // + entry->Identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); + + // + // Target Port Group's status code + // + entry->StatusCode = descriptor->StatusCode; + + // + // Vendor unique + // + entry->VendorUnique = descriptor->VendorUnique; + + // + // Number of target ports + // + numberTargetPorts = descriptor->NumberTargetPorts; + + NT_ASSERT(numberTargetPorts > 0); + + // + // Point to first target port identifier + // + descriptorIndex = descriptor->TargetPortIds; + + InitializeListHead(&entry->TargetPortList); + + for (index = 0; index < numberTargetPorts && NT_SUCCESS(status); index++) { + + if (((PUCHAR)descriptorIndex + ((index + 1) * sizeof(ULONG)) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to incorrect TPG buffer size (%u) passed in.\n", + Group, + status, + TPGs_BufferLength)); + + break; + } + + GetUlongFrom4ByteArray((PUCHAR)(&descriptorIndex[index]), identifier); + + listEntry = DsmpBuildTargetPortListEntry(DsmContext, + entry, + identifier); + + if (listEntry) { + + InsertTailList(&entry->TargetPortList, &listEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&entry->NumberTargetPorts); + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Failed to allocate memory for TP (identifier %x) of TPG %p.\n", + Group, + identifier, + entry)); + } + } + + if (NT_SUCCESS(status)) { + *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + (numberTargetPorts * sizeof(ULONG)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Failed to allocate memory for the TPG.\n", + Group)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (!NT_SUCCESS(status)) { + + // + // Delete the target port list and the target port group entry + // + numberTargetPorts = index - 1; + + if (entry) { + + PLIST_ENTRY delEntry; + + for (index = 0; index < numberTargetPorts; index++) { + + delEntry = RemoveHeadList(&entry->TargetPortList); + listEntry = CONTAINING_RECORD(delEntry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Cleaning up TPG %p's TP %x.\n", + Group, + entry, + listEntry->Identifier)); + + DsmpFreePool(listEntry); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Cleaning up TPG %p.\n", + Group, + entry)); + + DsmpFreePool(entry); + entry = NULL; + } + } + +__Exit_DsmpBuildTargetPortGroupEntry: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Exiting function with entry %p.\n", + Group, + entry)); + + return entry; +} + + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ) +/*++ + +Routine Description: + + This will search the passed in TPG's target port list for an identifier match. + + N.B: This routine must be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DsmContext + TargetPortGroup - The Target Port Group whose target ports need to be searched. + RelativeTargetPortId - Identifier of the target port entry being matched. + +Return Value: + + The target port list entry if match found, else NULL. + +--*/ +{ + PLIST_ENTRY entry = NULL; + PDSM_TARGET_PORT_LIST_ENTRY targetPort = NULL; + BOOLEAN found = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortListEntry (TPG %p): Entering function.\n", + TargetPortGroup)); + + for (entry = TargetPortGroup->TargetPortList.Flink; + entry != &TargetPortGroup->TargetPortList && !found; + entry = entry->Flink) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + NT_ASSERT(targetPort); + + if (targetPort) { + + if (targetPort->Identifier == RelativeTargetPortId) { + + NT_ASSERT(targetPort->TargetPortGroup == TargetPortGroup); + + found = TRUE; + } + } + } + + if (!found) { + targetPort = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortListEntry (TPG %p): Exiting function with target port %p.\n", + TargetPortGroup, + targetPort)); + + return targetPort; +} + + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpBuildTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ) +/*++ + +Routine Description: + + This will allocate and partially initialize a target port list entry. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + TargetPortGroup - The Target Port Group that this target port belongs to. + RelativeTargetPortId - Identifier of the target port entry being added. + +Return Value: + + The new target port list entry. + +--*/ +{ + PDSM_TARGET_PORT_LIST_ENTRY entry; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortListEntry (TPG %p): Entering function.\n", + TargetPortGroup)); + + entry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TARGET_PORT_LIST_ENTRY), + DSM_TAG_TARGET_PORT_LIST_ENTRY); + + if (entry) { + + InitializeListHead(&entry->TP_DeviceList); + + entry->Identifier = RelativeTargetPortId; + entry->TargetPortGroup = TargetPortGroup; + entry->TargetPortSig = DSM_TARGET_PORT_SIG; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortListEntry (TPG %p): Failed to allocate memory for target port (identifier %x).\n", + TargetPortGroup, + RelativeTargetPortId)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortListEntry (TPG %p): Exiting function with entry %p.\n", + TargetPortGroup, + entry)); + + return entry; +} + + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PUSHORT TargetPortGroupId + ) +/*++ + +Routine Description: + + This routine searches the list of TargetPortGroups of a Group to + find a match for the passed in TargetPortGroupId. + + N.B: This routine must be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DSM context. + Group - The group whose target port groups to search for a match. + TargetPortGroupId - Identifier of the target port group entry being searched. + +Return Value: + + The target port group entry which matches the passed in identifier. + +--*/ +{ + ULONG index; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; + BOOLEAN found = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroup (Group %p): Entering function.\n", + Group)); + + // + // Run through the target port group array + // + for (index = 0; index < DSM_MAX_PATHS && !found; index++) { + + targetPortGroupEntry = Group->TargetPortGroupList[index]; + + if (targetPortGroupEntry) { + + if (targetPortGroupEntry->Identifier == *TargetPortGroupId) { + + found = TRUE; + } + } + } + + if (!found) { + + targetPortGroupEntry = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroup (Group %p): Exiting function with targetPortGroupEntry %p.\n", + Group, + targetPortGroupEntry)); + + return targetPortGroupEntry; +} + + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPort( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN PULONG TargetPortGroupId + ) +/*++ + +Routine Description: + + This routine searches the list of TargetPorts to + find a match for the passed in TargetPortGroup and RelativeTargetPortId. + + N.B. Spin lock must be held by caller. + +Arguments: + + DsmContext - DSM context. + TargetPortGroup - the Target Port Group of which this target port is a member. + RelativeTargetPortId - Identifier of the target port entry being searched. + +Return Value: + + The target port entry which matches the passed in identifier. + +--*/ +{ + PLIST_ENTRY entry; + PDSM_TARGET_PORT_LIST_ENTRY targetPortListEntry = NULL; + BOOLEAN found = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPort (TPG %p): Entering function.\n", + TargetPortGroup)); + + // + // Run through the Target Port List + // + for (entry = TargetPortGroup->TargetPortList.Flink; + entry != &TargetPortGroup->TargetPortList && !found; + entry = entry->Flink) { + + // + // Extract the target port group structure. + // + targetPortListEntry = CONTAINING_RECORD(entry, + DSM_TARGET_PORT_LIST_ENTRY, + ListEntry); + NT_ASSERT(targetPortListEntry); + + if (targetPortListEntry) { + + NT_ASSERT(TargetPortGroup == targetPortListEntry->TargetPortGroup); + + // + // Compare with passed in identifier. + // + if (targetPortListEntry->Identifier == *TargetPortGroupId) { + + found = TRUE; + } + } + } + + if (!found) { + + targetPortListEntry = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPort (TPG %p): Exiting function with targetPortListEntry %p.\n", + TargetPortGroup, + targetPortListEntry)); + + return targetPortListEntry; +} + + +NTSTATUS +DsmpAddDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This routine adds DeviceInfo to an existing multi-path group. + + N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + Group - The multi-path group to which DeviceInfo should be added. + DeviceInfo - The new device. + DeviceState - The initial device state (active, passive,...) + +Return Value: + + UNSUCCESSFUL - If there are too many paths already. + SUCCESS + +--*/ +{ + ULONG numberDevices; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Ensure that this is a valid config - namely, it hasn't + // exceeded the number of paths supported. + // + numberDevices = * (volatile ULONG *) &Group->NumberDevices; + if (numberDevices < DSM_MAX_PATHS) { + +#if DBG + ULONG i; + + // + // Ensure that this isn't a second copy of the same pdo. + // + for (i = 0; i < numberDevices; i++) { + if (Group->DeviceList[i]->PortPdo == DeviceInfo->PortPdo) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Received same PDO %p twice.\n", + DeviceInfo, + DeviceInfo->PortPdo)); + } + } +#endif + + // + // Indicate one more device is present in this group. + // + Group->DeviceList[numberDevices] = DeviceInfo; + + // + // Indicate one more in the list. + // + InterlockedIncrement((LONG volatile*)&Group->NumberDevices); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Adding Device to Group %p\n", + DeviceInfo, + Group)); + + // + // Set-up this device's group id. + // + DeviceInfo->Group = Group; + + // + // One more deviceInfo entry. + // + InterlockedIncrement((LONG volatile*)&DsmContext->NumberDevices); + + // + // Finally, add it to the global list of devices. + // + InsertTailList(&DsmContext->DeviceList, + &DeviceInfo->ListEntry); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Max Paths already added for Group %p.\n", + DeviceInfo, + Group)); + + status = STATUS_UNSUCCESSFUL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +PDSM_CONTROLLER_LIST_ENTRY +DsmpFindControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_reads_(ControllerSerialNumberLength) IN PSTR ControllerSerialNumber, + _In_ IN SIZE_T ControllerSerialNumberLength, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ) +/*++ + +Routine Description: + + This routine compares the passed in serial number and SCSI address with the + entries in the list of controller objects. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + PortObject - Port FDO exposing the controller. + ScsiAddress - The scsi address to match. + ControllerSerialNumber - The serial number for which to find a match. + ControllerSerialNumberLength - Length of the passed in serial number, in bytes. + CodeSet - Code set used when building the passed in serial number. + AcquireLock - FALSE indicates that the caller has already acquired the spin lock. + +Return Value: + + Controller list entry if a match is found, else NULL + +--*/ +{ + KIRQL oldIrql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + PLIST_ENTRY entry; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; + BOOLEAN found = FALSE; + PDSM_CONTROLLER_LIST_ENTRY candidate = NULL; + + UNREFERENCED_PARAMETER(CodeSet); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindControllerEntry (SN %s): Entering function.\n", + ControllerSerialNumber)); + + if (AcquireLock) { + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + for (entry = DsmContext->ControllerList.Flink; + entry != &DsmContext->ControllerList && !found; + entry = entry->Flink) { + + controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); + NT_ASSERT(controllerEntry); + + if (!controllerEntry) { + + continue; + } + + // + // Serial numbers and Portal, Bus, and Target of the SCSI address must match. + // + if (!strncmp((const char*)controllerEntry->Identifier, + ControllerSerialNumber, + ControllerSerialNumberLength) && + (controllerEntry->ScsiAddress->PortNumber == ScsiAddress->PortNumber && + controllerEntry->ScsiAddress->PathId == ScsiAddress->PathId && + controllerEntry->ScsiAddress->TargetId == ScsiAddress->TargetId)) { + + if (controllerEntry->IdLength == ControllerSerialNumberLength) { + + found = TRUE; + + } else { + + if ((!candidate) || + (controllerEntry->IdLength > ControllerSerialNumberLength && ControllerSerialNumberLength == 32)) { + + candidate = controllerEntry; + } + } + } + } + + if (!found) { + + if (candidate) { + + controllerEntry = candidate; + + } else { + + controllerEntry = NULL; + } + } + + // + // If we found a matching controller entry, we need to make sure the Port + // Object (FDO) is updated. We also don't care about the LUN part of the + // SCSI address so we just set it to zero. + // + if (controllerEntry) { + controllerEntry->PortObject = PortObject; + controllerEntry->ScsiAddress->Lun = 0; + } + + if (AcquireLock) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindControllerEntry (SN %s): Exiting function with controllerEntry %p\n", + ControllerSerialNumber, + controllerEntry)); + + return controllerEntry; +} + + +_Ret_maybenull_ +_Must_inspect_result_ +_When_(return != NULL, __drv_allocatesMem(Mem)) +PDSM_CONTROLLER_LIST_ENTRY +DsmpBuildControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_ IN PSTR ControllerSerialNumber, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ) +/*++ + +Routine Description: + + This routine builds a new controller list entry with the passed in serial number info. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + DeviceObject - Controller's PDO. + PortObject - Port FDO exposing the controller. + ScsiAddress - scsi address of the controller. + ControllerSerialNumber - The serial number to associate with new entry. + CodeSet - Code set of the identifier that was used to build the serial number. + AcquireLock - TRUE indicates that the function must grab the spinlock. FALSE indicates + that caller has the spin lock held. + +Return Value: + + New controller list entry if we successfully built one, else NULL + +--*/ +{ + KIRQL oldIrql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Entering function - Controller %p seen through PortFDO %p.\n", + ControllerSerialNumber, + DeviceObject, + PortObject)); + + if (AcquireLock) { + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + controllerEntry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_CONTROLLER_LIST_ENTRY), + DSM_TAG_CONTROLLER_LIST_ENTRY); + + if (controllerEntry) { + + // + // Note: + // ControllerSerialNumber's length fits in a 32-bit value. + // See implementation in DsmpParseDeviceID() + // + ULONG length = (ULONG)strlen(ControllerSerialNumber); + + controllerEntry->Identifier = DsmpAllocatePool(NonPagedPoolNx, + length + 1, + DSM_TAG_SERIAL_NUM); + + if (controllerEntry->Identifier) { + + controllerEntry->ScsiAddress = DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_ADDRESS), + DSM_TAG_SCSI_ADDRESS); + if (controllerEntry->ScsiAddress) { + + RtlCopyMemory(controllerEntry->ScsiAddress, ScsiAddress, sizeof(SCSI_ADDRESS)); + + controllerEntry->DeviceObject = DeviceObject; + controllerEntry->PortObject = PortObject; + controllerEntry->ControllerSig = DSM_CONTROLLER_SIG; + controllerEntry->IdLength = length; + controllerEntry->IdCodeSet = CodeSet; + + RtlCopyMemory(controllerEntry->Identifier, + ControllerSerialNumber, + length); + + controllerEntry->RefCount = 0; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Failed to allocate resources for scsiaddress (controllerEntry %p).\n", + ControllerSerialNumber, + controllerEntry)); + + DsmpFreePool(controllerEntry->Identifier); + controllerEntry->Identifier = NULL; + DsmpFreePool(controllerEntry); + controllerEntry = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Failed to allocate resources for identifier (controllerEntry %p).\n", + ControllerSerialNumber, + controllerEntry)); + + DsmpFreePool(controllerEntry); + controllerEntry = NULL; + } + + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Failed to allocate memory for ControllerEntry.\n", + ControllerSerialNumber)); + } + + if (AcquireLock) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Exiting function with controllerEntry %p\n", + ControllerSerialNumber, + controllerEntry)); + + return controllerEntry; +} + + +VOID +DsmpFreeControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ __drv_freesMem(Mem) IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ) +/*++ + +Routine Description: + + This routine frees the allocations of the passed in controller list entry. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + ControllerEntry - Controller list entry. + +Return Value: + + Nothing + +--*/ +{ + PVOID tempAddress = (PVOID)ControllerEntry; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFreeControllerEntry (Entry %p): Entering function.\n", + ControllerEntry)); + + if (ControllerEntry->Identifier) { + DsmpFreePool(ControllerEntry->Identifier); + } + + if (ControllerEntry->ScsiAddress) { + DsmpFreePool(ControllerEntry->ScsiAddress); + } + + DsmpFreePool(ControllerEntry); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFreeControllerEntry (Entry %p): Exiting function.\n", + tempAddress)); + + return; +} + + +BOOLEAN +DsmpIsDeviceBelongsToController( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ) +/*++ + +Routine Description: + + This routine determines if the device passed in was exposed via the passed + in controller. + The match is to be based on VID and SCSI Address (using the Port, Bus + and Target comparison). + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + DeviceInfo - The device instance to match. + ControllerEntry - The controller object which we need to determine whether + DeviceInfo is exposed from. + +Return Value: + + TRUE - if the controller's VID and scsi address match + FALSE - not matched + +--*/ +{ + BOOLEAN saMatch = FALSE; + BOOLEAN vMatch = FALSE; + BOOLEAN match = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpIsDeviceBelongsToController (DevInfo %p): Entering function - ControllerEntry is %p.\n", + DeviceInfo, + ControllerEntry)); + + if (DeviceInfo->ScsiAddress && ControllerEntry->ScsiAddress) { + + saMatch = (DeviceInfo->ScsiAddress->PathId == ControllerEntry->ScsiAddress->PathId && + DeviceInfo->ScsiAddress->PortNumber == ControllerEntry->ScsiAddress->PortNumber && + DeviceInfo->ScsiAddress->TargetId == ControllerEntry->ScsiAddress->TargetId); + } + + if (saMatch) { + + INQUIRYDATA inquiryData = {0}; + UCHAR controllerVID[9] = {0}; + UCHAR deviceVID[9] = {0}; + + if (NT_SUCCESS(DsmpGetStandardInquiryData(ControllerEntry->DeviceObject, &inquiryData))) { + + RtlStringCchCopyA((PSTR)controllerVID, + ARRAYSIZE(controllerVID), + (PCSTR)(&inquiryData.VendorId)); + + RtlStringCchCopyA((PSTR)deviceVID, + ARRAYSIZE(deviceVID), + (PCSTR)(&DeviceInfo->Descriptor) + DeviceInfo->Descriptor.VendorIdOffset); + + + if (!strcmp((const char*)controllerVID, (const char*)deviceVID)) { + + vMatch = TRUE; + } + } + } + + match = saMatch & vMatch; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpIsDeviceBelongsToController (DevInfo %p): ControllerEntry %p. Exiting function with match = %x.\n", + DeviceInfo, + ControllerEntry, + match)); + + return match; +} + + +PDSM_DEVICE_INFO +DsmpFindDevInfoFromGroupAndFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAILOVER_GROUP FOGroup + ) +/*++ + +Routine Description: + + This routine will find the deviceInfo that is part of both the passed in Group + as well as passed in Fail-Over group. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + Group - The group that represents the device. + FOGroup - The FOG that the device is part of. + +Return Value: + + The deviceInfo that is part of both. + NULL - if not found. + +--*/ +{ + ULONG i; + PDSM_DEVICE_INFO deviceInfo = NULL; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindDevInfoFromGroupAndFOGroup (Group %p FOG %p): Entering function.\n", + Group, + FOGroup)); + + if (Group && FOGroup) { + + // + // Run through the list of devInfos in passed in Group + // + for (i = 0; i < DSM_MAX_PATHS; i++) { + + deviceInfo = Group->DeviceList[i]; + + if (deviceInfo) { + + if (deviceInfo->FailGroup == FOGroup) { + + break; + + } else { + + deviceInfo = NULL; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindFOGroup (Group %p FOG %p): Exiting function with deviceInfo %p.\n", + Group, + FOGroup, + deviceInfo)); + + return deviceInfo; +} + + +PDSM_FAILOVER_GROUP +DsmpFindFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + This routine will find the Fail-Over group that corresponds to PathId. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + PathId - The Path Identifier that corresponds to + an adapter/adapter-controller + +Return Value: + + The fail-over group. + NULL - if not found. + +--*/ +{ + PDSM_FAILOVER_GROUP failOverGroup = NULL; + PDSM_FAILOVER_GROUP retFOGroup = NULL; + PLIST_ENTRY entry; + ULONG i; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindFOGroup (PathId %p): Entering function.\n", + PathId)); + + // + // Run through the list of Fail-Over Groups + // + entry = DsmContext->FailGroupList.Flink; + for (i = 0; i < DsmContext->NumberFOGroups; i++, entry = entry->Flink) { + + // + // Extract the fail-over group structure. + // + failOverGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); + NT_ASSERT(failOverGroup); + + if (!failOverGroup) { + continue; + } + + // + // Check for a match of the PathId. + // + if (failOverGroup->PathId == PathId) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpFindFOGroup (PathId %p): Found a FO group %p.\n", + PathId, + failOverGroup)); + + retFOGroup = failOverGroup; + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindFOGroup (PathId %p): Exiting function with retFOGroup %p.\n", + PathId, + retFOGroup)); + + return retFOGroup; +} + + +PDSM_FAILOVER_GROUP +DsmpBuildFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PVOID *PathId + ) +/*++ + +Routine Description: + + This routine will build and partially initialise a fail-over group entry. + The FOG corresponds to the device list which will fail as a group. + + N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DeviceInfo - The first device to add to the group. + PathId - An identifier that is returned to mpio that id's the path. + +Return Value: + + The fail-over group entry. + NULL - on failed allocation. + +--*/ +{ + PDSM_FAILOVER_GROUP failOverGroup; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Entering function.\n", PathId)); + + // + // Allocate a new Fail Over Group + // + failOverGroup = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_FAILOVER_GROUP), + DSM_TAG_FO_GROUP); + if (failOverGroup) { + + InitializeListHead(&failOverGroup->FOG_DeviceList); + InitializeListHead(&failOverGroup->ZombieGroupList); + + // + // Get the current number of groups, and add the one that's being created. + // + InterlockedIncrement((LONG volatile*)&DsmContext->NumberFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Path that will be used for %p is %p.\n", + PathId, + DeviceInfo, + *PathId)); + + failOverGroup->PathId = *PathId; + + // + // Set the initial state to NORMAL. + // + failOverGroup->State = DSM_FG_NORMAL; + + failOverGroup->FailOverSig = DSM_FOG_SIG; + + // + // Add it to the global list. + // + InsertTailList(&DsmContext->FailGroupList, + &failOverGroup->ListEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Added new FOGroup %p with path %p. Count of FO Group %d.\n", + PathId, + failOverGroup, + *PathId, + DsmContext->NumberFOGroups)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Failed to allocate memory for FailOverGroup.\n", + PathId)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Exiting function with failOverGroup %p.\n", + PathId, + failOverGroup)); + + return failOverGroup; +} + + +NTSTATUS +DsmpUpdateFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This routine will add DeviceInfo to an existing FOG. + + N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + FailGroup - The fail-over group entry. + DeviceInfo - The new device. + +Return Value: + + STATUS_SUCCESS or appropriate error code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): Entering function. DeviceInfo %p.\n", + FailGroup, + DeviceInfo)); + + if (DeviceInfo && FailGroup) { + + fogDeviceListEntry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_FOG_DEVICELIST_ENTRY), + DSM_TAG_FOG_DEV_ENTRY); + + if (fogDeviceListEntry) { + + // + // Add the device to the list of devices that are on this path. + // + fogDeviceListEntry->DeviceInfo = DeviceInfo; + InterlockedIncrement((LONG volatile*)&FailGroup->Count); + InsertTailList(&FailGroup->FOG_DeviceList, &fogDeviceListEntry->ListEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): DevInfo %p added (current count: %d)\n", + FailGroup, + DeviceInfo, + FailGroup->Count)); + + // + // Set the device's F.O. Group. + // + DeviceInfo->FailGroup = FailGroup; + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): Failed to allocate memory for FOG devlist entry.\n", + FailGroup)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): Exiting function with status %x.\n", + FailGroup, + status)); + + return status; +} + + +VOID +DsmpRemoveDeviceFailGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ) +/*++ + +Routine Description: + + This routine will remove DeviceInfo from the FOG. + This routine is called in response to a removal of the device. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + FailGroup - The FOG from which DeviceInfo should be removed. + DeviceInfo - The now missing device. + AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively + +Return Value: + + NOTHING + +--*/ +{ + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings + PLIST_ENTRY entry; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry; + PLIST_ENTRY zombieEntry; + PDSM_ZOMBIEGROUP_ENTRY zombieGroup; + PDSM_ZOMBIEGROUP_ENTRY newZombieGroup; + BOOLEAN groupInZombieList = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (FOG %p): Entering function. DeviceInfo %p.\n", + FailGroup, + DeviceInfo)); + + if (FailGroup && DeviceInfo) { + + if (AcquireDSMLockExclusive) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + for (entry = FailGroup->FOG_DeviceList.Flink; + entry != &FailGroup->FOG_DeviceList; + entry = entry->Flink) { + + fogDeviceListEntry = CONTAINING_RECORD(entry, + DSM_FOG_DEVICELIST_ENTRY, + ListEntry); + DSM_ASSERT(fogDeviceListEntry); + + if (!fogDeviceListEntry) { + continue; + } + + if (fogDeviceListEntry->DeviceInfo == DeviceInfo) { + + DeviceInfo->FailGroup = NULL; + RemoveEntryList(entry); + DsmpFreePool(fogDeviceListEntry); + + InterlockedDecrement((LONG volatile*)&FailGroup->Count); + + // + // If a DeviceInfo is removed, we need to keep its group in a + // "zombie" list so that we can still access a fail-over group's + // associated groups even when all its devices are gone. + // + for (zombieEntry = FailGroup->ZombieGroupList.Flink; + zombieEntry != &(FailGroup->ZombieGroupList); + zombieEntry = zombieEntry->Flink) { + + zombieGroup = CONTAINING_RECORD(zombieEntry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + if (zombieGroup != NULL && + zombieGroup->Group != NULL && + zombieGroup->Group == DeviceInfo->Group) { + + groupInZombieList = TRUE; + break; + } + } + + // + // Create a new entry if the group does not exist in the zombie group list. + // + if (groupInZombieList == FALSE) { + newZombieGroup = (PDSM_ZOMBIEGROUP_ENTRY)DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_ZOMBIEGROUP_ENTRY), + DSM_TAG_ZOMBIEGROUP_ENTRY); + if (newZombieGroup != NULL) { + newZombieGroup->Group = DeviceInfo->Group; + InsertTailList(&FailGroup->ZombieGroupList, &newZombieGroup->ListEntry); + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (DevInfo %p): Failed to allocate memory for the zombie group.\n", + DeviceInfo)); + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (FOG %p): DevInfo %p removed from FOG (current count: %d)\n", + FailGroup, + DeviceInfo, + FailGroup->Count)); + + break; + } + } + + if (AcquireDSMLockExclusive) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (FOG %p): Exiting function.\n", + FailGroup)); + + return; +} + + +ULONG +DsmpRemoveDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This routine will remove DeviceInfo from Group. If it is the last DeviceInfo + in the Group, it has the added side-effect of cleaning up the Group entry + also. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + Group - The multi-path group from which DeviceInfo should be removed. + DeviceInfo - The device to remove. + +Return Value: + + Number of devices left in group. + +--*/ +{ + KIRQL irql; + ULONG i; + ULONG j; + ULONG numberDevices; + BOOLEAN freeGroup = FALSE; + PVOID tempAddress = (PVOID)Group; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceEntry (Group %p): Entering function. DeviceInfo %p.\n", + Group, + DeviceInfo)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find it's offset in the array of devices. + // + for (i = 0; i < Group->NumberDevices; i++) { + + if (Group->DeviceList[i] == DeviceInfo) { + + // + // Zero out it's entry. + // + Group->DeviceList[i] = NULL; + + // + // Reduce the number in the group. + // + InterlockedDecrement((LONG volatile*)&Group->NumberDevices); + + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceEntry (Group %p): Removing Device %p (desiredState %u) from Group\n", + Group, + DeviceInfo, + DeviceInfo->DesiredState)); + + // + // Collapse the array. + // Holding the spinlock, so that the state is consistent in other + // routines. + // + for (j = i; j < Group->NumberDevices; j++) { + + // + // Shuffle all entries down to fill the hole. + // + Group->DeviceList[j] = Group->DeviceList[j + 1]; + } + + // + // Zero out the last one. + // + Group->DeviceList[j] = NULL; + break; + } + } + + // + // Remove this devInfo from the TargetPort deviceList + // + DsmpRemoveDeviceFromTargetPortList(DeviceInfo); + + numberDevices = Group->NumberDevices; + + // + // See if anything is left in the Group. + // + if (Group->NumberDevices == 0) { + + Group->State = DSM_GP_FAILED; + + // + // Yank it from the Group list. + // + DsmpRemoveGroupEntry(DsmContext, Group, FALSE); + + freeGroup = TRUE; + } + + // + // Yank the device out of the Global list. + // + RemoveEntryList(&DeviceInfo->ListEntry); + InterlockedDecrement((LONG volatile*)&DsmContext->NumberDevices); + + // + // If the serial number buffer was allocated, need to free it. + // + if (DeviceInfo->SerialNumberAllocated) { + DsmpFreePool(DeviceInfo->SerialNumber); + } + + if (DeviceInfo->ScsiAddress) { + DsmpFreePool(DeviceInfo->ScsiAddress); + } + + // + // Fix up the Reservation List, if needed. + // + if (!freeGroup && Group->ReservationList) { + ULONG oldList; + + // + // Capture the list for debugging. + // + oldList = Group->ReservationList; + Group->ReservationList = 0; + + // + // Go through all devices in this group and find the one(s) registered. + // + for (i = 0; i < Group->NumberDevices; i++) { + + if (Group->DeviceList[i]->RegisterServiced) { + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmRemoveDeviceEntry (Group %p): Device %p at %d registered.\n", + Group, + Group->DeviceList[i], + i)); + + // + // Indicate its place. + // + Group->ReservationList |= (1 << i); + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmRemoveDeviceEntry (Group %p): Reservations Old (%x) New (%x).\n", + Group, + oldList, + Group->ReservationList)); + } + + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + // + // Free the allocation. + // + DsmpFreePool(DeviceInfo); + + if (freeGroup) { + + // + // Free the allocations. + // + if (Group->RegistryKeyName) { + DsmpFreePool(Group->RegistryKeyName); + } + + if (Group->HardwareId) { + DsmpFreePool(Group->HardwareId); + } + + DsmpFreePool(Group); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceEntry (Group %p): Exiting function - numberDevices = %x.\n", + tempAddress, + numberDevices)); + + return numberDevices; +} + + +VOID +DsmpRemoveDeviceFromTargetPortList( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This will remove a DeviceInfo from its target port device list. + + The caller should ensure that the DsmContext->SpinLock is held before + calling this function. + +Arguments: + + DeviceInfo - The DeviceInfo to be removed. + +Return Value: + + None + +--*/ +{ + if (DeviceInfo->TargetPort) { + + PLIST_ENTRY entry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY listEntry; + + for (entry = DeviceInfo->TargetPort->TP_DeviceList.Flink; + entry != NULL && entry != &DeviceInfo->TargetPort->TP_DeviceList; + entry = entry->Flink) { + + listEntry = CONTAINING_RECORD(entry, DSM_TARGET_PORT_DEVICELIST_ENTRY, ListEntry); + + if (listEntry) { + + if (listEntry->DeviceInfo == DeviceInfo) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFromTargetPortList: Removing device %p from target port entry %p.\n", + DeviceInfo, + listEntry)); + + RemoveEntryList(entry); + InterlockedDecrement((LONG volatile*)&DeviceInfo->TargetPort->Count); + DsmpFreePool(listEntry); + + DeviceInfo->TargetPort = NULL; + DeviceInfo->TargetPortGroup = NULL; + + break; + } + } + } + } +} + + +VOID +DsmpRemoveZombieGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY ZombieGroup + ) +/* ++ + +Routine Description: + + This will scan through all the Failover Groups and remove the given Group + from each Failover Group's zombie group list. + + The DSM lock should be aquired by the caller. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + ZombieGroup - Group entry that should be removed from FOGs' ZombieGroupList + +Return Value: + + None + +-- */ +{ + // + // Run through the list of Fail-Over Groups + // + ULONG i; + PDSM_FAILOVER_GROUP failOverGroup = NULL; + PLIST_ENTRY fogEntry; + PLIST_ENTRY groupEntry; + PDSM_ZOMBIEGROUP_ENTRY zombieGroupEntry; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveZombieGroupEntry (Group %p): Entering function.\n", + ZombieGroup)); + + fogEntry = DsmContext->FailGroupList.Flink; + for (i = 0; fogEntry != NULL && i < DsmContext->NumberFOGroups; i++, fogEntry = fogEntry->Flink) { + + failOverGroup = CONTAINING_RECORD(fogEntry, DSM_FAILOVER_GROUP, ListEntry); + if (failOverGroup != NULL) { + + for (groupEntry = failOverGroup->ZombieGroupList.Flink; + groupEntry != &(failOverGroup->ZombieGroupList); + groupEntry = groupEntry->Flink) { + + zombieGroupEntry = CONTAINING_RECORD(groupEntry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + if (zombieGroupEntry != NULL && + zombieGroupEntry->Group != NULL && + zombieGroupEntry->Group == ZombieGroup) { + + RemoveEntryList(groupEntry); + DsmpFreePool(zombieGroupEntry); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveZombieGroupEntry (Group %p): Found and removed a zombie group in (FOG %p)\n", + ZombieGroup, + failOverGroup)); + + // + // We removed the zombie group entry from this fail-over + // group, so we can move on to the next fail-over group. + // + break; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveZombieGroupEntry (Group %p): Exiting function.\n", + ZombieGroup)); +} + + +VOID +DsmpRemoveGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ) +/*++ + +Routine Description: + + This will remove a group entry from the DSM's list. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + GroupEntry - Group entry that should be removed from DSM's list + AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively + +Return Value: + + None + +--*/ +{ + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings + ULONG index; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; + PLIST_ENTRY entry; + PDSM_TARGET_PORT_LIST_ENTRY targetPort; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Entering function.\n", + GroupEntry)); + + if (AcquireDSMLockExclusive) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + NT_ASSERT(GroupEntry && GroupEntry->ListEntry.Flink && GroupEntry->ListEntry.Blink); + + // + // Since this group is being removed, we need to make sure it is also + // removed from all fail-over groups' zombie group lists. + // + DsmpRemoveZombieGroupEntry(DsmContext, GroupEntry); + + // + // Add it to the list of multi-path groups. + // + RemoveEntryList(&GroupEntry->ListEntry); + + GroupEntry->ListEntry.Flink = GroupEntry->ListEntry.Blink = NULL; + + InterlockedDecrement((LONG volatile*)&DsmContext->NumberGroups); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + // + // Clean up all its Target Port Groups + // + targetPortGroup = GroupEntry->TargetPortGroupList[index]; + + if (targetPortGroup) { + + GroupEntry->TargetPortGroupList[index] = NULL; + + // + // For each target port group, clean up all its target ports + // + while (!IsListEmpty(&targetPortGroup->TargetPortList)) { + + entry = RemoveHeadList(&targetPortGroup->TargetPortList); + + if (entry) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + + if (targetPort) { + + PLIST_ENTRY deviceEntry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY listEntry; + + while (!IsListEmpty(&targetPort->TP_DeviceList)) { + + deviceEntry = RemoveHeadList(&targetPort->TP_DeviceList); + + if (deviceEntry) { + + listEntry = CONTAINING_RECORD(deviceEntry, + DSM_TARGET_PORT_DEVICELIST_ENTRY, + ListEntry); + + if (listEntry) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Deleting device %p from TP %p list (TPG %p).\n", + GroupEntry, + listEntry->DeviceInfo, + targetPort, + targetPortGroup)); + + DsmpFreePool(listEntry); + + InterlockedDecrement((LONG volatile*)&targetPort->Count); + } + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Deleting target port %p from TPG %p list.\n", + GroupEntry, + targetPort, + targetPortGroup)); + + DsmpFreePool(targetPort); + + InterlockedDecrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); + } + } + } + + NT_ASSERT(targetPortGroup->NumberTargetPorts == 0); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Deleting target port group %p.\n", + GroupEntry, + targetPortGroup)); + + DsmpFreePool(targetPortGroup); + + InterlockedDecrement((LONG volatile*)&GroupEntry->NumberTargetPortGroups); + } + } + + NT_ASSERT(GroupEntry->NumberTargetPortGroups == 0); + + if (AcquireDSMLockExclusive) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Exiting function.\n", + GroupEntry)); + + return; +} + + +PDSM_FAILOVER_GROUP +DsmpSetNewPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDevice + ) +/*++ + +Routine Description: + + This routine will assign a new path to the multi-path group in + which FailingDevice resides. + + Caller must NOT hold spin lock. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + + FailingDevice - The device-path that is being moved + (due to failure, or admin. request) + +Return Value: + + The FOG containing the new path. + +--*/ +{ + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetNewPath (DevInfo %p): Entering function.\n", + FailingDevice)); + + if (DsmpIsSymmetricAccess(FailingDevice)) { + + DsmpSetLBForPathRemoval(DsmContext, FailingDevice, NULL, SpecialHandlingFlag); + + } else { + + DsmpSetLBForPathRemovalALUA(DsmContext, FailingDevice, NULL, SpecialHandlingFlag); + } + + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetNewPath (DevInfo %p): Exiting function with path (failGroup) %p.\n", + FailingDevice, + FailingDevice->Group->PathToBeUsed)); + + return FailingDevice->Group->PathToBeUsed; +} + + +PDSM_FAILOVER_GROUP +DsmpSetNewPathUsingGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group + ) +/*++ + +Routine Description: + + This routine will try to assign a new path using the given multi-path group. + This function should only be called during failover in the event that there + is no DeviceInfo with which to call DsmpSetNewPath(). + + Typically this will be called with one of a fail-over group's zombie groups. + + Caller must NOT hold spin lock. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + + Group - The multi-path group which to assign a new path. + +Return Value: + + The FOG containing the new path or NULL if no path was found. + +--*/ + +{ + ULONG i; + PDSM_DEVICE_INFO pDevInfo = NULL; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetNewPathUsingGroup (Group %p): Entering function.\n", + Group)); + + // + // Get the first available DeviceInfo. + // + for (i = 0; i < DSM_MAX_PATHS; i ++) { + if (Group->DeviceList[i] != NULL) { + pDevInfo = Group->DeviceList[i]; + break; + } + } + + if (pDevInfo == NULL) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetNewPathForZombieGroup (ZombieGroup %p): No failover group can be found.\n", + Group)); + return NULL; + } + + + if (DsmpIsSymmetricAccess(pDevInfo)) { + + DsmpSetLBForPathRemoval(DsmContext, pDevInfo, Group, SpecialHandlingFlag); + + } else { + + DsmpSetLBForPathRemovalALUA(DsmContext, pDevInfo, Group, SpecialHandlingFlag); + } + + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetNewPathForZombieGroup (ZombieGroup %p): Exiting function with path (failGroup) %p.\n", + Group, + Group->PathToBeUsed)); + + return Group->PathToBeUsed; + +} + + +NTSTATUS +DsmpUpdateTargetPortGroupDevicesStates( + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN DSM_DEVICE_STATE NewState + ) +/*++ + +Routine Description: + + This routine will update the target port group and all its appropriate + devInfos (ones not in remove pending, removed, or invalidated) with the + new state. The ALUAState will only be updated, NOT the real State. + Caller needs to update the real State based on the current LB policy. + + Note: This should be called with DsmContext Lock held and should only be + called after a SetTargetPortGroups request was sent down. + +Arguments: + + TargetPortGroup - TargetPortGroup whose state and deviceInfos need to be + updated + + NewState - The new state + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PLIST_ENTRY entry = NULL; + PDSM_TARGET_PORT_LIST_ENTRY targetPort = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Entering function.\n", + TargetPortGroup)); + + if (!TargetPortGroup) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Invalid TPG passed in.\n", + TargetPortGroup)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpUpdateTargetPortGroupDevicesStates; + } + + // + // First update TPG's asymmetric access state. + // + TargetPortGroup->AsymmetricAccessState = NewState; + + // + // Now update state of each of the devices belonging to this TPG. + // + for (entry = TargetPortGroup->TargetPortList.Flink; + entry != &TargetPortGroup->TargetPortList; + entry = entry->Flink) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + NT_ASSERT(targetPort); + + if (targetPort) { + + PLIST_ENTRY deviceEntry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device; + + for (deviceEntry = targetPort->TP_DeviceList.Flink; + deviceEntry != &targetPort->TP_DeviceList; + deviceEntry = deviceEntry->Flink) { + + tp_device = CONTAINING_RECORD(deviceEntry, + DSM_TARGET_PORT_DEVICELIST_ENTRY, + ListEntry); + + if (tp_device) { + + tp_device->DeviceInfo->ALUAState = NewState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Updated device %p alua state to %x.\n", + TargetPortGroup, + tp_device->DeviceInfo, + tp_device->DeviceInfo->ALUAState)); + } + } + } + } + +__Exit_DsmpUpdateTargetPortGroupDevicesStates: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Exiting function with status %x.\n", + TargetPortGroup, + status)); + + return status; +} + + +VOID +DsmpIncrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ) +{ + ULONG bytes = 0; + PCDB cdb = NULL; + ULONG cdbLength = 0; + BOOLEAN isReadWrite = FALSE; + ULONGLONG lastLba = 0; + ULONG numBlocks = 0; + ULONGLONG startLba = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIncrementCounters (FOG %p): Entering function.\n", + FailGroup)); + + if (Srb) { + + cdb = SrbGetCdb(Srb); + + if (cdb && DsmIsReadWrite(cdb->AsByte[0])) { + + isReadWrite = TRUE; + } + } + + InterlockedIncrement(&FailGroup->NumberOfRequestsInFlight); + + // + // Update counters that apply to read/write requests + // + if (isReadWrite) { + + bytes = SrbGetDataTransferLength(Srb); + + InterlockedExchangeAdd64((LONGLONG volatile*)&FailGroup->OutstandingBytesOfIO, bytes); + + cdbLength = SrbGetCdbLength(Srb); + + if (cdbLength == 16) { + + REVERSE_BYTES_QUAD(&startLba, &cdb->CDB16.LogicalBlock); + REVERSE_BYTES(&numBlocks, &cdb->CDB16.TransferLength); + + } else { + + REVERSE_BYTES(&startLba, &cdb->CDB10.LogicalBlockByte0); + REVERSE_BYTES_SHORT(&numBlocks, &cdb->CDB10.TransferBlocksMsb); + } + + lastLba = startLba + numBlocks - 1; + + InterlockedExchange64((LONGLONG volatile*)&FailGroup->LastLba, lastLba); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIncrementCounters (FOG %p): Exiting function.\n", + FailGroup)); + + return; +} + + +BOOLEAN +DsmpDecrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ) +{ + ULONG bytes = 0; + PCDB cdb = NULL; + BOOLEAN isReadWrite = FALSE; + BOOLEAN isDeletionEligible = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpDecrementCounters (FOG %p): Entering function.\n", + FailGroup)); + + if (Srb) { + + cdb = SrbGetCdb(Srb); + + if (cdb && DsmIsReadWrite(cdb->AsByte[0])) { + + isReadWrite = TRUE; + } + } + + // + // Update counters that apply to read/write requests + // + if (isReadWrite) { + + bytes = SrbGetDataTransferLength(Srb); + + InterlockedExchangeAdd64((LONGLONG volatile*)&FailGroup->OutstandingBytesOfIO, -(LONGLONG)bytes); + } + + NT_ASSERT(FailGroup->NumberOfRequestsInFlight > 0); + if (InterlockedCompareExchange(&FailGroup->NumberOfRequestsInFlight, 0, 0) > 0) { + + if(InterlockedDecrement(&FailGroup->NumberOfRequestsInFlight) == 0){ + + // + // If the inflight requests on the path is zero, if needed path can be removed. + // + isDeletionEligible = TRUE; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpDecrementCounters (FOG %p): Exiting function.\n", + FailGroup)); + + return isDeletionEligible; +} + + +PDSM_FAILOVER_GROUP +DsmpGetPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will pick a path, for processing a request, based + on the current LoadBalance policy that is set. + + N.B: This routine must be called with DSM Context Lock held in Shared mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmList - List of DSM Ids sent by MPIO + Srb - The read/write/verify request + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + FailOver Group that should be used for processing the request +--*/ +{ + // + // Algorithm: + // ========== + // Failover-only: + // -------------- + // If symmetric LUA (ie. ALUA not supported, or symmetric LUA using ALUA semantics (viz. storage reports + // implicit-only transitions and all TPGs in A/O): + // One path AO, <- this will be the only path used for I/O + // M paths in SB, <- one of these will be made active on failure of the above active path + // Rest of the paths Failed (Invalidated/PendingRemove/Removed) + // + // If ALUA: + // One path AO, <- this will be the only path used for I/O + // M paths in AU, SB or UA <- one of these made active on failover. Pref: AU > SB > UA. Also, controller affinity. + // Rest of the paths Failed + // + // Automatic failback will happen only if Preferred path has been set. + // This is the only policy that will support failback. + // + // Round-Robin: + // ------------ + // If symmetric LUA: + // N paths AO, <- round robin among these + // Rest of the paths Failed + // + // If ALUA: + // Round Robin policy not supported since all paths can't be in A/O state. + // + // Round-Robin With Subset: + // ------------------------ + // If symmetric LUA: + // N paths AO, <- round robin among these + // M paths SB, <- if no active paths left, make one of these active + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, <- round robin among these (NOTE: paths in AU not considered) + // M paths AU, SB or UA <- if no active paths, make subset of these active (based on TPG states after transition) + // Rest of the paths Failed + // + // Least-Queue Depth: + // ------------------ + // If symmetric LUA: + // N paths AO, <- one with least outstanding I/O is chosen + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, <- one with least outstanding I/O is chosen + // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG + // states after transition) - one with least outstanding I/O is chosen. + // Rest of the paths Failed + // + // Least-Weighted: + // --------------- + // If symmetric LUA: + // N paths AO, <- every path has an associated weight, path with least weight is used. + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, + // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG + // states after transition) - path with least weight used. + // Rest of the paths Failed + // + // Least-Blocks: + // ------------- + // If symmetric LUA: + // N paths AO, <- one with least cumulative outstanding IO is chosen + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, <- one with least cumulative outstanding IO is chosen + // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG + // states after transition) - one with least cumulative outstanding is chosen. + // + // Actual implementation of algorithm happens in the following routines: DsmpGetAnyActivePath, + // DsmpGetActivePathToBeUsed, flavors of DsmpSetLBForPathXXX. + // + + PDSM_FAILOVER_GROUP failGroup = NULL; + PDSM_DEVICE_INFO deviceInfo = DsmList->IdList[0]; + PDSM_GROUP_ENTRY groupEntry; + ULONG inx = 0; + + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Entering function.\n", + DsmList)); + + if (!(DsmList->Count && deviceInfo)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Called with no available paths.\n", + DsmList)); + + goto __Exit_DsmpGetPath; + } + + groupEntry = deviceInfo->Group; + DSM_ASSERT(groupEntry->GroupSig == DSM_GROUP_SIG); + + switch (groupEntry->LoadBalanceType) { + + case DSM_LB_FAILOVER: + case DSM_LB_WEIGHTED_PATHS: { + + // + // For FailOverOnly there is only one active path so we can + // just grab it from the cached location and go with it. + // For LeastWeightPath we always choose the lowest weighted + // one so we grab that and go + // + failGroup = groupEntry->PathToBeUsed; + + break; + } + + case DSM_LB_ROUND_ROBIN: + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + PDSM_DEVICE_INFO candidateDevice = NULL; + PDSM_GROUP_ENTRY newGroup = NULL; + ULONG newPath; + BOOLEAN foundPath = FALSE; + ULONG jnx = 0; + ULONG counter = 0; + BOOLEAN reset = FALSE; + + for (inx = 0; inx < DsmList->Count; inx++) { + + deviceInfo = DsmList->IdList[inx]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->FailGroup == groupEntry->PathToBeUsed) { + + // + // We've reached the devInfo that corresponds to the path + // that we should be using. If this devInfo is not in the + // right state to be used, we need to find the first candidate + // starting from this one to satisfy the request. + // To play it safe, we may have already considered a previous + // devInfo to be a candidate, that now needs to be reset to + // the one that we now find. + // + reset = TRUE; + } + +#if DBG + if (deviceInfo->TargetPortGroup && !DsmpIsDeviceFailedState(deviceInfo->State)) { + + if (deviceInfo->State != deviceInfo->ALUAState) { + + DSM_ASSERT(groupEntry->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && + deviceInfo->State == DSM_DEV_ACTIVE_UNOPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED); + } + } +#endif + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + if (!candidateDevice || reset) { + + candidateDevice = deviceInfo; + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Candidate device %p.\n", + DsmList, + candidateDevice)); + + jnx = inx; + } + + if (!groupEntry->PathToBeUsed || deviceInfo->FailGroup == groupEntry->PathToBeUsed) { + + // + // The devInfo that corresponds to the path that we were + // supposed to use, is in a state that makes it usable. + // So we've found our devInfo. + // + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + foundPath = TRUE; + candidateDevice = NULL; + + break; + } + } + } + + if (!foundPath) { + + if (candidateDevice) { + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)candidateDevice->FailGroup); + inx = jnx; + candidateDevice = NULL; + + } else { + + inx = 0; + } + } + + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Path to be used is %p.\n", + DsmList, + groupEntry->PathToBeUsed)); + + // + // The current chosen path is given by failGroup. Find the next path + // that should be chosen in the RoundRobin policy. Start with the + // device at index inx + 1, and look for the one with Active state. + // + for (counter = 0, jnx = inx + 1; + counter < DsmList->Count && !newGroup; + counter++, jnx++) { + + newPath = jnx % DsmList->Count; + + deviceInfo = DsmList->IdList[newPath]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + newGroup = deviceInfo->Group; + DSM_ASSERT(newGroup == groupEntry); + + InterlockedExchangePointer(&(newGroup->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): New Path is %p.\n", + DsmList, + newGroup->PathToBeUsed)); + + break; + } + } + + break; + } + + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { + + LONG leastQueueDepth = 0x7FFFFFFF; + + for (inx = 0; inx < DsmList->Count; inx++) { + + deviceInfo = DsmList->IdList[inx]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->FailGroup->NumberOfRequestsInFlight < leastQueueDepth) { + + leastQueueDepth = deviceInfo->FailGroup->NumberOfRequestsInFlight; + failGroup = deviceInfo->FailGroup; + } + } + + if (failGroup) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Path to be used for LQD is %p.\n", + DsmList, + failGroup)); + + } else { + + // + // For ALUA storage there are two cases where we are left with no + // TPG in the A/O state: + // 1) On storage that supports implicit transitions, a transition + // was initiated that left no TPG in the A/O state. + // 2) On storage that has explicit only transitions enabled, we tried + // making at least one path as A/O and failed. This can happen, + // for example, when STPG fails because this initiator is not + // registered or does not hold exclusive reservation over the + // target. + // + // For such storages, we should return some path instead of just + // failing the I/O. The path will likely be an A/U path until the + // storage does a transition to make a TPG A/O. + // + if (!DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmList->IdList[0])) { + + // + // Use the same path as the one used for the previous request. + // + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetPath (DsmIds %p): Using same path (FOG %p) as previous request for LQD.\n", + DsmList, + failGroup)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Failed to find a path for LQD.\n", + DsmList)); + } + } + + break; + } + + case DSM_LB_LEAST_BLOCKS: { + + ULONG bytes = 0; + PCDB cdb = NULL; + ULONG cdbLength = 0; + BOOLEAN isRead = FALSE; + BOOLEAN isWrite = FALSE; + PDSM_FAILOVER_GROUP lastPathUsed = groupEntry->PathToBeUsed; + ULONGLONG leastOutstandingIO = MAXULONGLONG; + ULONGLONG startLba = 0; + + // + // Use the last path under the following conditions: + // + // 1. This is not a read/write request or + // 2. This is a read/write request and + // a. The request is sequential and + // b. The cache is not exhausted + // + + if (Srb) { + + cdb = SrbGetCdb(Srb); + + if (cdb && DsmIsReadRequest(cdb->AsByte[0])) { + + isRead = TRUE; + } + + if (cdb && DsmIsWriteRequest(cdb->AsByte[0])) { + + isWrite = TRUE; + } + } + + if (isRead || isWrite) { + + if (groupEntry->UseCacheForLeastBlocks) { + + bytes = SrbGetDataTransferLength(Srb); + + cdbLength = SrbGetCdbLength(Srb); + + if (cdbLength == 16) { + + REVERSE_BYTES_QUAD(&startLba, &cdb->CDB16.LogicalBlock); + + } else { + + REVERSE_BYTES(&startLba, &cdb->CDB10.LogicalBlockByte0); + } + + // + // Check if: + // 1. The IO is sequential, AND + // 2. It is either: + // a. read request, OR + // b. write request and outstanding bytes will be within the cache limit + // + if ((lastPathUsed != NULL) && + (startLba >= lastPathUsed->LastLba) && + ((isRead) || + (isWrite && lastPathUsed->OutstandingBytesOfIO + bytes <= groupEntry->CacheSizeForLeastBlocks))) { + + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Sequential IO, so using same path %p for LeastBlocks.\n", + DsmList, + failGroup)); + } + } + + } else { + // + // The request is neither a read nor a write so use the same path. + // + failGroup = groupEntry->PathToBeUsed; + } + + if (!failGroup) { + + // + // Choose whichever Active/Optimized path has the least outstanding bytes. + // + for (inx = 0; inx < DsmList->Count; inx++) { + + deviceInfo = DsmList->IdList[inx]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->FailGroup->OutstandingBytesOfIO < leastOutstandingIO) { + + leastOutstandingIO = deviceInfo->FailGroup->OutstandingBytesOfIO; + failGroup = deviceInfo->FailGroup; + } + } + } + + if (failGroup) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Path to be used for LeastBlocks is %p.\n", + DsmList, + failGroup)); + + } else { + + // + // For ALUA storage there are two cases where we are left with no + // TPG in the A/O state: + // 1) On storage that supports implicit transitions, a transition + // was initiated that left no TPG in the A/O state. + // 2) On storage that has explicit only transitions enabled, we tried + // making at least one path as A/O and failed. This can happen, + // for example, when STPG fails because this initiator is not + // registered or does not hold exclusive reservation over the + // target. + // + // For such storages, we should return some path instead of just + // failing the I/O. The path will likely be an A/U path until the + // storage does a transition to make a TPG A/O. + // + if (!DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmList->IdList[0])) { + + // + // Use the same path as the one used for the previous request. + // + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetPath (DsmIds %p): Using same path (FOG %p) as previous request for LeastBlocks.\n", + DsmList, + failGroup)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Failed to find a path for LeastBlocks.\n", + DsmList)); + } + } + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Invalid LB Type %d set for group %p.\n", + DsmList, + groupEntry->LoadBalanceType, + groupEntry)); + + DSM_ASSERT(FALSE); + + break; + } + } + +__Exit_DsmpGetPath: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Exiting function with failGroup %p.\n", + DsmList, + failGroup)); + + return failGroup; +} + + +PVOID +DsmpGetPathIdFromPassThroughPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ PIRP Irp, + _Inout_ IN OUT NTSTATUS *Status + ) +/*++ + +Routine Description: + + This routine will pick the path that corresponds to the PathId + in the mpio pass through structure. + + NOTE: Caller must ensure that the IRP is either MPTP or MPTPD. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmList - List of DSM Ids sent by MPIO + Irp - The MPTP or MPTPD request + Status - Returned status + +Return Value: + + The PathId to which the request should be sent +--*/ +{ + PDSM_FAILOVER_GROUP failGroup = NULL; + PDSM_GROUP_ENTRY groupEntry; + PDSM_DEVICE_INFO deviceInfo; + ULONG inx = 0; + NTSTATUS status = STATUS_INVALID_PARAMETER; + KIRQL irql; + PVOID newPath = NULL; + BOOLEAN found = FALSE; + BOOLEAN useScsiAddress = FALSE; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + UCHAR pathId = 0; + UCHAR targetId = 0; + UCHAR portNumber = 0; + ULONGLONG mpioPathId = 0; + +#if DBG + BOOLEAN useMpioPathId = FALSE; +#endif + + // + // Extract the parameters from the passthrough based on the bitness of the + // process (32 or 64) and the type of passthrough (legacy or extended). + // +#if defined (_WIN64) + if (IoIs32bitProcess(Irp)) { + + if (DsmpIsMPIOPassThroughEx(controlCode)) { + PMPIO_PASS_THROUGH_PATH32_EX mpioPassThroughPath32 = (PMPIO_PASS_THROUGH_PATH32_EX)(Irp->AssociatedIrp.SystemBuffer); + PSCSI_PASS_THROUGH32_EX passThrough32 = (PSCSI_PASS_THROUGH32_EX)((PUCHAR)mpioPassThroughPath32 + mpioPassThroughPath32->PassThroughOffset); + + useScsiAddress = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + PSTOR_ADDRESS address; + if (passThrough32->StorAddressOffset < sizeof(SCSI_PASS_THROUGH_EX) || + passThrough32->StorAddressLength < sizeof(STOR_ADDRESS)) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + address = (PSTOR_ADDRESS)((PUCHAR)passThrough32 + passThrough32->StorAddressOffset); + if (address->Type != STOR_ADDRESS_TYPE_BTL8 || + address->AddressLength < STOR_ADDR_BTL8_ADDRESS_LENGTH) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + pathId = ((PSTOR_ADDR_BTL8)address)->Path; + targetId = ((PSTOR_ADDR_BTL8)address)->Target; + portNumber = mpioPassThroughPath32->PortNumber; + } else { + mpioPathId = mpioPassThroughPath32->MpioPathId; + } + + } else { + PMPIO_PASS_THROUGH_PATH32 mpioPassThroughPath32 = (PMPIO_PASS_THROUGH_PATH32)(Irp->AssociatedIrp.SystemBuffer); + + useScsiAddress = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + pathId = mpioPassThroughPath32->PassThrough.PathId; + targetId = mpioPassThroughPath32->PassThrough.TargetId; + portNumber = mpioPassThroughPath32->PortNumber; + } else { + mpioPathId = mpioPassThroughPath32->MpioPathId; + } + } + } else +#endif + if (DsmpIsMPIOPassThroughEx(controlCode)) { + PMPIO_PASS_THROUGH_PATH_EX mpioPassThroughPath = (PMPIO_PASS_THROUGH_PATH_EX)(Irp->AssociatedIrp.SystemBuffer); + PSCSI_PASS_THROUGH_EX passThrough = (PSCSI_PASS_THROUGH_EX)((PUCHAR)mpioPassThroughPath + mpioPassThroughPath->PassThroughOffset); + + useScsiAddress = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + PSTOR_ADDRESS address; + if (passThrough->StorAddressOffset < sizeof(SCSI_PASS_THROUGH_EX) || + passThrough->StorAddressLength < sizeof(STOR_ADDRESS)) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + address = (PSTOR_ADDRESS)((PUCHAR)passThrough + passThrough->StorAddressOffset); + if (address->Type != STOR_ADDRESS_TYPE_BTL8 || + address->AddressLength < STOR_ADDR_BTL8_ADDRESS_LENGTH) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + pathId = ((PSTOR_ADDR_BTL8)address)->Path; + targetId = ((PSTOR_ADDR_BTL8)address)->Target; + portNumber = mpioPassThroughPath->PortNumber; + } else { + mpioPathId = mpioPassThroughPath->MpioPathId; + } + } else { + PMPIO_PASS_THROUGH_PATH mpioPassThroughPath = (PMPIO_PASS_THROUGH_PATH)(Irp->AssociatedIrp.SystemBuffer); + + useScsiAddress = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + pathId = mpioPassThroughPath->PassThrough.PathId; + targetId = mpioPassThroughPath->PassThrough.TargetId; + portNumber = mpioPassThroughPath->PortNumber; + } else { + mpioPathId = mpioPassThroughPath->MpioPathId; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Entering function.\n", + DsmList)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + deviceInfo = DsmList->IdList[0]; + groupEntry = deviceInfo->Group; + DSM_ASSERT(groupEntry->GroupSig == DSM_GROUP_SIG); + // + // useMpioPathId is BOOLEAN (0 or 1) since MPIO_IOCTL_FLAG_USE_PATHID = 1 + // But since MPIO_IOCTL_FLAG_USE_SCSIADDRESS = 0x2, + // useScsiAddress could have a value of 2 if set. Use logical NOT to make boolean before comparing below + // + DSM_ASSERT(useMpioPathId == !useScsiAddress); + + for (inx = 0; inx < DSM_MAX_PATHS; inx++) { + + deviceInfo = groupEntry->DeviceList[inx]; + + if (deviceInfo) { + + failGroup = deviceInfo->FailGroup; + + if (failGroup) { + + if (useScsiAddress) { + + if (portNumber == deviceInfo->ScsiAddress->PortNumber && + pathId == deviceInfo->ScsiAddress->PathId && + targetId == deviceInfo->ScsiAddress->TargetId) { + + found = TRUE; + break; + } + } else { + + NT_ASSERT(useMpioPathId); + + if ((ULONGLONG)((ULONG_PTR)(failGroup->PathId)) == mpioPathId) { + + found = TRUE; + break; + } + } + } + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (found) { + + newPath = failGroup->PathId; + status = STATUS_SUCCESS; + + // + // This should not affect the next path chosen based on the + // current LB policy, so do NOT update groupEntry->PathToBeUsed + // + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Failed to get corresponding path.\n", + DsmList)); + } + + if (Status) { + + *Status = status; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Exiting function with path %p and status %x.\n", + DsmList, + newPath, + status)); + + return newPath; +} + + +BOOLEAN +DsmpShouldRetryTPGRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine determines if a Report/Set TargetPortGroup request (sent either + as a passThrough or as an IRP_MJ_SCSI) needs to be retried. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if sense information indicates a retry-able error, else FALSE. + +--*/ +{ + BOOLEAN retry = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): Entering function.\n", + SenseData)); + + // + // Two types of conditions need to be retried: + // 1. Asymmetric Access State Changed + // 2. Asymmetric Access State Transition + // + + // + // Check if asymmetric access state changed + // + retry = DsmpShouldRetryPassThroughRequest(SenseData, SenseDataSize); + if (!retry) { + + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + if (validSense) { + + if (senseKey == SCSI_SENSE_NOT_READY) { + + switch (addSenseCode) { + case SCSI_ADSENSE_LUN_NOT_READY: { + + // + // Check if asymmetric access state transitioning + // + if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION) { + + retry = TRUE; + } + break; + } + + case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: { + + if (addSenseCodeQualifier == SCSI_SENSEQ_REPORTED_LUNS_DATA_CHANGED) { + + retry = TRUE; + } + break; + } + + default: { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): AddSenseCode %x. Not retrying.\n", + SenseData, + addSenseCode)); + + retry = FALSE; + break; + } + } + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): Sense data size %d not big enough.\n", + SenseData, + SenseDataSize)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): Exiting function with retry %x.\n", + SenseData, + retry)); + + return retry; +} + + +BOOLEAN +DsmpIsDeviceRemoved( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine evaluate Sense Data and determine if LUN is available or not. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if device is no longer available, else FALSE. + +--*/ +{ + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + BOOLEAN bRemoved = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIsDeviceRemoved (SenseData %p): Entering function.\n", + SenseData)); + + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + + if (validSense) { + // + // SPC 3 6.25 suggests response should follow Test Unit Ready responses + // For now, we accept Ileegal Request as an indication of device not in available + // state. + // + if (senseKey == SCSI_SENSE_ILLEGAL_REQUEST) { + + ASSERT(addSenseCodeQualifier == 0); //LOGICAL UNIT NOT SUPPORTED + + bRemoved = TRUE; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpIsDeviceRemoved (SenseData %p): SenseKey %x AddSenseCode %x. Remove %x\n", + SenseData, + senseKey, + addSenseCode, + bRemoved)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIsDeviceRemoved (SenseData %p): Exiting function. Removed %x.\n", + SenseData, + bRemoved)); + + return bRemoved; +} + + +BOOLEAN +DsmpReservationCommand( + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb + ) +/*++ + +Routine Description: + + This routine examines the DeviceIoControlCode and Srb OpCode to determine + if this is PR request. + +Arguments: + + Irp - The Irp containing Srb. + Srb - The current non-read/write Srb. + +Return Value: + + TRUE - If it's a special-case command (some reservation-handling request). + +--*/ +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + UCHAR opCode = 0; + BOOLEAN isReservationCommand = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpReservationCommand (Irp %p): Entering function.\n", + Irp)); + + // + // Ensure it's a scsi request before checking the opcode. + // + if (irpStack->MajorFunction == IRP_MJ_SCSI) { + + PCDB cdb = SrbGetCdb(Srb); + if (cdb != NULL) { + opCode = cdb->AsByte[0]; + + if (opCode == SCSIOP_PERSISTENT_RESERVE_IN || opCode == SCSIOP_PERSISTENT_RESERVE_OUT) { + + // + // Set or release a reservation. + // + isReservationCommand = TRUE; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpReservationCommand (Irp %p): Exiting function - IsReservationCmd %x.\n", + Irp, + isReservationCommand)); + + return isReservationCommand; +} + + +BOOLEAN +DsmpMpioPassThroughPathCommand( + _In_ IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine examines the DeviceIoControlCode to determine whether this is + either a mpio pass through or a mpio pass through direct. If so, it needs + to be handled via a specific path indicated by the caller. + +Arguments: + + Irp - The Irp. + +Return Value: + + TRUE - If it is either MPTP or MPTPD. + FALSE - Otherwise. + +--*/ +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG ioctlCode; + BOOLEAN isMPTPCommand = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpMpioPassThroughPathCommand (Irp %p): Entering function.\n", + Irp)); + + if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + + // + // Check whether this is a MPTP, MPTPD, or an extended flavor. + // + ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + + if (ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH || + ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT || + ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_EX || + ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT_EX) { + + isMPTPCommand = TRUE; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpMpioPassThroughPathCommand (Irp %p): Exiting function - IsMpioPassThruPathCmd %!bool!.\n", + Irp, + isMPTPCommand)); + + return isMPTPCommand; +} + + +VOID +DsmpRequestComplete( + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID DsmContext + ) +/*++ + +Routine Description: + + This routine is called from mpio's completion routine when the Irp + has been completed by the port driver. Currently, it updates some counters + and free's the context back to the look-aside list. + +Arguments: + + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + DsmContext - DSM context given to MPIO during initialization + +Return Value: + + NONE + +--*/ + +{ + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_CONTEXT dsmContext = (PDSM_CONTEXT)DsmContext; + UCHAR opCode = 0xFF; + ULONG dataTransferLength = 0; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDSM_FAILOVER_GROUP failGroup = irpStack->Parameters.Others.Argument3; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRequestComplete (DevInfo %p): Entering function.\n", + DsmId)); + + DSM_ASSERT(DsmContext); + + if (Srb) { + PCDB cdb = SrbGetCdb(Srb); + if (cdb) { + opCode = cdb->AsByte[0]; + } + dataTransferLength = SrbGetDataTransferLength(Srb); + } + + // + // Extract the interesting bits from the context struct. + // + + if (failGroup) { + + if (DsmpDecrementCounters(failGroup, Srb)) { + + // + // If there are no requests on a path that is supposed to be removed, remove it now. + // + if (failGroup->State == DSM_FG_PENDING_REMOVE) { + + KIRQL oldIrql; + + NT_ASSERT(failGroup->Count == 0); + + oldIrql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + RemoveEntryList(&failGroup->ListEntry); + InterlockedDecrement((LONG volatile*)&dsmContext->NumberStaleFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRequestComplete (DevInfo %p): Removing FOGroup %p with path %p.\n", + DsmId, + failGroup, + failGroup->PathId)); + + DsmpFreePool(failGroup); + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), oldIrql); + } + } + } + + // + // Note: We use the deviceInfo passed in since the one saved off in the + // context may be stale in the case of a retried I/O + // + if (deviceInfo) { + + // + // If statistics gathering is enabled update the inflight request count + // for this device-path pairing. + // + if (!dsmContext->DisableStatsGathering) { + + // + // Indicate one less request on this device. + // Update the path that on which the increment was done. + // + if (InterlockedCompareExchange((LONG volatile*)&deviceInfo->NumberOfRequestsInProgress, 0, 0) > 0) { + InterlockedDecrement(&(deviceInfo->NumberOfRequestsInProgress)); + } + } + + // + // If statistics gathering is enabled, we are interested in read/write requests + // + if (!dsmContext->DisableStatsGathering) { + + // + // If it's a read or a write, update the stats. + // Use the path that was cached during dispatch. + // + if (DsmIsReadRequest(opCode)) { + + if (deviceInfo->DeviceStats.NumberReads <= MAXULONG) { + + InterlockedIncrement((LONG volatile*)&deviceInfo->DeviceStats.NumberReads); + } + + if ((MAXULONGLONG - dataTransferLength) > deviceInfo->DeviceStats.BytesRead) { + + deviceInfo->DeviceStats.BytesRead += dataTransferLength; + + } else { + + deviceInfo->DeviceStats.BytesRead = MAXULONGLONG; + } + + } else if (DsmIsWriteRequest(opCode)) { + + if (deviceInfo->DeviceStats.NumberWrites <= MAXULONG) { + + InterlockedIncrement((LONG volatile*)&deviceInfo->DeviceStats.NumberWrites); + } + + if ((MAXULONGLONG - dataTransferLength) > deviceInfo->DeviceStats.BytesWritten) { + + deviceInfo->DeviceStats.BytesWritten += dataTransferLength; + + } else { + + deviceInfo->DeviceStats.BytesWritten = MAXULONGLONG; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRequestComplete (DevInfo %p): Exiting function.\n", + DsmId)); + + return; +} + + +NTSTATUS +DsmpRegisterPersistentReservationKeys( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN Register + ) +/*++ + +Routine Description: + + This routine is used to build and send down the request to register + or unregister the persistent reservation keys to the device down + the path given by DeviceInfo. + +Arguments: + + DeviceInfo - Device-path pair to use for sending down the request + Register - Flag to indicate whether to register or unregister the keys. + +Return Value: + + STATUS_SUCCESS on success, else appropriate failure code. + +--*/ +{ + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PCDB cdb; + PPRO_PARAMETER_LIST parameters; + IO_STATUS_BLOCK ioStatus; + NTSTATUS status = STATUS_SUCCESS; + ULONG length; + PDSM_DEVICE_INFO deviceInfo = DeviceInfo; + PDSM_GROUP_ENTRY group; + ULONGLONG saKey; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Entering function - Register = %x.\n", + deviceInfo, + Register)); + + group = DeviceInfo->Group; + + NT_ASSERT(group && group->PRKeyValid); + + if (DeviceInfo->State >= DSM_DEV_FAILED) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Unusable - state %d.\n", + deviceInfo, + deviceInfo->State)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpRegisterPersistentReservationKeys; + } + + // + // Build a pass through command to process Persistent Reserve Out + // for registering the device. + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Failed to allocate memory for persistent reserve.\n", + deviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegisterPersistentReservationKeys; + } + + REVERSE_BYTES_QUAD(&saKey, &group->PersistentReservationRegisteredKey); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Attempting PR-Out SA %u, Type %u, Scope %u, PR-Key %I64x.\n", + deviceInfo, + group->PRServiceAction, + group->PRType, + group->PRScope, + saKey)); + +__RetryRequest: + + // + // Build the cdb to reserve the device (Logical Unit). The type of reservation + // scope and service action is whatever cluster service provided at the time of + // sending down registration to this device before this particular path was available. + // + cdb = (PCDB) passThrough->ScsiPassThrough.Cdb; + cdb->PERSISTENT_RESERVE_OUT.OperationCode = SCSIOP_PERSISTENT_RESERVE_OUT; + cdb->PERSISTENT_RESERVE_OUT.ServiceAction = group->PRServiceAction; + cdb->PERSISTENT_RESERVE_OUT.Scope = group->PRScope; + cdb->PERSISTENT_RESERVE_OUT.Type = group->PRType; + cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = sizeof(PRO_PARAMETER_LIST); + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 10; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 0; + passThrough->ScsiPassThrough.DataTransferLength = sizeof(PRO_PARAMETER_LIST); + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + parameters = (PPRO_PARAMETER_LIST)(passThrough->DataBuffer); + + // + // Copy the persistent reservation key given by cluster service to + // Service Action Reservation Key. This key will be registered + // with the device. + // + // Set ServiceActionReservationKey to the well-known key if we are registering. + // Note that to unregister ServiceActionReservationKey needs to be set to 0. + // + if (Register) { + + RtlCopyMemory(parameters->ServiceActionReservationKey, group->PersistentReservationRegisteredKey, 8); + + } else { + + RtlCopyMemory(parameters->ReservationKey, group->PersistentReservationRegisteredKey, 8); + RtlZeroMemory(parameters->ServiceActionReservationKey, 8); + } + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + DeviceInfo->TargetObject, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(ioStatus.Status))) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Persistent Reserve (Register Key) succeeded using %p.\n", + deviceInfo, + DeviceInfo)); + + } else { + + PUCHAR senseData; + UCHAR senseInfoLength; + + senseData = (PUCHAR)(passThrough->SenseInfoBuffer); + senseInfoLength = passThrough->ScsiPassThrough.SenseInfoLength; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): DevInfo %p, Register keys (%d): NTStatus %x, ScsiStatus %x.\n", + deviceInfo, + DeviceInfo, + Register, + ioStatus.Status, + passThrough->ScsiPassThrough.ScsiStatus)); + + if (DsmpShouldRetryPassThroughRequest((PVOID)senseData, senseInfoLength)) { + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + RtlZeroMemory(passThrough, length); + + goto __RetryRequest; + + } else if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Will change success to error status for register\n", + deviceInfo)); + + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + // + // Free the passthrough + data buffer. + // + DsmpFreePool(passThrough); + +__Exit_DsmpRegisterPersistentReservationKeys: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + + +BOOLEAN +DsmpShouldRetryPassThroughRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine determines if a passthrough request needs to be retried based on the + information in the passed in sense data. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if sense information indicates a retry-able error, else FALSE. + +--*/ +{ + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + BOOLEAN retry = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Entering function.\n", + SenseData)); + +#if DBG + if (SenseDataSize > 0) { + + ULONG inx; + PUCHAR senseInfo; + + + senseInfo = (PUCHAR) SenseData; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Sense info length %d. Sense Info : ", + SenseData, + SenseDataSize)); + + for (inx = 0; inx < SenseDataSize; inx++) { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "%x ", + senseInfo[inx])); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "\n")); + } +#endif + + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + NULL); + if (validSense) { + if (senseKey == SCSI_SENSE_UNIT_ATTENTION) { + + switch (addSenseCode) { + case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: + case SCSI_ADSENSE_BUS_RESET: + case SCSI_ADSENSE_PARAMETERS_CHANGED: { + retry = TRUE; + break; + } + + default: { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): AddSenseCode %x. Not retrying.\n", + SenseData, + addSenseCode)); + + retry = FALSE; + break; + } + } + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Sense data size %d not big enough.\n", + SenseData, + SenseDataSize)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Exiting function with retry %x.\n", + SenseData, + retry)); + + return retry; +} + + +BOOLEAN +DsmpShouldRetryPersistentReserveCommand( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine determines if a a PR request needs to be retried based on the + information in the passed in sense data. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if sense information indicates a retry-able error, else FALSE. + +--*/ +{ + BOOLEAN retry = FALSE; + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Entering function.\n", + SenseData)); + + retry = DsmpShouldRetryPassThroughRequest(SenseData, SenseDataSize); + + if (!retry) { + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + if (validSense) { + + // + // If the TPG is in transitioning state, retry the request + // + if ((senseKey == SCSI_SENSE_UNIT_ATTENTION || senseKey == SCSI_SENSE_NOT_READY) && + (addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && + addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION)) { + + retry = TRUE; + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Sense data size %d not big enough.\n", + SenseData, + SenseDataSize)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Exiting function with retry %x.\n", + SenseData, + retry)); + + return retry; +} + + +VOID +DsmpAllowStandbyPathsToRest( + _In_ PDSM_GROUP_ENTRY Group + ) +/*++ + +Routine Description: + + This routine is called when a new path is available for a device + and has a desired state of ACTIVE_O. Since this will be an ACTIVE_O + path we see if there are any paths with a desired state of Standby + but that are currently active. These paths can be safely moved by + to standby. + + This routine assumes that the lock is held + +Arguements: + + Group is the multipath group + +Return Value: + + None +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAllowStandbyPathsToRest (Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + if ((existingDeviceInfo->DesiredState == DSM_DEV_STANDBY) && + (existingDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED)) { + + existingDeviceInfo->State = DSM_DEV_STANDBY; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpAllowStandbyPathsToRest (Group %p): DevInfo %p changed to state %d at %d\n", + Group, + existingDeviceInfo, + existingDeviceInfo->State, + __LINE__)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAllowStandbyPathsToRest (Group %p): Exiting function.\n", + Group)); + return; +} + + +PDSM_DEVICE_INFO +DsmpGetAnyActivePath( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Exception, + _In_opt_ PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will return an active path from the list + + This routine assumes that the DSM lock is held + +Arguements: + + Group is the multipath group + Exception - if TRUE, indicates that the returned devInfo must not be the same + as the one passed in. + DeviceInfo - the must-not-match devInfo. Valid parameter only if Exception is + TRUE. + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + active path or NULL + +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + ULONG inx; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAnyActivePath (Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < DSM_MAX_PATHS; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + if (existingDeviceInfo && + existingDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + + if (Exception && existingDeviceInfo == DeviceInfo) { + continue; + } + + candidateDevInfo = existingDeviceInfo; + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAnyActivePath (Group %p): Exiting function with DevInfo %p\n", + Group, + candidateDevInfo)); + + return candidateDevInfo; +} + + +PDSM_DEVICE_INFO +DsmpGetActivePathToBeUsed( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Symmetric, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will return an active path from the list that should + be the next one used by the DSM + + This routine assumes that the DSM lock is held + +Arguements: + + Group is the multipath group + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + active path or NULL + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetActivePathToBeUsed (Group %p): Entering function.\n", + Group)); + + deviceInfo = NULL; + + switch (Group->LoadBalanceType) { + + case DSM_LB_LEAST_BLOCKS: + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { + + // + // Since we choose the path with the smallest queue or cumulative size in + // DsmpGetPath, we just pick any path now + // + + // fall through + } + + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: + case DSM_LB_ROUND_ROBIN: { + + // + // For RR and RRS we just pick any active path to start with + // and the DsmpGetPath will do the round robining + // + } + + case DSM_LB_FAILOVER: { + + deviceInfo = DsmpGetAnyActivePath(Group, FALSE, NULL, SpecialHandlingFlag); + + break; + } + + case DSM_LB_WEIGHTED_PATHS: { + + PDSM_DEVICE_INFO workDeviceInfo; + ULONG weight = (ULONG) -1; + ULONG inx; + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + workDeviceInfo = Group->DeviceList[inx]; + + if ((workDeviceInfo) && + (DsmpIsDeviceInitialized(workDeviceInfo)) && + (DsmpIsDeviceUsable(workDeviceInfo)) && + (DsmpIsDeviceUsablePR(workDeviceInfo)) && + (workDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) && + (workDeviceInfo->PathWeight < weight)) { + + // + // We found a path that is active and is at + // the lowest weight. Remember it. + // + weight = workDeviceInfo->PathWeight; + + deviceInfo = workDeviceInfo; + } + } + + break; + } + + default: { + + break; + } + } + + if (!deviceInfo && !Symmetric) { + + // + // In the case of implicit transitions, it is possible that a TPG hasn't yet + // been made A/O. So instead of not setting any path, fall back to using some + // other path. IO sent down this path may fail, but will be retried in + // InterpretError(). Hopefully by then, at least one TPG will have transitioned + // to A/O state. + // + // The same argument holds true if the storage supports both implicit and + // explicit transitions, since it is possible that after we explicitly changed + // the TPG states, an implicit transition left us with no path in A/O state. + // + // In the case of explicit only transitions, we tried making at least one path + // as A/O and failed. This can happen, for example, when STPG fails because + // this initiator is not registered or does not hold exclusive reservation over + // the target. Instead of not using any path, we can consider a path in A/U state, + // A/U being just a functional path state. + // + BOOLEAN sendTPG = FALSE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(Group, &sendTPG, SpecialHandlingFlag); + + if ((deviceInfo != NULL) && + ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_EXPLICIT) || + ((deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT) && + (deviceInfo->State <= DSM_DEV_ACTIVE_UNOPTIMIZED)))) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetActivePathToBeUsed (Group %p): Using best alternative candidate device %p\n", + Group, + deviceInfo)); + } else { + + deviceInfo = NULL; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetActivePathToBeUsed (Group %p): No active/alternative path available for group\n", + Group)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetActivePathToBeUsed (Group %p): Exiting function with devInfo %p.\n", + Group, + deviceInfo)); + + return deviceInfo; +} + + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivate( + _In_ PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will find another path in the group that is active + + This routine assumes that the DSM lock is held + + This is used by devices that support symmetric LUA. + +Arguements: + + Group is the multipath group + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Standby path or NULL if no standby path is available + +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivate(Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + if (existingDeviceInfo && + existingDeviceInfo->State == DSM_DEV_STANDBY && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + // + // If we don't as yet have a candidate, pick the first available one. + // However, our preference is one that is through the preferred TPG. + // + if (!candidateDevInfo || + existingDeviceInfo->TargetPortGroup && existingDeviceInfo->TargetPortGroup->Preferred) { + + candidateDevInfo = existingDeviceInfo; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivate (Group %p): Exiting function with devInfo %p.\n", + Group, + candidateDevInfo)); + + return candidateDevInfo; +} + + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivateALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PBOOLEAN SendTPG, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will find another path in the group that is active + + This is used by devices that don't support symmetric LUA. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguements: + + Group is the multipath group + SendTPG - output parameter that indicates if TPG command need to be sent down. + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Standby path or NULL if no standby path is available + +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivateALUA (Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + // + // The candidate for making A/O obviously mustn't be in a failed state + // and should have a path assigned. + // + if (existingDeviceInfo && + !DsmpIsDeviceFailedState(existingDeviceInfo->State) && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + // + // If we don't have any candidate currently, choose the very first + // one that is in a non-failure state, regardless of what state it + // may be in. + // + if (!candidateDevInfo) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = TRUE; + } + + // + // Might as well use one that the Admin desires for to be in A/O + // + if (existingDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // However, such a devInfo is not a better candidate if our candidate + // devInfo is also one that the Admin desires be in A/O, and it is + // through a preferred TPG. + // + if (!(existingDeviceInfo->DesiredState == candidateDevInfo->DesiredState && + candidateDevInfo->TargetPortGroup->Preferred)) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = TRUE; + } + } + + // + // Check if the current one is at least better than the candidate. + // + if (DsmpIsBetterDeviceState(candidateDevInfo->State, existingDeviceInfo->State)) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = TRUE; + } + + // + // We found one that we may have just masked as non-A/O. This is the + // best option as we don't have to send down an STPG. + // + if (existingDeviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = FALSE; + break; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivateALUA (Group %p): Exiting function with devInfo %p.\n", + Group, + candidateDevInfo)); + + return candidateDevInfo; +} + + +PDSM_DEVICE_INFO +DsmpFindStandbyPathInAlternateTpgALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will find another path in the group that is not in the same + TPG as the passed in DeviceInfo. + + This routine assumes that the DSM lock is held + + This is used by devices that support ALUA. + +Arguements: + + Group is the multipath group + DeviceInfo is the devInfo whose TPG must not be matched + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Standby path not in same TPG as passed in DeviceInfo + or NULL if no standby path is available + +--*/ +{ + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = DeviceInfo->TargetPortGroup; + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathInAlternateTpgALUA (DevInfo %p): Entering function.\n", + DeviceInfo)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + // + // We only care about deviceInfo if TPG is different + // + if (existingDeviceInfo && existingDeviceInfo->TargetPortGroup != targetPortGroup) { + + // + // The candidate for making A/O obviously mustn't be in a failed state + // and must be initialized + // + if (!DsmpIsDeviceFailedState(existingDeviceInfo->State) && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + // + // If we don't have any candidate currently, choose the very first + // one that is in a non-failure state, regardless of what state it + // may be in. + // + if (!candidateDevInfo) { + + candidateDevInfo = existingDeviceInfo; + continue; + } + + // + // Check if the current one is at least better than the candidate. + // + if (DsmpIsBetterDeviceState(candidateDevInfo->State, existingDeviceInfo->State)) { + + candidateDevInfo = existingDeviceInfo; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathInAlternateTpgALUA (DevInfo %p): Exiting function with devInfo %p.\n", + DeviceInfo, + candidateDevInfo)); + + return candidateDevInfo; +} + + +NTSTATUS +DsmpSetLBForDsmPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) + +/*++ + +Routine Description: + + This routine is called when a change is made to the DSM-wide default + load balance policy. It goes through each LUN representation (ie. Group + entry) and updates the appropriate ones (ie. ones for which the policy + was not chosen based on VID/PID or because of an explicit settings on + the LUN). It also then updates the path states in accordance with the + new LB policy. + +Arguements: + + DsmContext is the DSM context + LoadBalanceType is the new load balance policy to be applied + PreferredPath is the preferred failback path to be used (applicable only + if LB policy is Failover) + +Return Value: + + Success + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIrql; + PLIST_ENTRY entry; + ULONG groupIndex = 0; + ULONG devInfoIndex; + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO devInfo; + DSM_LOAD_BALANCE_TYPE newLoadBalancePolicy; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForDsmPolicyAdjustment (DsmContext %p): Entering function.\n", + DsmContext)); + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + for (entry = DsmContext->GroupList.Flink; entry != &(DsmContext->GroupList); entry = entry->Flink, groupIndex++) { + + group = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); + + newLoadBalancePolicy = LoadBalanceType; + + // + // Only LUNs that don't have their policy explicitly set + // and ones that don't have it set based on VID/PID are + // of interest to us here. + // + if (group->LBPolicySelection == DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY || + group->LBPolicySelection == DSM_DEFAULT_LB_POLICY_DSM_WIDE) { + + // + // Also, if the caller is trying to clear the DSM-wide + // default policy, then we don't even care about those + // LUNs whose policies were not set using this value. + // + if (newLoadBalancePolicy < DSM_LB_FAILOVER && + group->LBPolicySelection != DSM_DEFAULT_LB_POLICY_DSM_WIDE) { + + continue; + } + + // + // If the DSM-wide setting is being cleared, we need to fall back + // to using the default based on the array's ALUA capabilities. + // + if (newLoadBalancePolicy < DSM_LB_FAILOVER) { + + newLoadBalancePolicy = DSM_LB_ROUND_ROBIN; + group->PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + + + } else { + + // + // Since a new policy has been selected for the DSM-wide + // one, it needs to be applied to this LUN. + // + group->PreferredPath = (ULONGLONG)((ULONG_PTR)PreferredPath); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + } + + // + // If Round Robin is set and ALUA is enabled, we need to change the + // policy to Round Robin with Subset. + // + if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && newLoadBalancePolicy == DSM_LB_ROUND_ROBIN) { + + newLoadBalancePolicy = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + // + // Finally set the new load balance policy. + // + group->LoadBalanceType = newLoadBalancePolicy; + + // + // Path states need to be updated in accordance with the new policy. + // + for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { + + devInfo = group->DeviceList[devInfoIndex]; + DsmpSetNewDefaultLBPolicy(DsmContext, devInfo, group->LoadBalanceType, SpecialHandlingFlag); + } + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForDsmPolicyAdjustment (DsmContext %p): Exiting function with status %x\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForVidPidPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) + +/*++ + +Routine Description: + + This routine is called when a change is made to the default load balance + policy for a VID/PID. It goes through each LUN representation (ie. Group + entry) and updates the appropriate ones (ie. ones for which the policy + was not because of an explicit settings on the LUN). It also then updates + the path states in accordance with the new LB policy. + +Arguements: + + DsmContext is the DSM context + TargetHardwareId is the VID/PID whose matching LUNs policy need to be updated + LoadBalanceType is the new load balance policy to be applied + PreferredPath is the preferred failback path to be used (applicable only + if LB policy is Failover) + +Return Value: + + Success + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIrql; + PLIST_ENTRY entry; + ULONG groupIndex = 0; + ULONG devInfoIndex; + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO devInfo; + DSM_LOAD_BALANCE_TYPE dsmLoadBalanceType; + ULONGLONG dsmPreferredPath; + BOOLEAN useDsmLBSettings = FALSE; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): Entering function.\n", + TargetHardwareId)); + + status = DsmpQueryDsmLBPolicyFromRegistry(&dsmLoadBalanceType, &dsmPreferredPath); + + if (NT_SUCCESS(status)) { + + useDsmLBSettings = TRUE; + + } else { + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + status = STATUS_SUCCESS; + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): MSDSM-wide default LB policy not set.\n", + TargetHardwareId)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): Failed to query MSDMS-wide default LB setting. Status %x.\n", + TargetHardwareId, + status)); + } + } + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + for (entry = DsmContext->GroupList.Flink; entry != &(DsmContext->GroupList); entry = entry->Flink, groupIndex++) { + + group = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); + + // + // Only LUNs that don't have their policy explicitly set + // are of interest to us here. + // + if (group->LBPolicySelection < DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT) { + + // + // Figure out if this LUN matches the VID/PID of interest + // Device not of interest if it doesn't match the passed in target ID + // + if (wcscmp(group->HardwareId, TargetHardwareId) != 0) { + + continue; + } + + // + // Also, if the caller is trying to clear the VID/PID + // default policy, then we don't even care about those + // LUNs whose policies were not set using this value. + // + if (LoadBalanceType < DSM_LB_FAILOVER && + group->LBPolicySelection != DSM_DEFAULT_LB_POLICY_VID_PID) { + + continue; + } + + // + // If the VID/PID setting is being cleared, we need to fall back + // to using the DSM-wide default policy if it has been set, else + // we need to use the default based on the array's ALUA capabilities. + // + if (LoadBalanceType < DSM_LB_FAILOVER) { + + if (useDsmLBSettings) { + + // + // Even if the MSDSM-wide policy is specified as RR, if the storage + // is ALUA, we can't have the policy as RR, so we'll change it to + // RRWS instead. + // + if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && dsmLoadBalanceType == DSM_LB_ROUND_ROBIN) { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + + } else { + + group->LoadBalanceType = dsmLoadBalanceType; + } + + group->PreferredPath = (ULONGLONG)((ULONG_PTR)dsmPreferredPath); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + + } else { + + // + // Default LB type: + // is Round Robin if ALUA is not supported, or if ALUA support is implicit but access is symmetric, + // else Round Robin With Subset (since in ALUA, all paths aren't in A/O). + // + if (DsmpIsSymmetricAccess(group->DeviceList[0])) { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN; + + } else { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + + group->PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + } + + } else { + + // + // Since a new policy has been selected for the DSM-wide + // one, it needs to be applied to this LUN. + // + // However, if the VID/PID policy is specified as RR but the storage + // is ALUA, we can't have the policy as RR, so we'll change it to + // RRWS instead. + // + if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && LoadBalanceType == DSM_LB_ROUND_ROBIN) { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + + } else { + + group->LoadBalanceType = LoadBalanceType; + } + + group->PreferredPath = (ULONGLONG)((ULONG_PTR)PreferredPath); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; + } + + // + // Path states need to be updated in accordance with the new policy. + // + for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { + + devInfo = group->DeviceList[devInfoIndex]; + DsmpSetNewDefaultLBPolicy(DsmContext, devInfo, group->LoadBalanceType, SpecialHandlingFlag); + } + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): Exiting function with status %x\n", + TargetHardwareId, + status)); + + return status; +} + + +NTSTATUS +DsmpSetNewDefaultLBPolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called to adjust the path state of an instance of a + LUN for which a new default load balance policy was applied + following an admin request for such a change. + + This routine must be called with spinlock held. + +Arguements: + + DsmContext is the DSM context + DeviceInfo is the device info on which the new path state needs to be set + LoadBalanceType is the load balance policy in accordance with which the path state needs to be adjusted + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetNewDefaultLBPolicy (DevInfo %p): Entering function\n", + DeviceInfo)); + + if (!DeviceInfo) { + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpSetNewDefaultLBPolicy; + } + + if (!(DsmpIsDeviceInitialized(DeviceInfo) && DsmpIsDeviceUsable(DeviceInfo) && DsmpIsDeviceUsablePR(DeviceInfo)) || + DsmpIsDeviceFailedState(DeviceInfo->State)) { + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpSetNewDefaultLBPolicy; + } + + + group = DeviceInfo->Group; + + if (!DsmpIsSymmetricAccess(DeviceInfo)) { + + DsmpAdjustDeviceStatesALUA(group, NULL, SpecialHandlingFlag); + + } else { + + switch (LoadBalanceType) { + + // + // For failover, it is important the right path is + // chosen, ie. preferred path needs to be taken into + // consideration. + // + case DSM_LB_FAILOVER: { + + DsmpSetLBForPathArrival(DsmContext, DeviceInfo, SpecialHandlingFlag); + + break; + } + + // + // For all other policies, the state must be A/O. + // + default: { + + DeviceInfo->PreviousState = DeviceInfo->State; + DeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + break; + } + } + } + +__Exit_DsmpSetNewDefaultLBPolicy: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetNewDefaultLBPolicy (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathArrival( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a new path arrives for a multipath + group that doesn't support ALUA. The routine will set the path + to the appropriate state and fix up the other paths state if + they need to change. + + This is used by devices NOT supporting ALUA. + + This routine must be called with spinlock held. + +Arguements: + + DsmContext is the DSM context + NewDeviceInfo is the device info for the newly arrived path + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Entering function.\n", + NewDeviceInfo)); + + group = NewDeviceInfo->Group; + + if (!(DsmpIsDeviceInitialized(NewDeviceInfo) && DsmpIsDeviceUsable(NewDeviceInfo) && DsmpIsDeviceUsablePR(NewDeviceInfo))) { + + // + // Bad device instance. Nothing can be done about it. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_UNDETERMINED; + + NT_ASSERT(NewDeviceInfo->FailGroup == NULL); + + goto __Exit_DsmpSetLBForPathArrival; + } + + + if (group->NumberDevices == 1) { + + // + // if this is the only device for the group then we will always + // be active as every group must have at least one active path + // + if (NewDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // All's good + // + goto __Exit_DsmpSetLBForPathArrival; + + } else { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): State changed to %d at %d\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__)); + + goto __Exit_DsmpSetLBForPathArrival; + } + + switch(group->LoadBalanceType) { + case DSM_LB_FAILOVER: { + + // + // Get the current active path. + // + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + + // + // If the newly arriving path is the preferred path, this now should + // become our active path. + // + if (group->PreferredPath == ((ULONGLONG)((ULONG_PTR)(NewDeviceInfo->FailGroup->PathId)))) { + + // + // If current active path is not the preferred path, change its + // path state to standby. + // + if (deviceInfo && deviceInfo != NewDeviceInfo) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_STANDBY; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (Group %p): Preferred path back online. DevInfo %p changed to state %d\n", + group, + deviceInfo, + deviceInfo->State)); + } + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + // + // In the case of failover, we must have only a single + // active path. If this newly added path is configured as + // the one that is desired to be active then we make this + // active and set the standby path that was active back to + // standby unless the preferred path is the currently active + // path. If the newly added path is supposed to be + // standby then we leave it as standby. + // + if (NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { + + if (deviceInfo) { + + // + // If the preferred path is currently active, don't change + // it regardless of this path wanting to be in active state. + // + if (group->PreferredPath == ((ULONGLONG)((ULONG_PTR)(deviceInfo->FailGroup->PathId)))) { + + if (NewDeviceInfo != deviceInfo) { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_STANDBY; + } + + } else { + + // + // Since the preferred path is not active, make this + // path active since it wants to be so. This means + // changing the current active path to standby. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_STANDBY; + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + } else { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + } else { + + if (deviceInfo) { + + if (deviceInfo != NewDeviceInfo) { + + // + // This newly arrived device doesn't want to be in + // A/O, and we already have an active path, so make + // it standby. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_STANDBY; + } + } else { + + // + // Since we currently don't have an active path, this one + // needs to be made active, regardless of its path it wishes + // to be in. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + } + } + + break; + } + + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + // + // In RRWS, a set of paths can be active and another set of + // paths can be standby. We set the new path to the desired + // state unless the desired state is standby, but there are + // no active paths. Also if the desired state is Active we + // need to check if there are any existing paths that are + // also active but have a desired state of standby. For + // those we can move them back to standby. + // + if (NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // We are the active path coming back. Find out who has + // been the active one and place him back to standby + // + DsmpAllowStandbyPathsToRest(group); + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Changed to state %d at %d\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__)); + + } else { + + // + // if there are no paths already active then we've got + // to make this one AO, otherwise we can be non-AO + // + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + + if (!deviceInfo || deviceInfo == NewDeviceInfo) { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + NewDeviceInfo->State = DSM_DEV_STANDBY; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (%p): Changed to state %d at %d. Status %x\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__, + status)); + } + + status = STATUS_SUCCESS; + + break; + } + + case DSM_LB_LEAST_BLOCKS: + case DSM_LB_ROUND_ROBIN: + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: + case DSM_LB_WEIGHTED_PATHS: { + + // + // In RR, LWP, LB and LQD all paths are active so the new device + // becomes AO or AU. + // + if (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Changed to state %d at %d. Status %x\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__, + status)); + + status = STATUS_SUCCESS; + + break; + } + + default: { + status = STATUS_INVALID_PARAMETER; + break; + } + } + +__Exit_DsmpSetLBForPathArrival: + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(NewDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + NewDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): No FOG available for group %p\n", + NewDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Exiting function with status %x\n", + NewDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathArrivalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a new path arrives for a multipath + group. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Spin lock must NOT be held by caller + +Arguements: + + DsmContext is the DSM context + NewDeviceInfo is the device info for the newly arrived path + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings; + BOOLEAN lockHeld = FALSE; + PDSM_DEVICE_INFO preferredActiveDeviceInfo = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Entering function.\n", + NewDeviceInfo)); + + group = NewDeviceInfo->Group; + + if (!(DsmpIsDeviceInitialized(NewDeviceInfo) && DsmpIsDeviceUsable(NewDeviceInfo) && DsmpIsDeviceUsablePR(NewDeviceInfo))) { + + // + // Bad device instance. Nothing can be done about it. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_UNDETERMINED; + + NT_ASSERT(NewDeviceInfo->FailGroup == NULL); + + goto __Exit_DsmpSetLBForPathArrivalALUA; + } + + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + + if (group->NumberDevices == 1) { + + // + // If this is the only device for the group then it should be the + // active instance as every group must have at least one active path. + // + if (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + lockHeld = FALSE; + + if (NewDeviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + // + // If the device supports explicit transitions, set its state to A/O + // + status = DsmpSetDeviceALUAState(DsmContext, NewDeviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); + + } else { + + // + // Since the device supports only implicit transitions, send down + // RTPG and hope that the controller has made this one the A/O path. + // + status = DsmpGetDeviceALUAState(DsmContext, NewDeviceInfo, NULL); + + if (NT_SUCCESS(status)) { + + // + // Remember that at this point it is possible that this path + // is still non-A/O. We'll need to handle this in DsmGetPath + // as a special case where we don't find an A/O path but the + // storage supports implicit-only transitions. At that time, + // we mustn't blindly return a NULL path back. + // + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): RTPG returned state = %x, ALUA state = %x.\n", + NewDeviceInfo, + NewDeviceInfo->State, + NewDeviceInfo->ALUAState)); + } + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): State changed to %d at %d\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__)); + + } else { + + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + + // + // Irrespecitve of the policy, we must have at least one A/O path. + // + // For RRWS and FOO, this path is of interest if it is desired to be in A/O. + // + // Also, for failover-only, this new path is of interest if it is + // the preferred path. + // + // This path is also of interest if it is not A/O and desired state has not + // explicitly been set to non-A/O and it has been exposed through the + // preferred TPG. + // + if ((!deviceInfo) || + ((NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) && + (group->LoadBalanceType == DSM_LB_FAILOVER || + group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET)) || + (group->PreferredPath == (ULONGLONG)((ULONG_PTR)(NewDeviceInfo->FailGroup->PathId)) && + group->LoadBalanceType == DSM_LB_FAILOVER) || + (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED && + NewDeviceInfo->DesiredState == DSM_DEV_UNDETERMINED && + NewDeviceInfo->TargetPortGroup->Preferred)) { + + // + // Since this path is supposed to be active, we make it + // active and then allow any paths that are supposed to + // be standby go back to being standby + // + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + lockHeld = FALSE; + + // + // If explicit ALUA is supported, we need to send down STPG to make the change. + // + if (NewDeviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + status = DsmpSetDeviceALUAState(DsmContext, NewDeviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); + + } else { + + // + // If implicit ALUA, the controller may have made some + // changes to the TPG states. We just need to query it. + // We'll try and honor the Admin's request but can't + // guarantee it. + // + status = DsmpGetDeviceALUAState(DsmContext, NewDeviceInfo, NULL); + } + + // + // We prefer this newly arrived devInfo to be A/O + // + preferredActiveDeviceInfo = NewDeviceInfo; + } + } + + if (!lockHeld) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + } + + if (NT_SUCCESS(status)) { + + DsmpAdjustDeviceStatesALUA(group, preferredActiveDeviceInfo, SpecialHandlingFlag); + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Trying to query ALUA state failed with %x\n", + NewDeviceInfo, + status)); + } + + status = STATUS_SUCCESS; + +__Exit_DsmpSetLBForPathArrivalALUA: + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(NewDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + NewDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): No active/alternative path available for group %p\n", + NewDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + if (lockHeld) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Exiting function with status %x\n", + NewDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathRemoval( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a path is removed from a multipath + group. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Note: This is used by devices NOT supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + RemovedDeviceInfo is the device info for failing/going-away path + + Group is an optional group override. That is, if Group is not NULL, this + function will run the load balance policy on the given Group and not + the Group from the RemovedDeviceInfo. This should only be used when + it's impossible to get a pointer to the RemovedDeviceInfo. + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p, Group %p): Entering function.\n", + RemovedDeviceInfo, + Group)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + if (Group == NULL) { + + if (!(DsmpIsDeviceFailedState(RemovedDeviceInfo->State))) { + + RemovedDeviceInfo->LastKnownGoodState = RemovedDeviceInfo->State; + } + + RemovedDeviceInfo->PreviousState = RemovedDeviceInfo->State; + RemovedDeviceInfo->State = DSM_DEV_FAILED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): changed to state %d at %d\n", + RemovedDeviceInfo, + RemovedDeviceInfo->State, + __LINE__)); + + group = RemovedDeviceInfo->Group; + + } else { + // + // The caller has chosen to override the RemovedDeviceInfo->Group. + // + group = Group; + } + + switch(group->LoadBalanceType) { + case DSM_LB_FAILOVER: + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + // + // In the case of failover, we must have only a single + // active path. If the removed path was the active path we + // need to find another path to become active + // + // In RRWS, a set of paths can be active and another set of + // paths can be standby. If the removed path is an active + // path then we need to make sure there is another active + // path. If there is already another active path then there + // is nothing to do. If not then a path needs to be made + // active. + // + if (!DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag)) { + + deviceInfo = DsmpFindStandbyPathToActivate(group, SpecialHandlingFlag); + if (deviceInfo) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): DevInfo %p changed to state %d at %d\n", + RemovedDeviceInfo, + deviceInfo, + deviceInfo->State, + __LINE__)); + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): LB Policy FO/RRWS and other paths active, no path made active at %d\n", + RemovedDeviceInfo, + __LINE__)); + } + + break; + } + + case DSM_LB_LEAST_BLOCKS: + case DSM_LB_ROUND_ROBIN: + case DSM_LB_WEIGHTED_PATHS: + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { + + // + // In RR, LQD, LB and LWP, all paths are active so we don't + // need to worry about activating a new path + // + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): LB Policy RR, LWP or LQD, no path made active at %d\n", + RemovedDeviceInfo, + __LINE__)); + break; + } + + default: { + status = STATUS_INVALID_PARAMETER; + break; + } + } + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(RemovedDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): Removal: Updating PathToBeUsed in %p to %p\n", + RemovedDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): After remove No FOG available for group %p\n", + RemovedDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): Exiting function with status %x\n", + RemovedDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathRemovalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a path is removed from a multipath + group. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Note: This should NOT be called with DsmContext Lock held + This is used for devices supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + RemovedDeviceInfo is the device info for the failing/going-away path + + Group is an optional group override. That is, if Group is not NULL, this + function will run the load balance policy on the given Group and not + the Group from the RemovedDeviceInfo. This should only be used when + it's impossible to get a pointer to the RemovedDeviceInfo. + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + BOOLEAN lockHeld = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p, Group %p): Entering function.\n", + RemovedDeviceInfo, + Group)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + + if (Group == NULL) { + + if (!(DsmpIsDeviceFailedState(RemovedDeviceInfo->State))) { + + RemovedDeviceInfo->LastKnownGoodState = RemovedDeviceInfo->State; + } + + RemovedDeviceInfo->PreviousState = RemovedDeviceInfo->State; + RemovedDeviceInfo->State = DSM_DEV_FAILED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): changed to state %d at %d\n", + RemovedDeviceInfo, + RemovedDeviceInfo->State, + __LINE__)); + + group = RemovedDeviceInfo->Group; + + } else { + // + // The caller has chosen to override the RemovedDeviceInfo->Group. + // + group = Group; + } + + if (group->LoadBalanceType < DSM_LB_FAILOVER || + group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { + + status = STATUS_INVALID_PARAMETER; + + } else { + + // + // In the case of failover, we must have only a single + // active path. If the removed path was the active path we + // need to find another path to become active + // + // In rest of policies, set of paths can be active and another set of + // paths can be standby. If the removed path is an active + // path then we need to make sure there is another active + // path. If there is already another active path then there + // is nothing to do. If not then a path needs to be made + // active. + // + if (!DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag)) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + if (deviceInfo) { + + if (sendTPG) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + lockHeld = FALSE; + + // + // If explicit transition supported, we need to send down STPG to make the change. + // + if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + status = DsmpSetDeviceALUAState(DsmContext, deviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); + + } else { + + // + // If implicit ALUA, the controller may have made necessary + // changes to the TPG states. We just need to query it. + // + status = DsmpGetDeviceALUAState(DsmContext, deviceInfo, NULL); + } + + if (!lockHeld) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + } + + if (NT_SUCCESS(status)) { + + DsmpAdjustDeviceStatesALUA(group, deviceInfo, SpecialHandlingFlag); + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Trying to query for ALUA state failed with status %x\n", + RemovedDeviceInfo, + status)); + } + } else { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Device %p changed to state %d at %d\n", + RemovedDeviceInfo, + deviceInfo, + deviceInfo->State, + __LINE__)); + } + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Other paths active, no path made active at %d\n", + RemovedDeviceInfo, + __LINE__)); + } + + status = STATUS_SUCCESS; + } + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(RemovedDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Removal: Updating PathToBeUsed in %p to %p\n", + RemovedDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): No active/alternative path available for group %p\n", + RemovedDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + if (lockHeld) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Exiting function with status %x.\n", + RemovedDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathFailing( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when an IO that was sent using this path fails with + a fatal error. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Note: This is used by devices NOT supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + FailingDeviceInfo is the device info for the path on which IO failed + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailing (DevInfo %p): Entering function.\n", + FailingDeviceInfo)); + + // + // We need to do exactly what DsmpSetLBForPathRemoval() does, except + // that the devInfo may not really go away (may come back before a + // Pnp remove comes down for the real LUN) + // + if (MarkDevInfoFailed) { + status = DsmpSetLBForPathRemoval(DsmContext, FailingDeviceInfo, NULL, SpecialHandlingFlag); + } else { + status = DsmpSetLBForPathRemoval(DsmContext, FailingDeviceInfo, FailingDeviceInfo->Group, SpecialHandlingFlag); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailing (DevInfo %p): Exiting function with status %x\n", + FailingDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathFailingALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when an IO that was sent using this path fails with + a fatal error. The routine will set the path to the appropriate state and + send down a Set Target Port Groups command asynchronously to fix up the + other paths state if they need to change (actual work done in the completion + routine). + + Note: This should NOT be called with DsmContext Lock held + This is used for devices supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + FailingDeviceInfo is the device info for the failing/going-away path + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength; + PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; + PDSM_COMPLETION_CONTEXT completionContext = NULL; + PVOID senseInfo = NULL; + PSCSI_REQUEST_BLOCK srb = NULL; + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Entering function.\n", + FailingDeviceInfo)); + + if (MarkDevInfoFailed) { + if (!(DsmpIsDeviceFailedState(FailingDeviceInfo->State))) { + + FailingDeviceInfo->LastKnownGoodState = FailingDeviceInfo->State; + } + + FailingDeviceInfo->PreviousState = FailingDeviceInfo->State; + FailingDeviceInfo->State = DSM_DEV_FAILED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): changed to state %d at %d\n", + FailingDeviceInfo, + FailingDeviceInfo->State, + __LINE__)); + } + + group = FailingDeviceInfo->Group; + + if (group->LoadBalanceType < DSM_LB_FAILOVER || + group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { + + status = STATUS_INVALID_PARAMETER; + + } else { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Check if there are any active paths that can be used. + // + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + if (!deviceInfo) { + + // + // Check if an Set/Report TPG has already been sent for this failing devInfo + // + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(DsmContext, group, FailingDeviceInfo); + + if (!failDevInfoListEntry) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + if (deviceInfo) { + + if (sendTPG) { + + tpgCompletionContext = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TPG_COMPLETION_CONTEXT), + DSM_TAG_TPG_COMPLETION_CONTEXT); + + if (tpgCompletionContext) { + UCHAR senseInfoLength = SENSE_BUFFER_SIZE_EX; + + senseInfo = DsmpAllocatePool(NonPagedPoolNx, + senseInfoLength, + DSM_TAG_SCSI_SENSE_INFO); + + if (senseInfo) { + + srb = DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + DSM_TAG_SCSI_REQUEST_BLOCK); + + if (srb) { + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + if (completionContext) { + + // + // Update the target port group that needs to be made + // active/optimized. We will send down an STPG for + // storages that support both implicit and explicit. + // If the storage does NOT like our choice of A/O TPG, + // it will make an implicit transition. This is still + // a better option than solely relying on the storage's + // implicit transitions at this stage and ending up with + // no path in A/O state. + // + if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + + } else { + + // + // Find an active/optimized target port group that should + // have been set by the controller + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + } + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + failDevInfoListEntry = DsmpBuildFailPathDevInfoEntry(DsmContext, + group, + FailingDeviceInfo, + deviceInfo); + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (failDevInfoListEntry) { + + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); + tpgDescriptor->AsymmetricAccessState = DSM_DEV_ACTIVE_OPTIMIZED; + REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &deviceInfo->TargetPortGroup->Identifier); + + // + // Prevent the device info from being removed when a TPG is in-flight. + // + InterlockedIncrement(&FailingDeviceInfo->BlockRemove); + + completionContext->DeviceInfo = FailingDeviceInfo; + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = deviceInfo; + completionContext->RequestUnique2 = FALSE; + + tpgCompletionContext->CompletionContext = completionContext; + tpgCompletionContext->Srb = srb; + tpgCompletionContext->SenseInfoBuffer = senseInfo; + tpgCompletionContext->SenseInfoBufferLength = senseInfoLength; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Sending down TPG asynchronously for %p using devInfo %p (path %p).\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId, + deviceInfo, + deviceInfo->FailGroup->PathId)); + + if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + status = DsmpSetTargetPortGroupsAsync(deviceInfo, + DsmpPhase1ProcessPathFailingALUA, + tpgCompletionContext, + targetPortGroupsInfoLength, + targetPortGroupsInfo); + } else { + + status = DsmpReportTargetPortGroupsAsync(deviceInfo, + DsmpPhase2ProcessPathFailingALUA, + tpgCompletionContext, + targetPortGroupsInfoLength, + targetPortGroupsInfo); + } + + if (status != STATUS_PENDING) { + + // + // Request not sent down successfully. Free the allocations. + // + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + + // + // Allow the failing device to be removed. + // + InterlockedDecrement(&FailingDeviceInfo->BlockRemove); + } + } else { + + // + // Fail to build DevInfo entry. Free the allocations. + // + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(completionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(srb != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate SRB. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(senseInfo != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate senseInfo. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(tpgCompletionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate TPG completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Found alternative devInfo %p for failing path %p without need for TPG\n", + FailingDeviceInfo, + deviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Couldn't find a standby path to activate for failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + deviceInfo = failDevInfoListEntry->TempDeviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): There is an RTPG/STPG already in progress for this path %p. Returning alternative %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId, + deviceInfo)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Other paths active, no path made active at %d\n", + FailingDeviceInfo, + __LINE__)); + } + + status = STATUS_SUCCESS; + } + + if (deviceInfo) { + + // + // Update temporarily the next path to be used for the group as this devInfo + // + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + FailingDeviceInfo, + group, + group->PathToBeUsed)); + + } else { + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): No FOG available for group %p\n", + FailingDeviceInfo, + group)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Exiting function with status %x.\n", + FailingDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetPathForIoRetryALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN TPGException, + _In_ IN BOOLEAN DeviceInfoException + ) +/*++ + +Routine Description: + + This routine is called when an IO that was sent using this path fails with + a retry-able "ALUA" error. What this basically means is that most likely an + implicit transition has taken place and we need an RTPG to get the updated + path states. The routine will send down a Report Target Port Groups command + asynchronously to get the paths states (actual work done in the completion + routine). + + Note: This should NOT be called with DsmContext Lock held + This is used for devices supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + FailingDeviceInfo is the device info for the failing/going-away path + + TPGException is a flag used to indicate if the selected path must be from a + TPG that is different from FailingDeviceInfo's. This is + special handling for UA with sense "TPG in SB/UA state" + + DeviceInfoException is a flag used to indicate that the current FailindDeviceInfo + itself needs to be used again. This is special handling for + UA with sense "Asymmetric Access State Changed" + + (NOTE: TPGException and DeviceInfoException are mutually exclusive, although + it is okay for both to be FALSE) + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength; + PDSM_COMPLETION_CONTEXT completionContext = NULL; + PVOID senseInfo = NULL; + PSCSI_REQUEST_BLOCK srb = NULL; + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = NULL; + NTSTATUS throttleStatus = STATUS_UNSUCCESSFUL; + ULONG inflightRTPG; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Entering function.\n", + FailingDeviceInfo)); + + group = FailingDeviceInfo->Group; + + + if (group->LoadBalanceType < DSM_LB_FAILOVER || + group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { + + status = STATUS_INVALID_PARAMETER; + + } else { + + // + // First check to see if we need to find a candidate from a different TPG. + // + if (TPGException) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find a candidate in a TPG that is different from this one + // + deviceInfo = DsmpFindStandbyPathInAlternateTpgALUA(group, FailingDeviceInfo, SpecialHandlingFlag); + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Need to try a different TPG deviceInfo %p.\n", + FailingDeviceInfo, + deviceInfo)); + + } else if (DeviceInfoException) { + + // + // Retry on the same path + // + deviceInfo = FailingDeviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Will retry using same deviceInfo %p.\n", + FailingDeviceInfo, + deviceInfo)); + } + + // + // Check if an Report TPG has already been sent for this group + // + inflightRTPG = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 0); + + // + // If there is no RTPG currently in flight, use the best candidate found + // above to send down the RTPG. If there is already an RTPG inflight, + // we're done. The result of the RTPG should fix the path states. + // + if (!inflightRTPG) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): No RTPG in flight. Try sending one down.\n", + FailingDeviceInfo)); + + // + // If we need a candidate device, first get the currently active one. + // If we can't find one that way, resort to finding the best alternative. + // Basic idea is find SOME path instead of failing IOs back to the application. + // + if (!deviceInfo) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find the best candidate - ie. either a currently A/O path or + // the best alternative path to be made A/O. + // + deviceInfo = DsmpGetAnyActivePath(group, TRUE, deviceInfo, SpecialHandlingFlag); + if (!deviceInfo) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): No active path. Best alternative %p.\n", + FailingDeviceInfo, + deviceInfo)); + } + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + if (deviceInfo) { + + tpgCompletionContext = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TPG_COMPLETION_CONTEXT), + DSM_TAG_TPG_COMPLETION_CONTEXT); + + if (tpgCompletionContext) { + UCHAR senseInfoLength = SENSE_BUFFER_SIZE_EX; + + senseInfo = DsmpAllocatePool(NonPagedPoolNx, + senseInfoLength, + DSM_TAG_SCSI_SENSE_INFO); + + if (senseInfo) { + + srb = DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + DSM_TAG_SCSI_REQUEST_BLOCK); + + if (srb) { + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + + if (completionContext) { + + // + // Find an active/optimized target port group that should + // have been set by the controller + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + completionContext->DeviceInfo = FailingDeviceInfo; + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = deviceInfo; + completionContext->RequestUnique2 = TRUE; + + tpgCompletionContext->CompletionContext = completionContext; + tpgCompletionContext->Srb = srb; + tpgCompletionContext->SenseInfoBuffer = senseInfo; + tpgCompletionContext->SenseInfoBufferLength = senseInfoLength; + + // + // Now we are all set to send the RTPG request. + // check and set InFlightRTPG to make sure this thread is the only one with + // the RTPG active for this group, since it is possible to have more than one + // threads reaching up to this point in parallel + // + inflightRTPG = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 1, 0); + if (inflightRTPG) { + + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } else { + + // + // Prevent the device info from being removed when a TPG is in-flight. + // + InterlockedIncrement(&FailingDeviceInfo->BlockRemove); + + // + // First try and throttle the IO. The completion routine will + // take care of resuming the IO. + // + if (!InterlockedCompareExchange((LONG volatile*)&group->Throttled, 1, 0)) { + + DsmNotification(((PDSM_CONTEXT)DsmContext)->MPIOContext, + ThrottleIO_V2, + deviceInfo, + FALSE, + &throttleStatus, + 0); + + if (NT_SUCCESS(throttleStatus)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Successfully throttled IO. About to send RTPG. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + } else { + + // + // Throttle can fail when the MPDisk is + // 1. Being removed. (or) + // 2. In any other state other than Normal or Degraded. + // + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Throttle before RTPG failed. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + InterlockedDecrement((LONG volatile*)&FailingDeviceInfo->Group->Throttled); + } + } else { + + // + // Currently we don't expect this to happen + // + NT_ASSERT(FALSE); + } + + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Sending RTPG asynchronously. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + if (STATUS_PENDING != DsmpReportTargetPortGroupsAsync(deviceInfo, + DsmpPhase2ProcessPathFailingALUA, + tpgCompletionContext, + targetPortGroupsInfoLength, + targetPortGroupsInfo)) { + + + + // + // Request not sent down successfully. Free the allocations. + // + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + + // + // Allow the failing device to be removed. + // + InterlockedDecrement(&FailingDeviceInfo->BlockRemove); + + // + // Resume IO if we throttled requests before calling DsmpReportTargetPortGroupsAsync + // + if (InterlockedCompareExchange((LONG volatile*)&group->Throttled, 0, 1)) { + + NTSTATUS resumeStatus = STATUS_UNSUCCESSFUL; + + DsmNotification(((PDSM_CONTEXT)deviceInfo->DsmContext)->MPIOContext, + ResumeIO_V2, + deviceInfo, + TRUE, + &resumeStatus, + 0); + + if (!NT_SUCCESS(resumeStatus)) { + + // + // Resume can fail when + // 1. The MPDisk is being removed (or) + // 2. The MPDisk is any other state other than throttled (or) + // 3. There is a problem dispatching throttled requests. + // + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Resume IO failed.\n", + deviceInfo)); + } + + } + + InterlockedDecrement((LONG volatile*)&group->InFlightRTPG); + } + } + } else { + + NT_ASSERT(targetPortGroupsInfo != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate TPG info buffer. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + ExFreePool(srb); + ExFreePool(senseInfo); + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(completionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreePool(srb); + ExFreePool(senseInfo); + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(srb != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate SRB. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreePool(senseInfo); + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(senseInfo != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate senseInfo. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(tpgCompletionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate TPG completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't find a path for RTPG. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Other paths active, no path made active at %d\n", + FailingDeviceInfo, + __LINE__)); + } + + if(!deviceInfo) { + + // + // It is possible that there are only two TPGs with one of them in U/A + // state and the other in transitioning state. In such a case, we would + // not find an alternative TPG deviceInfo. In addition, if there is an + // RTPG in flight, we won't go down the path of forcibly picking any + // deviceInfo. This is to cover that scenario, else we're left with no + // deviceInfo to do the retry and we'll end up setting the group's PTBU + // to NULL thus failing the retried request (if for eg. the LB is RRWS). + // Need to ensure that we handle this exception case. + // + if (inflightRTPG) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find the best candidate - ie. either a currently A/O path or + // the best alternative path to be made A/O. + // + deviceInfo = DsmpGetAnyActivePath(group, TRUE, deviceInfo, SpecialHandlingFlag); + if (!deviceInfo) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + } + + if(deviceInfo) { + + // + // Update temporarily the next path to be used for the group as this devInfo + // + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + FailingDeviceInfo, + group, + group->PathToBeUsed)); + + } else { + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): No FOG available for group %p\n", + FailingDeviceInfo, + group)); + } + + status = STATUS_SUCCESS; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Exiting function with status %x.\n", + FailingDeviceInfo, + status)); + + return status; +} + + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpFindFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo + ) +/*++ + +Routine Description: + + This routine finds the entry that contains the alternate devInfo to use for + a failing one for the passed in devInfo. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + Context is the DSM's context info. + + Group is the group entry representing the device. + + FailingDevInfo is the device info whose entry needs to be found. + +Return Value: + + Pointer to the entry. NULL if it doesn't exist. + +--*/ +{ + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + PLIST_ENTRY entry = NULL; + + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindFailPathDevInfoEntry (DevInfo %p): Entering function.\n", + FailingDevInfo)); + + for (entry = Group->FailingDevInfoList.Flink; + entry != &Group->FailingDevInfoList; + entry = entry->Flink) { + + failDevInfoListEntry = CONTAINING_RECORD(entry, DSM_FAIL_PATH_PROCESSING_LIST_ENTRY, ListEntry); + NT_ASSERT(failDevInfoListEntry); + + if (failDevInfoListEntry) { + + if (failDevInfoListEntry->FailingDeviceInfo == FailingDevInfo) { + + break; + + } else { + + failDevInfoListEntry = NULL; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindFailPathDevInfoEntry (DevInfo %p): Exiting function returning entry %p.\n", + FailingDevInfo, + failDevInfoListEntry)); + + return failDevInfoListEntry; +} + + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpBuildFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo, + _In_ IN PDSM_DEVICE_INFO AlternateDevInfo + ) +/*++ + +Routine Description: + + When InterpretError() is called with an IRP that has failed with a fatal error, + if the device is ALUA it is possible that STPG needs to be sent to update a new + devInfo as being Active/Optimized. However, for in-flight IOs that weren't + queued by MPIO, we still need to return a path that can be used. + + This routine is builds an entry that contains the alternate devInfo to use for + a failing one. + + NOTE: Calling function should be holding the spin lock. + +Arguments: + + Context is the DSM's context info. + + Group is the group entry representing the device. + + FailingDevInfo is the device info that was used when the IRP failed. + + AlternateDevInfo is the new one to temporarily use until its state can be properly set. + +Return Value: + + Pointer to the newly built entry. + NULL if there were any errors building it. + +--*/ +{ + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Entering function.\n", + FailingDevInfo)); + + failDevInfoListEntry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_FAIL_PATH_PROCESSING_LIST_ENTRY), + DSM_TAG_FAIL_DEVINFO_LIST_ENTRY); + + if (failDevInfoListEntry) { + + failDevInfoListEntry->FailingDeviceInfo = FailingDevInfo; + failDevInfoListEntry->TempDeviceInfo = AlternateDevInfo; + InsertTailList(&Group->FailingDevInfoList, &failDevInfoListEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&Group->NumberFailingDevInfos); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Failed to allocate memory for entry.\n", + FailingDevInfo)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Exiting function returning entry %p.\n", + FailingDevInfo, + failDevInfoListEntry)); + + return failDevInfoListEntry; +} + + +NTSTATUS +DsmpPhase1ProcessPathFailingALUA( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This is the completion routine that is called when the STPG is sent down by + DsmpSetLBForPathFailingALUA. + + The caller SHOULD NOT acquire the DSM Context lock before calling this routine. + +Arguements: + + DeviceObject is the target device object to which Irp was sent + + Irp is the scsi pass through request for STPG + + Context is the completion context. + +Return Value: + + Status + +--*/ +{ + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PDSM_TPG_COMPLETION_CONTEXT context = (PDSM_TPG_COMPLETION_CONTEXT)Context; + PSCSI_REQUEST_BLOCK srb = context->Srb; + PVOID senseData = context->SenseInfoBuffer; + UCHAR senseDataLength = context->SenseInfoBufferLength; + NTSTATUS status = Irp->IoStatus.Status; + ULONG targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + PUCHAR targetPortGroupsInfo; + PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)(context->CompletionContext->RequestUnique1); + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + BOOLEAN releaseCompletionContextResources = TRUE; + KIRQL irql; + UCHAR scsiStatus = SrbGetScsiStatus(srb); + + UNREFERENCED_PARAMETER(DeviceObject); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): Entering function.\n", + deviceInfo)); + +#if DBG + KeQuerySystemTime(&context->CompletionContext->TickCount); +#endif + + if ((scsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(status))) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): STPG succeeded.\n", + deviceInfo)); + + } else if (NT_SUCCESS(status) && + scsiStatus == SCSISTAT_CHECK_CONDITION && + DsmpShouldRetryTPGRequest(senseData, senseDataLength)) { + + if ((context->NumberRetries)--) { + + // + // Retry the request + // + + NT_ASSERT(SrbGetDataBuffer(srb) == MmGetMdlVirtualAddress(Irp->MdlAddress)); + + // + // Reset byte count of transfer in SRB Extension. + // + SrbSetDataTransferLength(srb, Irp->MdlAddress->ByteCount); + + // + // Zero SRB statuses. + // + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + + nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + nextIrpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + // + // Save SRB address in next stack for port driver. + // + nextIrpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(Irp, DsmpPhase1ProcessPathFailingALUA, Context, TRUE, TRUE, TRUE); + + IoMarkIrpPending(Irp); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(context->CompletionContext->DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + goto __Exit_DsmpPhase1ProcessPathFailingALUA; + } + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + deviceInfo, + status, + scsiStatus)); + } + + if (NT_SUCCESS(status)) { + + // + // An explicit transition may cause changes to some other TPGs. + // So we need to query for the states of all the TPGs and update + // our internal list and its elements. + // + + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + if (STATUS_PENDING == DsmpReportTargetPortGroupsAsync(deviceInfo, + DsmpPhase2ProcessPathFailingALUA, + Context, + targetPortGroupsInfoLength, + targetPortGroupsInfo)) { + + releaseCompletionContextResources = FALSE; + + } else { + + DsmpFreePool(targetPortGroupsInfo); + } + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + } + } + + // + // Free the allocations. + // + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + + DsmpFreePool(Irp->UserBuffer); + + IoFreeIrp(Irp); + Irp = (PIRP) NULL; + + if (releaseCompletionContextResources) { + + // + // Release our hold on the device info so that it can be removed. + // + InterlockedDecrement(&context->CompletionContext->DeviceInfo->BlockRemove); + + ExFreeToNPagedLookasideList(&(context->CompletionContext->DsmContext)->CompletionContextList, context->CompletionContext); + DsmpFreePool(context->Srb); + DsmpFreePool(context->SenseInfoBuffer); +#pragma warning(suppress:6001) // DevDiv 818965 + DsmpFreePool(context); + } + +__Exit_DsmpPhase1ProcessPathFailingALUA: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): Exiting function.\n", + deviceInfo)); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +DsmpRemoveFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY FailPathDevInfoEntry + ) +/*++ + +Routine Description: + + This routine removes the entry pointed to from the passed in Group's list. + + NOTE: Calling function should be holding the spin lock. + +Arguments: + + Context is the DSM's context info. + + Group is the group entry representing the device. + + FailingPathDevInfoEntry is the entry that needs to be removed. + +Return Value: + + STATUS_SUCCESS if successful, else appropriate NT error code. + +--*/ +{ + PLIST_ENTRY entry = &FailPathDevInfoEntry->ListEntry; + PDSM_DEVICE_INFO deviceInfo = FailPathDevInfoEntry->FailingDeviceInfo; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRemoveFailPathDevInfoEntry (DevInfo %p): Entering function.\n", + deviceInfo)); + + RemoveEntryList(entry); + DsmpFreePool(FailPathDevInfoEntry); + InterlockedDecrement((LONG volatile*)&Group->NumberFailingDevInfos); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRemoveFailPathDevInfoEntry (DevInfo %p): Exiting function with status %x.\n", + deviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpPhase2ProcessPathFailingALUA( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This is the completion routine that is called when the RTPG is sent down by + DsmpPhase1ProcessPathFailingALUA. + + The caller SHOULD NOT acquire the DSM Context lock before calling this routine. + +Arguements: + + DeviceObject is the target device object to which Irp was sent + + Irp is the scsi pass through request for RTPG + + Context is the completion context. + +Return Value: + + Status + +--*/ +{ + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PDSM_TPG_COMPLETION_CONTEXT context = (PDSM_TPG_COMPLETION_CONTEXT)Context; + PSCSI_REQUEST_BLOCK srb = context->Srb; + PVOID senseData = context->SenseInfoBuffer; + UCHAR senseDataLength = context->SenseInfoBufferLength; + NTSTATUS status = Irp->IoStatus.Status; + PUCHAR header; + ULONG returnedDataLength = 0; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)(context->CompletionContext->RequestUnique1); + BOOLEAN decrementRTPGcount = (BOOLEAN)(context->CompletionContext->RequestUnique2); + KIRQL irql; + ULONG index; + PDSM_DEVICE_INFO devInfo; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + UCHAR scsiStatus = SrbGetScsiStatus(srb); + + UNREFERENCED_PARAMETER(DeviceObject); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Entering function.\n", + deviceInfo)); + +#if DBG + KeQuerySystemTime(&(context->CompletionContext)->TickCount); +#endif + + if ((status == STATUS_BUFFER_OVERFLOW) || + (NT_SUCCESS(status) && + (scsiStatus == SCSISTAT_GOOD))) { + + header = (PUCHAR)((PUCHAR)SrbGetDataBuffer(srb)); + GetUlongFrom4ByteArray(header, returnedDataLength); + + status = STATUS_SUCCESS; + if (returnedDataLength > SrbGetDataTransferLength(srb)) { + + status = STATUS_BUFFER_OVERFLOW; + } + } + + if ((scsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(status))) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): RTPG using path %p succeeded.\n", + deviceInfo, + deviceInfo->FailGroup->PathId)); + + header = (PUCHAR)((PUCHAR)SrbGetDataBuffer(srb)); + GetUlongFrom4ByteArray(header, returnedDataLength); + + // + // Allocate a buffer to hold the TPG info. + // + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength; + + // + // Copy it over. + // + RtlCopyMemory(targetPortGroupsInfo, + header, + targetPortGroupsInfoLength); + + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Failed to allocate mem for TPG.\n", + deviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } else if (NT_SUCCESS(status) && + scsiStatus == SCSISTAT_CHECK_CONDITION && + DsmpShouldRetryTPGRequest(senseData, senseDataLength)) { + + if ((context->NumberRetries)--) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Retrying check condition using path %p. Retries remaining %u.\n", + deviceInfo, + deviceInfo->FailGroup->PathId, + context->NumberRetries)); + + // + // Retry the request + // + + NT_ASSERT(SrbGetDataBuffer(srb) == MmGetMdlVirtualAddress(Irp->MdlAddress)); + + // + // Reset byte count of transfer in SRB Extension to true length. + // + SrbSetDataTransferLength(srb, targetPortGroupsInfoLength); + + // + // Zero SRB statuses. + // + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + + nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + nextIrpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + // + // Save SRB address in next stack for port driver. + // + nextIrpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(Irp, DsmpPhase2ProcessPathFailingALUA, Context, TRUE, TRUE, TRUE); + + IoMarkIrpPending(Irp); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(context->CompletionContext->DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + goto __Exit_DsmpPhase2ProcessPathFailingALUA; + } + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + deviceInfo, + status, + SrbGetScsiStatus(srb))); + + // Failed to get TPG Info. + // Here it is possible status is success, but scsiStatus is not. + // If so, set status to unsuccessful. + + if (NT_SUCCESS(status)) { + status = STATUS_UNSUCCESSFUL; + } + } + + if (NT_SUCCESS(status)) { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + // + // Parse the TPG information and update the device path states + // + status = DsmpParseTargetPortGroupsInformation(context->CompletionContext->DsmContext, + deviceInfo->Group, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroup = deviceInfo->Group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + if (NT_SUCCESS(status)) { + + PDSM_DEVICE_INFO tempDevice = NULL; + + // + // Update all the devInfo states. If the device is AO but + // not this device, make it fake AU. + // If device not in AO, make sure that it matches the TPG + // state. + // Ensure that: + // 1. All devices match their ALUA state. + // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it. + // 3. For FOO there must be only one A/O device. Preferably the preferred path. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + devInfo = group->DeviceList[index]; + + if (devInfo) { + + if (devInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // In implicit transitions, there is no guarantee that + // the TPG of chosen "deviceInfo" is in A/O state. So + // to play it safe, we hang on to the very first devInfo + // whose TPG is in A/O state. + // + if (!tempDevice && + !DsmpIsDeviceFailedState(devInfo->State)) { + + devInfo->PreviousState = devInfo->State; + devInfo->State = devInfo->ALUAState; + + tempDevice = devInfo; + } + + if (devInfo != deviceInfo) { + + if (!DsmpIsDeviceFailedState(devInfo->State)) { + + // + // For FOO, only one path can be in A/O. + // For RRWS, mask an A/O path if that isn't the desired state. + // + if ((group->LoadBalanceType == DSM_LB_FAILOVER) || + (group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && + devInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + devInfo->DesiredState != DSM_DEV_UNDETERMINED)) { + + // + // For implicit transitions, we may have saved off an A/O path. + // Don't undo that. + // + if (tempDevice != devInfo) { + + devInfo->PreviousState = devInfo->State; + devInfo->State = DSM_DEV_ACTIVE_UNOPTIMIZED; + } + + } else { + + devInfo->PreviousState = devInfo->State; + devInfo->State = devInfo->ALUAState; + } + } + } else { + + devInfo->PreviousState = devInfo->State; + + // + // For FOO, only one path can be in A/O state. + // The TPG of the selected "deviceInfo" is in A/O, so this + // can now very well be made the candidate. However, since + // it is possible that we saved off another candidate, we + // now need to replace that with this. + // + if (!DsmpIsDeviceFailedState(devInfo->State) && + devInfo->Group->LoadBalanceType == DSM_LB_FAILOVER && + tempDevice) { + + tempDevice->State = DSM_DEV_ACTIVE_UNOPTIMIZED; + } + + devInfo->State = devInfo->ALUAState; + + tempDevice = devInfo; + } + } else { + + if (!DsmpIsDeviceFailedState(devInfo->State)) { + + devInfo->PreviousState = devInfo->State; + devInfo->State = devInfo->ALUAState; + } + } + } + } + } + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + } + + + // + // Resume IO if we throttled requests. + // + if (InterlockedCompareExchange((LONG volatile*)&group->Throttled, 0, 1)) { + + NTSTATUS resumeStatus = STATUS_UNSUCCESSFUL; + + DsmNotification(((PDSM_CONTEXT)deviceInfo->DsmContext)->MPIOContext, + ResumeIO_V2, + deviceInfo, + TRUE, + &resumeStatus, + 0); + + if (!NT_SUCCESS(resumeStatus)) { + + // + // Resume can fail when + // 1. The MPDisk is being removed (or) + // 2. The MPDisk is any other state other than throttled (or) + // 3. There is a problem dispatching throttled requests. + // + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevObj %p): Resume IO failed.\n", + DeviceObject)); + } + + } + + if (decrementRTPGcount) { + + // + // Resetting InFlightRTPG after resume so that we don't get into situation where + // new DsmpSetPathForIoRetryALUA caller thread finds that InFlightRTPG is not set but Throttled is set + // + ULONG count = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 1); + + // + // If decrementRTPGCount flag is set, there must be atleast one RTPG in flight. + // + NT_ASSERT(count); + + UNREFERENCED_PARAMETER(count); + + } + + // + // Free the allocations. + // + if (targetPortGroupsInfo) { + + DsmpFreePool(targetPortGroupsInfo); + } + + // + // Release our hold on the device info so that it can be removed. + // + InterlockedDecrement(&context->CompletionContext->DeviceInfo->BlockRemove); + + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + + DsmpFreePool(Irp->UserBuffer); + + IoFreeIrp(Irp); + Irp = (PIRP) NULL; + + ExFreeToNPagedLookasideList(&(context->CompletionContext->DsmContext)->CompletionContextList, context->CompletionContext); + DsmpFreePool(srb); + DsmpFreePool(senseData); +#pragma warning(suppress:6001) // DevDiv 818965 + DsmpFreePool(context); + +__Exit_DsmpPhase2ProcessPathFailingALUA: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Exiting function.\n", + deviceInfo)); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +DsmpPersistentReserveOut( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine will handle determine which devices to send the request to based + on the service action of the PR-out command. + On REGISTER/REGISTER_AND_IGNORE_EXISTING, it will send the command down all + paths. If any path succeeds, the PR key will be stored. If failure down any + path, return failure. + On REGISTER/REGISTER_AND_IGNORE_EXISTING with key == 0 (ie. UNREGISTER), + the request is sent down every path. Failure is returned if request fails down + any path (but error is ignored if path happens to be one where prior + REGISTER/REGISTER_AND_IGNORE_EXISTING had failed in the first place). The + stored PR key is cleared irrespective of success/failure being returned. + On RESERVE/RELEASE, the command is sent down one path. If it fails, another + path is tried. Failure is returned only if none succeed. + On CLEAR, command is sent down one path. If it fails, another path is tried. + Failure is returned only if none succeed. The stored PR key is cleared + irrespective of success/failure being returned. + On PREEMPT, command is sent down one path. If it fails, another path is + tried. Failure is returned only if none succeed. + On PREEMPT_AND_ABORT, command is sent down one path. Failed request is not + retried. + + NOTE: If a path shows up later, REGISTER_AND_IGNORE_EXISTING request is + built by the IsPathActive routine using the saved PR key and sent down new path. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - The event to + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + PDSM_DEVICE_INFO servicingDeviceInfo = NULL; + PDSM_GROUP_ENTRY group; + LONG i; + ULONG count; + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDSM_COMPLETION_CONTEXT completionContext; + PCDB cdb = SrbGetCdb(Srb); + UCHAR serviceAction; + NTSTATUS returnStatus = STATUS_SUCCESS; + BOOLEAN sendDownAll = FALSE; + BOOLEAN savePRKeyIfAnySucceed = FALSE; + BOOLEAN retryOnAnother = FALSE; + BOOLEAN passOnlyIfAllSucceed = FALSE; + BOOLEAN ignoreIfPreviousFailed = FALSE; + BOOLEAN clearPRKey = FALSE; + KEVENT event; + PPRO_PARAMETER_LIST prOutParam = Irp->AssociatedIrp.SystemBuffer; + PUCHAR index = NULL; + UCHAR prKey[8] = {0}; + PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; + PIO_STACK_LOCATION irpStack; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + BOOLEAN statusUpdated = FALSE; + ULONGLONG currentTickCount; + ULONGLONG finalTickCount; + ULONG tickLength = KeQueryTimeIncrement(); + PVOID senseInfoBuffer = NULL; + UCHAR senseInfoBufferLength = 0; + BOOLEAN srbCopySucceeded = FALSE; + UCHAR prType; + UCHAR prScope; + ULONGLONG saKey; + ULONGLONG resKey; + ULONG SpecialHandlingFlag = 0; + + UNREFERENCED_PARAMETER(Event); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Cache away a copy of the SRB + // + srbCopy = SrbAllocateCopy(Srb, NonPagedPoolNx, DSM_TAG_SCSI_REQUEST_BLOCK); + if (srbCopy == NULL) { + returnStatus = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpPersistentReserveOut; + } + + deviceInfo = DsmIds->IdList[0]; + group = deviceInfo->Group; + + prType = cdb->PERSISTENT_RESERVE_OUT.Type; + prScope = cdb->PERSISTENT_RESERVE_OUT.Scope; + serviceAction = cdb->PERSISTENT_RESERVE_OUT.ServiceAction; + + NT_ASSERT(serviceAction >= RESERVATION_ACTION_REGISTER && serviceAction <= RESERVATION_ACTION_REGISTER_IGNORE_EXISTING); + + index = prOutParam->ServiceActionReservationKey; + RtlCopyMemory(&prKey, index, 8); + REVERSE_BYTES_QUAD(&saKey, &prOutParam->ServiceActionReservationKey); + + REVERSE_BYTES_QUAD(&resKey, &prOutParam->ReservationKey); + + switch (serviceAction) { + case RESERVATION_ACTION_REGISTER: + case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING: { + + // + // The command must be sent down all paths. + // + sendDownAll = TRUE; + + // + // Return failure if it fails down even one of the paths. + // + passOnlyIfAllSucceed = TRUE; + + if (DsmpIsPersistentReservationKeyZeroKey(ARRAY_SIZE(prKey), prKey)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): NULL PR key, service action %u\n", + DsmIds, + serviceAction)); + + // + // If unregister fails, don't report it back as error if the + // previous register/register_and_ignore_existing down that + // path had failed too. + // + ignoreIfPreviousFailed = TRUE; + + // + // Clear the group PR key irrespective of the status that is + // going to be returned to clusdisk. + // + clearPRKey = TRUE; + + } else { + + // + // If register/register_and_ignore_existing succeed down any of + // the paths, save off the PR key for the group entry. + // + savePRKeyIfAnySucceed = TRUE; + } + + break; + } + + case RESERVATION_ACTION_RESERVE: + case RESERVATION_ACTION_RELEASE: + case RESERVATION_ACTION_PREEMPT: + case RESERVATION_ACTION_PREEMPT_ABORT: + case RESERVATION_ACTION_CLEAR: { + + if (serviceAction != RESERVATION_ACTION_PREEMPT_ABORT) { + + // + // Apart from preempt_abort, all the others must be retried + // (down another path) if they fail down the chosen path. + // + retryOnAnother = TRUE; + } + + if (serviceAction == RESERVATION_ACTION_CLEAR) { + + // + // Clear the stored PR key for the group entry irrespective of + // the status that is going to be returned back. + // + clearPRKey = TRUE; + } + + break; + } + + default: { + + returnStatus = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Invalid service action %u.\n", + DsmIds, + serviceAction)); + + goto __Exit_DsmpPersistentReserveOut; + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Srb %p, Service Action %u, Type %u, Scope %u, \ + \n\t\t\t\tservice action reservation key %I64x, reservation key %I64x.\n", + DsmIds, + Srb, + serviceAction, + prType, + prScope, + saKey, + resKey)); + + // + // Allocate a context for the completion routine. + // + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + if (!completionContext) { + + returnStatus = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Failed to allocate completion context.\n", + DsmIds, + serviceAction)); + + goto __Exit_DsmpPersistentReserveOut; + } + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Indicate the target for this request. + // + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = (PVOID)&event; + completionContext->RequestUnique2 = cdb->PERSISTENT_RESERVE_OUT.OperationCode; + + count = group->NumberDevices; + + for (i = count - 1; i >= 0; i--) { + + // + // A PR command may fail with a "retry-able" UA when reservation is + // released or preempted (on every I_T_L nexus except the one on which + // it was released/preempted). In such a case we should retry the PR + // command on the same path. + // + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + finalTickCount = currentTickCount + (DSM_SECONDS_TO_TICKS(group->MaxPRRetryTimeDuringStateTransition) / tickLength); + + if (!sendDownAll && !retryOnAnother) { + + // + // If the request doesn't need to be retried (down another path) on + // failure, better choose the path that has maximum chances of + // success. + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), + SpecialHandlingFlag); + + if (!deviceInfo) { + + returnStatus = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - No active/alternative path for device %p.\n", + DsmIds, + serviceAction, + group)); + + break; + } + + } else { + + deviceInfo = group->DeviceList[i]; + + // + // Ignore "bad" paths for now. If the path becomes "good" again, + // IsPathActive() will send down the register. + // Also, don't consider newly arrived paths for which the group has + // a reservation but register has not yet been sent down. This rule + // applies only to requests that are not Register. + // + if ((DsmpIsDeviceFailedState(deviceInfo->State) || !DsmpIsDeviceInitialized(deviceInfo)) || + (!DsmpIsDeviceUsablePR(deviceInfo) && !sendDownAll)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Ignoring bad instance - state %x, init %x, key reg %x (key valid %x).\n", + DsmIds, + deviceInfo->State, + deviceInfo->Initialized, + deviceInfo->PRKeyRegistered, + deviceInfo->Group->PRKeyValid)); + + deviceInfo = NULL; + } + + } + + if (!deviceInfo) { + + // + // Maybe a remove came through and caused a collapse of the device + // list, thus making this entry empty. + // + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Couldn't find path for device %p.\n", + DsmIds, + serviceAction, + group)); + + continue; + } + +__DsmpPersistentReserveOut_RetryRequest: + + IoMarkIrpPending(Irp); + + completionContext->DeviceInfo = deviceInfo; + + // + // Set-up a completion routine. + // + IoSetCompletionRoutine(Irp, + DsmpPersistentReserveCompletion, + completionContext, + TRUE, + TRUE, + TRUE); + + // + // Always send the original request down a new path + // + irpStack = IoGetNextIrpStackLocation(Irp); + srbCopySucceeded = SrbCopySrb(Srb, SrbGetSrbLength(Srb), srbCopy); + NT_ASSERT(srbCopySucceeded == TRUE); + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Clear the sense buffer if it exists + // + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + if (senseInfoBuffer) { + RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); + } + + servicingDeviceInfo = deviceInfo; + + // + // Issue the request and wait. + // + status = DsmSendRequest(DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo); + + if (status == STATUS_PENDING) { + + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + + status = Irp->IoStatus.Status; + } + + if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down successfully on %p.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId)); + + if (!passOnlyIfAllSucceed) { + + // + // Success down any one path means success + // + returnStatus = status; + } + + if (savePRKeyIfAnySucceed) { + + RtlCopyMemory(&group->PersistentReservationRegisteredKey, &prKey, 8); + + group->PRServiceAction = serviceAction; + group->PRType = prType; + group->PRScope = prScope; + group->PRKeyValid = TRUE; + deviceInfo->PRKeyRegistered = TRUE; + } + + if (!sendDownAll) { + + // + // Need for retrying on another path only necessary in the case + // of request failing down the chosen path. Since the request + // succeeded down this path, we are done. + // + break; + } + } else { + + BOOLEAN recordFailure; + + // + // Check to see if the request failed because of a "transient error", + // like reservations released for example. If so, this is NOT an actual + // error and the request must be retried. Multiple retries may be required + // if for example the UA indicates that the TPGs are in transitioning state. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + Srb->SrbStatus & SRB_STATUS_ERROR && + SrbGetScsiStatus(Srb) == SCSISTAT_CHECK_CONDITION) { + + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + + if (DsmpShouldRetryPersistentReserveCommand(senseInfoBuffer, senseInfoBufferLength) && + currentTickCount < finalTickCount) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u returned UA with error %x. Retrying same path %p.\n", + DsmIds, + serviceAction, + status, + deviceInfo->FailGroup->PathId)); + + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + + goto __DsmpPersistentReserveOut_RetryRequest; + } + } + + // + // The return status is STATUS_SUCCESS by default. This means that if the + // request failed on the first path and was retried down every other path + // but fails down all of them, the return status is never updated. + // So cache the first failure status to cover the above scenario. + // + if (!statusUpdated) { + + returnStatus = status; + statusUpdated = TRUE; + } + + recordFailure = TRUE; + if (ignoreIfPreviousFailed && !deviceInfo->PRKeyRegistered) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Ignoring status %x for path %p.\n", + DsmIds, + serviceAction, + status, + deviceInfo->FailGroup->PathId)); + + // + // Okay to ignore this failure if the previous failed. + // + recordFailure = FALSE; + } + + if (passOnlyIfAllSucceed && recordFailure) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Saving status %x for return.\n", + DsmIds, + serviceAction, + status)); + + // + // Save the failure status to return back. + // + returnStatus = status; + } + + // + // If the request is not to be sent down all paths, and also + // a retry (along a different path) on failure is not required, + // we're done - just return this failure. + // + if (!(sendDownAll || retryOnAnother)) { + + returnStatus = status; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down %p failed with %x. Breaking out.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + + break; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down %p failed with %x. Sending down another path.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + } + } + + // + // If we are here, it is either because the request needs to be sent down + // all paths, or because the request failed down the chosen path and needs + // to be retried down a new path. + // + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + } + + if (clearPRKey) { + + for (i = 0; (ULONG)i < group->NumberDevices; i++) { + + deviceInfo = group->DeviceList[i]; + + if (deviceInfo) { + + deviceInfo->RegisterServiced = FALSE; + deviceInfo->PRKeyRegistered = FALSE; + } + } + group->PersistentReservationRegisteredKey[0] = group->PersistentReservationRegisteredKey[1] = + group->PersistentReservationRegisteredKey[2] = group->PersistentReservationRegisteredKey[3] = + group->PersistentReservationRegisteredKey[4] = group->PersistentReservationRegisteredKey[5] = + group->PersistentReservationRegisteredKey[6] = group->PersistentReservationRegisteredKey[7] = 0; + + group->PRKeyValid = FALSE; + group->ReservationList = 0; + } + + if (savePRKeyIfAnySucceed && group->PRKeyValid) { + + ULONG ordinal; + + for (i = 0; (ULONG)i < group->NumberDevices; i++) { + + deviceInfo = group->DeviceList[i]; + + if (deviceInfo) { + + deviceInfo->RegisterServiced = TRUE; + ordinal = (1 << i); + group->ReservationList |= ordinal; + } + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT for %u completed with status %x.\n", + DsmIds, + serviceAction, + returnStatus)); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + +__Exit_DsmpPersistentReserveOut: + + if (srbCopy != NULL) { + DsmpFreePool(srbCopy); + } + + currentIrpStack->Parameters.Others.Argument3 = servicingDeviceInfo; + Irp->IoStatus.Status = returnStatus; + if ((!NT_SUCCESS(returnStatus)) && + (SrbGetSrbStatus(Srb) == SRB_STATUS_SUCCESS)) { + SrbSetSrbStatus(Srb, DsmpNtStatusToSrbStatus(returnStatus)); + } + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Exiting function returning IRP status %x.\n", + DsmIds, + returnStatus)); + + return returnStatus; +} + + + +NTSTATUS +DsmpPersistentReserveIn( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine will handle determine which devices to send the request to based + on the service action of the PR-in command. + On READ KEYS, it will send down one path. In case of failure, other paths will + be tried until one succeeds. Failure is returned only if it fails down all paths. + On READ_RESERVATION/REPORT_CAPABILITIES, command is sent down one path. Failed + request is not retried. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - The event to + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + PDSM_DEVICE_INFO servicingDeviceInfo = NULL; + PDSM_GROUP_ENTRY group; + LONG i; + ULONG count; + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDSM_COMPLETION_CONTEXT completionContext; + PCDB cdb = SrbGetCdb(Srb); + UCHAR serviceAction; + BOOLEAN retryOnAnother = FALSE; + KEVENT event; + PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; + PIO_STACK_LOCATION irpStack; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONGLONG currentTickCount; + ULONGLONG finalTickCount; + ULONG tickLength = KeQueryTimeIncrement(); + PVOID senseInfoBuffer = NULL; + UCHAR senseInfoBufferLength = 0; + BOOLEAN srbCopySucceeded = FALSE; + ULONG SpecialHandlingFlag = 0; + + UNREFERENCED_PARAMETER(Event); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Cache away a copy of the SRB + // + srbCopy = SrbAllocateCopy(Srb, NonPagedPoolNx, DSM_TAG_SCSI_REQUEST_BLOCK); + if (srbCopy == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpPersistentReserveIn; + } + + deviceInfo = DsmIds->IdList[0]; + group = deviceInfo->Group; + + serviceAction = cdb->PERSISTENT_RESERVE_IN.ServiceAction; + + switch (serviceAction) { + case RESERVATION_ACTION_READ_RESERVATIONS: + case RESERVATION_ACTION_READ_KEYS: { + + // + // If there is a failure on the chosen path, retry on another path. + // + retryOnAnother = TRUE; + break; + } + + case SPC3_RESERVATION_ACTION_REPORT_CAPABILITIES: { + + break; + } + + default: { + + NT_ASSERT(FALSE); + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpPersistentReserveIn; + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Srb %p. Service Action %u.\n", + DsmIds, + Srb, + serviceAction)); + + // + // Allocate a context for the completion routine. + // + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + if (!completionContext) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - Failed to allocate completion context.\n", + DsmIds, + serviceAction)); + + goto __Exit_DsmpPersistentReserveIn; + } + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Indicate the target for this request. + // + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = (PVOID)&event; + completionContext->RequestUnique2 = cdb->PERSISTENT_RESERVE_IN.OperationCode; + + count = group->NumberDevices; + + for (i = count - 1; i >= 0; i--) { + + // + // A PR command may fail with a "retry-able" UA when reservation is + // released or preempted (on every I_T_L nexus except the one on which + // it was released/preempted). In such a case we should retry the PR + // command on the same path. + // + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + finalTickCount = currentTickCount + (DSM_SECONDS_TO_TICKS(group->MaxPRRetryTimeDuringStateTransition) / tickLength); + + if (!retryOnAnother) { + + // + // If the request doesn't need to be retried (down another path) on + // failure, better choose the path that has maximum chances of + // success. + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), + SpecialHandlingFlag); + + if (!deviceInfo) { + + status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - No active/alternative path for device %p.\n", + DsmIds, + serviceAction, + group)); + + break; + } + + } else { + + deviceInfo = group->DeviceList[i]; + + if (DsmpIsDeviceFailedState(deviceInfo->State) || !DsmpIsDeviceInitialized(deviceInfo)) { + + // + // Ignore "bad" paths for now. + // + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Ignoring bad instance - state %x, init %x.\n", + DsmIds, + deviceInfo->State, + deviceInfo->Initialized)); + + deviceInfo = NULL; + } + } + + if (!deviceInfo) { + + // + // Maybe a remove came through and caused a collapse of the device + // list, thus making this entry empty. + // + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - Couldn't find path for device %p.\n", + DsmIds, + serviceAction, + group)); + + continue; + } + +__DsmpPersistentReserveIn_RetryRequest: + + IoMarkIrpPending(Irp); + + completionContext->DeviceInfo = deviceInfo; + + // + // Set-up a completion routine. + // + IoSetCompletionRoutine(Irp, + DsmpPersistentReserveCompletion, + completionContext, + TRUE, + TRUE, + TRUE); + + // + // Always send the original request down a new path + // + irpStack = IoGetNextIrpStackLocation(Irp); + srbCopySucceeded = SrbCopySrb(Srb, SrbGetSrbLength(Srb), srbCopy); + NT_ASSERT(srbCopySucceeded == TRUE); + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Clear the sense buffer if it exists + // + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + if (senseInfoBuffer) { + RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); + } + + servicingDeviceInfo = deviceInfo; + + // + // Issue the request and wait. + // + status = DsmSendRequest(DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo); + + if (status == STATUS_PENDING) { + + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + + status = Irp->IoStatus.Status; + } + + if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u sent down successfully on %p.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId)); +#if DBG + if (serviceAction == RESERVATION_ACTION_READ_KEYS) { + + PPRI_REGISTRATION_LIST prInRegistrationList = Irp->AssociatedIrp.SystemBuffer; + ULONG numberOfKeys; + ULONG keyIndex; + ULONGLONG prKey; + + REVERSE_BYTES(&numberOfKeys, &prInRegistrationList->AdditionalLength); + numberOfKeys /= 8; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): %u registrations keys present:\n", + DsmIds, + numberOfKeys)); + + for (keyIndex = 0; keyIndex < numberOfKeys; keyIndex++) { + + REVERSE_BYTES_QUAD(&prKey, &(prInRegistrationList->ReservationKeyList[keyIndex])); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Registration Key %u: %I64x\n", + DsmIds, + keyIndex, + prKey)); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "\n")); + + } else if (serviceAction == RESERVATION_ACTION_READ_RESERVATIONS) { + + PPRI_RESERVATION_LIST prInReservationList = Irp->AssociatedIrp.SystemBuffer; + ULONG numberOfDescriptors; + PPRI_RESERVATION_DESCRIPTOR prInReservationDescriptor = prInReservationList->Reservations; + ULONGLONG prKey = 0; + + REVERSE_BYTES(&numberOfDescriptors, &prInReservationList->AdditionalLength); + numberOfDescriptors /= sizeof(PRI_RESERVATION_DESCRIPTOR); + NT_ASSERT(numberOfDescriptors <= 1); + + if (numberOfDescriptors == 1) { + REVERSE_BYTES_QUAD(&prKey, &prInReservationDescriptor->ReservationKey); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): %u Reservation Key: %I64x\n", + DsmIds, + numberOfDescriptors, + prKey)); + } +#endif + // + // Done. + // + break; + + } else { + + // + // Check to see if the request failed because of a "transient error", + // like reservations released for example. If so, this is NOT an actual + // error and the request must be retried. Multiple retries may be required + // if for example the UA indicates that the TPGs are in transitioning state. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + Srb->SrbStatus & SRB_STATUS_ERROR && + SrbGetScsiStatus(Srb) == SCSISTAT_CHECK_CONDITION) { + + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + + if (group->PRKeyValid && + DsmpShouldRetryPersistentReserveCommand(senseInfoBuffer, senseInfoBufferLength) && + currentTickCount < finalTickCount) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u returned UA with error %x. Retrying same path %p.\n", + DsmIds, + serviceAction, + status, + deviceInfo->FailGroup->PathId)); + + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + + goto __DsmpPersistentReserveIn_RetryRequest; + } + } + + // + // If a retry (along a different path) on failure is not required, + // we're done - just return this failure. + // + if (!retryOnAnother) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u down %p failed with %x. Breaking out.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + + break; + } + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u down %p failed with %x. Sending down another path.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + + // + // If we are here, it is because the request failed down the chosen path + // and needs to be retried down a new path. + // + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u completed with status %x.\n", + DsmIds, + serviceAction, + status)); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + +__Exit_DsmpPersistentReserveIn: + + if (srbCopy != NULL) { + DsmpFreePool(srbCopy); + } + + currentIrpStack->Parameters.Others.Argument3 = servicingDeviceInfo; + Irp->IoStatus.Status = status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Exiting function returning IRP status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpPersistentReserveCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + General-purpose completion routine for PR in and out commands sent synchronously. + +Arguments: + + DeviceObject - Target of the request. + Irp - Command being sent. + Context - The event on which the caller is waiting. + +Return Value: + + NTSTATUS + +--*/ + +{ + PDSM_COMPLETION_CONTEXT context = Context; + PKEVENT event; + + // It is required to specify a DSM completion context + // when setting DsmpPersistentReserveCompletion as completion routine. + _Analysis_assume_(context != NULL); + + event = (PKEVENT)(context->RequestUnique1); + + UNREFERENCED_PARAMETER(DeviceObject); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveCompletion: DevInfo %p, IRP %p, Context %p\n", + context->DeviceInfo, + Irp, + Context)); + + if (Irp->PendingReturned) { + + IoMarkIrpPending(Irp); + } + + KeSetEvent(event, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + diff --git a/tests/projects/wdk/wdm/msdsm/dsmtrace.mof b/tests/projects/wdk/wdm/msdsm/dsmtrace.mof new file mode 100644 index 000000000..d9e8cf4cc --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/dsmtrace.mof @@ -0,0 +1,111 @@ +#pragma classflags("forceupdate") +#pragma namespace("\\\\.\\root\\WMI") +// +// Copyright (C) 2004 Microsoft Corporation +// +// WPP Generated File +// + +//ModuleName = wppCtlGuid (Init called in Function DriverEntry) +[Dynamic, + Description("MSDSM Driver Tracing Provider"), + guid("{DEDADFF5-F99F-4600-B8C9-2D4D9B806B5B}"), + locale("MS\\0x409")] +class MSDSMGuid : EventTrace +{ + [Description ("Enable Flags"), + ValueDescriptions{ + "TRACE_FLAG_GENERAL Flag", + "TRACE_FLAG_PNP Flag", + "TRACE_FLAG_POWER Flag", + "TRACE_FLAG_RW Flag", + "TRACE_FLAG_IOCTL Flag", + "TRACE_FLAG_QUEUE Flag", + "TRACE_FLAG_WMI Flag", + "TRACE_FLAG_TIMER Flag", + "TRACE_FLAG_INIT Flag", + "TRACE_FLAG_LOCK Flag", + "TRACE_FLAG_DEBUG1 Flag", + "TRACE_FLAG_DEBUG2 Flag", + "TRACE_FLAG_MCN Flag", + "TRACE_FLAG_ISR Flag", + "TRACE_FLAG_ENUM Flag"}, + DefineValues{ + "TRACE_FLAG_GENERAL", + "TRACE_FLAG_PNP", + "TRACE_FLAG_POWER", + "TRACE_FLAG_RW", + "TRACE_FLAG_IOCTL", + "TRACE_FLAG_QUEUE", + "TRACE_FLAG_WMI", + "TRACE_FLAG_TIMER", + "TRACE_FLAG_INIT", + "TRACE_FLAG_LOCK", + "TRACE_FLAG_DEBUG1", + "TRACE_FLAG_DEBUG2", + "TRACE_FLAG_MCN", + "TRACE_FLAG_ISR", + "TRACE_FLAG_ENUM"}, + Values{ + "TRACE_FLAG_GENERAL", + "TRACE_FLAG_PNP", + "TRACE_FLAG_POWER", + "TRACE_FLAG_RW", + "TRACE_FLAG_IOCTL", + "TRACE_FLAG_QUEUE", + "TRACE_FLAG_WMI", + "TRACE_FLAG_TIMER", + "TRACE_FLAG_INIT", + "TRACE_FLAG_LOCK", + "TRACE_FLAG_DEBUG1", + "TRACE_FLAG_DEBUG2", + "TRACE_FLAG_MCN", + "TRACE_FLAG_ISR", + "TRACE_FLAG_ENUM"}, + ValueMap{ + "0x00000001", + "0x00000002", + "0x00000004", + "0x00000008", + "0x00000010", + "0x00000020", + "0x00000040", + "0x00000080", + "0x00000100", + "0x00000200", + "0x00000400", + "0x00000800", + "0x00001000", + "0x00002000", + "0x00004000"} + ] + uint32 Flags; + [Description ("Levels"), + ValueDescriptions{ + "Abnormal exit or termination", + "Severe errors that need logging", + "Warnings such as allocation failure", + "Includes non-error cases", + "Detailed traces from intermediate steps" }, + DefineValues{ + "TRACE_LEVEL_FATAL", + "TRACE_LEVEL_ERROR", + "TRACE_LEVEL_WARNING" + "TRACE_LEVEL_INFORMATION", + "TRACE_LEVEL_VERBOSE" }, + Values{ + "Fatal", + "Error", + "Warning", + "Information", + "Verbose" }, + ValueMap{ + "0x1", + "0x2", + "0x3", + "0x4", + "0x5" }, + ValueType("index") + ] + uint32 Level; +}; diff --git a/tests/projects/wdk/wdm/msdsm/intrface.c b/tests/projects/wdk/wdm/msdsm/intrface.c new file mode 100644 index 000000000..eeebf7f93 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/intrface.c @@ -0,0 +1,5198 @@ +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + intrface.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM) + devices that conform with SPC-3 specs. + It exports behaviors that mpio.sys will use to determine how to + multipath these devices. + + This file contains DriverEntry and all the functions that are + exported to MPIO. + + This DSM is targetted towards Windows 2008 and above. + +Environment: + + kernel mode only + +--*/ + +#include "precomp.h" + +#ifdef DEBUG_USE_WPP +#include "intrface.tmh" +#endif + +#pragma warning (disable:4305) + + +// +// Flag to indicate whether to NT_ASSERT or ignore a particular condition. +// +BOOLEAN DoAssert = TRUE; + +// +// OS Version Info +// MSDSM is targetted towards Windows Server 2008 and above. +// +BOOLEAN gServer2008AndAbove = FALSE; + +// +// Global to cache MPIO's Control Object. +// +PDEVICE_OBJECT gMPIOControlObject = NULL; + +// +// Flag to indicate if the MPIO control object was referenced. +// +BOOLEAN gMPIOControlObjectRefd = FALSE; + +// +// Global to cache the Driver Object. +// +PDRIVER_OBJECT gDsmDriverObject = NULL; + + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) +#endif + +// +// The code. +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine is called when the driver is loaded. + +Arguments: + + DriverObject - Supplies the driver object. + RegistryPath - Supplies the registry path. + +Return Value: + + NTSTATUS + +--*/ +{ + PDSM_CONTEXT dsmContext = NULL; + PFILE_OBJECT fileObject; + WCHAR dosDeviceName[64] = DSM_MPIO_CONTROL_OBJECT_SYMLINK; + UNICODE_STRING mpUnicodeName; + NTSTATUS status = STATUS_SUCCESS; + MPIO_VERSION_INFO versionInfo = {0}; + DSM_TYPE dsmMode = DsmType3; + DSM_MPIO_CONTEXT mpctlContext; + IO_STATUS_BLOCK ioStatus; + + + // + // Initialize the tracing subsystem. + // Any failure is handled by ETW itself. + // + WPP_INIT_TRACING(DriverObject, RegistryPath); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Entering function.\n", + DriverObject)); + + gDsmDriverObject = DriverObject; + + // + // Determine the OS version. + // + gServer2008AndAbove = RtlIsNtDdiVersionAvailable(NTDDI_VISTA); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Server2008AndAbove is %!bool!.\n", + DriverObject, + gServer2008AndAbove)); + + // + // MSDSM is supported only on Server 2008 and above. + // + if (!gServer2008AndAbove) { + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DriverEntry; + } + + // + // Build the mpio symbolic link name. + // + RtlInitUnicodeString(&mpUnicodeName, dosDeviceName); + + // + // Get a pointer to mpio's deviceObject. + // + status = IoGetDeviceObjectPointer(&mpUnicodeName, + FILE_READ_ATTRIBUTES, + &fileObject, + &gMPIOControlObject); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Failed to communicate with MPIO control object. Status %x.\n", + DriverObject, + status)); + + goto __Exit_DriverEntry; + } + + ObReferenceObject(gMPIOControlObject); + gMPIOControlObjectRefd = TRUE; + ObDereferenceObject(fileObject); + + status = DsmGetVersion(&versionInfo, sizeof(MPIO_VERSION_INFO)); + + if (!NT_SUCCESS(status)) { + + // + // If we can't get the version, that means we aren't using a compatible + // version of MPIO drivers and so should not continue. + // + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): MPIO version unknown - DSM exiting.\n", + DriverObject)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DriverEntry; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): MPIO version %d.%d.%d.%d.\n", + DriverObject, + versionInfo.MajorVersion, + versionInfo.MinorVersion, + versionInfo.ProductBuild, + versionInfo.QfeNumber)); + + RtlZeroMemory(&gDsmInitData, sizeof(DSM_INIT_DATA)); + + // + // Must be newer than 1.0.7.0 to support DSM type 2 upwards. + // + if ((versionInfo.MajorVersion > 1) || + (versionInfo.MinorVersion >= 1) || + (versionInfo.ProductBuild > 7) || + (versionInfo.QfeNumber >= 1)) { + + // + // Must be newer than 1.18 to support DSM's versioning + // + if (versionInfo.MajorVersion > 1 || + versionInfo.MinorVersion > 17) { + + dsmMode = DsmType6; + + { + RTL_OSVERSIONINFOW osVersion = {0}; + + osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + RtlGetVersion(&osVersion); + + gDsmInitData.DsmVersion.MajorVersion = osVersion.dwMajorVersion; + gDsmInitData.DsmVersion.MinorVersion = osVersion.dwMinorVersion; + gDsmInitData.DsmVersion.ProductBuild = osVersion.dwBuildNumber; + gDsmInitData.DsmVersion.QfeNumber = 0; + } + } + } else { + + // + // We cannot use this DSM with older versions of the MPIO drivers. + // + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): MPIO version not supported - DSM exiting.\n", + DriverObject)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DriverEntry; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Setting DSM type to %d.\n", + DriverObject, + dsmMode)); + + // + // Build the init data structure. + // + dsmContext = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_CONTEXT), + DSM_TAG_DSM_CONTEXT); + if (!dsmContext) { + + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Failed to allocate memory for DSM Context.\n", + DriverObject)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DriverEntry; + } + + // + // Set-up the init data + // + gDsmInitData.DsmContext = (PVOID) dsmContext; + gDsmInitData.InitDataSize = sizeof(DSM_INIT_DATA); + + gDsmInitData.DsmInquireDriver = DsmInquire; + gDsmInitData.DsmCompareDevices = DsmCompareDevices; + gDsmInitData.DsmGetControllerInfo = DsmGetControllerInfo; + gDsmInitData.DsmSetDeviceInfo = DsmSetDeviceInfo; + gDsmInitData.DsmIsPathActive = DsmIsPathActive; + gDsmInitData.DsmPathVerify = DsmPathVerify; + gDsmInitData.DsmInvalidatePath = DsmInvalidatePath; + gDsmInitData.DsmMoveDevice = DsmMoveDevice; + gDsmInitData.DsmRemovePending = DsmRemovePending; + gDsmInitData.DsmRemoveDevice = DsmRemoveDevice; + gDsmInitData.DsmRemovePath = DsmRemovePath; + gDsmInitData.DsmSrbDeviceControl = DsmSrbDeviceControl; + gDsmInitData.DsmLBGetPath = DsmLBGetPath; + gDsmInitData.DsmInterpretErrorEx = DsmInterpretError; + gDsmInitData.DsmUnload = DsmUnload; + gDsmInitData.DsmSetCompletion = DsmSetCompletion; + gDsmInitData.DsmCategorizeRequest = DsmCategorizeRequest; + gDsmInitData.DsmBroadcastSrb = DsmBroadcastRequest; + gDsmInitData.DsmIsAddressTypeSupported = DsmIsAddressTypeSupported; + gDsmInitData.DsmDeviceNotUsed = DsmDeviceNotUsed; + + // + // Since MSDSM is for SPC-3 compliant devices, MPIO should be able to build + // a serial number for the device. + // + gDsmInitData.DsmDeviceSerialNumber = NULL; + + // + // Notifies MPIO of the appropriate Type support + // + gDsmInitData.DsmType = dsmMode; + + gDsmInitData.DriverObject = DriverObject; + + + // + // Set-up the WMI Info. + // + DsmpWmiInitialize(&gDsmInitData.DsmWmiInfo, RegistryPath); + DsmpDsmWmiInitialize(&gDsmInitData.DsmWmiGlobalInfo, RegistryPath); + + RtlInitUnicodeString(&gDsmInitData.DisplayName, DSM_FRIENDLY_NAME); + + // + // Initialize some of the fields in DSM Context structure. + // + KeInitializeSpinLock(&dsmContext->SupportedDevicesListLock); + InitializeListHead(&dsmContext->GroupList); + InitializeListHead(&dsmContext->DeviceList); + InitializeListHead(&dsmContext->FailGroupList); + InitializeListHead(&dsmContext->ControllerList); + InitializeListHead(&dsmContext->StaleFailGroupList); + + // + // Build the list context structures used for completion processing. + // + ExInitializeNPagedLookasideList(&dsmContext->CompletionContextList, + NULL, + NULL, + POOL_NX_ALLOCATION, + sizeof(DSM_COMPLETION_CONTEXT), + DSM_TAG_GENERIC, + 0); + + RtlZeroMemory(&mpctlContext, sizeof(DSM_MPIO_CONTEXT)); + + // + // Send the IOCTL to mpio.sys to register ourselves. + // + DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_REGISTER, + gMPIOControlObject, + &gDsmInitData, + &mpctlContext, + sizeof(DSM_INIT_DATA), + sizeof(DSM_MPIO_CONTEXT), + TRUE, + &ioStatus); + + status = ioStatus.Status; + + if (NT_SUCCESS(status)) { + + dsmContext->MPIOContext = mpctlContext.MPIOContext; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Registered with MPIO.\n", + DriverObject)); + + DriverObject->DriverUnload = DsmDriverUnload; + + // + // Query the registry for disabling/enabling statistics gathering + // + if (STATUS_OBJECT_NAME_NOT_FOUND == DsmpGetStatsGatheringChoice(dsmContext, (PULONG)&dsmContext->DisableStatsGathering)) { + + // + // If the value does not exist, write the default to registry. + // + DsmpSetStatsGatheringChoice(dsmContext, (ULONG)dsmContext->DisableStatsGathering); + } + } + +__Exit_DriverEntry: + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Exiting function successfully.\n", + DriverObject)); + } else { + + // + // Since the DSM is going to be unloaded but without DriverUnload being + // called, we need to perform cleanup here. + // + if (dsmContext != NULL) { + DsmpFreeDSMResources(dsmContext); + dsmContext = NULL; + } + + if (gMPIOControlObjectRefd) { + + // + // Drop the reference on MPIO's control object. + // + ObDereferenceObject(gMPIOControlObject); + gMPIOControlObjectRefd = FALSE; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Exiting function with status %x.\n", + DriverObject, + status)); + + // + // Stop the tracing subsystem. + // NOTE: once we unregister ETW, no more TracePrint can be done, so we + // must ensure that ETW unregister is the last thing that happens. + // + WPP_CLEANUP(gDsmDriverObject); + } + + return status; +} + + +VOID +DsmDriverUnload( + _In_ IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine is called when the driver is unloaded. + +Arguments: + + DriverObject - Supplies the driver object. + +Return Value: + + Nothing + +--*/ +{ + DSM_DEREGISTER_DATA deregisterData; + IO_STATUS_BLOCK ioStatus; + + deregisterData.DeregisterDataSize = sizeof(DSM_DEREGISTER_DATA); + deregisterData.DriverObject = DriverObject; + deregisterData.DsmContext = gDsmInitData.DsmContext; + deregisterData.MpioContext = ((PDSM_CONTEXT)(gDsmInitData.DsmContext))->MPIOContext; + // + // Send the IOCTL to mpio.sys to de-register ourselves. + // + DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_DEREGISTER, + gMPIOControlObject, + &deregisterData, + NULL, + sizeof(DSM_DEREGISTER_DATA), + 0, + TRUE, + &ioStatus); + + NT_ASSERT(NT_SUCCESS(ioStatus.Status)); + + + + return; +} + + +NTSTATUS +DsmInquire( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetDevice, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, + _Out_ OUT PVOID *DsmIdentifier + ) +/*++ + +Routine Description: + + This routine is used to determine if TargetDevice belongs to + the DSM. If this is a supported device DsmIdentifier will be + updated with 'deviceInfo'. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + TargetDevice - DeviceObject for the child device. + PortObject - The Port driver FDO on which TargetDevice resides. + Descriptor - Pointer to the device descriptor corresponding to TargetDevice. + Rehash of inquiry data, plus serial number information + (if applicable). + DeviceIdList - VPD Page 0x83 information. + DsmIdentifier - Pointer to be filled in by the DSM on success. + +Return Value: + + STATUS_NOT_SUPPORTED - if not on the SupportList. + STATUS_INSUFFICIENT_RESOURCES - No mem. + STATUS_SUCCESS +--*/ +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = NULL; + PDSM_GROUP_ENTRY group; + BOOLEAN newGroup; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; + PDSM_TARGET_PORT_LIST_ENTRY targetPortEntry = NULL; + PSTR serialNumber = NULL; + SIZE_T serialNumberLength = 0; + NTSTATUS status; + ULONG allocationLength; + BOOLEAN serialNumberAllocated = FALSE; + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + BOOLEAN supported = FALSE; + BOOLEAN spinlockHeld = FALSE; + UCHAR vendorId[9] = {0}; + UCHAR productId[17] = {0}; + INQUIRYDATA inquiryData; + UCHAR alua = DSM_DEVINFO_ALUA_NOT_SUPPORTED; + ULONG index; + PDSM_IDS controllerObjects = NULL; + PDEVICE_OBJECT controllerDeviceObject; + PLIST_ENTRY entry = NULL; + PSTORAGE_DESCRIPTOR_HEADER controllerIdHeader = NULL; + PULONG relativeTargetPortId = NULL; + PUSHORT targetPortGroupId = NULL; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PSTR controllerSerialNumber; + BOOLEAN match = FALSE; + BOOLEAN doneUpdating = FALSE; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; + PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device = NULL; + PWSTR hardwareId = NULL; + PWCHAR deviceName = NULL; + ULONG tempResult = 0; + ULONG maxPRRetryTimeDuringStateTransition = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME; + BOOLEAN useCacheForLeastBlocks = FALSE; + ULONGLONG cacheSizeForLeastBlocks = 0; + BOOLEAN fakeControllerEntryExists = FALSE; + STORAGE_IDENTIFIER_CODE_SET serialNumberCodeSet = StorageIdCodeSetReserved; + +#if DBG + BOOLEAN multiport; +#endif + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Entering function.\n", + TargetDevice)); + + // + // 1. Get standard inquiry for the device. Check if SPC-3 compliant. + // If not compliant, check SupportedDeviceList. + // 2. Create device serial number. + // 3. Create a partially populated deviceInfo. + // DeviceDescriptor. + // SCSI address. + // Save off serial number. + // ALUA, port FDO, etc. + // 4. Create device name. + // 5. If ALUA support, send down Report Target Port Groups. + // 6. Find the group. If none, build one. + // 7. If new group, build target port groups and target ports info. + // Else, update target port groups and target ports info. + // 8. If both implicit as well as explicit transitions allowed, disable implicit. + // 9. Get list of controllers objects and get VPD 0x83 for each (only if no + // match for existing ones). + // Match returned ids of type 0x5 with what was returned in Report Target Port Groups. + // If no type 0x5 identifier, use SCSI address. + // Create controller list (delete stale entries). + // + + + // + // Query the registry to find out what devices are being supported + // on this machine. + // + DsmpGetDeviceList(dsmContext); + + status = DsmpGetStandardInquiryData(TargetDevice, &inquiryData); + + if (NT_SUCCESS(status)) { + + supported = DsmpCheckScsiCompliance(TargetDevice, + &inquiryData, + Descriptor, + DeviceIdList); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to get inquiry data with status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Since the device isn't SPC-3 compliant, check if the device is on the + // SupportedDeviceList. + // + if (!supported) { + + + if (!supported) { + + // + // Get the inquiry data embedded in the device descriptor. + // + RtlStringCchCopyA((LPSTR)vendorId, + sizeof(vendorId) / sizeof(vendorId[0]), + (LPCSTR)(&inquiryData.VendorId)); + + RtlStringCchCopyA((LPSTR)productId, + sizeof(productId) / sizeof(productId[0]), + (LPCSTR)(&inquiryData.ProductId)); + + supported = DsmpDeviceSupported(dsmContext, + (PCSZ)vendorId, + (PCSZ)productId); + } + + if (!supported) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Unsupported Device.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + + // + // Find out if device can be accessed via mulitple ports. This info is + // important since it will determine whether or not to send down a + // ReportTargetPortGroups command. + // +#if DBG + multiport = (inquiryData.MultiPort & 0x10) ? TRUE : FALSE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Is %ws multiported.\n", + TargetDevice, + multiport ? L"" : L"not")); +#endif + + // + // Query the assymmetric states transition method + // + switch ((inquiryData.Reserved >> 0x4) & 0x3) { + case 1: alua = DSM_DEVINFO_ALUA_IMPLICIT; + break; + + case 2: alua = DSM_DEVINFO_ALUA_EXPLICIT; + break; + + case 3: alua = DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT; + break; + + default: alua = DSM_DEVINFO_ALUA_NOT_SUPPORTED; + break; + } + + // + // Get some information about this device. The preferred info is + // from the Device ID Page. + // + if (DeviceIdList) { + + // + // This will parse out the 'best' identifier and return + // a NULL-terminated ascii string. + // + serialNumber = (PSTR)DsmpParseDeviceID(DeviceIdList, + DSM_DEVID_SERIAL_NUMBER, + NULL, + &serialNumberCodeSet, + FALSE); + + if (!serialNumber) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): NULL serial number.\n", + TargetDevice)); + + // + // Either an allocation failed, or the DeviceIdList is malformed. + // + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Indicate that the serialnumber buffer is allocated. + // + serialNumberAllocated = TRUE; + serialNumberLength = strlen((const char*)serialNumber); + + } else { + + // + // Get the serial number of this device. Use the serial number + // page (0x80). Ensure that the device's serial number is + // present. If not, can't claim support for this drive. + // + + if (!Descriptor || + (Descriptor->SerialNumberOffset == MAXULONG) || + (Descriptor->SerialNumberOffset == 0)) { + + // + // The port driver currently doesn't get the VPD page 0x80, + // if the device doesn't support GET_SUPPORTED_PAGES. Check to + // see whether there actually is a serial number. + // + serialNumber = DsmpGetSerialNumber(TargetDevice); + + if (!serialNumber) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): serialNumber = NULL.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + + } else { + serialNumberAllocated = TRUE; + serialNumberLength = strlen((const char*)serialNumber); + } + } + } + + // + // Allocate for the device. This is also used as DsmId. + // + allocationLength = sizeof(DSM_DEVICE_INFO); + + // + // As DSM_DEVICE_INFO has storage for the descriptor, add only + // the additional stuff that's at the end. + // + if (Descriptor) { + status = RtlULongSub(Descriptor->Size, sizeof(STORAGE_DEVICE_DESCRIPTOR), &tempResult); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Arithmetic underflow - status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + + status = RtlULongAdd(allocationLength, tempResult, &allocationLength); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Arithmetic overflow - status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + deviceInfo = DsmpAllocatePool(NonPagedPoolNx, + allocationLength, + DSM_TAG_DEV_INFO); + if (!deviceInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to allocate Device Info.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + deviceInfo->State = deviceInfo->PreviousState = deviceInfo->TempPreviousStateForLB = deviceInfo->ALUAState = deviceInfo->LastKnownGoodState = DSM_DEV_NOT_USED_STATE; + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + // + // Copy over the StorageDescriptor. + // + if (Descriptor) { + RtlCopyMemory(&deviceInfo->Descriptor, + Descriptor, + Descriptor->Size); + } + + // + // Get the scsi address for this device. Note that on success, DsmGetScsiAddress() + // will allocate memory which we are responsible for freeing. + // + status = DsmGetScsiAddress(TargetDevice, + &deviceInfo->ScsiAddress); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Error %x while getting scsi address.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Capture the serial number allocated flag. + // + deviceInfo->SerialNumberAllocated = serialNumberAllocated; + + // + // Set the serial number. + // + if (!serialNumberAllocated) { + + PSTORAGE_DEVICE_DESCRIPTOR descriptor; + + // + // serialNumber is not pointing to the buffer passed by MPIO. Update + // it to point to the Device Descriptor allocated by the DSM. + // + descriptor = &(deviceInfo->Descriptor); + + NT_ASSERT(descriptor->SerialNumberOffset != 0 && descriptor->SerialNumberOffset != MAXULONG); + + serialNumber = (PCHAR)descriptor + descriptor->SerialNumberOffset; + serialNumberLength = strlen((const char*)serialNumber); + } + + if (alua == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT)) { + + BOOLEAN disableImplicit = FALSE; + + status = DsmpDisableImplicitStateTransition(TargetDevice, &disableImplicit); + + if (NT_SUCCESS(status)) { + + if (disableImplicit) { + + alua &= ~DSM_DEVINFO_ALUA_IMPLICIT; + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Disabled implicit ALUA state transition.\n", + TargetDevice)); + + // + // Record that the storage actually supported implicit also, but we + // turned it OFF. + // + deviceInfo->ImplicitDisabled = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Storage support both transitions but does NOT allow disabling Implicit.\n", + TargetDevice)); + } + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to disable implicit ALUA state transitions - status %x.\n", + TargetDevice, + status)); + } + } + + deviceInfo->SerialNumber = serialNumber; + + // + // Save the Physical Device Object (PDO) of the device. + // Used to verify that no two devices have the same PDO. + // + deviceInfo->PortPdo = TargetDevice; + + // + // Save the FDO of the adapter. Used for handling reserve\release + // + deviceInfo->PortFdo = PortObject; + + // + // Set the signature. + // + deviceInfo->DeviceSig = DSM_DEVICE_SIG; + + deviceInfo->DsmContext = DsmContext; + + deviceInfo->ALUASupport = alua; + + // + // Build the name (using serialnumber) that will be used as registry key + // to store Load Balance settings for this device. + // + deviceName = DsmpBuildDeviceName(deviceInfo, serialNumber, serialNumberLength); + + if (!deviceName) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to allocate device name for %p.\n", + TargetDevice, + deviceInfo)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + + // + // Send down ReportTargetPortGroups command and keep the info handy. + // + if (alua != DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + status = DsmpReportTargetPortGroups(TargetDevice, + &targetPortGroupsInfo, + &targetPortGroupsInfoLength); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to report target port groups for %p. Status %x.\n", + TargetDevice, + deviceInfo, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // We've just sent down an RTPG (relatively expensive operation), and it + // succeeded, so sending down one more as part part of the initialization + // in PathVerify() since it is going to be called almost immediately. + // + deviceInfo->IgnorePathVerify = TRUE; + } + + // + // Query the registry for max time to retry failed PR requests + // + DsmpGetMaxPRRetryTime(DsmContext, &maxPRRetryTimeDuringStateTransition); + + // + // Query the registry to see if the user has overridden the default + // Least Blocks settings. + // + status = DsmpQueryCacheInformationFromRegistry(DsmContext, + &useCacheForLeastBlocks, + &cacheSizeForLeastBlocks); + + if (!NT_SUCCESS(status)) { + // + // Couldn't get the settings from the registry so fall back on the + // default for Least Blocks. + // + useCacheForLeastBlocks = TRUE; + cacheSizeForLeastBlocks = DSM_LEAST_BLOCKS_DEFAULT_THRESHOLD; + } + + // + // Build LUN's hardware id. Needs to be called at PASSIVE_LEVEL, so + // do it before grabbing the lock. The hardware id of the group is + // later set under the protection of the lock. + // + hardwareId = DsmpBuildHardwareId(deviceInfo); + + irql = ExAcquireSpinLockExclusive(&(((PDSM_CONTEXT)DsmContext)->DsmContextLock)); + spinlockHeld = TRUE; + + status = STATUS_SUCCESS; + + // + // See if there is an existing Multi-path group to which this belongs. + // (same serial number). + // + group = DsmpFindDevice(DsmContext, deviceInfo, FALSE); + if (!group) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): First device %p in the group.\n", + TargetDevice, + deviceInfo)); + + newGroup = TRUE; + + // + // This device doesn't belong to any group yet. So Build a multi-path + // group entry. This'll represents all paths to a particular device. + // + group = DsmpBuildGroupEntry(DsmContext, deviceInfo); + if (group) { + + // + // Set the registry key name for the new group + // + group->RegistryKeyName = deviceName; + deviceName = NULL; + + // + // Cache the LUN's hardware id + // + if (!hardwareId) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build a hardwareId for %p.\n", + TargetDevice, + deviceInfo)); + } + + group->HardwareId = hardwareId; + hardwareId = NULL; + + group->UseCacheForLeastBlocks = useCacheForLeastBlocks; + group->CacheSizeForLeastBlocks = cacheSizeForLeastBlocks; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to allocate Group Entry for %p.\n", + TargetDevice, + deviceInfo)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Found group %p for device %p.\n", + TargetDevice, + group, + deviceInfo)); + + newGroup = FALSE; + + if (!group->HardwareId) { + + // + // If we weren't successful in previously building the hardware id for this LUN, + // retry doing it again now. + // + hardwareId = DsmpBuildHardwareId(deviceInfo); + if (!hardwareId) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build a hardwareId for %p.\n", + TargetDevice, + deviceInfo)); + } + + group->HardwareId = hardwareId; + hardwareId = NULL; + } + + // + // Sanity check that we haven't been presented with device instances + // with different ALUA support. So compare with the first device instance. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + if (group->DeviceList[index]) { + + break; + } + } + + if (index < DSM_MAX_PATHS) { + + // + // Only acceptable conditions are: + // 1. both have same support, + // 2. one has explicit, while other has both explicit-and-implicit (this + // is a potential valid case because DsmpDisableImplicitStateTransition + // may have failed). + // + if (!((deviceInfo->ALUASupport == group->DeviceList[index]->ALUASupport) || + ((deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT && deviceInfo->ImplicitDisabled) && + (group->DeviceList[index]->ALUASupport == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT))) || + ((group->DeviceList[index]->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT && group->DeviceList[index]->ImplicitDisabled) && + (deviceInfo->ALUASupport == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT))))) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Mismatch in device instances' ALUA support %d vs %d.\n", + TargetDevice, + deviceInfo->ALUASupport, + group->DeviceList[index]->ALUASupport)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + } + + if (NT_SUCCESS(status)) { + + NT_ASSERT(group); + + group->MaxPRRetryTimeDuringStateTransition = maxPRRetryTimeDuringStateTransition; + + if (alua == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + // + // Since the device doesn't support ALUA, it is automatically + // symmetric LU access. + // + group->Symmetric = TRUE; + + if (newGroup) { + + // + // This is the first in the group, so make it the active device. + // The actual active/passive devices will be set-up when + // LB policies are set by the user. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + // + // Already something active, this will be the fail-over device + // until the load-balance groups are set-up. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_STANDBY; + } + + } else { + + if (DeviceIdList == NULL) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): No Device ID List.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + if (alua == DSM_DEVINFO_ALUA_IMPLICIT) { + + // + // Assume that the LU access is symmetric. When parsing the TPG + // info, if we find that not all TPGs are in the same LU access + // state, then we know that this the access is asymmetric. + // + group->Symmetric = TRUE; + } + + // + // Build TPG and TP info + // + status = DsmpParseTargetPortGroupsInformation(DsmContext, + group, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build TPG information - status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; + + targetPortGroup = group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + // + // Find the target port through which this devInfo was exposed. + // + relativeTargetPortId = (PULONG)DsmpParseDeviceID(DeviceIdList, + DSM_DEVID_RELATIVE_TARGET_PORT, + NULL, + NULL, + FALSE); + NT_ASSERT(relativeTargetPortId); + + if (!relativeTargetPortId) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Couldn't retrieve relative TP id.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Find the target port group + // + targetPortGroupId = (PUSHORT)DsmpParseDeviceID(DeviceIdList, + DSM_DEVID_TARGET_PORT_GROUP, + NULL, + NULL, + FALSE); + NT_ASSERT(targetPortGroupId); + + if (targetPortGroupId) { + + // + // Find the target port group entry + // + targetPortGroupEntry = DsmpFindTargetPortGroup(DsmContext, + group, + targetPortGroupId); + + NT_ASSERT(targetPortGroupEntry); + + if (targetPortGroupEntry) { + + // + // Look through the target port group to find the target port + // + targetPortEntry = DsmpFindTargetPort(DsmContext, + targetPortGroupEntry, + relativeTargetPortId); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Couldn't find TPG Id %x's entry.\n", + TargetDevice, + *targetPortGroupId)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + NT_ASSERT(targetPortEntry); + + if (!targetPortEntry) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Couldn't find relative TP %x's entry.\n", + TargetDevice, + *relativeTargetPortId)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Update the devInfo with the target port and target port group + // info + // + deviceInfo->TargetPortGroup = targetPortGroupEntry; + deviceInfo->TargetPort = targetPortEntry; + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->ALUAState = deviceInfo->TargetPortGroup->AsymmetricAccessState; + + tp_device = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TARGET_PORT_DEVICELIST_ENTRY), + DSM_TAG_TP_DEVICE_LIST_ENTRY); + + if (!tp_device) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Insufficient resources allocating TP device list entry.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Add the device to the list of devices that are exposed via this target port. + // + tp_device->DeviceInfo = deviceInfo; + InterlockedIncrement((LONG volatile*)&targetPortEntry->Count); + InsertTailList(&targetPortEntry->TP_DeviceList, &tp_device->ListEntry); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to retrieve TPG Id.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + + if (NT_SUCCESS(status)) { + + // + // Add the deviceInfo to the list. DO NOT modify the status + // variable if this function returns SUCCESS. + // + status = DsmpAddDeviceEntry(DsmContext, + group, + deviceInfo); + if (NT_SUCCESS(status)) { + + *DsmIdentifier = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Added device %p to group %p.\n", + TargetDevice, + *DsmIdentifier, + group)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to add device %p to group %p - status %x.\n", + TargetDevice, + deviceInfo, + group, + status)); + + // + // We weren't able to add this deviceInfo to the list so we must + // remove its entry on the target port list before the deviceInfo + // is freed. + // + DsmpRemoveDeviceFromTargetPortList(deviceInfo); + + if (newGroup) { + + DsmpRemoveGroupEntry(DsmContext, group, FALSE); + + DsmpFreePool(group); + group = NULL; + } + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + spinlockHeld = FALSE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Device %p added. State %d, Desired State %d\n", + TargetDevice, + deviceInfo, + deviceInfo->State, + deviceInfo->DesiredState)); + + // + // Update the global list of controller objects + // + controllerObjects = DsmGetAssociatedDevice(dsmContext->MPIOContext, + PortObject, + 0x0C); + if (controllerObjects) { + + // + // This loop needs its own status variable so that it does not + // inadvertently overwrite a STATUS_SUCCESS from the code above. + // + NTSTATUS matchStatus = STATUS_SUCCESS; + PSCSI_ADDRESS controllerScsiAddress = NULL; + + // + // Walk through the list and get VPD 0x83 data and associate the devInfo + // with the controller object. + // + for (index = 0; index < controllerObjects->Count; index++) { + + STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; + + // + // Free the previously allocated SCSI address, if any. + // + if (controllerScsiAddress) { + DsmpFreePool(controllerScsiAddress); + controllerScsiAddress = NULL; + } + + controllerDeviceObject = (PDEVICE_OBJECT)controllerObjects->IdList[index]; + NT_ASSERT(controllerDeviceObject); + + if (!controllerDeviceObject) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Controller list %p's index %x is NULL.\n", + TargetDevice, + controllerObjects, + index)); + + continue; + } + + matchStatus = DsmpGetDeviceIdList(controllerDeviceObject, &controllerIdHeader); + NT_ASSERT(NT_SUCCESS(matchStatus)); + + if (!NT_SUCCESS(matchStatus)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to get DeviceId list for controller %p - status %x.\n", + TargetDevice, + controllerDeviceObject, + matchStatus)); + + continue; + } + + controllerSerialNumber = DsmpParseDeviceID((PSTORAGE_DEVICE_ID_DESCRIPTOR)controllerIdHeader, + DSM_DEVID_SERIAL_NUMBER, + NULL, + &codeSet, + FALSE); + NT_ASSERT(controllerSerialNumber); + DsmpFreePool(controllerIdHeader); + + if (!controllerSerialNumber) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to parse serial number for controller %p.\n", + TargetDevice, + controllerDeviceObject)); + + continue; + } + + // + // Note that on success, DsmGetScsiAddress() will allocate memory + // which we are responsible for freeing. + // + matchStatus = DsmGetScsiAddress(controllerDeviceObject, &controllerScsiAddress); + NT_ASSERT(NT_SUCCESS(matchStatus)); + + if (!NT_SUCCESS(matchStatus)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to get controller %p's scsi address - status %x.\n", + TargetDevice, + controllerDeviceObject, + matchStatus)); + + continue; + } + + controllerEntry = DsmpFindControllerEntry(DsmContext, + PortObject, + controllerScsiAddress, + controllerSerialNumber, + strlen(controllerSerialNumber), + codeSet, + TRUE); + + if (!controllerEntry) { + + controllerEntry = DsmpBuildControllerEntry(DsmContext, + controllerDeviceObject, + PortObject, + controllerScsiAddress, + controllerSerialNumber, + codeSet, + TRUE); + + if (!controllerEntry) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build an entry for controller %p.\n", + TargetDevice, + controllerDeviceObject)); + + continue; + } + + InsertHeadList(&dsmContext->ControllerList, &controllerEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&dsmContext->NumberControllers); + } + + controllerEntry->DeviceObject = controllerDeviceObject; + + // + // Parse the DeviceIdList for all the 0x5 type identifiers + // and for each, compare the target port groups and target ports to match + // the device to its controller. + // + if (!match) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to match devInfo %p with controller %p's Ids.\n", + TargetDevice, + deviceInfo, + controllerDeviceObject)); + + match = DsmpIsDeviceBelongsToController(DsmContext, + deviceInfo, + controllerEntry); + } + + if (match && !doneUpdating) { + + InterlockedIncrement((LONG volatile*)&(controllerEntry->RefCount)); + deviceInfo->Controller = controllerEntry; + doneUpdating = TRUE; + } + } + + // + // Free the last SCSI address allocated in the loop, if any. + // + if (controllerScsiAddress) { + DsmpFreePool(controllerScsiAddress); + controllerScsiAddress = NULL; + } + } + + // + // If there was no controller to associate this device with, use a fake one. + // Note that we only really care about matching on the Port and Target + // portions of the SCSI address. + // + if (!deviceInfo->Controller) { + + for (entry = dsmContext->ControllerList.Flink; + entry != &dsmContext->ControllerList; + entry = entry->Flink) { + + controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); + + if ((controllerEntry->IsFakeController) && + (controllerEntry->ScsiAddress->PortNumber == deviceInfo->ScsiAddress->PortNumber) && + (controllerEntry->ScsiAddress->TargetId == deviceInfo->ScsiAddress->TargetId)) { + + fakeControllerEntryExists = TRUE; + break; + } + } + + // + // If no fake one exists as yet for this port FDO, create one now. + // + if (!fakeControllerEntryExists) { + + CHAR fakeControllerSerialNumber[] = "FakeController"; + SCSI_ADDRESS fakeControllerScsiAddress = {0}; + fakeControllerScsiAddress.PortNumber = deviceInfo->ScsiAddress->PortNumber; + fakeControllerScsiAddress.TargetId = deviceInfo->ScsiAddress->TargetId; + + controllerEntry = DsmpBuildControllerEntry(DsmContext, + NULL, + PortObject, + &fakeControllerScsiAddress, + fakeControllerSerialNumber, + StorageIdCodeSetBinary, + TRUE); + + if (controllerEntry) { + + InsertHeadList(&dsmContext->ControllerList, &controllerEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&dsmContext->NumberControllers); + controllerEntry->IsFakeController = TRUE; + } + } + + if (controllerEntry) { + InterlockedIncrement((LONG volatile*)&(controllerEntry->RefCount)); + } + + deviceInfo->Controller = controllerEntry; + } + +__Exit_DsmInquire: + + if (spinlockHeld) { + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + } + + if (NT_SUCCESS(status)) { + + NT_ASSERT(*DsmIdentifier); + + } else { + + // + // If there was any sort of ERROR, the deviceInfo will NOT be put on + // MSDSM's internal list that is accessible to other threads. Thus, + // we are safe to free the memory below and we do not require any + // synchronization mechanism to do so. + // + + // + // Check to see whether the serial number buffer was allocated, or just + // an offset into the Descriptor. + // + if (serialNumberAllocated) { + + // + // Need to free this before returning. + // + DsmpFreePool(serialNumber); + } + + if (deviceInfo) { + + if (deviceInfo->ScsiAddress) { + DsmpFreePool(deviceInfo->ScsiAddress); + } + + DsmpFreePool(deviceInfo); + } + } + + // + // If deviceName is not NULL then it hasn't been assigned to any GROUP. + // Free the allocated memory. + // + if (deviceName) { + DsmpFreePool(deviceName); + } + + // + // If hardwareId is not NULL then it hasn't been assigned to any GROUP. + // Free the allocated memory. + // + if (hardwareId) { + DsmpFreePool(hardwareId); + } + + if (targetPortGroupsInfo) { + DsmpFreePool(targetPortGroupsInfo); + } + + if (relativeTargetPortId) { + DsmpFreePool(relativeTargetPortId); + } + + if (targetPortGroupId) { + DsmpFreePool(targetPortGroupId); + } + + if (controllerObjects) { + DsmpFreePool(controllerObjects); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Exiting function with status %x.\n", + TargetDevice, + status)); + + return status; +} + + +BOOLEAN +DsmCompareDevices( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId1, + _In_ IN PVOID DsmId2 + ) +/*++ + +Routine Description: + + This routine is called to determine if the device ids represent + the same underlying physical device. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmId1/2 - Identifers returned from DMS_INQUIRE_DRIVER. + +Return Value: + + TRUE if DsmIds correspond to the same underlying device. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo0 = DsmId1; + PDSM_DEVICE_INFO deviceInfo1 = DsmId2; + PSTR serialNumber0; + PSTR serialNumber1; + SIZE_T length; + BOOLEAN match = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmCompareDevices (DevInfo %p): Entering function - comparing with %p.\n", + deviceInfo0, + deviceInfo1)); + + // + // Get the two serial numbers. They were either embedded in + // the STORAGE_DEVICE_DESCRIPTOR or built by directly issuing + // the VPD request. + // + serialNumber0 = deviceInfo0->SerialNumber; + serialNumber1 = deviceInfo1->SerialNumber; + + if (serialNumber0 && serialNumber1) { + + // + // Get the length of the base-device Serial Number. + // + length = strlen((const char*)serialNumber0); + + // + // If the lengths match, compare the contents. + // + if (length == strlen((const char*)serialNumber1)) { + + if (RtlEqualMemory(serialNumber0, serialNumber1, length)) { + match = TRUE; + } + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmCompareDevices (DevInfo %p): Serialnumber not assigned for %p and\\or %p.\n", + DsmId1, + deviceInfo0, + deviceInfo1)); + } + + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmCompareDevices (DevInfo %p): Exiting function with match = %!bool!.\n", + DsmId1, + match)); + + return match; +} + + +NTSTATUS +DsmGetControllerInfo( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN ULONG Flags, + _Inout_ IN OUT PCONTROLLER_INFO *ControllerInfo + ) +/*++ + +Routine Description: + + This routine is used to get information about the controller that + the device corresponding to DsmId in on. Currently this DSM controls + hardware that doesn't expose controllers directly. Therefore State + is always NO_CNTRL. This information is used mainly by whatever + WMI admin utilities want it. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + + DsmId - Value returned from DMSInquireDriver. + + Flags - Bitfield of modifiers. If ALLOCATE is not set, ControllerInfo + will have a valid buffer for the DSM to operate on. + + ControllerInfo - Pointer for the DSM to place the allocated controller + info pertaining to DsmId + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES if memory allocation fails. + + STATUS_SUCCESS on success + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = deviceInfo->Controller; + PCONTROLLER_INFO controllerInfo = NULL; + LARGE_INTEGER time; + ULONG controllerId = 0; + NTSTATUS status = STATUS_SUCCESS; + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmGetControllerInfo (DevInfo %p): Entering function.\n", + DsmId)); + + // + // Check to see whether a controller id has already been made-up. + // + if (!controllerEntry) { + + // + // Since this device is in an enclosure that doesn't have controllers, + // e.g. JBOD, make one up. + // + KeQuerySystemTime(&time); + + // + // Use only the lower 32-bits. + // + controllerId = time.LowPart; + } + + // + // Check the Flags + // + if (Flags & DSM_CNTRL_FLAGS_ALLOCATE) { + + // + // This is the first call. Need to allocate the controller structure. + // + controllerInfo = DsmpAllocatePool(NonPagedPoolNx, + sizeof(CONTROLLER_INFO), + DSM_TAG_CTRL_INFO); + if (!controllerInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmGetControllerInfo (DevInfo %p): Failed to allocate memory for Controller Info\n", + DsmId)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmGetControllerInfo; + } + + if (!controllerEntry) { + + // + // Indicate that there are no specific controllers. + // + controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; + + // + // Set the identifier to the value generated earlier. + // Indicate that it's Binary, not ASCII. + // + controllerInfo->Identifier.Type = StorageIdCodeSetBinary; + controllerInfo->Identifier.Length = 8; + + RtlCopyMemory(controllerInfo->Identifier.SerialNumber, + &controllerId, + sizeof(controllerId)); + + } else { + + // + // If either implicit or explicit ALUA state transition is supported, + // every controller is active. Else, if the devInfo's is in Active + // state, the controller is obviously in the active state. + // + if ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED) || + (DsmpIsDeviceStateActive(deviceInfo->State))) { + + controllerInfo->State = DSM_CONTROLLER_ACTIVE; + + } else { + + controllerInfo->State = DSM_CONTROLLER_STANDBY; + } + + controllerInfo->Identifier.Type = controllerEntry->IdCodeSet; + controllerInfo->Identifier.Length = controllerEntry->IdLength; + + if (controllerInfo->Identifier.Length > 32) { + + controllerInfo->Identifier.Length = 32; + } + + RtlCopyMemory(controllerInfo->Identifier.SerialNumber, + controllerEntry->Identifier, + controllerInfo->Identifier.Length); + + controllerInfo->DeviceObject = controllerEntry->DeviceObject; + } + + *ControllerInfo = controllerInfo; + + } else if (Flags & DSM_CNTRL_FLAGS_CHECK_STATE) { + + // + // Get the passed in struct. + // + controllerInfo = *ControllerInfo; + + // + // If the enclosures supported by this DSM actually had controllers, + // there would be a list of them and a search based on + // ControllerIdentifier would be made. + // + controllerEntry = deviceInfo->Controller; + + if (!controllerEntry) { + + controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; + + } else { + + // + // If either implicit or explicit ALUA state transition is supported, + // every controller is active. Else, if the devInfo's is in Active + // state, the controller is obviously in the active state. + // + if ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED) || + (DsmpIsDeviceStateActive(deviceInfo->State))) { + + controllerInfo->State = DSM_CONTROLLER_ACTIVE; + + } else { + + controllerInfo->State = DSM_CONTROLLER_STANDBY; + } + } + } + +__Exit_DsmGetControllerInfo: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmGetControllerInfo (DevInfo %p): Exiting function with status %x.\n", + DsmId, + status)); + + return status; +} + + +NTSTATUS +DsmSetDeviceInfo( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetObject, + _In_ IN PVOID DsmId, + _Inout_ IN OUT PVOID *PathId + ) +/*++ + +Routine Description: + + This routine associates the DsmId to the controlling MPDisk PDO, + the targetObject for DSM-initiated requests, and to a Path + (given by PathId). + This routine will update the PathId in a way that better explains + the topology to MPIO. + Additionally, if we are in failover LB policy, failback if this + path is preferred path. + Also, if PR is being used, send registration down this path. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + TargetObject - The D.O. to which DSM-initiated requests should be sent. + DsmId - Value returned from DMSInquireDriver. + PathId - Id that represents the path. The value passed in may be used + as is, or the DSM optionally can update it if it requires + additional state info to be kept. + +Return Value: + + INSUFFICENT_RESOURCES for no-mem conditions. + STATUS_SUCCESS + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_FAILOVER_GROUP failGroup; + PDSM_CONTEXT dsmContext; + PSCSI_ADDRESS scsiAddress; + ULONG primaryPath = 0; + ULONG optimizedPath = 0; + ULONG pathWeight = 0; + ULONG pathId; + NTSTATUS status = STATUS_SUCCESS; + WCHAR registryKeyName[256] = {0}; + BOOLEAN newFOGroup = FALSE; + BOOLEAN registryKeyExists = FALSE; + KIRQL irql; + PVOID tempPathId = *PathId; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + UCHAR explicitlySet = FALSE; + BOOLEAN vidpidPolicySet = FALSE; + BOOLEAN overallPolicySet = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Entering function.\n", + DsmId)); + + // + // 1. Set default LB policy. + // 2. Query LB policy from registry and update if necessary. + // 3. Set default value for primaryPath and optimizedPath based on device's + // access state + // 4. Map deviceInfo to real LUN by saving off the target for I/O + // 5. Build pathId from SCSI address + // 6. Find FOG for device. If none found, build one. + // Add deviceInfo to FOG. + // 7. Query registry for pathWeight, primaryPath and optimizedPath + // Update deviceInfo with results of query. + // 8. Compare deviceInfo access state with persistent value (based on + // primaryPath and optimizedPath) and update its DesiredState. + // + + if (!TargetObject) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): No target object.\n", + deviceInfo)); + + // + // This deviceInfo will have no path or targetObject associated with it. + // Mark it in a failed state so it won't be used to handle any requests. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_UNDETERMINED; + + goto __Exit_DsmSetDeviceInfo; + } + + // + // Default LB type is Round Robin. + // + loadBalanceType = DSM_LB_ROUND_ROBIN; + + // + // Override the default with whatever is the overall policy that needs to be + // applied for all LUNs controlled by MSDSM. + // + // Override that policy if one has been set for this device's VID/PID. + // + // Override that policy with whatever has been explicitly set for this particular + // device. + // + // In order to perform the above, first query the policy for this particular device. + // If it has not been explicity set, use MSDSM's overall policy or VID/PID policy. + // + status = DsmpQueryDeviceLBPolicyFromRegistry(deviceInfo, + group->RegistryKeyName, + &loadBalanceType, + &preferredPath, + &explicitlySet); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to query LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(NT_SUCCESS(status)); + + // + // This deviceInfo will have no path or targetObject associated with it. + // Mark it in a failed state so it won't be used to handle any requests. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_UNDETERMINED; + + goto __Exit_DsmSetDeviceInfo; + } + + // + // If this device's policy was not explicitly set, check to see if a policy + // was set for this device's VID/PID and use that. + // If VID/PID policy is not set, query the overall default policy + // that needs to be applied to all devices controlled by this DSM. + // If this setting hasn't been set, we'll fall back to using the default that was + // determined based on the storage's ALUA capabilities. + // + if (!explicitlySet) { + + status = DsmpQueryTargetLBPolicyFromRegistry(deviceInfo, + &loadBalanceType, + &preferredPath); + + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; + vidpidPolicySet = TRUE; + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // Since the policy hasn't been set for this VID/PID, check if + // overall MSDSM-wide policy has been set. + // + status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, + &preferredPath); + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + overallPolicySet = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to query Dsm overall LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to query VID/PID LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + } else { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT; + } + + if (!explicitlySet && !vidpidPolicySet && !overallPolicySet) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + + } + + // + // If ALUA is enabled and the load balance policy is set to Round Robin, + // we need to set it to Round Robin with Subset instead. + // + if (!DsmpIsSymmetricAccess(deviceInfo) && loadBalanceType == DSM_LB_ROUND_ROBIN) { + loadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + group->LoadBalanceType = loadBalanceType; + group->PreferredPath = preferredPath; + dsmContext = (PDSM_CONTEXT) DsmContext; + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + + // + // Save the registry key name under which Load balance policies + // are stored. This will be used to query the LB policy later. + // + if (group->RegistryKeyName) { + + registryKeyExists = TRUE; + + if (!NT_SUCCESS(RtlStringCchCopyNW(registryKeyName, + sizeof(registryKeyName) / sizeof(registryKeyName[0]), + group->RegistryKeyName, + ((sizeof(registryKeyName) / sizeof(registryKeyName[0])) - sizeof(WCHAR))))) { + + registryKeyName[(sizeof(registryKeyName) / sizeof(registryKeyName[0])) - 1] = L'\0'; + } + } + + // + // TargetObject is the destination for any requests created by this driver. + // Save this for future reference. + // + deviceInfo->TargetObject = TargetObject; + + // + // Set the PathId - All devices on the same PathId will + // failover together. Currently the pathId is constructed + // from Port Number, Bus Number, and Target Id of the device. + // + scsiAddress = deviceInfo->ScsiAddress; + NT_ASSERT(scsiAddress); + + pathId = 0x77; + pathId <<= 8; + pathId |= scsiAddress->PortNumber; + pathId <<= 8; + pathId |= scsiAddress->PathId; + pathId <<= 8; + pathId |= scsiAddress->TargetId; + + *PathId = ((PVOID)((ULONG_PTR)(pathId))); + + // + // PathId indicates the path on which this device resides. Meaning + // that when a Fail-Over occurs all device's on the same path fail + // together. Search for a matching F.O. Group + // + failGroup = DsmpFindFOGroup(DsmContext, *PathId); + + // + // If not found, create a new failover group + // + if (!failGroup) { + + failGroup = DsmpBuildFOGroup(DsmContext, deviceInfo, PathId); + + if (failGroup) { + + newFOGroup = TRUE; + failGroup->MPIOPath = tempPathId; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to build FO Group.\n", + DsmId)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + // + // If this path is in the midst of failover processing, mark it as "good" + // again. + // + failGroup->State = DSM_FG_NORMAL; + + // + // add this deviceInfo to the f.o. group. + // + status = DsmpUpdateFOGroup(DsmContext, failGroup, deviceInfo); + NT_ASSERT(NT_SUCCESS(status)); + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + if (NT_SUCCESS(status)) { + + if (registryKeyExists) { + + NTSTATUS queryStatus = STATUS_INVALID_PARAMETER; + ULONGLONG pathId64; + + // + // If the overall default policy or a target-level policy has been set and + // this device's policy has not been explicitly set, there's no use querying + // its individual path (desired) states. + // + if ((!overallPolicySet && !vidpidPolicySet) || (explicitlySet)) { + + // + // Created a new failover group. Query the LB policy + // for this device from registry. + // + pathId64 = (ULONGLONG)((ULONG_PTR)*PathId); + + queryStatus = DsmpQueryLBPolicyForDevice(registryKeyName, + pathId64, + loadBalanceType, + &primaryPath, + &optimizedPath, + &pathWeight); + } + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + + if (NT_SUCCESS(queryStatus)) { + + deviceInfo->PathWeight = pathWeight; + + // + // If device doesn't support ALUA, update the device state + // based on the primary path info in the registry. + // + if (DsmpIsSymmetricAccess(deviceInfo)) { + + if (primaryPath) { + + deviceInfo->DesiredState = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + deviceInfo->DesiredState = DSM_DEV_STANDBY; + } + + } else { + + DSM_DEVICE_STATE devState; + + if (primaryPath) { + + devState = optimizedPath ? DSM_DEV_ACTIVE_OPTIMIZED : DSM_DEV_ACTIVE_UNOPTIMIZED; + + } else { + + devState = optimizedPath ? DSM_DEV_STANDBY : DSM_DEV_UNAVAILABLE; + } + + // + // For ALUA, desired state makes sense for FOO. + // For RRWS, we assume desired state was explicitly selected + // by Admin if the ALUA state is different from the path + // state. Only under such cases would the path state have + // been saved in registry. + // In all other policies, state must just match the TPG state. + // + if (group->LoadBalanceType == DSM_LB_FAILOVER || + group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + deviceInfo->DesiredState = devState; + + } else { + + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + } + } + } else if (queryStatus == STATUS_OBJECT_NAME_NOT_FOUND) { + + deviceInfo->PathWeight = pathWeight; + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + + } else { + + deviceInfo->PathWeight = 0; + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): PathWeight %x, DesiredState %x, State %x, PrevState %x.\n", + deviceInfo, + deviceInfo->PathWeight, + deviceInfo->DesiredState, + deviceInfo->State, + deviceInfo->PreviousState)); + + if (NT_SUCCESS(status)) { + + deviceInfo->Initialized = TRUE; + + } else if (!NT_SUCCESS(status) && newFOGroup) { + + // + // This deviceInfo will have no path associated with it. + // Mark it in a failed state so it won't be used to handle any requests. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_UNDETERMINED; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): No path associated with instance. Changing state from %u to %u.\n", + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + + DsmpRemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo, TRUE); + + if (failGroup->Count == 0) { + + // + // Yank it from the list. + // + RemoveEntryList(&failGroup->ListEntry); + InterlockedDecrement((LONG volatile*)&dsmContext->NumberFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Removing FOGroup %p with path %p. Count of FOGroups %d.\n", + DsmId, + failGroup, + failGroup->PathId, + dsmContext->NumberFOGroups)); + + // + // Free the zombie group list and then the failover group. + // + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + } + } + +__Exit_DsmSetDeviceInfo: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Exiting function with status %x.\n", + DsmId, + status)); + + return status; +} + + +BOOLEAN +DsmIsPathActive( + _In_ IN PVOID DsmContext, + _In_ IN PVOID PathId, + _In_ IN PVOID DsmId + ) +/*++ + +Routine Description: + + This routine is used to determine whether the path to DsmId is usable + (ie. able to handle requests without a failover). + + Also, after a failover, the path validity will be queried. + If the path error was transitory and the DSM feels that the path is good, + then this request will be re-issued to determine whether it is usable. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + PathId - Value set in SetPathId. + DsmId - DSM Id returned during DsmInquire. + +Return Value: + + TRUE if the path is active. FALSE otherwise. +--*/ +{ + PDSM_FAILOVER_GROUP foGroup; + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_CONTEXT dsmContext = (PDSM_CONTEXT) DsmContext; + KIRQL irql; + BOOLEAN retVal; + ULONG SpecialHandlingFlag = 0; + + // + // 1. If PR and reserved by this node, register the PR keys. + // 2. Find the FOG for the passed in PathId + // 3. Depending on the LB policy, set the appropriate devInfo states + // If FailOver, and DesiredState is AO, change the active + // devInfos to non-active state and make this one AO. + // If ALUA supported, send down SetTPG to make this change, + // else directly make the change. + // If RR/LWP/LQD, make this DevInfo ActiveOptimized. + // If RRS, and DesiredState is AO, change the active devInfos to + // their desired states and then make this one AO. + // If DesiredState is not AO, find a devInfo in AO state. If + // one is found, make this devInfo's state its desired state, + // else if one isn't found, make this one AO. + // 3. If this is preferredPath, and LB policy is failover-only, change the + // access state of deviceInfo to AO. + // If there is another devInfo currently in AO, change its state too. + // If ALUA supported, send down SetTPG to make these changes. + // 4. Get the appropriate AO DeviceInfo and mark the group's PTBU to its + // pathId. + // + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Entering function.\n", + DsmId)); + + // + // Initialize this instance to be usable so that during the possible processing + // of PR register, this device can be a candidate for certain kind of requests. + // + deviceInfo->Usable = TRUE; + + // + // New path arriving. If this Node owns the reservation register this path. + // + if (group->PRKeyValid) { + + NTSTATUS prRegStatus; + ULONG i; + PDSM_DEVICE_INFO devInfo; + ULONG ordinal; + + prRegStatus = DsmpRegisterPersistentReservationKeys(deviceInfo, TRUE); + + deviceInfo->RegisterServiced = TRUE; + + if (NT_SUCCESS(prRegStatus)) { + + deviceInfo->PRKeyRegistered = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Failed (status %x) to register PR key\n", + deviceInfo, + prRegStatus)); + } + + for (i = 0; i < group->NumberDevices; i++) { + + devInfo = group->DeviceList[i]; + if (devInfo && devInfo == deviceInfo) { + + ordinal = (1 << i); + group->ReservationList |= ordinal; + break; + } + } + } + + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + + // + // Get the F.O. Group information. + // + foGroup = DsmpFindFOGroup(DsmContext, PathId); + + // + // If there are any devices on this path, and it's not in a failed state + // it's capable of handling requests. So it's active. + // + if ((foGroup) && + (foGroup->Count) && + (foGroup->State == DSM_FG_NORMAL)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Path %p is usable.\n", + DsmId, + PathId)); + + retVal = TRUE; + + // + // Update the next path to be used for the group if it not set already. + // + deviceInfo = (PDSM_DEVICE_INFO)DsmId; + + group = deviceInfo->Group; + DSM_ASSERT(group != NULL); + DSM_ASSERT(group->GroupSig == DSM_GROUP_SIG); + + // + // If an invalidated path came back online before PnP removes came in, + // then MPIO's path recovery thread would have sent down a PathVerify + // just moments before by which we changed the state of the FOG to + // normal. Now it is time to change the deviceInfo's state to a "good" + // state. + // + if (deviceInfo->State >= DSM_DEV_FAILED) { + + DSM_ASSERT(deviceInfo->State == DSM_DEV_INVALIDATED); + + if (DsmpIsSymmetricAccess(deviceInfo)) { + + // + // Mark it as AO. The SetLBForPathArrival will update the state + // appropriately. + // + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + // + // Set it to the state that was reported during the last RTPG + // call that was made. + // + deviceInfo->State = deviceInfo->ALUAState; + } + } + + if (DsmpIsSymmetricAccess(deviceInfo)) { + + DsmpSetLBForPathArrival(DsmContext, deviceInfo, SpecialHandlingFlag); + + } else { + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + DsmpSetLBForPathArrivalALUA(DsmContext, deviceInfo, SpecialHandlingFlag); + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): State set to %d\n", + deviceInfo, + deviceInfo->State)); + + if (group->PathToBeUsed == NULL) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Will set PathToBeUsed for %p\n", + deviceInfo, + group)); + + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(deviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): FOG %p set for PathToBeUsed for %p\n", + deviceInfo, + deviceInfo->FailGroup, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): No active/alternative path available for group %p\n", + DsmId, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Path %p is NOT usable.\n", + DsmId, + PathId)); + + retVal = FALSE; + } + + ((PDSM_DEVICE_INFO)DsmId)->Usable = retVal; + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Exiting function with retVal = %!bool!.\n", + DsmId, + retVal)); + + return retVal; +} + + +NTSTATUS +DsmPathVerify( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + This routine ensures that the path to the device indicated by DsmId + is healthy. It's called periodically by the bus driver, and also + after a fail-over condition has been dealt with to ensure that + the path is able to handle requests. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmId - Value returned from DMSInquire. + PathId - Value set in SetPathId. + +Return Value: + + NTSTATUS +--*/ + +{ + PDSM_CONTEXT dsmCtxt = (PDSM_CONTEXT) DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_FAILOVER_GROUP foGroup; + NTSTATUS status = STATUS_UNSUCCESSFUL; + BOOLEAN found = FALSE; + KIRQL irql; + PLIST_ENTRY entry; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Entering function.\n", + DsmId)); + + if (DsmpIsDeviceInitialized(deviceInfo)) { + + irql = ExAcquireSpinLockExclusive(&(dsmCtxt->DsmContextLock)); + + // + // Get the failover group + // + foGroup = DsmpFindFOGroup(DsmContext, PathId); + + if (foGroup) { + + // + // Find the device. + // + for (entry = foGroup->FOG_DeviceList.Flink; + entry != &foGroup->FOG_DeviceList; + entry = entry->Flink) { + + fogDeviceListEntry = CONTAINING_RECORD(entry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); + + if (fogDeviceListEntry && fogDeviceListEntry->DeviceInfo == deviceInfo) { + + status = STATUS_SUCCESS; + found = TRUE; + + break; + } + } + } else { + + // + // This is not a good thing. It indicates that either we + // returned a bogus path to the bus-driver on a fail-over, + // or that the path evaporated between polls and PnP hasn't + // torn stuff down. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Failed to find failover group for path %p.\n", + DsmId, + PathId)); + + status = STATUS_DEVICE_NOT_CONNECTED; + } + + ExReleaseSpinLockExclusive(&(dsmCtxt->DsmContextLock), irql); + + if (NT_SUCCESS(status)) { + + if (found) { + + // + // Send down TUR if ALUA is not supported. + // Else, send down ReportTargetPortGroups (sending TUR down non-A/O path will + // always result in a check condition). + // + if (deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Sending TUR using %p to verify path %p.\n", + DsmId, + deviceInfo, + deviceInfo->FailGroup->PathId)); + + status = DsmSendTUR(deviceInfo->TargetObject); + + } else { + + // + // Check for whether we should ignore sending down an RTPG: + // Flag set indicates that this PathVerify() is happening in response to device + // arrival and can be skipped since Inquire() has just already sent down an RTPG. + // All that needs to be done is to clear the flag so that subsequent PathVerify() + // sent in response to InitiateFO will send RTPG as a ping. + // This is an optimization with the idea of helping speed up boot time, which is + // is adversely impacted, especially if there are many LUNs, each with many paths. + // + if (deviceInfo->IgnorePathVerify) { + + deviceInfo->IgnorePathVerify = FALSE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Returning success immediately since RTPG was already just sent.\n", + DsmId)); + + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Sending RTPG using %p to verify path %p.\n", + DsmId, + deviceInfo, + deviceInfo->FailGroup->PathId)); + + status = DsmpGetDeviceALUAState(dsmCtxt, deviceInfo, NULL); + + // + // Since this RTPG may have resulted in us losing a UA, adjust + // the states if needed. + // + if (NT_SUCCESS(status)) { + + DsmpAdjustDeviceStatesALUA(group, NULL, SpecialHandlingFlag); + } + } + } + } + + if (NT_SUCCESS(status)) { + + if (deviceInfo->State >= DSM_DEV_FAILED) { + + foGroup->State = DSM_FG_NORMAL; + deviceInfo->State = deviceInfo->LastKnownGoodState; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Exiting function with status %x.\n", + DsmId, + status)); + + return status; +} + + +NTSTATUS +DsmInvalidatePath( + _In_ IN PVOID DsmContext, + _In_ IN ULONG ErrorMask, + _In_ IN PVOID PathId, + _Inout_ IN OUT PVOID *NewPathId + ) +/*++ + +Routine Description: + + This routine will mark up devices as failed on PathId, and find + an appropriate path to return to MPIO. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + ErrorMask - Value returned from InterpretError. + PathId - The failing path. + NewPathId - Pointer to the new path. + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_CONTEXT context = DsmContext; + PDSM_FAILOVER_GROUP failGroup; + PDSM_FAILOVER_GROUP newPath = NULL; + PDSM_FAILOVER_GROUP pathId; + PDSM_DEVICE_INFO deviceInfo; + LIST_ENTRY reservedDeviceList; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PLIST_ENTRY entry; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; + BOOLEAN lockHeld = FALSE; + + UNREFERENCED_PARAMETER(ErrorMask); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Entering function.\n", + PathId)); + + DSM_ASSERT(ErrorMask & DSM_FATAL_ERROR); + + *NewPathId = NULL; + + InitializeListHead(&reservedDeviceList); + + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + lockHeld = TRUE; + + // + // Get the fail-over group corresponding to the PathId. + // + failGroup = DsmpFindFOGroup(DsmContext, PathId); + + if (!failGroup || failGroup->State == DSM_FG_FAILED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Failed to find FailOver group.\n", + PathId)); + + status = STATUS_NO_SUCH_DEVICE; + goto __Exit_DsmInvalidatePath; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Context %p, FOG %p failing.\n", + PathId, + DsmContext, + failGroup)); + + // + // Mark the path as failed. + // + failGroup->State = DSM_FG_FAILED; + + // + // Check to see whether the port driver and PnP removed the devices + // BEFORE the fail-over indication actually occurred. Work-around + // of several Fibre miniports. + // + if (failGroup->Count == 0) { + + // + // There are no longer any devices in this fail-over group, which means + // in order to get a back-pointer to the groups using this fail-over + // group, we need to go through the "zombie" group list. This should + // allow us to find a new path ID to return. + //Then go through failGroup->ZombieGroupList to do failover for each group. + // + PDSM_ZOMBIEGROUP_ENTRY group; + PDSM_GROUP_ENTRY groupEntry; + + // + // Initialize all the entries to indicate that they haven't been processed. + // + for (entry = failGroup->ZombieGroupList.Flink; entry != &(failGroup->ZombieGroupList); entry = entry->Flink) { + + group = CONTAINING_RECORD(entry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + group->Processed = FALSE; + } + + // + // Since we need to drop the spin lock while processing an entry, it is possible + // that a removal in parallel frees up this entry during that time, thus making it + // impossible for us to move to the next entry in the list. + // In order to safely access each of the entries, we mark an entry as being processed + // just before dropping the spinlock, and always start processing from the beginning + // of the list, skipping over the already processed ones. + // + entry = failGroup->ZombieGroupList.Flink; + + while (entry != &(failGroup->ZombieGroupList)) { + + group = CONTAINING_RECORD(entry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + entry = entry->Flink; + + if (!group || !group->Group || group->Processed) { + continue; + } + + group->Processed = TRUE; + groupEntry = group->Group; + + ExReleaseSpinLockExclusive(&context->DsmContextLock, irql); + lockHeld = FALSE; + + pathId = DsmpSetNewPathUsingGroup((PDSM_CONTEXT)DsmContext, groupEntry); + + if (!newPath) { + newPath = pathId; // Save off first good alternative path that we find + } + + if (!lockHeld) { + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + lockHeld = TRUE; + entry = failGroup->ZombieGroupList.Flink; + } + } + + if (!newPath) { + // + // This indicates that all of the devices have already been removed. + // If there were reservations outstanding, the RemoveDevice code + // should have updated them. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Failed to find new path using zombie group list.\n", + PathId)); + } + + } else { + + + // + // Process each device in the fail-over group + // + for (entry = failGroup->FOG_DeviceList.Flink; + entry != &failGroup->FOG_DeviceList; + entry = entry->Flink) { + + fogDeviceListEntry = CONTAINING_RECORD(entry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); + + if (!fogDeviceListEntry) { + continue; + } + + // + // Get the deviceInfo. + // + deviceInfo = fogDeviceListEntry->DeviceInfo; + + if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { + + deviceInfo->LastKnownGoodState = deviceInfo->State; + } + + // + // Set the state of the Failing Device + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_INVALIDATED; + + InterlockedIncrement(&deviceInfo->BlockRemove); + + ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); + lockHeld = FALSE; + + pathId = DsmpSetNewPath(DsmContext, deviceInfo); + + if (!newPath) { + newPath = pathId; // Save off first good alternative path that we find + } + + if (!lockHeld) { + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + lockHeld = TRUE; + } + + InterlockedDecrement(&deviceInfo->BlockRemove); + } + } + + if (!newPath) { + + // + // This indicates that no acceptable paths + // were found. Return the error to mpctl. + // + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): No valid path found.\n", + PathId)); + + status = STATUS_NO_SUCH_DEVICE; + + } else { + + // + // return the new path. + // + *NewPathId = newPath->PathId; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Returning %p as newPath.\n", + PathId, + newPath->PathId)); + } + +__Exit_DsmInvalidatePath: + + if (lockHeld) { + ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Exiting function with status %x.\n", + PathId, + status)); + + return status; +} + + +NTSTATUS +DsmMoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PVOID MPIOPath, + _In_ IN PVOID SuggestedPath, + _In_ IN ULONG Flags + ) +/*++ + +Routine Description: + + This routine is invoked in response to an administrative request. + The device that's associated with SuggestedPath will be made active, and the + current active device, moved to stand-by. + +Arguments: + + DsmContext - Context value given to the multipath driver during registration. + DsmIds - The collection of DSM IDs that pertain to the MPDisk. + MPIOPath - The original path value passed to SetDeviceInfo. + SuggestedPath - The path which should become the active path. + Flags - Bitmask indicating the intent of the move. + +Return Value: + + NTSTATUS - STATUS_SUCCESS, unless SuggestedPath is somehow invalid. + STATUS_INVALID_PARAMETER is ADMIN is set and the path is invalid. + +--*/ +{ + PDSM_CONTEXT context = DsmContext; + PDSM_DEVICE_INFO deviceInfo; + PDSM_FAILOVER_GROUP failGroup; + ULONG i; + NTSTATUS status; + KIRQL irql; + BOOLEAN adminRequest = FALSE; + PDSM_GROUP_ENTRY group = NULL; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmMoveDevice (DsmIds %p): Entering function - DsmContext %p MPIOPath (%p) SuggestedPath %p.\n", + DsmIds, + DsmContext, + MPIOPath, + SuggestedPath)); + + // + // Capture the value of the ADMIN flag bit. + // Currently, permanent assignment of the device to "preferred path" isn't supported. + // This driver doesn't care about the pending remove flag (currently). + // + adminRequest = (BOOLEAN)(Flags & DSM_MOVE_ADMIN_REQUEST); + + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + + group = ((PDSM_DEVICE_INFO)(DsmIds->IdList[0]))->Group; + + // + // Find the first active device. + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), + SpecialHandlingFlag); + + if (!deviceInfo) { + + // + // Didn't find an active device. Should LOG. + // Use the first one to piggy-back the request. + // + deviceInfo = DsmIds->IdList[0]; + } + + // + // Get the fail-over group associated with the Path. + // + failGroup = DsmpFindFOGroup(DsmContext, + SuggestedPath); + + if (!failGroup) { + + // + // The caller has made a terrible mistake. + // If it's an ADMIN request, blow it off. + // + if (adminRequest) { + status = STATUS_INVALID_PARAMETER; + } else { + + // + // Try to set another path. + // + // Note that failGroup will be NULL going into + // SetNewPath. This is OK. + // + status = STATUS_SUCCESS; + } + } else { + status = STATUS_SUCCESS; + } + + if (status == STATUS_SUCCESS) { + + // + // Set the new path, using SuggestedPath. + // + InterlockedIncrement(&deviceInfo->BlockRemove); + ExReleaseSpinLockExclusive(&context->DsmContextLock, irql); + failGroup = DsmpSetNewPath(context, + deviceInfo); + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + InterlockedDecrement(&deviceInfo->BlockRemove); + + // + // If we were able to make the suggested path active, that should be used. + // + for (i = 0, status = STATUS_UNSUCCESSFUL; i < DsmIds->Count && !NT_SUCCESS(status); i++) { + + deviceInfo = DsmIds->IdList[i]; + + if (deviceInfo->FailGroup == failGroup) { + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)failGroup); + status = STATUS_SUCCESS; + } + } + } + } + + ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmMoveDevice (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmRemovePending( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ) +/*++ + +Routine Description: + + This routine indicates that the device represented by DsmId will be + removed, so the deviceInfo is marked up to indicate the pending removal, + so that it won't be used. + +Arguments: + + DsmContext - Context value given to the multipath driver + during registration. + DsmId - Value referring to the failed device. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePending (DevInfo %p): Entering function.\n", + DsmId)); + + // + // DsmpSetNewPath then finds the next available device. This is basically a + // fail-over for just this device. + // + InterlockedIncrement(&deviceInfo->BlockRemove); + DsmpSetNewPath(DsmContext, deviceInfo); + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + InterlockedDecrement(&deviceInfo->BlockRemove); + + if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { + + deviceInfo->LastKnownGoodState = deviceInfo->State; + } + + // + // Mark the device as being unavailable since remove will be sent shortly. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_REMOVE_PENDING; + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePending (DevInfo %p): Exiting function.\n", + DsmId)); + + return STATUS_SUCCESS; +} + +NTSTATUS +DsmRemoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + The device is gone and the port pdo has been removed. This routine will + update the internal structures and free any allocations. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmId - Value referring to the failed device. + PathId - The path on which the Device lives. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + KIRQL irql; + PDSM_FAILOVER_GROUP failGroup = deviceInfo->FailGroup; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + LONG block; + + UNREFERENCED_PARAMETER(PathId); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemoveDevice (DevInfo %p): Entering function.\n", + DsmId)); + + do { + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + block = deviceInfo->BlockRemove; + NT_ASSERT(block >= 0); + + if (block) { + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + KeStallExecutionProcessor(10000); + } + + } while (block); + + if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { + + deviceInfo->LastKnownGoodState = deviceInfo->State; + } + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_REMOVED; + + // + // Decrement the reference count for this device's controller entry and + // delete the entry if its reference count is now zero. + // + if (deviceInfo->Controller) { + + if (InterlockedDecrement((LONG volatile*)&(deviceInfo->Controller->RefCount)) == 0) { + + RemoveEntryList(&(deviceInfo->Controller->ListEntry)); + DsmpFreeControllerEntry(dsmContext, deviceInfo->Controller); + deviceInfo->Controller = NULL; + InterlockedDecrement((LONG volatile*)&(dsmContext->NumberControllers)); + } + } + + // + // Ensure that the device has been fully initialized before trying to + // remove it from the FOG. If SetDeviceInfo has yet to be invoked, there + // will yet to be an association set. + // + if (failGroup) { + + // + // Remove its entry from the Fail-Over Group. + // + DsmpRemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo, FALSE); + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + // + // Remove it from it's multi-path group. This has the side-effect + // of cleaning up the Group if the number of devices goes to zero. + // + DsmpRemoveDeviceEntry(DsmContext, group, deviceInfo); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemoveDevice (DevInfo %p): Exiting function.\n", + DsmId)); + + return STATUS_SUCCESS; +} + + +NTSTATUS +DsmRemovePath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + This routine indicates that the path is no longer valid, and that it should + be removed. Internal counts will be updated and any allocations associated + with this path freed. + +Arguments: + + DsmContext - Context value given to the multipath driver during registration. + PathId - The path to remove. + +Return Value: + + NTSTATUS of the operation. + +--*/ + +{ + PDSM_FAILOVER_GROUP failGroup; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Entering function.\n", + PathId)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + failGroup = DsmpFindFOGroup(DsmContext, PathId); + + if (failGroup) { + + // + // The claim is that a path won't be removed, until all + // the devices on it are. + // + if (failGroup->Count == 0) { + + // + // Yank it from the list. + // + RemoveEntryList(&failGroup->ListEntry); + InterlockedDecrement((LONG volatile*)&DsmContext->NumberFOGroups); + + // + // Move this over to the stale FOG list if there are inflight requests. + // Otherwise free the allocation. + // + if (InterlockedCompareExchange(&failGroup->NumberOfRequestsInFlight, 0, 0) > 0) { + + failGroup->State = DSM_FG_PENDING_REMOVE; + InsertTailList(&DsmContext->StaleFailGroupList, &failGroup->ListEntry); + InterlockedIncrement((LONG volatile*)&DsmContext->NumberStaleFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Outstanding requests %d. Moving FOGroup %p with path %p to stale path list.\n", + PathId, + failGroup->NumberOfRequestsInFlight, + failGroup, + failGroup->PathId)); + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Removing FOGroup %p with path %p. Count of FOGroups %d.\n", + PathId, + failGroup, + failGroup->PathId, + DsmContext->NumberFOGroups)); + + // + // Free the zombie group list and then the failover group. + // + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Count %d. Not removing FOGroup %p.\n", + PathId, + failGroup->Count, + failGroup)); + + // + // Should never be here. + // + NT_ASSERT(failGroup->Count == 0); + } + } else { + + // + // It's already been removed. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Did not find the FO group.\n", + PathId)); + + NT_ASSERT(failGroup); + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Exiting function.\n", + PathId)); + + return STATUS_SUCCESS; +} + + +PVOID +DsmLBGetPath( + _In_ IN PVOID DsmContext, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PDSM_IDS DsmList, + _In_ IN PVOID CurrentPath, + _Out_ OUT NTSTATUS *Status + ) +/*++ + +Routine Description: + + This routine is used by mpio to handle load-balancing. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + Srb - The current read/write Srb. + DsmList - List of our DSM IDs. + CurrentPath - The last path that was returned for this multi-path group. + Status - Storage to place NTSTATUS of the call. + +Return Value: + + The path ID to which the request should be sent. + +--*/ + +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo; + PDSM_GROUP_ENTRY group; + PDSM_FAILOVER_GROUP failGroup = NULL; + PVOID newPath = NULL; + PDSM_FAILOVER_GROUP oldFailGroup = NULL; + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failPathDevInfoEntry = NULL; + PCDB cdb = NULL; + UCHAR opCode = 0xFF; + BOOLEAN lockInExclusiveMode = FALSE; + ULONG SpecialHandlingFlag = 0; + + + if (Srb) { + cdb = SrbGetCdb(Srb); + if (cdb) { + opCode = cdb->AsByte[0]; + + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Entering function.\n", + DsmList)); + + // + // Up-front checking to minimally validate the list of + // DsmId's being passed in. + // + NT_ASSERT(DsmList->Count && DsmList->IdList[0]); + if (!(DsmList->Count && DsmList->IdList[0])) { + + *Status = STATUS_NO_SUCH_DEVICE; + goto __Exit_DsmLBGetPath; + } + + deviceInfo = DsmList->IdList[0]; + group = deviceInfo->Group; + + + failGroup = DsmpGetPath(dsmContext, DsmList, Srb, SpecialHandlingFlag); + + // + // If there wasn't a single active/optimized path found, check to see if + // there is an STPG in progress that may be making a path A/O. + // + if (!failGroup) { + + // + // Take the last path used. + // + oldFailGroup = DsmpFindFOGroup(dsmContext, CurrentPath); + + // + // Find the devInfo corresponding to this path. + // + deviceInfo = DsmpFindDevInfoFromGroupAndFOGroup(dsmContext, + group, + oldFailGroup); + + if (deviceInfo) { + + // + // Check if there is an alternate devInfo to be used temporarily + // for this deviceInfo + // + failPathDevInfoEntry = DsmpFindFailPathDevInfoEntry(dsmContext, + group, + deviceInfo); + + if (failPathDevInfoEntry) { + + // + // Use the alternate devInfo for now temporarily while the STPG + // that was previously sent (asynchronously) works on making the + // appropriate path active/optimized. + // + failGroup = (failPathDevInfoEntry->TempDeviceInfo)->FailGroup; + } + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Couldn't find FOG but FO in progress, so returning devInfo %p (FOG %p path %p).\n", + DsmList, + deviceInfo, + deviceInfo->FailGroup, + deviceInfo->FailGroup->PathId)); + } else { + + // + // Check if there is an RTPG in progress, if yes, return some path + // for the IO to be sent down. + // + if (InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 0)) { + + BOOLEAN sendTPG = FALSE; + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + if (deviceInfo) { + + failGroup = deviceInfo->FailGroup; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Couldn't find FOG but RTPG inflight, so returning devInfo %p (FOG %p path %p).\n", + DsmList, + deviceInfo, + deviceInfo->FailGroup, + deviceInfo->FailGroup->PathId)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Couldn't find FOG but RTPG inflight, even then couldn't find alternative devInfo.\n", + DsmList)); + } + } + } + } + + if (failGroup) { + + newPath = failGroup->PathId; + *Status = STATUS_SUCCESS; + + // + // If this is a retried request, our SetCompletion would have been bypassed, + // and our completion routine won't yet get called, so update the old and + // the new paths' stats. + // + if (Srb && DsmIsReadWrite(opCode)) { + + PDSM_FAILOVER_GROUP oldPath; + PIRP irp = (PIRP)SrbGetOriginalRequest(Srb); + PIO_STACK_LOCATION irpStack; + + // + // This indicates that the request is being retried. So we need to: + // 1. Update old path's and new path's request count + // 2. If the old path was supposed to be removed, check if there are + // no more requests are outstanding, and if yes, remove the path + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + oldPath = irpStack->Parameters.Others.Argument3; + + if (oldPath) { + + NT_ASSERT(oldPath->FailOverSig == DSM_FOG_SIG); + + if (DsmpDecrementCounters(oldPath, Srb)) { + + // + // If there are no requests on a path that is supposed to be removed, + // remove it now. + // + if (oldPath->State == DSM_FG_PENDING_REMOVE) { + KIRQL irql; + + NT_ASSERT(oldPath->Count == 0); + + // + // We need to acquire the DsmContextLock in Exclusive mode since + // we are removing a path from the Failover Group list. + // + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + lockInExclusiveMode = TRUE; + + RemoveEntryList(&oldPath->ListEntry); + InterlockedDecrement((LONG volatile*)&dsmContext->NumberStaleFOGroups); + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmLBGetPath (DsmIds %p): Removing FOGroup %p with path %p.\n", + DsmList, + oldPath, + oldPath->PathId)); + + DsmpFreePool(oldPath); + } + } + + irpStack->Parameters.Others.Argument3 = failGroup; + + DsmpIncrementCounters(failGroup, Srb); + } + } + + } else { + + *Status = STATUS_NO_SUCH_DEVICE; + + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Failed to get FO group in LBGetPath.\n", + DsmList)); + + + } + +__Exit_DsmLBGetPath: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Exiting function returning path %p for request %p.\n", + DsmList, + newPath, + Srb)); + + return newPath; +} + +_Success_(return == DSM_PATH_SET) +ULONG +DsmCategorizeRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID CurrentPath, + _Outptr_result_maybenull_ OUT PVOID *PathId, + _Out_ OUT NTSTATUS *Status + ) +/*++ + +Routine Description: + + This routine is called when a request is received other than a read/write. + It will determine the best path to which the request is to be sent. + + In order to support clusters, reserve and release need to be handled + via SrbControl. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmIds - List of our DSM IDs. + Irp - The Irp containing Srb. + Srb - The current non-read/write Srb. + CurrentPath - The last path that was returned for this multi-path group. + PathId - Placeholder for the PathID + Status - Storage to place NTSTATUS of the call. + +Return Value: + + DSM_PATH_SET - Indicates PathID is valid. + DSM_ERROR - Couldn't get a path. + +--*/ +{ + ULONG dsmStatus; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmCategorizeRequest (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Determine whether this is a special-case request. + // + if (DsmpReservationCommand(Irp, Srb)) { + + dsmStatus = DSM_WILL_HANDLE; + goto __Exit_DsmCategorizeRequest; + } + + + // + // If this is a mpio pass through or a mpio pass through direct request, + // pick the path that corresponds to the pathId specified. + // + if (DsmpMpioPassThroughPathCommand(Irp)) { + + *PathId = DsmpGetPathIdFromPassThroughPath(DsmContext, + DsmIds, + Irp, + &status); + } else { + + // + // For requests other than reservation-handling and pass through, punt + // it back to the bus-driver. Need to get a path for the request first, + // so call the Load-Balance function. + // + *PathId = DsmLBGetPath(DsmContext, + Srb, + DsmIds, + CurrentPath, + &status); + } + + if (NT_SUCCESS(status)) { + + if (!*PathId) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmCategorizeRequest (DsmIds %p): DSM_PATH_SET didn't return a path.\n", + DsmIds)); + } + + // + // Indicate that the path is updated, and mpctl should handle the request. + // + dsmStatus = DSM_PATH_SET; + + } else { + + // + // Indicate the error back to mpctl. + // + dsmStatus = DSM_ERROR; + + // + // Mark-up the Srb to show that a failure has occurred. + // This value is really only for this DSM to know what to do + // in the InterpretError routine - Fatal Error. + // It could be something more meaningful. + // + if (Srb) { + Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + } + + *PathId = NULL; + } + + // + // Pass back status info to mpctl. + // + *Status = status; + +__Exit_DsmCategorizeRequest: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmCategorizeRequest (DsmIds %p): Exiting function with categorization %x.\n", + DsmIds, + dsmStatus)); + + return dsmStatus; +} + + +NTSTATUS +DsmBroadcastRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine is called when the DSM has indicated that Srb should be + sent to the device down all paths. The DSM will update IoStatus + information and status, but not complete the request. + + Currently MSDSM doesn't have a need for this. + +Arguments: + + DsmIds - The collection of DSM IDs that pertain to the MPDisk. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - DSM sets this once all sub-requests have completed and + the original request's IoStatus has been setup. + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(Srb); + UNREFERENCED_PARAMETER(Irp); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmBroadcastRequest (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Currently nothing is handled via Broadcast. Just set the event to + // free up the request handling in the bus-driver. + // + NT_ASSERT(NT_SUCCESS(status)); + KeSetEvent(Event, 0, FALSE); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmBroadcastReqeust (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmSrbDeviceControl( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine is called when the DSM has indicated that it wants to handle + it internally (via returning DSM_WILL_HANDLE in CategorizeRequest). + + It should set IoStatus (Status and Information) and the Event, but not + complete the request. + +Arguments: + + DsmContext - The DSM's context + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - Event to be set when the DSM is finished if DsmHandled is TRUE + +Return Value: + + NTSTATUS of the request. + +--*/ +{ + PDSM_CONTEXT dsmContext = DsmContext; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status; + UCHAR opCode = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmSrbDeviceControl (DsmIds %p): Entering function.\n", + DsmIds)); + + if (!DsmIds || !DsmIds->Count) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmSrbDeviceControl (DsmIds %p): No DsmIds passed in.\n", + DsmIds)); + + status = STATUS_NO_SUCH_DEVICE; + goto __Exit_DsmSrbDeviceControl; + } + + if (irpStack->MajorFunction == IRP_MJ_SCSI) { + + // + // Determine the operation. + // + PCDB cdb = SrbGetCdb(Srb); + if (cdb) { + opCode = cdb->AsByte[0]; + } + + if (opCode == SCSIOP_PERSISTENT_RESERVE_OUT) { + + status = DsmpPersistentReserveOut(dsmContext, + DsmIds, + Irp, + Srb, + Event); + + } else if (opCode == SCSIOP_PERSISTENT_RESERVE_IN) { + + status = DsmpPersistentReserveIn(dsmContext, + DsmIds, + Irp, + Srb, + Event); + + } else { + + // + // Should never be here. + // + DSM_ASSERT(FALSE); + status = STATUS_INVALID_DEVICE_REQUEST; + } + } else { + // + // Should never be here. + // + DSM_ASSERT(irpStack->MajorFunction == IRP_MJ_SCSI); + status = STATUS_INVALID_DEVICE_REQUEST; + } + +__Exit_DsmSrbDeviceControl: + if (status != STATUS_PENDING) { + + // + // Set-up the Irp status for mpio's completion of the request. + // If it was IRP_MJ_SCSI, one of the helper routines set Srb->SrbStatus + // already. + // + if ((irpStack->MajorFunction == IRP_MJ_SCSI) && + (Srb != NULL) && + (Srb->SrbStatus == SRB_STATUS_PENDING)) { + + Srb->SrbStatus = SRB_STATUS_ERROR; + } + + Irp->IoStatus.Status = status; + + // + // Set the event to free up the request handling in the bus-driver. + // + KeSetEvent(Event, 0, FALSE); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmSrbDeviceControl (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +VOID +DsmSetCompletion( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT PDSM_COMPLETION_INFO DsmCompletion + ) +/*++ + +Routine Description: + + This routine is called before the actual submission of a request, + but after the categorisation of the I/O. This will be called only + for those requests not handled by the DSM directly: + Read/Write + Other requests not handled by SrbControl or Broadcast + +Arguments: + + DsmContext - The DSM's context. + DsmId - Identifer that was indicated when the request was + categorized (or be LBGetPath) + Irp - Irp containing Srb. + Srb - The request + DsmCompletion - Completion info structure to be filled out by DSM. + +Return Value: + + None + +--*/ +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDSM_FAILOVER_GROUP failGroup = deviceInfo->FailGroup; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmSetCompletion (DevInfo %p): Entering function.\n", + DsmId)); + + // + // Save off the path that was selected to service this request in Argument3. + // + irpStack->Parameters.Others.Argument3 = failGroup; + + DsmpIncrementCounters(failGroup, Srb); + + if (!dsmContext->DisableStatsGathering) { + + // + // Indicate one more request on this device down this path. + // + InterlockedIncrement(&deviceInfo->NumberOfRequestsInProgress); + } + + // + // Update the passed-in struct with our routine and context values. + // + DsmCompletion->DsmCompletionRoutine = DsmpRequestComplete; + DsmCompletion->DsmContext = DsmContext; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmSetCompletion (DevInfo %p): Exiting function.\n", + DsmId)); + + return; +} + + +ULONG +DsmInterpretError( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT NTSTATUS *Status, + _Out_ OUT PBOOLEAN Retry, + _Out_ OUT PLONG RetryInterval, + ... + ) +/*++ + +Routine Description: + + This routine is invoked by MPIO if Status is other than SUCCESS. + A few NTSTATUS and SRB_STATUS values indicate a fatal error. + Also checked are unit attentions, for which a retry is requested. + +Arguments: + + DsmContext - The DSM's context. + DsmId - Identifers returned from DMS_INQUIRE_DRIVER. + Srb - The Srb with an error. + Status - NTSTATUS of the operation. Can be updated. + Retry - Allows the DSM to indicate whether to retry the IO. + RetryInterval - Lets DSM specify (in seconds) when this specific I/O + should be retried. Use MAXLONG to use the default + retry interval. Use zero to retry immediately. + +Return Value: + + DSM_FATAL_ERROR indicates a fatal error. + +--*/ +{ + // + // The requests that will be encountered can be divided into four categories: + // 1. The request that has failed. + // 2. Subsequent requests that were sent down the failing path that will + // complete with failure. + // 3. Requests that were already submitted to LBGetPath() just before InterpretError() + // was called for the failed request (but have yet to have the LB policy + // algo run). + // 4. Requests that come into the Dispatch() routine after the failed request + // has been processed by InterpretError(). + // + // For the failed request: + // ======================= + // 1. Find a standby path to make active/optimized. + // 2. Send STPG asynchronously as a scsi pass through via IRP_MJ_SCSI (this + // way it can be sent at DISPATCH_IRQL) after setting a completion routine. + // 3. Save the devInfo corresponding to the standby path for the failing devInfo. + // 4. Return FATAL to MPIO so that new IO are queued. + // 5. In the completion routine, update the new states for the devInfos. Then + // clear the saved (previously) standby devInfo for the failing devInfo. + // + // For the subsequent request that will fail (since it was sent on the failing path): + // ================================================================================== + // 1. If a standby devInfo has been saved off, it indicates that an STPG was + // already sent, so no need to send another one. + // 2. Return FATAL to MPIO so that this request gets queued. + // + // For the requests that were already submitted to LBGetPath() during this time: + // ============================================================================= + // 1. If there is no active path, check if a standby devInfo has been saved + // away. If it has, return this path. Such requests will fail with check + // condition saying path used is in standby. + // 2. In InterpretError() retry (since the error indicates that request + // completed before STPG completed) without decrementing the remaining + // retries count. + // + // For new requests that come into Dispatch() after above processing: + // ================================================================== + // We don't need to worry about such requests, since MPIO will queue them + // automatically. + // + + PDSM_DEVICE_INFO deviceInfo = DsmId; + ULONG errorMask = 0; + PVOID senseData = SrbGetSenseInfoBuffer(Srb); + UCHAR senseDataLength = SrbGetSenseInfoBufferLength(Srb); + BOOLEAN failover = FALSE; + BOOLEAN retry = FALSE; + BOOLEAN handled = FALSE; + BOOLEAN sendTPG = FALSE; + BOOLEAN tpgException = FALSE; + BOOLEAN devInfoException = FALSE; + PCDB cdb = SrbGetCdb(Srb); + UCHAR opCode = 0; + UCHAR scsiStatus = SrbGetScsiStatus(Srb); + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + + if (cdb) { + opCode = cdb->AsByte[0]; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Entering function.\n", + DsmId)); + + *RetryInterval = MAXLONG; + + if ((scsiStatus == SCSISTAT_RESERVATION_CONFLICT) || + (*Status == STATUS_DEVICE_BUSY)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Srb %p. Either busy or res. conflict (%x %x).\n", + DsmId, + Srb, + scsiStatus, + *Status)); + } + + // + // Go ahead and get the sense data if it's valid. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { + + NT_ASSERT(senseData != NULL); + + validSense = ScsiGetSenseKeyAndCodes(senseData, + senseDataLength, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + } + + // + // Sense data relating to logical block provisioning should be failed + // immediately back to the class layer for handling. + // + if (validSense) { + if (senseKey == SCSI_SENSE_NOT_READY && + addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && + addSenseCodeQualifier == SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Temporary resource exhaustion. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + + } else if (senseKey == SCSI_SENSE_DATA_PROTECT && + addSenseCode == SCSI_ADSENSE_WRITE_PROTECT && + addSenseCodeQualifier == SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Permanent resource exhaustion. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + + } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && + addSenseCode == SCSI_ADSENSE_LB_PROVISIONING && + addSenseCodeQualifier == SCSI_SENSEQ_SOFT_THRESHOLD_REACHED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Soft threshold reached. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + + } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && + addSenseCode == SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED && + addSenseCodeQualifier == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Inquiry data changed. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && + addSenseCode == SCSI_ADSENSE_PARAMETERS_CHANGED && + addSenseCodeQualifier == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Capacity data changed. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + } + } + + if (handled) { + return errorMask; + } + + // + // Check the NT Status first. + // Several are clearly failover conditions. + // + switch (*Status) { + case STATUS_DEVICE_NOT_CONNECTED: + case STATUS_DEVICE_DOES_NOT_EXIST: + case STATUS_NO_SUCH_DEVICE: + case STATUS_DELETE_PENDING: { + + // + // The port pdo has either been removed or is + // very broken. A fail-over is necessary. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Will initiate fail over. Status %x. Opcode %x.\n", + DsmId, + *Status, + opCode)); + + handled = TRUE; + failover = TRUE; + break; + } + + case STATUS_IO_DEVICE_ERROR: { + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { + + if (validSense) { + + // + // See if it's a unit attention. + // + if (senseKey == SCSI_SENSE_UNIT_ATTENTION) { + + switch (addSenseCode) { + + case SCSI_ADSENSE_PARAMETERS_CHANGED: { + + switch (addSenseCodeQualifier) { + + case SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED: + case SPC3_SCSI_SENSEQ_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): TPG states have changed. Requesting retry on Srb %p. Will send asyn RTPG.\n", + DsmId, + Srb)); + + // + // Retry but after sending RTPG, which will update the path states. + // + sendTPG = TRUE; + retry = TRUE; + handled = TRUE; + errorMask = DSM_RETRY_DONT_DECREMENT; + + if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED) { + + // + // Worth retrying on the same path. + // + devInfoException = TRUE; + NT_ASSERT(!tpgException); + } + + break; + } + + + case SPC3_SCSI_SENSEQ_RESERVATIONS_RELEASED: { + + // + // This request needs to be immediately retried down the same path. + // + retry = TRUE; + *RetryInterval = 0; + handled = TRUE; + InterlockedExchangePointer(&(deviceInfo->Group->PathToBeUsed), deviceInfo->FailGroup); + break; + } + + case SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED: + case SPC3_SCSI_SENSEQ_RESERVATIONS_PREEMPTED: + case SPC3_SCSI_SENSEQ_REGISTRATIONS_PREEMPTED: + case SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Failing request. STATUS_IO_DEVICE_ERROR (params changed). SrbStatus (%x) Scsi (%x) AddQual (%u).\n", + DsmId, + Srb->SrbStatus, + scsiStatus, + addSenseCodeQualifier)); + + // + // Just fail these back. + // + handled = TRUE; + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION for params changed. ASCQ %x. Asking for retry on Srb %p.\n", + DsmId, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary. + // + retry = TRUE; + handled = TRUE; + + break; + } + } + + break; + } + + + case SPC3_SCSI_ADSENSE_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR: { + + if (addSenseCodeQualifier == 0x00) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION (commands cleared by another initiator). Fail back to upper level. Srb %p.\n", + DsmId, + Srb)); + + // + // Commands cleared by another Initiator + // + handled = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION (commands cleared by another initiator). ASCQ %x. Asking for retry on Srb %p.\n", + DsmId, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary. + // + retry = TRUE; + handled = TRUE; + } + + + break; + } + + case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: { + + if (addSenseCodeQualifier == SCSI_SENSEQ_VOLUME_SET_MODIFIED || + addSenseCodeQualifier == SCSI_SENSEQ_REPORTED_LUNS_DATA_CHANGED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): VolumeSet/LunsData changed. Fail Srb %p.\n", + DsmId, + Srb)); + + // + // Fail back to upper layers. + // + handled = TRUE; + + break; + + } else { + + // + // Fall through to default case (ie. retry the request) + // + } + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION. ASC %x, ASCQ %x. Asking for retry on Srb %p.\n", + DsmId, + addSenseCode, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary. + // + retry = TRUE; + handled = TRUE; + + break; + } + } + } else if (senseKey == SCSI_SENSE_NOT_READY) { + + if (addSenseCode == SCSI_ADSENSE_LUN_NOT_READY) { + + if (scsiStatus == SCSISTAT_CHECK_CONDITION) { + + switch (addSenseCodeQualifier) { + + // + // See if failure is due to device's current TPG state. + // + // If the failure is PORT_IN_STANDBY_STATE, we leave DSM_RETRY_DONT_DECREMENT unset if no active path exists, + // because otherwise MPIO will not be able to find a better path, and it will get into an infinite loop + // of trying and failing the command on a Standby path. See WCxeTfs:89150 + // + case SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION: + case SPC3_SCSI_SENSEQ_TARGET_PORT_IN_UNAVAILABLE_STATE: + + errorMask = DSM_RETRY_DONT_DECREMENT; + + case SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE: + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): TPG-transition/TPG-SB/TPG-UA. ASCQ %x. Will send down async RTPG. Asking for retry on Srb %p.\n", + DsmId, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary but without decrementing the remaining + // retries count. However, we may need to send down an STPG/RTPG also. + // And we must set PTBU to a path that is in a different TPG. + // + sendTPG = TRUE; + tpgException = TRUE; + NT_ASSERT(!devInfoException); + retry = TRUE; + handled = TRUE; + + if ((addSenseCodeQualifier == SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE) && + DsmIsReadWrite(opCode)) { + + PDSM_CONTEXT context = (PDSM_CONTEXT) deviceInfo->DsmContext; + KIRQL oldIrql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + BOOLEAN activePathExists = ( NULL != DsmpGetAnyActivePath(deviceInfo->Group, FALSE, NULL, 0) ); + ExReleaseSpinLockExclusive(&(context->DsmContextLock), oldIrql); + + if (activePathExists) { + errorMask = DSM_RETRY_DONT_DECREMENT; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Not decrementing error counter, as an active path exists in group %p and opcode %x is r/w\n", + DsmId, + deviceInfo->Group, + opCode)); + } + } + + break; + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Manual intervention required. Asking for retry on Srb %p.\n", + DsmId, + Srb)); + + // + // This may be caused by NDU of controller firmware. It does not + // necessarily indicate that the device won't be ready via other path(s). + // Worth retrying instead of immediately failing back. + // + retry = TRUE; + handled = TRUE; + + break; + } + } + } + } + } + + } else if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): BUS_RESET. Failing back Srb %p.\n", + DsmId, + Srb)); + + // + // Upper layers will retry in this case. If we retry here it will + // have a multiplicative effect which may result in a very long + // IO completion time if the device persistently times out. + // + retry = FALSE; + handled = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Failing request. STATUS_IO_DEVICE_ERROR. SrbStatus (%x) ScsiStatus (%x).\n", + DsmId, + Srb->SrbStatus, + scsiStatus)); + } + + break; + } + + case STATUS_BUFFER_OVERFLOW: { + + if (DsmIsReadWrite(opCode)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): BUFFER_OVERFLOW: Retry.\n", + DsmId)); + + // + // Retry these, as this condition might indicate a torn write. + // + retry = TRUE; + handled = TRUE; + } + + break; + } + + case STATUS_DEVICE_BUSY: { + + // + // See if it's a check condition for TPG states in transition. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + scsiStatus == SCSISTAT_CHECK_CONDITION) { + + if (validSense) { + + if (senseKey == SCSI_SENSE_NOT_READY && + addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && + addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): TPG transition. Will send down async RTPG. Asking for retry on Srb %p.\n", + DsmId, + Srb)); + + // + // Indicate that a retry is necessary but without decrementing the remaining + // retries count. However, we may need to send down an STPG/RTPG also. + // And we must set PTBU to a path that is in a different TPG. + // + sendTPG = TRUE; + tpgException = TRUE; + NT_ASSERT(!devInfoException); + retry = TRUE; + handled = TRUE; + errorMask = DSM_RETRY_DONT_DECREMENT; + } + + } + } + + break; + } + + case STATUS_DEVICE_NOT_READY: { + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + scsiStatus == SCSISTAT_CHECK_CONDITION) { + + if (validSense) { + if (senseKey == SCSI_SENSE_NOT_READY && + addSenseCode == SCSI_ADSENSE_LUN_NOT_READY) { + + switch (addSenseCodeQualifier) { + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Manual intervention required. Asking for retry on Srb %p.\n", + DsmId, + Srb)); + + // + // This may be caused by NDU of controller firmware. It does not + // necessarily indicate that the device won't be ready via other path(s). + // Worth retrying instead of immediately failing back. + // + retry = TRUE; + handled = TRUE; + + break; + } + + case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: { + // + // This indicates a logical block provisioning temporary resource exhaustion + // condition and therefore we must allow the class layer to handle it. + // + retry = FALSE; + handled = TRUE; + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Unhandled AddQual %x.\n", + DsmId, + addSenseCodeQualifier)); + + break; + } + } + } + } + } + } + + + default: { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Unhandled status code %x.\n", + DsmId, + *Status)); + + break; + } + } + + if (!handled) { + + // + // The NTSTATUS didn't indicate a fail-over condition, but + // check various srb status for failover-class error. + // + switch (Srb->SrbStatus) { + case SRB_STATUS_SELECTION_TIMEOUT: + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_NO_DEVICE: + case SRB_STATUS_NO_HBA: + case SRB_STATUS_INVALID_PATH_ID: { + + // + // All of these are fatal. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): SrbStatus 0x%x. Will initiate fail over.\n", + DsmId, + Srb->SrbStatus)); + + failover = TRUE; + break; + } + + + default: { + + if ((scsiStatus == SCSISTAT_CHECK_CONDITION) && + (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { + + if (validSense) { + + switch (senseKey) { + + case SCSI_SENSE_NO_SENSE: { + + if (addSenseCode == SCSI_ADSENSE_NO_SENSE && + addSenseCodeQualifier == SCSI_SENSEQ_CAUSE_NOT_REPORTABLE) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): CheckCondition with no sense info. Will initiate fail over.\n", + DsmId)); + + // + // This could be a transient error generated + // in response to potentially a hardware fault. + // Worth trying another path. + // + failover = TRUE; + handled = TRUE; + } + + break; + } + + case SCSI_SENSE_ILLEGAL_REQUEST: { + + if (addSenseCode == SCSI_ADSENSE_INVALID_LUN) { + + if (addSenseCodeQualifier == 0x00) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Invalid LUN. Will initiate fail over.\n", + DsmId)); + + // + // LUN may still exist on other path(s). + // Worth a failover. + // + failover = TRUE; + handled = TRUE; + } + } + + break; + } + + case SCSI_SENSE_HARDWARE_ERROR: { + + if (addSenseCode == SPC3_SCSI_ADSENSE_LOGICAL_UNIT_COMMAND_FAILED) { + + if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_SET_TARGET_PORT_GROUPS_FAILED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): STPG failed. Will initiate fail over.\n", + DsmId)); + + // + // If an STPG failed, treat as FATAL and get another + // path set to A/O via another STPG. + // + failover = TRUE; + handled = TRUE; + } + } else if ((addSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addSenseCodeQualifier == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) || + (addSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addSenseCodeQualifier == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) { + + // + // Could potentially indicate a dropped FC packet. Retry (along another + // path, based on the LB policy). + // + retry = TRUE; + handled = TRUE; + } + + break; + } + + default: { + + break; + } + } + } + } + + if (!handled) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Unhandled SRB Status 0x%x. Sense data %x|%x|%x.\n", + DsmId, + Srb->SrbStatus, + validSense ? senseKey : 0xFF, + validSense ? addSenseCode : 0xFF, + validSense ? addSenseCodeQualifier : 0xFF)); + } + + break; + } + } + } + + if (failover) { + ULONG SpecialHandlingFlag = 0; + + // + // If ALUA is supported, then it is possible that we may need to send + // down an STPG so build an IRP and fill in the SRB for STPG and send it down. + // + if (!DsmpIsSymmetricAccess(deviceInfo)) { + + DsmpSetLBForPathFailingALUA(DsmContext, deviceInfo, TRUE, SpecialHandlingFlag); + + } else { + + // + // If device doesn't support ALUA, we just need to update + // states without sending down any commands (STPG) + // + DsmpSetLBForPathFailing(DsmContext, deviceInfo, TRUE, SpecialHandlingFlag); + } + + errorMask = DSM_FATAL_ERROR; + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInterpretError(DevInfo %p): Device changed to state %d\n", + deviceInfo, + deviceInfo->State)); + +#if DBG + { + ULONG inx; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_DEVICE_INFO tempDevInfo; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Device %p in group %p being marked as failed. NTStatus 0x%x.\n", + DsmId, + deviceInfo, + group, + *Status)); + + for (inx = 0; inx < group->NumberDevices; inx++) { + + tempDevInfo = group->DeviceList[inx]; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Device %p at %d. State %d.\n", + DsmId, + tempDevInfo, + inx, + tempDevInfo->State)); + } + } +#endif // DBG + } + + if (retry) { + + if (sendTPG) { + + // + // If ALUA is supported, send down STPG/RTPG as appropriate. + // + if (!DsmpIsSymmetricAccess(deviceInfo)) { + + DsmpSetPathForIoRetryALUA(DsmContext, deviceInfo, tpgException, devInfoException); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInterpretError(DevInfo %p): SRB request %p will be retried. PTBU set to %p.\n", + deviceInfo, + Srb, + deviceInfo->Group->PathToBeUsed)); + } + } + } + + + *Retry = retry; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Exiting function returning errorMask %x.\n", + DsmId, + errorMask)); + + return errorMask; +} + +BOOLEAN +DsmIsAddressTypeSupported( + _In_ IN PVOID DsmContext, + _In_ IN ULONG AddressType + ) +/*++ + +Routine Description: + + This routine is called when MPIO wants to know if the DSM supports a + particular storage address type. + + This routine must be provided for DSMs of DsmType6 or higher. + +Arguments: + + DsmContext - Context value passed to DsmInitialize() + AddressType - The storage address type being queried. + +Return Value: + + TRUE - If the DSM supports the given storage address type. + FALSE - If the DSM does not support the given storage address type. + +--*/ +{ + UNREFERENCED_PARAMETER(DsmContext); + + if (AddressType == STORAGE_ADDRESS_TYPE_BTL8) + { + return TRUE; + } + + return FALSE; +} + +NTSTATUS +DsmDeviceNotUsed( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ) +/*++ + +Routine Description: + + This routine indicates that the device represented by DsmId will not be + initialized completely by MPIO. + The DSM_ID list passed to other functions will no longer contain DsmId, + so internal structures should be updated accordingly. + + This routine must be provided for DSMs of DsmType6 or higher. + +Arguments: + + DsmContext - Context value given to the multipath driver during registration. + DsmId - Value referring to the uninitialized device. + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)DsmId; + + DSM_ASSERT(deviceInfo->Group != NULL); + DSM_ASSERT(deviceInfo->Group->GroupSig == DSM_GROUP_SIG); + + // + // Undo anything we did to build up the device in DsmInquire(). + // + DsmRemoveDevice((PDSM_CONTEXT)DsmContext, DsmId, deviceInfo->FailGroup); + + return STATUS_SUCCESS; +} + +NTSTATUS +DsmUnload( + _In_ IN PVOID DsmContext + ) +/*++ + +Routine Description: + + This routine is called when the main module requires the DSM to be unloaded + (ie. prior to the main module unload). + +Arguments: + + DsmContext - Context value passed to DsmInitialize() + +Return Value: + + STATUS_SUCCESS; + +--*/ + +{ + PVOID tempAddress = DsmContext; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmUnload (DsmCtxt %p): Entering function.\n", + DsmContext)); + + DsmpFreeDSMResources((PDSM_CONTEXT) DsmContext); + + if (gMPIOControlObjectRefd) { + + ObDereferenceObject(gMPIOControlObject); + gMPIOControlObjectRefd = FALSE; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmUnload (DsmCtxt %p): Exiting function.\n", + tempAddress)); + + // + // Stop the tracing subsystem. + // + WPP_CLEANUP(gDsmDriverObject); + + return STATUS_SUCCESS; +} + diff --git a/tests/projects/wdk/wdm/msdsm/msdsm.h b/tests/projects/wdk/wdm/msdsm/msdsm.h new file mode 100644 index 000000000..e1ddeb52d --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/msdsm.h @@ -0,0 +1,1403 @@ +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + msdsm.h + +Abstract: + + Header for the Microsoft Device Specific Module (DSM). + +Environment: + + kernel mode only + +Notes: + +--*/ + +#ifndef _MSDSM_H_ +#define _MSDSM_H_ + +// +// Maximum number of paths per device supported by the DSM. +// This is a limit currently set by MPIO itself and needs to be updated if MPIO +// supports more paths-per-device in the future. +// +#define DSM_MAX_PATHS 32 + +// +// MPIO control object's well known symbolic name +// +#define DSM_MPIO_CONTROL_OBJECT_SYMLINK L"\\DosDevices\\MPIOControl" + +// +// Location of System class node in the registry +// +#define DSM_SYSTEM_CLASS_GUID_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\{4D36E97D-E325-11CE-BFC1-08002BE10318}" + +// +// Values used for matching and figuring out the DriverVersion +// +#define DSM_INF_PATH L"InfPath" +#define DSM_MSDSM_INF_PATH L"msdsm.inf" +#define DSM_DRIVER_VERSION L"DriverVersion" +#define DSM_DRIVER_VERSION_FIELD_DELIMITER L'.' +#define DSM_BUFFER_MAXCOUNT 64 + +// +// MSDSM's display name. +// +#define DSM_FRIENDLY_NAME L"Microsoft DSM" + +// +// Name of the value for the supported devices in the registry, found in the +// DSM's Services' Parameters key +// +#define DSM_SUPPORTED_DEVICELIST_VALUE_NAME L"DsmSupportedDeviceList" + +// +// Value used to determine if per-IO statistics gathering needs to be turned OFF +// +#define DSM_DISABLE_STATISTICS L"DsmDisableStatistics" + +// +// Names of the values in the registry for whether to use the same path for +// sequential IOs when employing Least Blocks load balance policy, as well +// as its size. +// +#define DSM_USE_CACHE_FOR_LEAST_BLOCKS L"DsmUseCacheForLeastBlocks" +#define DSM_CACHE_SIZE_FOR_LEAST_BLOCKS L"DsmCacheSizeForLeastBlocks" + +// +// Name of the value in the registry for the maximum request retry time during ALUA +// state transitions. This value is found in the DSM's Services' Parameters key, and +// applies only to Persistent Reservation commands. +// +#define DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME L"DsmMaximumStateTransitionTime" + + +// +// Default max amount of time (in seconds) that a PR failing with retry-able UA will be retried +// +#define DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME 3 + +// +// Macro to translate seconds to ticks. Each system tick is 10^(-7) seconds. +// +#define DSM_SECONDS_TO_TICKS(_Seconds) ((_Seconds) * 10000000) + +// +// Size of the buffer allocated to retrieve device serial number. +// This is as defined by SPC-3 spec. The identifier with the biggest size is +// SCSI name type (0x8). +// +#define DSM_SERIAL_NUMBER_BUFFER_SIZE 255 + +// +// Number of LB Policies that are supported by this driver. +// +#define DSM_NUMBER_OF_LB_POLICIES 6 + +// +// Size of the buffer passed to read in Persistent Reserve keys. +// +#define DSM_READ_PERSISTENT_KEYS_BUFFER_SIZE 4096 + +// +// The default threshold for sequential IO for the Least Blocks load balance +// policy is 1MB. +// +#define DSM_LEAST_BLOCKS_DEFAULT_THRESHOLD 0x00100000 + +// +// Initialization data structure that needs to be filled in for MPIO +// +DSM_INIT_DATA gDsmInitData; + +// +// Macro used to round of a number to the nearest 8 byte aligned one. +// +#ifdef AlignOn8Bytes +#undef AlignOn8Bytes +#endif +#define AlignOn8Bytes(x) (((x) + 7) & ~7) + +// +// Macro for determining minimum of two numbers +// +#ifdef MIN +#undef MIN +#endif +#define MIN(a, b) ((ULONGLONG)(a) < (ULONGLONG)(b) ? (a) : (b)) + +// +// Macro used to convert a 4 byte array to a ULONG (where byte 0 MSB, byte 3 LSB) +// +#define GetUlongFrom4ByteArray(UCharArray, ULongValue) \ + ((UNALIGNED UCHAR *)&(ULongValue))[3] = ((UNALIGNED UCHAR *)(UCharArray))[0]; \ + ((UNALIGNED UCHAR *)&(ULongValue))[2] = ((UNALIGNED UCHAR *)(UCharArray))[1]; \ + ((UNALIGNED UCHAR *)&(ULongValue))[1] = ((UNALIGNED UCHAR *)(UCharArray))[2]; \ + ((UNALIGNED UCHAR *)&(ULongValue))[0] = ((UNALIGNED UCHAR *)(UCharArray))[3]; + +// +// Macro used to convert a ULONG into a 4 byte array (as big-endian) +// +#define Get4ByteArrayFromUlong(ULongValue, UCharArray) \ + ((UNALIGNED UCHAR *)(UCharArray))[3] = ((UNALIGNED UCHAR *)&(ULongValue))[0]; \ + ((UNALIGNED UCHAR *)(UCharArray))[2] = ((UNALIGNED UCHAR *)&(ULongValue))[1]; \ + ((UNALIGNED UCHAR *)(UCharArray))[1] = ((UNALIGNED UCHAR *)&(ULongValue))[2]; \ + ((UNALIGNED UCHAR *)(UCharArray))[0] = ((UNALIGNED UCHAR *)&(ULongValue))[3]; + +// +// Macro to check if passed in opcode is a read, write +// +#define DsmIsReadRequest(_Opcode) (_Opcode == SCSIOP_READ || _Opcode == SCSIOP_READ16) +#define DsmIsWriteRequest(_Opcode) (_Opcode == SCSIOP_WRITE || _Opcode == SCSIOP_WRITE16) +#define DsmIsReadWrite(_Opcode) (_Opcode == SCSIOP_READ || _Opcode == SCSIOP_READ16 || \ + _Opcode == SCSIOP_WRITE || _Opcode == SCSIOP_WRITE16) + +#define DsmIsReadCapacity( _Opcode ) (_Opcode == SCSIOP_READ_CAPACITY || _Opcode == SCSIOP_READ_CAPACITY16) + + +// +// Macro to find the number of bytes consumed by the array +// +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + + +// +// Signature used to identify various structures. +// Used solely for debugging purposes. +// +#define DSM_DEVICE_SIG 0xAAAAAAAA +#define DSM_GROUP_SIG 0x55555555 +#define DSM_FOG_SIG 0x88888888 +#define DSM_TARGET_PORT_GROUP_SIG 0x33333333 +#define DSM_TARGET_PORT_SIG 0xCCCCCCCC +#define DSM_CONTROLLER_SIG 0xEEEEEEEE + +#define WNULL (L'\0') +#define WNULL_SIZE (sizeof(WNULL)) + +#if DBG + +// +// NT_ASSERT wrapper. +// +#define DSM_ASSERT(exp) if (DoAssert) { \ + NT_ASSERT(exp); \ + } + +#else // DBG + +#define DSM_ASSERT(exp) + +#endif // DBG + +#define DSM_PARAMETER_PATH_W L"MSDSM\\Parameters" + +// +// Pool Tags used in memory allocation +// +#define DSM_TAG_GENERIC '00ZZ' +#define DSM_TAG_PASS_THRU '10ZZ' +#define DSM_TAG_GROUP_ENTRY '20ZZ' +#define DSM_TAG_FO_GROUP '30ZZ' +#define DSM_TAG_DSM_CONTEXT '40ZZ' +#define DSM_TAG_DEV_INFO '50ZZ' +#define DSM_TAG_SERIAL_NUM '60ZZ' +#define DSM_TAG_CTRL_INFO '70ZZ' +#define DSM_TAG_SUPPORTED_DEV '80ZZ' +#define DSM_TAG_REG_PATH '90ZZ' +#define DSM_TAG_FOG_DEV_ENTRY 'A0ZZ' +#define DSM_TAG_DEV_ID 'B0ZZ' +#define DSM_TAG_DEV_NAME 'C0ZZ' +#define DSM_TAG_LB_POLICY 'D0ZZ' +#define DSM_TAG_PR_KEYS 'E0ZZ' +#define DSM_TAG_RESERVED_DEVICE 'F0ZZ' +#define DSM_TAG_BIN_TO_ASCII '01ZZ' +#define DSM_TAG_TARGET_PORT_LIST_ENTRY '11ZZ' +#define DSM_TAG_TARGET_PORT_GROUP_ENTRY '21ZZ' +#define DSM_TAG_RELATIVE_TARGET_PORT_ID '31ZZ' +#define DSM_TAG_TARGET_PORT_GROUPS '41ZZ' +#define DSM_TAG_CONTROLLER_LIST_ENTRY '51ZZ' +#define DSM_TAG_CONTROLLER_INFO '61ZZ' +#define DSM_TAG_IO_STATUS_BLOCK '71ZZ' +#define DSM_TAG_DEVICE_ID_LIST '81ZZ' +#define DSM_TAG_TP_DEVICE_LIST_ENTRY '91ZZ' +#define DSM_TAG_RETRY_RESERVE 'A1ZZ' +#define DSM_TAG_WORKITEM 'B1ZZ' +#define DSM_TAG_SCSI_ADDRESS 'C1ZZ' +#define DSM_TAG_FAIL_DEVINFO_LIST_ENTRY 'D1ZZ' +#define DSM_TAG_TPG_COMPLETION_CONTEXT 'E1ZZ' +#define DSM_TAG_SCSI_REQUEST_BLOCK 'F1ZZ' +#define DSM_TAG_SCSI_SENSE_INFO '02ZZ' +#define DSM_TAG_SPT_DATA_BUFFER '12ZZ' +#define DSM_TAG_REG_KEY_RELATED '22ZZ' +#define DSM_TAG_DEV_HARDWARE_ID '32ZZ' +#define DSM_TAG_REG_VALUE_RELATED '42ZZ' +#define DSM_TAG_ZOMBIEGROUP_ENTRY '52ZZ' +#define DSM_TAG_PERSISTENT_RESERVATION '62ZZ' + +// +// Parameters subkey name under HKLM\System\CCS\Services\MSDSM +// +#define DSM_SERVICE_PARAMETERS L"Parameters" + +// +// Load Balance settings are persisted in the registry under this key +// +#define DSM_LOAD_BALANCE_SETTINGS L"DsmLoadBalanceSettings" + +// +// Load Balance settings on a VID/PID basis are persistented in the registry +// under this key +// +#define DSM_TARGETS_LOAD_BALANCE_SETTING L"DsmTargetsLoadBalanceSetting" + +// +// Values persisted per device: +// 1. Load Balance Policy +// 2. Preferred Path +// 3. Whether LB policy has been explicitly set +// +#define DSM_LOAD_BALANCE_POLICY L"DsmLoadBalancePolicy" +#define DSM_PREFERRED_PATH L"DsmPreferredPath" +#define DSM_POLICY_EXPLICITLY_SET L"DsmLoadBalancePolicyExplicitlySet" + +// +// Prefix for subkey created for each path +// +#define DSM_PATH L"DSMPath" + +// +// Values persisted per path: +// 1. Whether primary +// 2. Whether optimized +// 3. Path weight. +// +// Primary Optimized State +//==================================== +// True True Active-Optimized +// True False Active-Unoptimized +// False True StandBy +// False False Unavailable +// +#define DSM_PRIMARY_PATH L"DsmPrimaryPath" +#define DSM_OPTIMIZED_PATH L"DsmOptimizedPath" +#define DSM_PATH_WEIGHT L"DsmPathWeight" + +// +// Indicates that device doesn't support ALUA. +// +#define DSM_DEVINFO_ALUA_NOT_SUPPORTED 0 + +// +// Implies that device supports implicit ALUA transistions. +// +#define DSM_DEVINFO_ALUA_IMPLICIT 1 + +// +// Implies that device supports explicit ALUA state transitions. +// +#define DSM_DEVINFO_ALUA_EXPLICIT 2 + +// +// Type of device identifier (VPD 0x83) +// +typedef enum _DSM_DEVID_TYPE { + DSM_DEVID_SERIAL_NUMBER = 1, + DSM_DEVID_RELATIVE_TARGET_PORT, + DSM_DEVID_TARGET_PORT_GROUP +} DSM_DEVID_TYPE, *PDSM_DEVID_TYPE; + +#define _DSM_TERNARY_BOOLEAN UCHAR +typedef _DSM_TERNARY_BOOLEAN DSM_TERNARY_BOOLEAN, *PDSM_TERNARY_BOOLEAN; +#define DSM_TERNARY_UNKNOWN 0 +#define DSM_TERNARY_TRUE 1 +#define DSM_TERNARY_FALSE 2 + +// +// Macro to determine if _Id2 is more preferred than _Id1 to build a device's +// serial number. +// +#define DsmpIsPreferredDeviceId(_Id1, _Id2) (((_Id2) == StorageIdTypeScsiNameString) || \ + ((_Id2) == StorageIdTypeFCPHName && (_Id1) != StorageIdTypeScsiNameString) || \ + ((_Id2) == StorageIdTypeEUI64 && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName) || \ + ((_Id2) == StorageIdTypeVendorId && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName && (_Id1) != StorageIdTypeEUI64) || \ + ((_Id2) == StorageIdTypeVendorSpecific && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName && (_Id1) != StorageIdTypeEUI64 && (_Id1) != StorageIdTypeVendorId)) + +// +// Device State +// +typedef enum _DSM_DEVICE_STATE { + + // + // If ALUA is not supported, this state indicates that the device is active + // and a request can be sent to the device. + // If ALUA is supported, then this state indicates optimizied device-path + // pair for the device. + // + DSM_DEV_ACTIVE_OPTIMIZED = 0, + + // + // If ALUA is not supported, this state is not used. + // If ALUA is supported, then this state indicates active but unoptimized + // device-path pairing for the device. Can be used in in case no + // active/optimized path is available to service the IO. + // + DSM_DEV_ACTIVE_UNOPTIMIZED, + + // + // If ALUA is not supported, this state indicates that the device is in + // standby state. A request can be sent to the device in this state. + // If ALUA is supported, then this state indicates standby device-path + // pairing and only certain requests can be handled in this state. + // + DSM_DEV_STANDBY, + + // + // If ALUA is not supported, this state is not used. + // If ALUA is supported, then this state indicates that the device-path pairing + // is not active and incapable of handling any requests. + // + DSM_DEV_UNAVAILABLE, + + // + // If ALUA is not supported, this state is not used. + // If ALUA is supported, then this state indicates that the device-path pairing + // (actually its TPG) is in a transitioning state. + // + DSM_DEV_TRANSITIONING = 15, + + // + // Initial state when devInfo is created. + // + DSM_DEV_NOT_USED_STATE = 16, + + // + // Indicates that the state was undetermined (this is applicable only for + // a deviceInfo's DesiredState or if the device instance's path was not + // determined). + // + DSM_DEV_UNDETERMINED, + + // + // Indicates that a request sent down previously failed with a fatal error + // + DSM_DEV_FAILED, + + // + // Indicates that InvalidatePath has been called + // + DSM_DEV_INVALIDATED, + + // + // This indicates the device is about to be removed. No new request + // should be sent to the device. + // + DSM_DEV_REMOVE_PENDING, + + // + // This indicates the device has been removed. + // + DSM_DEV_REMOVED + +} DSM_DEVICE_STATE, *PDSM_DEVICE_STATE; + +// +// Device states supported +// +#define DSM_STATE_ACTIVE_OPTIMIZED_SUPPORTED 0 +#define DSM_STATE_STANDBY_SUPPORTED 1 +#define DSM_STATE_ACTIVE_UNOPTIMIZED_SUPPORTED 2 +#define DSM_STATE_UNAVAILABLE_SUPPORTED 4 + + +// +// Macro to determine if devInfo is in a failure state. +// +#define DsmpIsDeviceFailedState(_State) ((_State) > DSM_DEV_NOT_USED_STATE) + +// +// Macro to determine if devInfo was initialized. +// +#define DsmpIsDeviceInitialized(_DeviceInfo) ((_DeviceInfo)->Initialized) + +// +// Macro to determine if device is "usable" (ie. IsPathActive was successfully called). +// +#define DsmpIsDeviceUsable(_DeviceInfo) ((_DeviceInfo)->Usable) + +// +// Macro to determine if devInfo was used to send down registration. +// It the devInfo's group is not reserved, then the devInfo doesn't need to have +// had a register go down it. +// It the group is reserved, then the devInfo MUST have had a register go down it +// for it to be used. +// +#define DsmpIsDeviceUsablePR(_DeviceInfo) (!(_DeviceInfo)->Group->PRKeyValid || (_DeviceInfo)->PRKeyRegistered) + + +// +// Macro to determine if _State2 is a more preferred state than _State1. +// +#define DsmpIsBetterDeviceState(_State1, _State2) (((_State1) == DSM_DEV_STANDBY && (_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED) || \ + ((_State1) == DSM_DEV_UNAVAILABLE && \ + ((_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED || (_State2) == DSM_DEV_STANDBY)) || \ + ((_State1) == DSM_DEV_TRANSITIONING && \ + ((_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED || (_State2) == DSM_DEV_STANDBY) || (_State2) == DSM_DEV_UNAVAILABLE)) + +// +// Macro to determine if passed in _State is active. +// +#define DsmpIsDeviceStateActive(_State) ((_State) == DSM_DEV_ACTIVE_OPTIMIZED || (_State) == DSM_DEV_ACTIVE_UNOPTIMIZED) + +// +// Macro to determine if symmetric access to the storage +// +#define DsmpIsSymmetricAccess(_DeviceInfo) ((_DeviceInfo)->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED || \ + ((_DeviceInfo)->ALUASupport == DSM_DEVINFO_ALUA_IMPLICIT && \ + (_DeviceInfo)->Group->Symmetric)) + +// +// Multi-path Group State +// +typedef enum _DSM_GROUP_STATE { + + // + // This indicates that the device is in working state. + // + DSM_GP_NORMAL = 1, + + // + // This indicates that there is a pending reservation failover + // + DSM_GP_PENDING, + + // + // This indicates that the device has lost all its paths + // + DSM_GP_FAILED + +} DSM_GROUP_STATE, *PDSM_GROUP_STATE; + +// +// Fail-Over Group State +// +typedef enum _DSM_FAILOVER_GROUP_STATE { + + // + // This indicates that the path is in working state. + // + DSM_FG_NORMAL = 1, + + // + // This indicates the path which had failed earlier + // is back to working state now. + // + DSM_FG_FAILBACK, + + // + // This indicates the path is about to be removed + // + DSM_FG_PENDING_REMOVE, + + // + // This indicates the path has failed. + // + DSM_FG_FAILED + +} DSM_FAILOVER_GROUP_STATE, *PDSM_FAILOVER_GROUP_STATE; + +#define DsmpIsPathFailedState(_State) ((_State) >= DSM_FG_PENDING_REMOVE) + +// +// DSM Context is the global driver context that gets passed to each of the DSM +// entry points. +// +// The DSM Context will maintain a list of all DeviceInfos (device-path pairing). +// It will maintain a list of Group entries. Each entry in the Group list will +// represent a LUN's different instances down different paths (i.e. DeviceInfos). +// Each entry in the Group will maintain a list of target port groups. +// Each entry in the target port group list will maintain a list of target +// ports that make up the target port group. Every deviceInfo that isn't +// in a failure state will be in the same state as the Asymmetric Access +// State of the target port group. +// There will be a list of Fail Over Group entries, where each entry represents +// the list of devices that fail over as a group (i.e. devices on the same path). +// There will also be a list of controller entries, representing the controllers +// on all storages connected to the system. +// +typedef struct _DSM_CONTEXT { + + // + // Used to synchronize access to the SupportedDevices list. + // + KSPIN_LOCK SupportedDevicesListLock; + + // + // List of supported devices - added into the INF. + // + UNICODE_STRING SupportedDevices; + + // + // Used to synchronize access to the elements in this structure. + // + EX_SPIN_LOCK DsmContextLock; + + // + // Flag cached that indicates if statistics don't need to be gathered + // + BOOLEAN DisableStatsGathering; + + UCHAR Reserved[3]; + + // + // Number of devices currently found. + // + ULONG NumberDevices; + + // + // List of devices. + // + LIST_ENTRY DeviceList; + + // + // Number of multi-path groups. + // + ULONG NumberGroups; + + // + // List of multi-path groups. + // + LIST_ENTRY GroupList; + + // + // Number of fail-over groups. + // + ULONG NumberFOGroups; + + // + // List of fail-over groups. + // + LIST_ENTRY FailGroupList; + + // + // Number of controllers. + // + ULONG NumberControllers; + + // + // List of controllers + // + LIST_ENTRY ControllerList; + + // + // Number of stale fail-over groups + // + ULONG NumberStaleFOGroups; + + // + // List of stale fail-over groups maintained for paths for which all devices + // have gotten removed but for which there is still outstanding IO-statistics + // + LIST_ENTRY StaleFailGroupList; + + // + // Context value passed to the DSM from MPIO. + // + PVOID MPIOContext; + + + // + // Look-aside list of completion routine context structures. + // + NPAGED_LOOKASIDE_LIST CompletionContextList; + +} DSM_CONTEXT, *PDSM_CONTEXT; + +// +// Statistics structure. Used by the device and path routines. +// +typedef struct _DSM_STATS { + + ULONG NumberReads; + ULONG NumberWrites; + ULONGLONG BytesRead; + ULONGLONG BytesWritten; + +} DSM_STATS, *PDSM_STATS; + + +// +// Information about each device that is supported by the DSM. +// +typedef struct _DSM_DEVICE_INFO { + + // + // To link to the next device info structure in the list + // + LIST_ENTRY ListEntry; + + // + // The device SIG. Used for debug. + // + ULONG DeviceSig; + + // + // Back-pointer to the DSM_CONTEXT. + // + PVOID DsmContext; + + // + // The underlying port driver PDO. + // + PDEVICE_OBJECT PortPdo; + + // + // The port FDO to which PortPdo is attached. + // + PDEVICE_OBJECT PortFdo; + + // + // The DeviceObject to which I/Os generated by the DSM should + // be sent. This is given to us by MPIO. + // + PDEVICE_OBJECT TargetObject; + + // + // The multi-path group to which this device belongs. + // + struct _DSM_GROUP_ENTRY *Group; + + // + // The fail-over group to which this device belongs. + // + struct _DSM_FAILOVER_GROUP *FailGroup; + + // + // The controller through which this device showed up. + // + struct _DSM_CONTROLLER_LIST_ENTRY *Controller; + + // + // The Target Port Group that this device belongs to. + // + struct _DSM_TARGET_PORT_GROUP_ENTRY *TargetPortGroup; + + // + // The Target Port that this device was exposed via. + // + struct _DSM_TARGET_PORT_LIST_ENTRY *TargetPort; + + // + // The current state of this device: ACTIVE_O, ACTIVE_U, STANDBY, UNAVAILABLE, etc. + // + DSM_DEVICE_STATE State; + + // + // Previous state of this device. Updated whenever this deviceInfo makes a + // state transition. + // + DSM_DEVICE_STATE PreviousState; + + // + // The desired state of this device: based on PrimaryPath and OptimizedPath + // specified in the registry. + // + DSM_DEVICE_STATE DesiredState; + + // + // The ALUA state of the TPG immediately after a ReportTPG is issued. + // + DSM_DEVICE_STATE ALUAState; + + // + // Holds state information temporarily while applying LB policy. Used in case + // changes need to be reverted in case of failure to apply the policy. + // + DSM_DEVICE_STATE TempPreviousStateForLB; + + // + // This is to save off the last known non-failed state. + // In case of an error down this deviceInfo, it is marked to be in Failed state. + // However, if no remove comes down for this device and a PathVerify down this + // deviceInfo succeeds, we need to put the deviceInfo back into a usable state. + // + DSM_DEVICE_STATE LastKnownGoodState; + + // + // This counter indicates that this deviceInfo is being used and a remove + // must thus wait until the counter falls to 0. + // + LONG BlockRemove; + + + // + // This indicates whether this device has handled a register/register_ignore_existing request, + // irrespective of the actual status of the operation. + // + BOOLEAN RegisterServiced; + + // + // This flag is set when a register/register_ignore_existing succeeds down this device-path pair. + // + BOOLEAN PRKeyRegistered; + + // + // Indicates whether the serial number was embedded in the device + // descriptor, or it was allocated. + // + BOOLEAN SerialNumberAllocated; + + // + // Flag to indicate that SetDeviceInfo has been called (and succeeded) on this device + // + BOOLEAN Initialized; + + // + // Flag to indicate that IsPathActive has been called (and succeeded) on this device. + // + BOOLEAN Usable; + + // + // Flag to indicate if IALUAE was disabled (via mode select) + // + BOOLEAN ImplicitDisabled; + + // + // Flag to indicate that RTPG has already been sent down in Inquire, so + // PathVerify can ignore sending down one more if it is called during + // device initialization. + // + BOOLEAN IgnorePathVerify; + + // + // Bit map indicating whether (and what kind) of ALUA support. + // + UCHAR ALUASupport; + + // + // Weight assigned to this path by management application. This is used + // when doing Load Balancing based on weighted paths. + // + ULONG PathWeight; + + // + // Number of requests outstanding on this device. + // + LONG NumberOfRequestsInProgress; + + // + // I/O, Fail-Over statistics. + // + DSM_STATS DeviceStats; + + // + // The device's serial number. + // + PSTR SerialNumber; + + // + // The scsi address of the port pdo. + // + PSCSI_ADDRESS ScsiAddress; + + + // + // Kernel structure that describes this device. Passed in to Inquire. + // + // NOTE: Descriptor should be the LAST field in this structure + // + STORAGE_DEVICE_DESCRIPTOR Descriptor; + +} DSM_DEVICE_INFO, *PDSM_DEVICE_INFO; + +typedef enum _DSM_DEFAULT_LB_POLICY_TYPE { + DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY = 0, // DSM assigned based on LUN access capability + DSM_DEFAULT_LB_POLICY_DSM_WIDE, // Admin has set a DSM-wide default policy + DSM_DEFAULT_LB_POLICY_VID_PID, // Admin has set a default policy for LUN's VID/PID + DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT // Admin has explicitly set the policy on the LUN +} DSM_DEFAULT_LB_POLICY_TYPE, *PDSM_DEFAULT_LB_POLICY_TYPE; + +typedef ULONG DSM_LOAD_BALANCE_TYPE, *PDSM_LOAD_BALANCE_TYPE; + + +// +// Information about multi-path groups: The same device found via multiple paths +// are put under one group. Each group will have it's own Load Balance policy +// settings. In other words, Load Balance policy settings are on per-device basis. +// +typedef struct _DSM_GROUP_ENTRY { + + // + // To link to the next entry in the multi-path group. + // + LIST_ENTRY ListEntry; + + // + // Group signature. Used for debug. + // + ULONG GroupSig; + + // + // Ordinal of creation. Never decremented. + // + ULONG GroupNumber; + + // + // State of the group. + // + DSM_GROUP_STATE State; + + // + // Number of devices in the multi-path group. + // + ULONG NumberDevices; + + // + // Array of devices belonging to this group. + // + PDSM_DEVICE_INFO DeviceList[DSM_MAX_PATHS]; + + // + // Max time to retry failed PR requests + // + ULONG MaxPRRetryTimeDuringStateTransition; + + // + // Number of target port groups that this device is accessible via. + // + ULONG NumberTargetPortGroups; + + // + // Array of the target port groups that this LUN belongs in. + // + struct _DSM_TARGET_PORT_GROUP_ENTRY *TargetPortGroupList[DSM_MAX_PATHS]; + + // + // Key used in Persistent Reserve\Release. This key is provided to the DSM + // by Cluster service. If cluster service has provided the key PRKeyValid + // is set to TRUE. PRKeyValid is set to FALSE otherwise. + // PRServiceAction, PRType and PRScope are the service action, type and + // scope associated with the PR registration. + // + UCHAR PersistentReservationRegisteredKey[8]; + UCHAR PRServiceAction; + UCHAR PRType; + UCHAR PRScope; + UCHAR PRKeyValid; + + + // + // Flag used to denote that LU access is symmetric down all paths + // + BOOLEAN Symmetric; + + // + // Flag to indicate whether or not to use same path for sequential IO + // when employing Least Blocks load balance policy. + // + BOOLEAN UseCacheForLeastBlocks; + + // + // Flag used to indicate if a throttle request succeeded. + // + ULONG Throttled; + + // + // Counter to track the number of RTPG in flight. + // + ULONG InFlightRTPG; + + // + // A bitmask of which devices are currently reserved. + // + ULONG ReservationList; + + // + // Which type of Load Balancing is being performed. + // + DSM_LOAD_BALANCE_TYPE LoadBalanceType; + + // + // Indicates how the Load Balancing policy was selected. + // + DSM_DEFAULT_LB_POLICY_TYPE LBPolicySelection; + + // + // The path to use when possible - if in F.O. Only, if failover had taken + // place and this path comes back online, failback to this path will take + // place. + // + ULONGLONG PreferredPath; + + // + // The path to choose when Round Robin Load Balance policy is in use + // + PVOID PathToBeUsed; + + // + // Size of cache set by Admin. Used in case of handling sequential + // IO in Least Blocks policy. + // + ULONGLONG CacheSizeForLeastBlocks; + + // + // The HardwareId (VID/PID) of the LUN + // + PWSTR HardwareId; + + // + // The registry key under which Load Balance Policy settings + // are stored in the registry for this Device Group. + // + PWSTR RegistryKeyName; + + // + // Number of failing deviceInfos + // + ULONG NumberFailingDevInfos; + + // + // To link the list of failed A/O devInfos and the corresponding non-A/O + // devInfos that are temporarily being used to service IO until STPG can + // properly update the device states. This is applicable only for ALUA + // devices. + // + LIST_ENTRY FailingDevInfoList; + + // + // General Purpose Event. + // + KEVENT Event; + +} DSM_GROUP_ENTRY, *PDSM_GROUP_ENTRY; + +// +// The collection of devices on one path. These fail-over as a unit. +// A path is considered an I_T nexus, i.e. Initiator port to Target (controller) port. +// +typedef struct _DSM_FAILOVER_GROUP { + + // + // To link to the next entry in the failover group + // + LIST_ENTRY ListEntry; + + // + // Signature. Used for debug. + // + ULONG FailOverSig; + + // + // State of the Path. + // + DSM_FAILOVER_GROUP_STATE State; + + // + // The pathId corresponding to this FOG. It may or may not be + // the same as what MPIO gave us as the default value. + // + PVOID PathId; + + // + // The default pathId (port FDO). + // + PDEVICE_OBJECT MPIOPath; + + // + // Last LBA + // + ULONGLONG LastLba; + + // + // Cumulative outstanding IO (in terms of size) + // + ULONGLONG OutstandingBytesOfIO; + + // + // Count of inflight IOs. This will be used in LQD load balance policy. + // + volatile LONG NumberOfRequestsInFlight; + + // + // Number of devices in this FOG. + // + ULONG Count; + + // + // List of devices that will over together. + // + LIST_ENTRY FOG_DeviceList; + + // + // List of zombie groups (in case a device is removed before the failover + // processing begins). + // + LIST_ENTRY ZombieGroupList; + +} DSM_FAILOVER_GROUP, *PDSM_FAILOVER_GROUP; + + +// +// Information about a target port group entry for a given LUN. +// Note: This is not a global list of all TPGs that are built. It is local to a Group entry. +// +typedef struct _DSM_TARGET_PORT_GROUP_ENTRY { + + // + // Signature. Used for debug. + // + ULONG TargetPortGroupSig; + + // + // The asymmetric access state for this target port group: + // ACTIVE_O, ACTIVE_U, STANDBY or UNAVAILABLE + // + DSM_DEVICE_STATE AsymmetricAccessState; + + // + // Flag to indicate if this is the preferred target port group. + // + BOOLEAN Preferred; + + // + // Supported access states + // + BOOLEAN ActiveOptimizedSupported; + BOOLEAN ActiveUnoptimizedSupported; + BOOLEAN StandBySupported; + BOOLEAN UnavailableSupported; + + // + // Indicates if the device reports asymmetric state as being under transition. + // + BOOLEAN TransitioningSupported; + + // + // Flag to indicate if this has been returned in any subsequent RTPG after + // it is initially built. (If this flag is not set after parsing the RTPG + // information, it indicates that this TPG entry is stale and should be + // deleted). + // + BOOLEAN Traversed; + + UCHAR Reserved; + + // + // The target group identifier + // + USHORT Identifier; + + // + // Status code + // + UCHAR StatusCode; + + // + // Vendor unique + // + UCHAR VendorUnique; + + // + // Backpointer to owning group + // + PDSM_GROUP_ENTRY Group; + + // + // Number of target ports that make up this group + // + ULONG NumberTargetPorts; + + // + // Linked list of target ports that make up this target port group. + // + LIST_ENTRY TargetPortList; + +} DSM_TARGET_PORT_GROUP_ENTRY, *PDSM_TARGET_PORT_GROUP_ENTRY; + + +// +// Information about each target port list entry for a given target port group. +// Note: this is not a global list of all TPs. It is local to a given TPG entry. +// +typedef struct _DSM_TARGET_PORT_LIST_ENTRY { + + // + // Link + // + LIST_ENTRY ListEntry; + + // + // Signature. Used for debug. + // + ULONG TargetPortSig; + + // + // Relative target port identifier + // + ULONG Identifier; + + // + // Backpointer to owning target port group + // + PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup; + + // + // Number of device instances exposed via this target port + // + ULONG Count; + + // + // List of device instances exposed via this target port + // + LIST_ENTRY TP_DeviceList; + +} DSM_TARGET_PORT_LIST_ENTRY, *PDSM_TARGET_PORT_LIST_ENTRY; + +// +// Information about each controller entry +// +typedef struct _DSM_CONTROLLER_LIST_ENTRY { + + // + // To link to the next contoller entry. + // + LIST_ENTRY ListEntry; + + // + // It's signature. Used for debug. + // + ULONG ControllerSig; + + // + // Device object (this controller's PDO). + // + PDEVICE_OBJECT DeviceObject; + + // + // Port FDO through which this controller object was exposed. + // + PDEVICE_OBJECT PortObject; + + // + // Identifier. + // + _Field_size_(IdLength) PUCHAR Identifier; + + // + // Identifier length. + // + ULONG IdLength; + + // + // Identifier code set. + // + STORAGE_IDENTIFIER_CODE_SET IdCodeSet; + + // + // Controller's SCSI address. + // + PSCSI_ADDRESS ScsiAddress; + + // + // Number of references to this entry. + // + UCHAR RefCount; + + // + // Flag to indicate whether this is a fake entry built for storage that do + // NOT have controllers + // + BOOLEAN IsFakeController; + + UCHAR Reserved[2]; + +} DSM_CONTROLLER_LIST_ENTRY, *PDSM_CONTROLLER_LIST_ENTRY; + +// +// Generic linked list of devices +// +typedef struct _DSM_DEVICELIST_ENTRY { + + // + // To link to the next device info structure in the list + // + LIST_ENTRY ListEntry; + + // + // Representation of device-path pair + // + PDSM_DEVICE_INFO DeviceInfo; + +} DSM_DEVICELIST_ENTRY, *PDSM_DEVICELIST_ENTRY; + +// +// Zombie Group List Entry +// +typedef struct _DSM_ZOMBIEGROUP_ENTRY { + + // + // To link to the next zombie group structure in the list + // + LIST_ENTRY ListEntry; + + // + // Pointer to actual group entry + // + PDSM_GROUP_ENTRY Group; + + // + // Flag to indicate that the failover thread has processed this entry. + // + BOOLEAN Processed; + +} DSM_ZOMBIEGROUP_ENTRY, *PDSM_ZOMBIEGROUP_ENTRY; + +// +// Linked list of devices that will failover as a group +// +typedef DSM_DEVICELIST_ENTRY DSM_FOG_DEVICELIST_ENTRY, *PDSM_FOG_DEVICELIST_ENTRY; + +// +// Linked list of the same device being exposed off of a particular target port +// (possibly because the controller is connected to multiple HBAs). +// +typedef DSM_DEVICELIST_ENTRY DSM_TARGET_PORT_DEVICELIST_ENTRY, *PDSM_TARGET_PORT_DEVICELIST_ENTRY; + +// +// Information about each failing devInfo and its corresponding devInfo +// being used temporarily to service requests until STPG can update new +// device states. +// +typedef struct _DSM_FAIL_PATH_PROCESSING_LIST_ENTRY { + + // + // To link to the next device info structure in the list + // + LIST_ENTRY ListEntry; + + // + // Representation of the failing device-path pair + // + PDSM_DEVICE_INFO FailingDeviceInfo; + + // + // Representation of the new candidate device-path pair that will take over + // processing of requests + // + PDSM_DEVICE_INFO TempDeviceInfo; + +} DSM_FAIL_PATH_PROCESSING_LIST_ENTRY, *PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY; + +// +// Completion context structure. +// +typedef struct _DSM_COMPLETION_CONTEXT { + + // + // The device that handled the request. + // + PDSM_DEVICE_INFO DeviceInfo; + + // + // The global context. + // + PDSM_CONTEXT DsmContext; + + // + // These are used to store control code, pointer to KEVENT, etc. + // + PVOID RequestUnique1; + + ULONG_PTR RequestUnique2; + +#if DBG + // + // Request time-stamp. + // + LARGE_INTEGER TickCount; +#endif + +} DSM_COMPLETION_CONTEXT, *PDSM_COMPLETION_CONTEXT; + +// +// Completion context structure for report/set target port groups. +// +typedef struct _DSM_TPG_COMPLETION_CONTEXT { + + PDSM_COMPLETION_CONTEXT CompletionContext; + + PSCSI_REQUEST_BLOCK Srb; + + PVOID SenseInfoBuffer; + + ULONG NumberRetries; + + UCHAR SenseInfoBufferLength; + +} DSM_TPG_COMPLETION_CONTEXT, *PDSM_TPG_COMPLETION_CONTEXT; + +// +// Version number used to determine whice version of MPIO_DSM_Path to use. +// +#define DSM_WMI_VERSION_1 1 +#define DSM_WMI_VERSION_2 2 + +// +// Version of MPIO_DSM_Path that is currently supported by this DSM. +// +#define DSM_WMI_VERSION DSM_WMI_VERSION_2 + +// +// This struct is used to save Load Balance Policy Settings in the registry +// +typedef struct _DSM_LOAD_BALANCE_POLICY_SETTINGS { + + WCHAR RegistryKeyName[256]; + ULONG LoadBalancePolicy; + ULONG PathCount; + MPIO_DSM_Path_V2 DsmPath[1]; + +} DSM_LOAD_BALANCE_POLICY_SETTINGS, *PDSM_LOAD_BALANCE_POLICY_SETTINGS; + +// +// This structure is used to pass in information used by the workitem +// to failover reservations down another path. +// +typedef struct _DSM_RETRY_RESERVE { + + PDSM_COMPLETION_CONTEXT CompletionContext; + + PIRP Irp; + + PKEVENT Event; + +} DSM_RETRY_RESERVE, *PDSM_RETRY_RESERVE; + +// +// This structure defines the workitem that will be used to handle reservation +// failover. +// +typedef struct _DSM_WORKITEM { + + // + // Work item that should be freed by the worker routine + // + PIO_WORKITEM WorkItem; + + // + // Context to be passed to worker routine + // + PVOID Context; + +} DSM_WORKITEM, *PDSM_WORKITEM; + +#endif // _MSDSM_H + + diff --git a/tests/projects/wdk/wdm/msdsm/msdsm.mof b/tests/projects/wdk/wdm/msdsm/msdsm.mof new file mode 100644 index 000000000..53ee61392 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/msdsm.mof @@ -0,0 +1,82 @@ +// +// Copyright (C) 2004 Microsoft Corporation +// +// +// Microsoft DSM's internal classes +// + +// +// Perf class. +// +[WMI, + guid("{a34d03ec-6b0b-46a1-9178-82525f41133f}")] +class MSDSM_DEVICEPATH_PERF +{ + [WmiDataId(1), + Description("Path Identifier.") : amended + ] uint64 PathId; + + [WmiDataId(2), + Description("Number of Read Requests.") : amended + ] uint32 NumberReads; + + [WmiDataId(3), + Description("Number of Write Requests.") : amended + ] uint32 NumberWrites; + + [WmiDataId(4), + Description("Total Bytes Read.") : amended + ] uint64 BytesRead; + + [WmiDataId(5), + Description("Total Bytes Written.") : amended + ] uint64 BytesWritten; +}; + +[WMI, + Dynamic, + Provider("WmiProv"), + Description("Retrieve MSDSM Performance Information.") : amended, + Locale("MS\\0x409"), + guid("{875b8871-4889-4114-93f6-cd064c001cea}")] +class MSDSM_DEVICE_PERF +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + Description("Number of paths.") : amended + ] uint32 NumberPaths; + + [WmiDataId(2), + read, + Description("Array of Performance Information per path for the device.") : amended, + WmiSizeIs("NumberPaths") + ] MSDSM_DEVICEPATH_PERF PerfInfo[]; +}; + +// +// Methods +// Clear perf counters. +// +[Dynamic, + Provider("WMIProv"), + WMI, + Description("MSDSM WMI Methods") : amended, + guid("{04517f7e-92bb-4ebe-aed0-54339fa5f544}"), + locale("MS\\0x409") +] +class MSDSM_WMI_METHODS +{ + + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiMethodId(1), + Implemented, + Description("Clear path performance counters for the device.") : amended + ] void MSDsmClearCounters(); +}; diff --git a/tests/projects/wdk/wdm/msdsm/msdsm.rc b/tests/projects/wdk/wdm/msdsm/msdsm.rc new file mode 100644 index 000000000..743640342 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/msdsm.rc @@ -0,0 +1,24 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 2004 +// +// File: msdsm.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Microsoft Device Specific Module" +#define VER_INTERNALNAME_STR "msdsm.sys" +#define VER_ORIGINALFILENAME_STR "msdsm.sys" + +#include "common.ver" + +MofResourceName MOFDATA msdsm.bmf +DsmMofResourceName MOFDATA msdsmdsm.bmf diff --git a/tests/projects/wdk/wdm/msdsm/msdsmdsm.mof b/tests/projects/wdk/wdm/msdsm/msdsmdsm.mof new file mode 100644 index 000000000..f85eea270 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/msdsmdsm.mof @@ -0,0 +1,141 @@ +// +// Copyright (C) 2004 Microsoft Corporation +// +// Microsoft DSM's DSM-specific classes +// + +// +// Class used for retrieving and setting MSDSM-wide default load balance policy. +// +[WMI, + Dynamic, + Provider("WmiProv"), + Description("MSDSM-wide default load balance policies.") : amended, + Locale("MS\\0x409"), + guid("{c81b5681-f3ca-4c98-9325-707d0d62ffc4}")] +class MSDSM_DEFAULT_LOAD_BALANCE_POLICY +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, write, + Description("Load Balance Policy to be applied to devices controlled by MSDSM.") : amended + ] uint32 LoadBalancePolicy; + + [WmiDataId(2), + read, + Description("Reserved.") : amended + ] uint32 Reserved; + + // + // Preferred path. + // + [WmiDataId(3), + read, write, + Description("Preferred Path.") : amended + ] uint64 PreferredPath; +}; + +// +// Embedded class that describes a target and the default load balance policy +// of its LUNs. +// +[WMI, + guid("{ddb00a72-0fab-418b-a89e-97370ae293a4}")] +class MSDSM_TARGET_DEFAULT_POLICY_INFO +{ + // + // VID-PID string as an 8 + 16 character concatenated string. + // Spaces should be used to make the VID 8 chars and the PID 16 chars. + // + [WmiDataId(1), + MaxLen(31), + Description("Concatenated VendorID (8 characters) and ProductID (16 characters).") : amended + ] string HardwareId; + + // + // The default load balance policy to be applied to LUNs from the target + // whose hardware id matches the VID/PID above. + // NOTE: Setting this to 0 will act as removal of default setting for this + // target. + // + [WmiDataId(2)] uint32 LoadBalancePolicy; + + // + // Used for alignment reasons. + // + [WmiDataId(3)] uint32 Reserved; + + // + // Preferred path. + // + [WmiDataId(4)] uint64 PreferredPath; +}; + +// +// Class used for retrieving and setting target-level default load balance policy. +// +[WMI, + Dynamic, + Provider("WmiProv"), + Description("Target-level default load balance policies.") : amended, + Locale("MS\\0x409"), + guid("{5ccbcd91-1b56-4327-a2f3-0960335f8846}")] +class MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, write, + Description("Number of targets specified.") : amended + ] uint32 NumberDevices; + + [WmiDataId(2), + read, + Description("Reserved.") : amended + ] uint32 Reserved; + + [WmiDataId(3), + read, write, + MaxLen(31), + Description("Array of target hardware identifiers with policy and preferred path information.") : amended, + WmiSizeIs("NumberDevices") + ] MSDSM_TARGET_DEFAULT_POLICY_INFO TargetDefaultPolicyInfo[]; +}; + +// +// Supported devices list class. +// +[WMI, + Dynamic, + Provider("WmiProv"), + Description("Retrieve MSDSM's supported devices list.") : amended, + Locale("MS\\0x409"), + guid("{c362d67c-371e-44d8-8bba-044619e4f245}")] +class MSDSM_SUPPORTED_DEVICES_LIST +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + Description("Number of supported devices.") : amended + ] uint32 NumberDevices; + + [WmiDataId(2), + read, + Description("Reserved.") : amended + ] uint32 Reserved; + + [WmiDataId(3), + read, + MaxLen(31), + Description("Array of device hardware identifiers.") : amended, + WmiSizeIs("NumberDevices") + ] string DeviceId[]; +}; diff --git a/tests/projects/wdk/wdm/msdsm/precomp.h b/tests/projects/wdk/wdm/msdsm/precomp.h new file mode 100644 index 000000000..bca8595db --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/precomp.h @@ -0,0 +1,34 @@ + +/*++ + +Copyright (c) 2004 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompiled header file for Microsoft Device Specific Module (DSM). + +Revision History: + +--*/ + +#pragma once + +#define DEBUG_MAIN_SOURCE 1 + +#include +#include + +#include "dsm.h" +#include "mpiodisk.h" +#include "msdsm.h" +#include "prototypes.h" +#include "trace.h" +#include "srbhelper.h" + +#include +#include + diff --git a/tests/projects/wdk/wdm/msdsm/precompsrc.c b/tests/projects/wdk/wdm/msdsm/precompsrc.c new file mode 100644 index 000000000..5944cf515 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/tests/projects/wdk/wdm/msdsm/prototypes.h b/tests/projects/wdk/wdm/msdsm/prototypes.h new file mode 100644 index 000000000..ebcf42029 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/prototypes.h @@ -0,0 +1,1436 @@ + +/*++ + +Copyright (C) 2004 Microsoft Corporation + +Module Name: + + prototypes.h + +Abstract: + + Contains function prototypes for all the functions defined + by Microsoft Device Specific Module (DSM). + +Environment: + + kernel mode only + +Notes: + +--*/ + +#pragma warning (disable:4214) // bit field usage +#pragma warning (disable:4200) // zero-sized array + +#ifndef _PROTOTYPES_H_ +#define _PROTOTYPES_H_ + +#define DSM_VENDOR_ID_LEN 8 +#define DSM_PRODUCT_ID_LEN 16 +#define DSM_VENDPROD_ID_LEN 24 + +// +// In accordance with SPC-3 specs +// +#define SPC3_TARGET_PORT_GROUPS_HEADER_SIZE 4 + +typedef struct _SPC3_CDB_REPORT_TARGET_PORT_GROUPS { + UCHAR OperationCode; + UCHAR ServiceAction : 5; + UCHAR Reserved1 : 3; + UCHAR Reserved2[4]; + UCHAR AllocationLength[4]; + UCHAR Reserved3; + UCHAR Control; +} SPC3_CDB_REPORT_TARGET_PORT_GROUPS, *PSPC3_CDB_REPORT_TARGET_PORT_GROUPS; + +typedef struct _SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR { + UCHAR AsymmetricAccessState : 4; + UCHAR Reserved : 3; + UCHAR Preferred : 1; + UCHAR ActiveOptimizedSupported : 1; + UCHAR ActiveUnoptimizedSupported : 1; + UCHAR StandbySupported : 1; + UCHAR UnavailableSupported : 1; + UCHAR Reserved2 : 3; + UCHAR TransitioningSupported : 1; + USHORT TPG_Identifier; + UCHAR Reserved3; + UCHAR StatusCode; + UCHAR VendorUnique; + UCHAR NumberTargetPorts; + ULONG TargetPortIds[0]; +} SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR, *PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR; + +typedef struct _SPC3_CDB_SET_TARGET_PORT_GROUPS { + UCHAR OperationCode; + UCHAR ServiceAction : 5; + UCHAR Reserved1 : 3; + UCHAR Reserved2[4]; + UCHAR ParameterListLength[4]; + UCHAR Reserved3; + UCHAR Control; +} SPC3_CDB_SET_TARGET_PORT_GROUPS, *PSPC3_CDB_SET_TARGET_PORT_GROUPS; + +typedef struct _SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR { + UCHAR AsymmetricAccessState : 4; + UCHAR Reserved1 : 4; + UCHAR Reserved2; + USHORT TPG_Identifier; +} SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR, *PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR; + +typedef struct _SPC3_CONTROL_EXTENSION_MODE_PAGE { + UCHAR PageCode : 6; + UCHAR SubpageFormat : 1; + UCHAR ParametersSavable : 1; + UCHAR SubpageCode; + UCHAR PageLength[2]; + UCHAR ImplicitALUAEnable : 1; + UCHAR ScsiPrecendence : 1; + UCHAR TimestampChangeable : 1; + UCHAR Reserved1 : 5; + UCHAR InitialPriority : 4; + UCHAR Reserved2 : 4; + UCHAR Reserved3[26]; +} SPC3_CONTROL_EXTENSION_MODE_PAGE, *PSPC3_CONTROL_EXTENSION_MODE_PAGE; + +#define SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS 0xA3 +#define SPC3_SCSIOP_SET_TARGET_PORT_GROUPS 0xA4 +#define SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS 0xA +#define SPC3_RESERVATION_ACTION_REPORT_CAPABILITIES 0x2 + +#define SPC3_SCSI_ADSENSE_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR 0x2F +#define SPC3_SCSI_ADSENSE_LOGICAL_UNIT_COMMAND_FAILED 0x67 + +#define SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED 0x1 +#define SPC3_SCSI_SENSEQ_RESERVATIONS_PREEMPTED 0x3 +#define SPC3_SCSI_SENSEQ_RESERVATIONS_RELEASED 0x4 +#define SPC3_SCSI_SENSEQ_REGISTRATIONS_PREEMPTED 0x5 +#define SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED 0x6 +#define SPC3_SCSI_SENSEQ_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x7 +#define SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED 0x9 +#define SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION 0xA +#define SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE 0xB +#define SPC3_SCSI_SENSEQ_TARGET_PORT_IN_UNAVAILABLE_STATE 0xC + +#define SPC3_SCSI_SENSEQ_SET_TARGET_PORT_GROUPS_FAILED 0xA + +#define SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT 10 +#define SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT 10 + + +// +// Function prototypes for functions intrface.c +// + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD DsmDriverUnload; + +NTSTATUS +DsmInquire ( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetDevice, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, + _Out_ OUT PVOID *DsmIdentifier + ); + +BOOLEAN +DsmCompareDevices( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId1, + _In_ IN PVOID DsmId2 + ); + +NTSTATUS +DsmGetControllerInfo( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN ULONG Flags, + _Inout_ IN OUT PCONTROLLER_INFO *ControllerInfo + ); + +NTSTATUS +DsmSetDeviceInfo( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetObject, + _In_ IN PVOID DsmId, + _Inout_ IN OUT PVOID *PathId + ); + +BOOLEAN +DsmIsPathActive( + _In_ IN PVOID DsmContext, + _In_ IN PVOID PathId, + _In_ IN PVOID DsmId + ); + +NTSTATUS +DsmPathVerify( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ); + +NTSTATUS +DsmInvalidatePath( + _In_ IN PVOID DsmContext, + _In_ IN ULONG ErrorMask, + _In_ IN PVOID PathId, + _Inout_ IN OUT PVOID *NewPathId + ); + +NTSTATUS +DsmMoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PVOID MPIOPath, + _In_ IN PVOID SuggestedPath, + _In_ IN ULONG Flags + ); + +NTSTATUS +DsmRemovePending( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ); + +NTSTATUS +DsmRemoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ); + +NTSTATUS +DsmRemovePath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ); + +NTSTATUS +DsmSrbDeviceControl( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +PVOID +DsmLBGetPath( + _In_ IN PVOID DsmContext, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PDSM_IDS DsmList, + _In_ IN PVOID CurrentPath, + _Out_ OUT NTSTATUS *Status + ); + +ULONG +DsmInterpretError( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT NTSTATUS *Status, + _Out_ OUT PBOOLEAN Retry, + _Out_ OUT PLONG RetryInterval, + ... + ); + +NTSTATUS +DsmUnload( + _In_ IN PVOID DsmContext + ); + +VOID +DsmSetCompletion( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT PDSM_COMPLETION_INFO DsmCompletion + ); + +_Success_(return == DSM_PATH_SET) +ULONG +DsmCategorizeRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID CurrentPath, + _Outptr_result_maybenull_ OUT PVOID *PathId, + _Out_ OUT NTSTATUS *Status + ); + +NTSTATUS +DsmBroadcastRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +BOOLEAN +DsmIsAddressTypeSupported( + _In_ IN PVOID DsmContext, + _In_ IN ULONG AddressType + ); + +NTSTATUS +DsmDeviceNotUsed( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ); + + +// +// Function prototypes for functions in dsmmain.c +// + +VOID +DsmpFreeDSMResources( + _In_ IN PDSM_CONTEXT DsmContext + ); + +PDSM_GROUP_ENTRY +DsmpFindDevice( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ); + +PDSM_GROUP_ENTRY +DsmpBuildGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +NTSTATUS +DsmpParseTargetPortGroupsInformation( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ); + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength + ); + +_Success_(return!=0) +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpUpdateTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ); + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpBuildTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ); + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ); + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpBuildTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ); + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PUSHORT TargetPortGroupId + ); + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPort( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN PULONG TargetPortGroupId + ); + +NTSTATUS +DsmpAddDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +PDSM_CONTROLLER_LIST_ENTRY +DsmpFindControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_reads_(ControllerSerialNumberLength) IN PSTR ControllerSerialNumber, + _In_ IN SIZE_T ControllerSerialNumberLength, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ); + +_Ret_maybenull_ +_Must_inspect_result_ +_When_(return != NULL, __drv_allocatesMem(Mem)) +PDSM_CONTROLLER_LIST_ENTRY +DsmpBuildControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_ IN PSTR ControllerSerialNumber, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ); + +VOID +DsmpFreeControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ __drv_freesMem(Mem) IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ); + +BOOLEAN +DsmpIsDeviceBelongsToController( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ); + +PDSM_DEVICE_INFO +DsmpFindDevInfoFromGroupAndFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAILOVER_GROUP FOGroup + ); + +PDSM_FAILOVER_GROUP +DsmpFindFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ); + +PDSM_FAILOVER_GROUP +DsmpBuildFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PVOID *PathId + ); + +NTSTATUS +DsmpUpdateFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +VOID +DsmpRemoveDeviceFailGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ); + +ULONG +DsmpRemoveDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +VOID +DsmpRemoveDeviceFromTargetPortList( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +PDSM_FAILOVER_GROUP +DsmpSetNewPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDevice + ); + +PDSM_FAILOVER_GROUP +DsmpSetNewPathUsingGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group + ); + +VOID +DsmpRemoveZombieGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY ZombieGroup + ); + +NTSTATUS +DsmpUpdateTargetPortGroupDevicesStates( + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN DSM_DEVICE_STATE NewState + ); + +VOID +DsmpIncrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +DsmpDecrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ); + +PDSM_FAILOVER_GROUP +DsmpGetPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN ULONG SpecialHandlingFlag + ); + +PVOID +DsmpGetPathIdFromPassThroughPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ IN PIRP Irp, + _Inout_ IN OUT NTSTATUS *Status + ); + +VOID +DsmpRemoveGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ); + +BOOLEAN +DsmpMpioPassThroughPathCommand( + _In_ IN PIRP Irp + ); + +BOOLEAN +DsmpReservationCommand( + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +DsmpRequestComplete( + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID DsmContext + ); + +NTSTATUS +DsmpRegisterPersistentReservationKeys( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN Register + ); + + +BOOLEAN +DsmpShouldRetryPassThroughRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +BOOLEAN +DsmpShouldRetryPersistentReserveCommand( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +BOOLEAN +DsmpShouldRetryTPGRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +BOOLEAN +DsmpIsDeviceRemoved( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +PDSM_DEVICE_INFO +DsmpGetActivePathToBeUsed( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Symmetric, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpGetAnyActivePath( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Exception, + _In_opt_ PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivate( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivateALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PBOOLEAN SendTPG, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpFindStandbyPathInAlternateTpgALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForDsmPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetLBForVidPidPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetNewDefaultLBPolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathArrival( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathArrivalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathRemoval( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathRemovalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathFailing( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathFailingALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetPathForIoRetryALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN TPGException, + _In_ IN BOOLEAN DeviceInfoException + ); + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpFindFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo + ); + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpBuildFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo, + _In_ IN PDSM_DEVICE_INFO AlternateDevInfo + ); + +IO_COMPLETION_ROUTINE DsmpPhase1ProcessPathFailingALUA; + +NTSTATUS +DsmpRemoveFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY FailPathDevInfoEntry + ); + +IO_COMPLETION_ROUTINE DsmpPhase2ProcessPathFailingALUA; + +NTSTATUS +DsmpPersistentReserveOut( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +__inline +BOOLEAN +DsmpIsPersistentReservationKeyZeroKey( + _In_ ULONG KeyLength, + _In_reads_bytes_(KeyLength) PUCHAR Key + ) +{ + BOOLEAN zeroKey = FALSE; + + NT_ASSERT(KeyLength == 8); + + if ((KeyLength) == 8 && + (Key[0] == 0 && Key[1] == 0 && Key[2] == 0 && Key[3] == 0 && + Key[4] == 0 && Key[5] == 0 && Key[6] == 0 && Key[7] == 0)) { + + zeroKey = TRUE; + } + + return zeroKey; +} + + +NTSTATUS +DsmpPersistentReserveIn( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +IO_COMPLETION_ROUTINE DsmpPersistentReserveCompletion; + + +// +// Function prototypes for functions in utils.c +// + +_Success_(return != NULL) +__drv_allocatesMem(Mem) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_(((PoolType&0x2))!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, + _Post_maybenull_ _Must_inspect_result_) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, + _Post_notnull_) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_Post_writable_byte_size_(NumberOfBytes) +PVOID +DsmpAllocatePool( + _In_ _Strict_type_match_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG Tag + ); + +_Success_(return != NULL) +_Post_maybenull_ +_Must_inspect_result_ +__drv_allocatesMem(Mem) +_Post_writable_byte_size_(*BytesAllocated) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +PVOID +DsmpAllocateAlignedPool( + _In_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG AlignmentMask, + _In_ IN ULONG Tag, + _Out_ OUT SIZE_T *BytesAllocated + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DsmpFreePool( + _In_opt_ __drv_freesMem(Mem) IN PVOID Block + ); + +NTSTATUS +DsmpGetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG StatsGatherChoice + ); + +NTSTATUS +DsmpSetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _In_ IN ULONG StatsGatherChoice + ); + + +NTSTATUS +DsmpGetDeviceList( + _In_ IN PDSM_CONTEXT Context + ); + +_Success_(return==0) +NTSTATUS +DsmpGetStandardInquiryData( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PINQUIRYDATA InquiryData + ); + +BOOLEAN +DsmpCheckScsiCompliance( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PINQUIRYDATA InquiryData, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList + ); + +BOOLEAN +DsmpDeviceSupported( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PCSTR VendorId, + _In_ IN PCSTR ProductId + ); + +BOOLEAN +DsmpFindSupportedDevice( + _In_ IN PUNICODE_STRING DeviceName, + _In_ IN PUNICODE_STRING SupportedDevices + ); + +_Success_(return!=0) +PVOID +DsmpParseDeviceID ( + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID, + _In_ IN DSM_DEVID_TYPE DeviceIdType, + _In_opt_ IN PULONG IdNumber, + _Out_opt_ PSTORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN Legacy + ); + +PUCHAR +DsmpBinaryToAscii( + _In_reads_(Length) IN PUCHAR HexBuffer, + _In_ IN ULONG Length, + _Inout_ IN OUT PULONG UpdateLength, + _In_ IN BOOLEAN Legacy + ); + +PSTR +DsmpGetSerialNumber( + _In_ IN PDEVICE_OBJECT DeviceObject + ); + + +NTSTATUS +DsmpDisableImplicitStateTransition( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PBOOLEAN DisableImplicit + ); + +PWSTR +DsmpBuildHardwareId( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +PWSTR +DsmpBuildDeviceNameLegacyPage0x80( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + + +PWSTR +DsmpBuildDeviceName( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(SerialNumberLength) IN PSTR SerialNumber, + _In_ IN SIZE_T SerialNumberLength + ); + +NTSTATUS +DsmpApplyDeviceNameCorrection( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy, + _In_ IN SIZE_T DeviceNameLegacyLen, + _In_reads_(DeviceNameLen) PWSTR DeviceName, + _In_ IN SIZE_T DeviceNameLen + ); + +NTSTATUS +DsmpQueryDeviceLBPolicyFromRegistry( + _In_ PDSM_DEVICE_INFO DeviceInfo, + _In_ PWSTR RegistryKeyName, + _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Inout_ PULONGLONG PreferredPath, + _Inout_ PUCHAR ExplicitlySet + ); + +NTSTATUS +DsmpQueryTargetLBPolicyFromRegistry( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ); + +NTSTATUS +DsmpQueryDsmLBPolicyFromRegistry( + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetDsmLBPolicyInRegistry( + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetVidPidLBPolicyInRegistry( + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpOpenLoadBalanceSettingsKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE LoadBalanceSettingsKey + ); + +NTSTATUS +DsmpOpenTargetsLoadBalanceSettingKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey + ); + +NTSTATUS +DsmpOpenDsmServicesParametersKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE ParametersSettingsKey + ); + +IO_COMPLETION_ROUTINE DsmpReportTargetPortGroupsSyncCompletion; + +_Success_(return==0) +NTSTATUS +DsmpReportTargetPortGroups( + _In_ PDEVICE_OBJECT DeviceObject, + _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo, + _Out_ PULONG TargetPortGroupsInfoLength + ); + +NTSTATUS +DsmpReportTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo + ); + +NTSTATUS +DsmpQueryLBPolicyForDevice( + _In_ IN PWSTR RegistryKeyName, + _In_ IN ULONGLONG PathId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONG PrimaryPath, + _Out_ OUT PULONG OptimizedPath, + _Out_ OUT PULONG PathWeight + ); + +VOID +DsmpGetDSMPathKeyName( + _In_ ULONGLONG DSMPathId, + _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName, + _In_ ULONG DsmPathKeyNameSize + ); + +UCHAR +DsmpGetAsciiForBinary( + _In_ UCHAR BinaryChar + ); + +NTSTATUS +DsmpGetDeviceIdList ( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor + ); + +NTSTATUS +DsmpSetTargetPortGroups( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ); + +NTSTATUS +DsmpSetTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo + ); + +PDSM_LOAD_BALANCE_POLICY_SETTINGS +DsmpCopyLoadBalancePolicies( + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ); + +NTSTATUS +DsmpPersistLBSettings( + _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings + ); + +NTSTATUS +DsmpSetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN DSM_DEVICE_STATE DevState + ); + +NTSTATUS +DsmpGetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_opt_ IN PDSM_DEVICE_STATE DevState + ); + +NTSTATUS +DsmpAdjustDeviceStatesALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_WORKITEM +DsmpAllocateWorkItem( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PVOID Context + ); + +VOID +DsmpFreeWorkItem( + _In_ IN PDSM_WORKITEM DsmWorkItem + ); + +VOID +DsmpFreeZombieGroupList( + _In_ IN PDSM_FAILOVER_GROUP FailGroup + ); + +NTSTATUS +DsmpRegCopyTree( + _In_ IN HANDLE SourceKey, + _In_ IN HANDLE DestKey + ); + +NTSTATUS +DsmpRegDeleteTree( + _In_ IN HANDLE KeyRoot + ); + +#if defined (_WIN64) +VOID +DsmpPassThroughPathTranslate32To64( + _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64 + ); + +VOID +DsmpPassThroughPathTranslate64To32( + _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32 + ); +#endif + +NTSTATUS +DsmpGetMaxPRRetryTime( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG RetryTime + ); + +NTSTATUS +DsmpQueryCacheInformationFromRegistry( + _In_ IN PDSM_CONTEXT DsmContext, + _Out_ OUT PBOOLEAN UseCacheForLeastBlocks, + _Out_ OUT PULONGLONG CacheSizeForLeastBlocks + ); + +BOOLEAN +DsmpConvertSharedSpinLockToExclusive( + _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock + ); + + +// +// Function prototypes for functions in wmi.c +// + +VOID +DsmpDsmWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiGlobalInfo, + _In_ IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +DsmGlobalQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _Out_writes_to_(BufferAvail, *DataLength) OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ); + +NTSTATUS +DsmGlobalSetData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG BufferAvail, + _In_reads_bytes_(BufferAvail) IN PUCHAR Buffer, + ... + ); + +VOID +DsmpWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiInfo, + _In_ IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +DsmQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _When_(GuidIndex == 0 || GuidIndex == 7, _Pre_notnull_ _Const_) + _When_(!(GuidIndex == 0 || GuidIndex == 7), _Out_writes_to_(BufferAvail, *DataLength)) + OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ); + +NTSTATUS +DsmpQueryLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Out_writes_bytes_(*OutBufferSize) OUT PVOID Buffer + ); + +NTSTATUS +DsmpQuerySupportedLBPolicies( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG BufferAvail, + _In_ IN ULONG DsmWmiVersion, + _Out_ OUT PULONG OutBufferSize, + _Out_writes_to_(BufferAvail, *OutBufferSize) OUT PUCHAR Buffer + ); + +NTSTATUS +DsmExecuteMethod( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG MethodId, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Inout_ IN OUT PUCHAR Buffer, + ... + ); + +NTSTATUS +DsmpClearLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ); + +NTSTATUS +DsmpSetLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _In_ IN PVOID Buffer + ); + +NTSTATUS +DsmpValidateSetLBPolicyInput( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SetLoadBalancePolicyIN, + _In_ IN ULONG InBufferSize + ); + +VOID +DsmpSaveDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ); + +VOID +DsmpRestorePreviousDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ); + +VOID +DsmpUpdateDesiredStateAndWeight( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ); + +NTSTATUS +DsmpQueryDevicePerf( + _In_ PDSM_CONTEXT DsmContext, + _In_ PDSM_IDS DsmIds, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + +NTSTATUS +DsmpClearPerfCounters( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ); + +NTSTATUS +DsmpQuerySupportedDevicesList( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + +NTSTATUS +DsmpQueryTargetsDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + +NTSTATUS +DsmpQueryDsmDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + + +// +// Function prototypes for functions in debug.c +// + +VOID +DsmpDebugPrint( + _In_ ULONG DebugPrintLevel, + _In_ PCCHAR DebugMessage, + ... + ); + +// +// SRB Helpers not found in srbhelper.h +// +_Success_(return != 0) +__drv_allocatesMem(mem) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_(((PoolType&0x2))!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, + _Post_maybenull_ _Must_inspect_result_) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, + _Post_notnull_ ) +__inline PSTORAGE_REQUEST_BLOCK_HEADER +SrbAllocateCopy( + _Inout_ PVOID Srb, + _In_ _Strict_type_match_ POOL_TYPE PoolType, + _In_ ULONG Tag + ) +/* + +Description: + This function returns an allocated copy of the given SRB. The memory is + allocated using DsmpAllocatePool(). + + ***It is up to the caller to free the memory returned by this function.*** + +Arguments: + Srb - A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK. + PoolType - The pool type to use. See documentation for ExAllocatePoolWithTag(). + Tag - The allocation tag to use. See documentation for ExAllocatePoolWithTag(). + +Returns: + NULL, if the copy could not be allocated; or + A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK that is + direct copy of the given SRB. + +*/ +{ + PSTORAGE_REQUEST_BLOCK srb = (PSTORAGE_REQUEST_BLOCK)Srb; + PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; + ULONG allocationSize = 0; + + if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) + { + allocationSize = srb->SrbLength; + NT_ASSERT(allocationSize >= (sizeof(STORAGE_REQUEST_BLOCK) + sizeof(STOR_ADDR_BTL8))); + } + else + { + allocationSize = SCSI_REQUEST_BLOCK_SIZE; + NT_ASSERT(allocationSize >= sizeof(SCSI_REQUEST_BLOCK)); + } + + #pragma warning(suppress: 28160 28118) // False-positive; PoolType is simply passed through + srbCopy = (PSTORAGE_REQUEST_BLOCK_HEADER)DsmpAllocatePool(PoolType, allocationSize, Tag); + if (srbCopy != NULL) + { + RtlCopyMemory(srbCopy, Srb, allocationSize); + } + + return srbCopy; +} + +__inline +BOOLEAN DsmpIsMPIOPassThroughEx( + ULONG ControlCode + ) +// +// Returns TRUE if the given passthrough IOCTL's control code indicates it is +// an "extended" passthrough. Returns FALSE otherwise. +// +{ + if (ControlCode == IOCTL_MPIO_PASS_THROUGH_PATH_EX || + ControlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT_EX) { + return TRUE; + } else { + return FALSE; + } +} + +__inline +UCHAR DsmpNtStatusToSrbStatus( + _In_ NTSTATUS Status + ) +/*++ + +Routine Description: + + Translate an NT status value into a SCSI Srb status code. + +Arguments: + + Status - Supplies the NT status code to translate. + +Return Value: + + SRB status code. + +--*/ +{ + switch (Status) { + + case STATUS_DEVICE_BUSY: + return SRB_STATUS_BUSY; + + case STATUS_INVALID_DEVICE_REQUEST: + return SRB_STATUS_BAD_FUNCTION; + + case STATUS_INSUFFICIENT_RESOURCES: + return SRB_STATUS_INTERNAL_ERROR; + + case STATUS_INVALID_PARAMETER: + return SRB_STATUS_INVALID_REQUEST; + + default: + if (NT_SUCCESS (Status)) { + return SRB_STATUS_SUCCESS; + } else { + return SRB_STATUS_ERROR; + } + } +} + + +#endif // _PROTOTYPES_H_ + diff --git a/tests/projects/wdk/wdm/msdsm/trace.h b/tests/projects/wdk/wdm/msdsm/trace.h new file mode 100644 index 000000000..b9449cc8f --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/trace.h @@ -0,0 +1,35 @@ + +/*++ + +Copyright (C) 2004 Microsoft Corporation + +Module Name: + + trace.h + +Abstract: + + Header file included by the Microsoft Device Specific Module (DSM). + + This file contains Windows tracing related defines. + +Environment: + + kernel mode only + +Notes: + +--*/ + +// +// Set component ID for DbgPrintEx calls +// +#define DEBUG_COMP_ID DPFLTR_MSDSM_ID + +// +// Include header file and setup GUID for tracing +// +#include +#define WPP_GUID_MSDSM (DEDADFF5, F99F, 4600, B8C9, 2D4D9B806B5B) +#define WPP_CONTROL_GUIDS WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_MSDSM) + diff --git a/tests/projects/wdk/wdm/msdsm/utils.c b/tests/projects/wdk/wdm/msdsm/utils.c new file mode 100644 index 000000000..91d8f44ce --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/utils.c @@ -0,0 +1,7946 @@ + +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + utils.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM). + It exports behaviours that mpio.sys will use to determine how to + multipath SPC-3 compliant devices. + + This file contains utility routines. + +Environment: + + kernel mode only + +Notes: + +--*/ + +#include "precomp.h" + +#ifdef DEBUG_USE_WPP +#include "utils.tmh" +#endif + +#pragma warning (disable:4305) + +extern BOOLEAN DoAssert; + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, DsmpBuildDeviceNameLegacyPage0x80) + #pragma alloc_text(PAGE, DsmpBuildDeviceName) + #pragma alloc_text(PAGE, DsmpApplyDeviceNameCorrection) + #pragma alloc_text(PAGE, DsmpOpenLoadBalanceSettingsKey) + #pragma alloc_text(PAGE, DsmpQueryLBPolicyForDevice) + #pragma alloc_text(PAGE, DsmpOpenTargetsLoadBalanceSettingKey) + #pragma alloc_text(PAGE, DsmpOpenDsmServicesParametersKey) +#endif + +_Success_(return != NULL) +__drv_allocatesMem(Mem) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_(((PoolType&0x2))!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, + _Post_maybenull_ _Must_inspect_result_) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, + _Post_notnull_ ) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_Post_writable_byte_size_(NumberOfBytes) +PVOID +DsmpAllocatePool( + _In_ _Strict_type_match_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG Tag + ) +/*+++ + +Routine Description : + + Allocates memory from the specified pool using the given tag. + If the allocation is successful, the entire buffer will be zeroed. + +Arguements: + + PoolType - Pool to allocate from (NonPaged, Paged, etc) + NumberOfBytes - Size of the buffer to allocate + Tag - Tag (DSM_TAG_XXX) to be used for this allocation. + These tags are defined in msdsm.h + +Return Value: + + Pointer to the buffer if allocation is successful + NULL otherwise + +--*/ +{ + PVOID Block = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocatePool (Tag %u): Entering function.\n", + Tag)); + + #pragma warning(suppress: 28118) // False-positive; PoolType is simply passed through + Block = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); + if (Block) { + RtlZeroMemory(Block, NumberOfBytes); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocatePool (Tag %u): Exiting function with allocated block %p.\n", + Tag, + Block)); + + return Block; +} + + +_Success_(return != NULL) +_Post_maybenull_ +_Must_inspect_result_ +__drv_allocatesMem(Mem) +_Post_writable_byte_size_(*BytesAllocated) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +PVOID +#pragma warning(suppress:28195) // Allocation is not guaranteed, caller needs to check return value +DsmpAllocateAlignedPool( + _In_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG AlignmentMask, + _In_ IN ULONG Tag, + _Out_ OUT SIZE_T *BytesAllocated + ) +/*+++ + +Routine Description : + + Allocates memory from the specified pool using the given tag and alignment requirement. + If the allocation is successful, the entire buffer will be zeroed. + +Arguements: + + PoolType - Pool to allocate from (NonPaged, Paged, etc) + NumberOfBytes - Size of the buffer to allocate + AlignmentMask - Alignment requirement specified by the device + Tag - Tag (DSM_TAG_XXX) to be used for this allocation. + These tags are defined in msdsm.h + BytesAllocated - Returns the number of bytes allocated, if the routine was successful + +Return Value: + + Pointer to the buffer if allocation is successful + NULL otherwise + +--*/ +{ + PVOID Block = NULL; + UINT_PTR align64 = (UINT_PTR)AlignmentMask; + ULONG totalSize = (ULONG)NumberOfBytes; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocateAlignedPool (Tag %u): Entering function.\n", + Tag)); + + if (BytesAllocated == NULL) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit; + } + + *BytesAllocated = 0; + + if (AlignmentMask) { + + status = RtlULongAdd((ULONG)NumberOfBytes, AlignmentMask, &totalSize); + } + + if (NT_SUCCESS(status)) { + + #pragma warning(suppress: 6014 28118) // Block isn't leaked, this function is marked as an allocator; PoolType is simply passed through + Block = ExAllocatePoolWithTag(PoolType, totalSize, Tag); + + if (Block != NULL) { + + if (AlignmentMask) { + + Block = (PVOID)(((UINT_PTR)Block + align64) & ~align64); + } + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +__Exit: + + if (NT_SUCCESS(status)) { + + RtlZeroMemory(Block, totalSize); + *BytesAllocated = totalSize; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocateAlignedPool (Tag %u): Exiting function with allocated block %p.\n", + Tag, + Block)); + + return Block; +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DsmpFreePool( + _In_opt_ __drv_freesMem(Mem) IN PVOID Block + ) +/*+++ + +Routine Description : + + Frees the block passed in. + +Arguements: + + Block - pointer to the memory to free. + +Return Value: + + Nothing + +--*/ +{ + PVOID tempAddress = Block; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreePool (Block %p): Entering function.\n", + Block)); + + if (Block) { + + ExFreePool(Block); + Block = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreePool (Block %p): Exiting function.\n", + tempAddress)); + + return; +} + + +NTSTATUS +DsmpGetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG StatsGatherChoice + ) +/*++ + +Routine Description: + + This routine is used to determine if the Admin wants statitics to be collected + on every IO. It queries the the services key for the value under + "msdsm\Parameters\DsmDisableStatistics" + +Arguments: + + Context - The DSM Context value. + StatsGatherChoice - Returns the choice of whether or not to gather statistics + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + WCHAR registryKeyName[56] = {0}; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStatsGatherChoice (DsmCtxt %p): Entering function.\n", + Context)); + + if (!StatsGatherChoice) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "DsmpGetStatsGatherChoice (DsmCtxt %p): Invalid parameter - StatsGatherChoice is NULL.\n", + Context)); + + goto __Exit_DsmpGetStatsGatherChoice; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for the supporteddeviceList and + // the second which is the 'NULL' terminator. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_DISABLE_STATISTICS; + queryTable[0].EntryContext = StatsGatherChoice; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + +__Exit_DsmpGetStatsGatherChoice: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + +NTSTATUS +DsmpSetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _In_ IN ULONG StatsGatherChoice + ) +/*++ + +Routine Description: + + This routine is used to set the value that indicates whether statistics will + be gathered on every IO. It updates the services key for the value under + "msdsm\Parameters\DsmDisableStatistics" + +Arguments: + + Context - The DSM Context value. + StatsGatherChoice - Value indicating whether to gather statistics (TRUE) or not (FALSE) + +Return Value: + + Status of the RtlWriteRegistryValue call. + +--*/ +{ + WCHAR registryKeyName[56] = {0}; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetStatsGatherChoice (DsmCtxt %p): Entering function.\n", + Context)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + + status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES, + registryKeyName, + DSM_DISABLE_STATISTICS, + REG_DWORD, + &StatsGatherChoice, + sizeof(ULONG)); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + + +NTSTATUS +DsmpGetDeviceList( + _In_ IN PDSM_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine is used to build the supported device list by querying the services + key for the values under "msdsm\Parameters\DsmSupportedDeviceList" + +Arguments: + + Context - The DSM Context value. It contains storage for the multi_sz string that may + be built. + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + WCHAR registryKeyName[56] = {0}; + UNICODE_STRING inquiryStrings; + WCHAR defaultIDs[] = { L"\0" }; + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceList (DsmCtxt %p): Entering function.\n", + Context)); + + RtlZeroMemory(queryTable, sizeof(queryTable)); + RtlInitUnicodeString(&inquiryStrings, NULL); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for the supporteddeviceList and + // the second which is the 'NULL' terminator. + // + // Indicate that there is NO call-back routine, and to give back the MULTI_SZ as + // one blob, as opposed to individual unicode strings. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND | RTL_QUERY_REGISTRY_TYPECHECK; + + // + // The value to query. + // + queryTable[0].Name = DSM_SUPPORTED_DEVICELIST_VALUE_NAME; + + // + // Where to put the strings. Note that we need to use an empty unicode_string + // for the query or else RtlQueryRegistryValues will only fill in enough + // entries as specified by the size of the unicode string's buffer, which + // is why we can't use Context->SupportedDevices directly in the call. + // + queryTable[0].EntryContext = &inquiryStrings; + queryTable[0].DefaultType = (REG_MULTI_SZ << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_MULTI_SZ; + queryTable[0].DefaultData = defaultIDs; + queryTable[0].DefaultLength = sizeof(defaultIDs); + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + + // + // If we successfully queried for the supported device list, we need to delete + // our cached list and update it with this new one. + // + if (NT_SUCCESS(status)) { + + KIRQL oldIrql; + PWCHAR tempBuffer = NULL; + + tempBuffer = DsmpAllocatePool(NonPagedPoolNx, inquiryStrings.MaximumLength, DSM_TAG_REG_VALUE_RELATED); + + // + // This is a "best effort" operation. If we are unable to allocate a + // buffer for the strings, we just continue using our old cached list. + // We do NOT fall back to using inquiryStrings's buffer as we want to + // be able to work with the supported devices list at raised IRQL. + // + if (tempBuffer) { + + RtlCopyMemory(tempBuffer, inquiryStrings.Buffer, inquiryStrings.Length); + + KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql); + DsmpFreePool(Context->SupportedDevices.Buffer); + Context->SupportedDevices.Buffer = tempBuffer; + Context->SupportedDevices.Length = inquiryStrings.Length; + Context->SupportedDevices.MaximumLength = inquiryStrings.MaximumLength; + KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceList (DsmCtxt %p): Failed to allocate supported device list's buffer.\n", + Context)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + ExFreePool(inquiryStrings.Buffer); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceList (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + +_Success_(return==0) +NTSTATUS +DsmpGetStandardInquiryData( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PINQUIRYDATA InquiryData + ) +/*++ + +Routine Description: + + Helper routine to send an inquiry with EVPD cleared to get the standard inquiry data. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + InquiryData - Pointer to inquiry data that will be returned to caller. + +Return Value: + + STATUS_SUCCESS or failure NTSTATUS code. + +--*/ +{ + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PCDB cdb; + IO_STATUS_BLOCK ioStatus; + ULONG length; + NTSTATUS status = STATUS_SUCCESS; + PINQUIRYDATA inquiryData; + PSENSE_DATA senseData; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): Entering function.\n", + DeviceObject)); + + if (InquiryData == NULL) { + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpGetStandardInquiryData; + } + + // + // Build a standard inquiry command. + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): Failed to allocate mem for passthrough.\n", + DeviceObject)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpGetStandardInquiryData; + } + +__Retry_Request: + + // + // Build the cdb for SCSI-3 standard inquiry. + // + cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; + cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY; + cdb->CDB6INQUIRY3.EnableVitalProductData = 0; + cdb->CDB6INQUIRY3.AllocationLength = sizeof(INQUIRYDATA); + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 6; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 1; + passThrough->ScsiPassThrough.DataTransferLength = sizeof(INQUIRYDATA); + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + DeviceObject, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { + + // + // Get the returned data. + // + inquiryData = (PINQUIRYDATA)(passThrough->DataBuffer); + + RtlCopyMemory(InquiryData, inquiryData, sizeof(INQUIRYDATA)); + + } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (NT_SUCCESS(ioStatus.Status)) && + (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + // + // Retry the request + // + RtlZeroMemory(passThrough, length); + goto __Retry_Request; + + } else { + + // Failed to get inquiry data + // Here it is possible that status is success, but scsi status is not. + // If so, set status to unsuccessful. + if (NT_SUCCESS(status)){ + status = STATUS_UNSUCCESSFUL; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n", + DeviceObject, + status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + +__Exit_DsmpGetStandardInquiryData: + + // + // Free the passthrough + data buffer. + // + if (passThrough) { + DsmpFreePool(passThrough); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + + +BOOLEAN +DsmpCheckScsiCompliance( + _In_ IN PDEVICE_OBJECT TargetObject, + _In_ IN PINQUIRYDATA InquiryData, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList + ) +/*++ + +Routine Description: + + Helper routine to determine if the device is SPC-3 compliant. + +Arguments: + + DeviceObject - The port PDO that we're determining compliance for. + InquiryData - Pointer to its inquiry data. + Descriptor - Pointer to its VPD page 0x80 data + DeviceIdList - Pointer to its VPD page 0x83 data + +Return Value: + + TRUE if compliant, else FALSE. + +--*/ +{ + BOOLEAN supported = FALSE /* TRUE */; + UCHAR deviceType; + UCHAR qualifier; + + UNREFERENCED_PARAMETER(DeviceIdList); + UNREFERENCED_PARAMETER(Descriptor); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpCheckScsiCompliance (DevObj %p): Entering function.\n", + TargetObject)); + + deviceType = InquiryData->DeviceType & 0x1F; + qualifier = (InquiryData->DeviceTypeQualifier >> 0x5) & 0x7; + + if ((deviceType | qualifier) == 0x7F) { + + supported = FALSE; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpCheckScsiCompliance (DevObj %p): Exiting function with Supported = %u.\n", + TargetObject, + supported)); + + return supported; +} + + +BOOLEAN +DsmpDeviceSupported( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PCSTR VendorId, + _In_ IN PCSTR ProductId + ) +/*++ + +Routine Description: + + This routine determines whether the device is supported by traversing the SupportedDevice + list and comparing to the VendorId/ProductId values passed in. + +Arguments: + + Context - Context value given to the multipath driver during registration. + VendorId - Pointer to the inquiry data VendorId. + ProductId - Pointer to the inquiry data ProductId. + +Return Value: + + TRUE - If VendorId/ProductId is found. + +--*/ +{ + UNICODE_STRING deviceName; + UNICODE_STRING productName; + ANSI_STRING ansiVendor; + ANSI_STRING ansiProduct; + NTSTATUS status; + BOOLEAN supported = FALSE; + KIRQL oldIrql; + UNICODE_STRING tempStrings; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Entering function.\n", + Context)); + + KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql); + + RtlInitUnicodeString(&tempStrings, NULL); + tempStrings.Buffer = DsmpAllocatePool(NonPagedPoolNx, Context->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED); + + if (tempStrings.Buffer) { + + RtlCopyMemory(tempStrings.Buffer, Context->SupportedDevices.Buffer, Context->SupportedDevices.Length); + tempStrings.Length = Context->SupportedDevices.Length; + tempStrings.MaximumLength = Context->SupportedDevices.MaximumLength; + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate temporary list (error %x).\n", + Context, + status)); + + KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); + + goto __Exit_DsmpDeviceSupported; + } + + KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); + + // + // The SupportedDevice list was built in DriverEntry from the services key. + // + if (tempStrings.MaximumLength == 0) { + + // + // List is empty. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): No supported Device in the list.\n", + Context)); + + goto __Exit_DsmpDeviceSupported; + } + + RtlInitUnicodeString(&productName, NULL); + + // + // Convert the inquiry fields into ansi strings. + // + RtlInitAnsiString(&ansiVendor, VendorId); + RtlInitAnsiString(&ansiProduct, ProductId); + + // + // Allocate the deviceName buffer. Needs to be 8+16 plus NULL. + // (productId length + vendorId length + NULL). + // + deviceName.MaximumLength = 25 * sizeof(WCHAR); + deviceName.Buffer = DsmpAllocatePool(PagedPool, deviceName.MaximumLength, DSM_TAG_SUPPORTED_DEV); + + if (deviceName.Buffer) { + + // + // Convert the vendorId to unicode. + // + status = RtlAnsiStringToUnicodeString(&deviceName, &ansiVendor, FALSE); + if (NT_SUCCESS(status)) { + + // + // Convert the productId to unicode. + // + status = RtlAnsiStringToUnicodeString(&productName, &ansiProduct, TRUE); + + if (NT_SUCCESS(status)) { + + // + // 'cat' them. + // + status = RtlAppendUnicodeStringToString(&deviceName, &productName); + + if (NT_SUCCESS(status)) { + + // + // Run the list of supported devices that was captured from the registry + // and see if this one is in the list. + // + supported = DsmpFindSupportedDevice(&deviceName, + &tempStrings); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Failed to append product name. Status %x.\n", + Context, + status)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Failed to convert ansi vendor string to unicode. Status %x\n", + Context, + status)); + } + + DsmpFreePool(deviceName.Buffer); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate device name buffer.\n", + Context)); + } + +__Exit_DsmpDeviceSupported: + + if (tempStrings.Buffer) { + DsmpFreePool(tempStrings.Buffer); + } + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Exiting function with supported = %u.\n", + Context, + supported)); + + return supported; +} + + +BOOLEAN +DsmpFindSupportedDevice( + _In_ IN PUNICODE_STRING DeviceName, + _In_ IN PUNICODE_STRING SupportedDevices + ) +/*++ + +Routine Description: + + This routine compares the two unicode strings for a match. + +Arguments: + + DeviceName - String built from the current device's inquiry data. + SupportedDevices - MULTI_SZ of devices that are supported. + +Return Value: + + TRUE - If VendorId/ProductId is found. + +--*/ +{ + PWSTR devices = SupportedDevices->Buffer; + ULONG bufferLengthLeft = SupportedDevices->MaximumLength / sizeof(WCHAR); + UNICODE_STRING unicodeString; + USHORT originalLength = DeviceName->Length; + LONG compare; + BOOLEAN supported = FALSE; + WCHAR tempString[32]; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindSupportedDevice (DevName %ws): Entering function.\n", + DeviceName->Buffer)); + + // + // 'devices' is the current buffer in the MULTI_SZ built from + // the registry. + // + while (devices[0]) { + + RtlZeroMemory(tempString, sizeof(tempString)); + + if (!NT_SUCCESS(RtlStringCchCopyNW(tempString, sizeof(tempString) / sizeof(tempString[0]), devices, bufferLengthLeft))) { + + tempString[(sizeof(tempString) / sizeof(tempString)) - 1] = L'\0'; + } + + // + // Make the current entry into a unicode string. + // + RtlInitUnicodeString(&unicodeString, tempString); + + // + // Compare this one with the current device. + // However, for storages that make up the product id on-the-fly, MPIO + // allows for matching based just on substring (product-id-prefix so to + // speak). + // + if (unicodeString.Length < DeviceName->Length) { + DeviceName->Length = unicodeString.Length; + } + + compare = RtlCompareUnicodeStrings(unicodeString.Buffer, + unicodeString.Length / sizeof(WCHAR), + DeviceName->Buffer, + DeviceName->Length / sizeof(WCHAR), + TRUE); + + DeviceName->Length = originalLength; + + if (compare == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpFindSupportedDevice (DevName %ws): Device support found in the registry.\n", + DeviceName->Buffer)); + + supported = TRUE; + break; + } + + // + // Advance to next entry in the MULTI_SZ. + // + devices += (unicodeString.MaximumLength / sizeof(WCHAR)); + + bufferLengthLeft -= (unicodeString.MaximumLength / sizeof(WCHAR)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindSupportedDevice (DevName %ws): Exiting function with Supported = %u.\n", + DeviceName->Buffer, + supported)); + + return supported; +} + +_Success_(return!=0) +PVOID +DsmpParseDeviceID( + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID, + _In_ IN DSM_DEVID_TYPE DeviceIdType, + _In_opt_ IN PULONG IdNumber, + _Out_opt_ OUT PSTORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN Legacy + ) +/*++ + +Routine Description: + + This routine builds a serial number string based on the information + in the VPD page 0x83 data if serial number is requested, else it + returns the appropriate identifier requested. + + Caller must free the buffer. + +Arguments: + + DeviceIdList - VPD Page 0x83 information. + DeviceIdType - Type of identifier that the DeviceID is being parsed for + IdNumber - If there are multiple identifiers of type DeviceIdType, this parameter + determines which among them to actually return. + IMPORTANT: This number is one-based (not zero-based). + CodeSet - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. This + returns the code set that was used when building the serial number. + Legacy - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. If the + code set of the identifier is StorageIdCodeSetBinary, this determines + whether to use the legacy method of binary to ascii conversion. + +Return Value: + + Requested Device identifier. + +--*/ +{ + PSTORAGE_IDENTIFIER identifier; + STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; // Preload with a bogus value. + STORAGE_IDENTIFIER_TYPE type = 0xF; + STORAGE_ASSOCIATION_TYPE association = 0xF; + ULONG numberIds; + ULONG i; + ULONG identifierSize = 0; + PUCHAR bytes = NULL; + PVOID buffer = NULL; + BOOLEAN done = FALSE; + ULONG idNumber = MAXULONG; + ULONG matches = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpParseDeviceID (DevIdDesc %p): Entering function - IdType %x.\n", + DeviceID, + DeviceIdType)); + + if (IdNumber) { + idNumber = *IdNumber; + } + + // + // Get the number of encapsulated identifiers. + // + numberIds = DeviceID->NumberOfIdentifiers; + + if (idNumber != MAXULONG && idNumber > numberIds) { + goto __Exit_DsmpParseDeviceID; + } + + // + // Get a pointer to the first one. + // + identifier = (PSTORAGE_IDENTIFIER)(DeviceID->Identifiers); + + for (i = 0; i < numberIds && !done; i++) { + + switch (DeviceIdType) { + + case DSM_DEVID_SERIAL_NUMBER: { + + // + // The way this works is that we will go through all the identifiers + // Order of preference will be LUN-associated over Target-associated. + // Further, upon same association, preference will be based on type as + // follows: 0x8, 0x3, 0x2, 0x1, 0x0. + // So an existing identifier will be discarded if a better one is found. + // If two identifiers have the same type, we will prefer the one will + // the larger length. + // + + // + // 1. Ensure that the association is for either the LUN or target. (If neither, ignore id). + // 2. If association is with target, don't it consider if current candidate has assocation with LUN. + // 3. If considering this identifier, order of preference is 8 > 3 > 2 > 1 > 0. + // 4. If this id type is same as current candidate, consider it only if it is of greater length. + // + if (((identifier->Association == StorageIdAssocDevice) || + (identifier->Association == 0x2 && association != StorageIdAssocDevice)) && + ((type == identifier->Type && identifierSize < identifier->IdentifierSize) || + (type != identifier->Type && DsmpIsPreferredDeviceId(type, identifier->Type)))) { + + // + // Get a pointer to the id itself. + // + bytes = identifier->Identifier; + + // + // The id's size. + // + identifierSize = identifier->IdentifierSize; + + // + // Get the type, code set, and association. + // + type = identifier->Type; + codeSet = identifier->CodeSet; + association = identifier->Association; + + matches++; + } + + break; + } + + case DSM_DEVID_RELATIVE_TARGET_PORT: { + + // + // Ensure that the association is for the target port. + // + if (identifier->Association != StorageIdAssocPort) { + + if ((i + 1) < numberIds) { + identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); + } + + continue; + } + + if (identifier->Type == StorageIdTypePortRelative) { + + // + // Get a pointer to the id itself. + // + bytes = identifier->Identifier; + + // + // The id's size. + // + identifierSize = identifier->IdentifierSize; + + type = identifier->Type; + codeSet = identifier->CodeSet; + association = identifier->Association; + + matches++; + } + + break; + } + + case DSM_DEVID_TARGET_PORT_GROUP: { + + // + // Ensure that the association is for the target port. + // + if (identifier->Association != StorageIdAssocPort) { + + if ((i + 1) < numberIds) { + identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); + } + + continue; + } + + if (identifier->Type == 0x5) { + + // + // Get a pointer to the id itself. + // + bytes = identifier->Identifier; + + // + // Move this by two bytes because first two bytes are reservered + // + bytes += sizeof(USHORT); + + // + // The id's size. Reduce the size by 2 bytes (to account + // for the reservered bytes) + // + identifierSize = identifier->IdentifierSize - sizeof(USHORT); + + type = identifier->Type; + codeSet = identifier->CodeSet; + association = identifier->Association; + + matches++; + } + + break; + } + + default: break; + } + + + if (idNumber != MAXULONG && idNumber == matches) { + done = TRUE; + } + + // + // Advance to the next identifier in the buffer. + // + if ((i + 1) < numberIds) { + identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); + } + } + + if (idNumber != MAXULONG && idNumber > matches) { + goto __Exit_DsmpParseDeviceID; + } + + if (DeviceIdType == DSM_DEVID_SERIAL_NUMBER) { + + if (type != StorageIdTypeScsiNameString && + type != StorageIdTypeFCPHName && + type != StorageIdTypeEUI64 && + type != StorageIdTypeVendorId && + type != StorageIdTypeVendorSpecific) { + + DSM_ASSERT(FALSE); + bytes = NULL; + identifierSize = 0; + type = association = 0xF; + codeSet = StorageIdCodeSetReserved; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpParseDeviceID (DevIdDesc %p): IdentifierSize = %u, Type = %u, Association = %u, CodeSet = %u.\n", + DeviceID, + identifierSize, + type, + association, + codeSet)); + + if (!bytes) { + goto __Exit_DsmpParseDeviceID; + } + + if (codeSet == StorageIdCodeSetBinary) { + + // + // Need to convert to ascii. + // + buffer = DsmpBinaryToAscii(bytes, + identifierSize, + &identifierSize, + Legacy); + + } else { + + if (identifierSize) { + // + // Allocate a buffer that is the size of the data, plus one for NULL. + // + buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize + 1, DSM_TAG_DEV_ID); + DSM_ASSERT(buffer); + + if (buffer) { + + // + // Copy over the id. + // + RtlCopyMemory(buffer, bytes, identifierSize); + } + } + } + + if (CodeSet) { + *CodeSet = codeSet; + } + + } else { + + if (identifierSize) { + + DSM_ASSERT((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) || + (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT))); + + _Analysis_assume_((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) || + (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT))); + + buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize, DSM_TAG_DEV_ID); + + if (buffer) { + + if (DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT) { + + GetUlongFrom4ByteArray(bytes, *((PULONG)buffer)); + + } else if (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP) { + + *((PUSHORT)buffer) = (bytes[0] << 8) | (bytes[1]); + } + } + } + } + +__Exit_DsmpParseDeviceID: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpParseDeviceID (DevIdDesc %p): Exiting function with buffer %p.\n", + DeviceID, + buffer)); + + return buffer; +} + + +PUCHAR +DsmpBinaryToAscii( + _In_reads_(Length) IN PUCHAR HexBuffer, + _In_ IN ULONG Length, + _Inout_ IN OUT PULONG UpdateLength, + _In_ IN BOOLEAN Legacy + ) +/*++ + +Routine Description: + + This routine will convert HexBuffer into an ascii NULL-terminated string. + + Note: This routine will allocate memory for storing the ascii string. It is + the responsibility of the caller to free this buffer. + +Arguments: + + HexBuffer - Pointer to the binary data. + Length - Length, in bytes, of HexBuffer. + UpdateLength - Storage to place the actual length of the returned string. + Legacy - Use the legacy method for the conversion. + +Return Value: + + Serial Number string, or NULL if an error occurred. + +--*/ +{ + static UCHAR IntegerTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + ULONG i; + ULONG j; + ULONG actualLength; + PUCHAR buffer = NULL; + UCHAR highWord; + UCHAR lowWord; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBinaryToAscii (HexBuff %p): Entering function.\n", + HexBuffer)); + + if (Length == 0) { + *UpdateLength = 0; + goto __Exit_DsmpBinaryToAscii; + } + + if (Legacy) { + // + // Do a pre-test on the buffer to determine the length actually needed. + // + for (i = 0, actualLength = 0; i < Length; i++) { + + if (HexBuffer[i] < 0x10) { + actualLength++; + } else { + actualLength += 2; + } + } + + // + // Add room for a terminating NULL. + // + actualLength++; + } else { + // + // We need one character for each nibble, plus one for the terminating NULL. + // + actualLength = (Length * 2) + 1; + } + + // + // Allocate the buffer. + // + buffer = DsmpAllocatePool(NonPagedPoolNx, + actualLength, + DSM_TAG_BIN_TO_ASCII); + if (!buffer) { + *UpdateLength = 0; + goto __Exit_DsmpBinaryToAscii; + } + + for (i = 0, j = 0; i < Length && j < actualLength; i++) { + + if (Legacy && (HexBuffer[i] < 0x10)) { + + // + // If legacy is mentioned and it's 0x0F or less, + // just convert the entire byte. + // + buffer[j++] = IntegerTable[HexBuffer[i]]; + } else { + + // + // Split out each nibble from the binary byte. + // + highWord = HexBuffer[i] >> 4; + lowWord = HexBuffer[i] & 0x0F; + + // + // Using the lookup table, convert and stuff into + // the ascii buffer. + // + buffer[j++] = IntegerTable[highWord]; + buffer[j++] = IntegerTable[lowWord]; + } + } + + // + // Update the caller's length field. + // + *UpdateLength = actualLength; + +__Exit_DsmpBinaryToAscii: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBinaryToAscii (HexBuff %p): Exiting function with buffer %s.\n", + HexBuffer, + (const char*) buffer)); + + return buffer; +} + + +PSTR +DsmpGetSerialNumber( + _In_ IN PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + Helper routine to send an inquiry with EVPD set to get the serial number page. + Used if the serial number is not embedded in the device descriptor (this device probably + doesn't support VPD page 0x00). + + Note: This routine will allocate memory for storing the serial number. It is + the responsibility of the caller to free this buffer. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + +Return Value: + + The serial number (null-terminated string) or NULL if the call fails. + +--*/ +{ + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PVPD_SERIAL_NUMBER_PAGE serialPage; + PCDB cdb; + PSTR serialNumber = NULL; + IO_STATUS_BLOCK ioStatus; + ULONG length; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Entering function.\n", + DeviceObject)); + + // + // Build an inquiry command with EVPD and pagecode of 0x80 (serial number). + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for passthrough.\n", + DeviceObject)); + + goto __Exit_DsmpGetSerialNumber; + } + + // + // Build the cdb. + // + cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; + cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; + cdb->CDB6INQUIRY.Reserved1 = 1; + cdb->CDB6INQUIRY.PageCode = VPD_SERIAL_NUMBER; + cdb->CDB6INQUIRY.AllocationLength = DSM_SERIAL_NUMBER_BUFFER_SIZE; + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 6; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 1; + passThrough->ScsiPassThrough.DataTransferLength = DSM_SERIAL_NUMBER_BUFFER_SIZE; + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + DeviceObject, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(ioStatus.Status))) { + + ULONG inx; + + // + // Get the returned data. + // + serialPage = (PVPD_SERIAL_NUMBER_PAGE)(passThrough->DataBuffer); + + // + // Allocate a buffer to hold just the serial number plus a null terminator + // + serialNumber = DsmpAllocatePool(NonPagedPoolNx, + serialPage->PageLength + 1, + DSM_TAG_SERIAL_NUM); + if (serialNumber) { + + // + // Copy it over. + // + RtlCopyMemory(serialNumber, serialPage->SerialNumber, serialPage->PageLength); + + // + // Some devices return binary data for the serial number. + // Convert to a more ascii-ish format so that other routines don't have a problem. + // + for (inx = 0; inx < serialPage->PageLength; inx++) { + if (serialNumber[inx] == '\0') { + serialNumber[inx] = ' '; + } + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for serialnumber.\n", + DeviceObject)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + DeviceObject, + ioStatus.Status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + +__Exit_DsmpGetSerialNumber: + + // + // Free the passthrough + data buffer. + // + if (passThrough) { + + DsmpFreePool(passThrough); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Exiting function with serial number %s.\n", + DeviceObject, + (const char*)serialNumber)); + + // + // Return the sn. + // + return serialNumber; +} + + +NTSTATUS +DsmpDisableImplicitStateTransition( + _In_ IN PDEVICE_OBJECT TargetDevice, + _Out_ OUT PBOOLEAN DisableImplicit + ) +/*++ + +Routine Description: + + Send down request to disable implicit ALUA state transition. + The function first sends down a mode sense to get the control extension mode + sense data. It then clears the IALUAE bit and sends down a mode select. + +Arguements: + + TargetDevice - Device object that will be target of this command. + DisableImplicit - Flag returned to the caller to indicate whether or not + implicit transitions are disabled. + +Return Value : + + STATUS_SUCCESS if the command succeeds. + Appropriate NTSTATUS code on failure + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PCDB cdb; + IO_STATUS_BLOCK ioStatus; + ULONG length; + PSPC3_CONTROL_EXTENSION_MODE_PAGE controlExtensionPage = NULL; + PSENSE_DATA senseData = NULL; + BOOLEAN implicitDisabled = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Entering function.\n", + TargetDevice)); + + // + // First build the mode sense command to get the control extension parameters. + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Failed to allocate mem for passthrough.\n", + TargetDevice)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpDisableImplicitStateTransition; + } + +__Retry_ModeSense: + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 6; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 1; + passThrough->ScsiPassThrough.DataTransferLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + // + // Build the cdb for mode sense. + // + cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.Dbd = 1; + cdb->MODE_SENSE.PageCode = 0xA; + cdb->MODE_SENSE.SubPageCode = 0x01; + cdb->MODE_SENSE.AllocationLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + TargetDevice, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { + + controlExtensionPage = (PSPC3_CONTROL_EXTENSION_MODE_PAGE)(passThrough->DataBuffer); + + if (controlExtensionPage->ImplicitALUAEnable) { + + controlExtensionPage->ImplicitALUAEnable = 0; + +__Retry_ModeSelect: + + RtlZeroMemory(passThrough->SenseInfoBuffer, passThrough->ScsiPassThrough.SenseInfoLength); + + passThrough->ScsiPassThrough.DataIn = 0; + + // + // Build the cdb for mode select. + // + RtlZeroMemory(cdb, 6); + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.SPBit = 0; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + TargetDevice, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { + + implicitDisabled = TRUE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions turned off successfully.\n", + TargetDevice)); + + } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (NT_SUCCESS(status)) && + (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { + + // + // Retry the request + // + goto __Retry_ModeSelect; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): ModeSelect failed - NTStatus 0x%x, ScsiStatus 0x%x.\n", + TargetDevice, + status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + } else { + + implicitDisabled = TRUE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions already turned OFF.\n", + TargetDevice)); + } + } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (NT_SUCCESS(status)) && + (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + // + // Retry the request + // + RtlZeroMemory(passThrough, length); + goto __Retry_ModeSense; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): ModeSense failed - NTStatus 0x%x, ScsiStatus 0x%x.\n", + TargetDevice, + status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + +__Exit_DsmpDisableImplicitStateTransition: + + // + // Free the passthrough + data buffer. + // + if (passThrough) { + DsmpFreePool(passThrough); + } + + // + // Return whether IALUAE is set to 0. + // + if (DisableImplicit) { + + *DisableImplicit = implicitDisabled; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Exiting function with status %x.\n", + TargetDevice, + status)); + + return status; +} + + +PWSTR +DsmpBuildHardwareId( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + Construct a string concatinating VendorId with ProductId. + +Arguements: + + DeviceInfo - Device Extension + +Return Value : + + NULL terminated hardware id if it was built successfully. + NULL in case of failure. + +--*/ +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PWSTR hardwareId = NULL; + SIZE_T vendorIDLength = 0; + SIZE_T productIDLength = 0; + PCSZ vendorIdOffset; + PCSZ productIdOffset; + SIZE_T sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + ANSI_STRING ansiString; + UNICODE_STRING unicodeString; + ULONG offset; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Entering function.\n", + DeviceInfo)); + + deviceDescriptor = &(DeviceInfo->Descriptor); + + // + // Save the vendorid and productid offset in Device Descriptor + // + offset = deviceDescriptor->ProductIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->VendorIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + if (!vendorIDLength || !productIDLength) { + + status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Invalid vendor and/or product id.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildHardwareId; + } + + sizeNeeded = vendorIDLength + productIDLength; + hardwareId = DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_HARDWARE_ID); + if (!hardwareId) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Failed to allocate memory for device name.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpBuildHardwareId; + } + + // + // Build the NULL terminated hardwareId whose format is : + // + // VendorIdProductId + // + vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->VendorIdOffset); + RtlInitAnsiString(&ansiString, vendorIdOffset); + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT)vendorIDLength; + unicodeString.Buffer = hardwareId; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Failed to convert vendor id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildHardwareId; + } + + productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset); + RtlInitAnsiString(&ansiString, productIdOffset); + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT)productIDLength; + unicodeString.Buffer = hardwareId + strlen(((PCHAR)deviceDescriptor) + offset); + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Failed to convert product id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildHardwareId; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): HardwareId is %ws.\n", + DeviceInfo, + hardwareId)); + +__Exit_DsmpBuildHardwareId: + + if (hardwareId && !NT_SUCCESS(status)) { + DsmpFreePool(hardwareId); + hardwareId = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Exiting function with deviceName %ws.\n", + DeviceInfo, + hardwareId)); + + return hardwareId; +} + + +PWSTR +DsmpBuildDeviceNameLegacyPage0x80( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + Construct a string from VendorId, ProductId, and SerialNumber (page 0x80 + info) of the device. + +Arguements: + + DeviceInfo - Device Extension + +Return Value : + + STATUS_SUCCESS if the device name was built successfully. + + Appropriate NTSTATUS code on failure + +--*/ +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PWCHAR deviceName = NULL; + PWCHAR tmpPtr; + PWCHAR vendorID = NULL; + PWCHAR productID = NULL; + PWCHAR serialID = NULL; + ANSI_STRING ansiString; + UNICODE_STRING unicodeString; + UNICODE_STRING unicodeDeviceName; + SIZE_T vendorIDLength = 0; + SIZE_T productIDLength = 0; + SIZE_T serialIDLength = 0; + ULONG offset; + SIZE_T sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Entering function.\n", + DeviceInfo)); + + deviceDescriptor = &(DeviceInfo->Descriptor); + + // + // Save the vendorid, productid, and serialnumber offset + // in Device Descriptor + // + offset = deviceDescriptor->VendorIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->ProductIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->SerialNumberOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + serialIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + // + // Allocate buffers to use to convert the IDs from ANSI to Unicode and + // eventually build the device name. + // + if (vendorIDLength > 0) { + vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME); + if (!vendorID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for vendor ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (productIDLength > 0) { + productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME); + if (!productID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for product ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (serialIDLength > 0) { + serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME); + if (!serialID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for serial ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + sizeNeeded = vendorIDLength + productIDLength + serialIDLength; + if (sizeNeeded > 0) { + + // + // Account for the terminating NULL if serial id is empty. + // + + sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE); + + deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME); + if (!deviceName) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for device name.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + + status = STATUS_UNSUCCESSFUL; + } + + if (!NT_SUCCESS(status)) { + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // Build the NULL terminated device name whose format is : + // + // VendorId_ProductId_SerialNumber + // + + unicodeDeviceName.Length = 0; + unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded; + unicodeDeviceName.Buffer = deviceName; + + if (vendorIDLength) { + + PCSZ vendorIdOffset; + + vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->VendorIdOffset); + + RtlInitAnsiString(&ansiString, vendorIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) vendorIDLength; + unicodeString.Buffer = vendorID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert vendor id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(vendorID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate vendor ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + if (productIDLength) { + + PCSZ productIdOffset; + + productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->ProductIdOffset); + + RtlInitAnsiString(&ansiString, productIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) productIDLength; + unicodeString.Buffer = productID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert product id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(productID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, productID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate product ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + // + // Serial number + // + if (serialIDLength) { + + PCSZ serialNumberOffset; + + serialNumberOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->SerialNumberOffset); + + RtlInitAnsiString(&ansiString, serialNumberOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) serialIDLength; + unicodeString.Buffer = serialID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert serial number to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(serialID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate serial number to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Device Name is %ws.\n", + DeviceInfo, + deviceName)); + +__Exit_DsmpBuildDeviceNameLegacyPage0x80: + + if (vendorID) { + DsmpFreePool(vendorID); + } + + if (productID) { + DsmpFreePool(productID); + } + + if (serialID) { + DsmpFreePool(serialID); + } + + if (deviceName && !NT_SUCCESS(status)) { + DsmpFreePool(deviceName); + deviceName = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Exiting function with deviceName %ws.\n", + DeviceInfo, + deviceName)); + + return deviceName; +} + + + +PWSTR +DsmpBuildDeviceName( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(SerialNumberLength) IN PSTR SerialNumber, + _In_ IN SIZE_T SerialNumberLength + ) +/*++ + +Routine Description: + + Construct a string from VendorId, ProductId, and SerialNumber (page 0x83 + identifiers) of the device. + +Arguements: + + DeviceInfo - Device Extension + SerialNumber - Device serial number built from appropriate page 0x83 identifier + SerialNumberLength - Length (in chars) of the passed in serial number buffer + +Return Value : + + Device name if it was built successfully. + NULL in case of failure. + +--*/ +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PWCHAR deviceName = NULL; + PWCHAR tmpPtr; + PWCHAR vendorID = NULL; + PWCHAR productID = NULL; + PWCHAR serialID = NULL; + ANSI_STRING ansiString; + UNICODE_STRING unicodeString; + UNICODE_STRING unicodeDeviceName; + SIZE_T vendorIDLength = 0; + SIZE_T productIDLength = 0; + SIZE_T serialIDLength = 0; + ULONG offset; + SIZE_T sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Entering function.\n", + DeviceInfo)); + + deviceDescriptor = &(DeviceInfo->Descriptor); + + // + // Save the vendorid, productid, and serialnumber offset + // in Device Descriptor + // + offset = deviceDescriptor->VendorIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->ProductIdOffset; + if ((offset != 0) && (offset != -1)) { + + productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + if (SerialNumber) { + + serialIDLength = (SerialNumberLength * sizeof(WCHAR)) + WNULL_SIZE; + } + + // + // Allocate buffers to use to convert the IDs from ANSI to Unicode and + // eventually build the device name. + // + if (vendorIDLength > 0) { + vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME); + if (!vendorID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for vendor ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (productIDLength > 0) { + productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME); + if (!productID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for product ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (serialIDLength > 0) { + serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME); + if (!serialID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for serial ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + sizeNeeded = vendorIDLength + productIDLength + serialIDLength; + if (sizeNeeded > 0) { + + // + // Account for the terminating NULL if serial id is empty. + // + + sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE); + + deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME); + if (!deviceName) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for device name.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + + status = STATUS_UNSUCCESSFUL; + } + + if (!NT_SUCCESS(status)) { + goto __Exit_DsmpBuildDeviceName; + } + + // + // Build the NULL terminated device name whose format is : + // + // VendorId_ProductId_SerialNumber + // + + unicodeDeviceName.Length = 0; + unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded; + unicodeDeviceName.Buffer = deviceName; + + if (vendorIDLength) { + + PCSZ vendorIdOffset; + + vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->VendorIdOffset); + + RtlInitAnsiString(&ansiString, vendorIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) vendorIDLength; + unicodeString.Buffer = vendorID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to convert vendor id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(vendorID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate vendor ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + if (productIDLength) { + + PCSZ productIdOffset; + + productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->ProductIdOffset); + + RtlInitAnsiString(&ansiString, productIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) productIDLength; + unicodeString.Buffer = productID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to convert product id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(productID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, productID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate product ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + // + // Serial number + // + if (serialIDLength) { + + PSTR serialNumberOffset; + + serialNumberOffset = SerialNumber; + + RtlInitAnsiString(&ansiString, serialNumberOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) serialIDLength; + unicodeString.Buffer = serialID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to convert serial number to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(serialID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate serial number to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Device Name is %ws.\n", + DeviceInfo, + deviceName)); + +__Exit_DsmpBuildDeviceName: + + if (vendorID) { + DsmpFreePool(vendorID); + } + + if (productID) { + DsmpFreePool(productID); + } + + if (serialID) { + DsmpFreePool(serialID); + } + + if (deviceName && !NT_SUCCESS(status)) { + DsmpFreePool(deviceName); + deviceName = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Exiting function with deviceName %ws.\n", + DeviceInfo, + deviceName)); + + return deviceName; +} + + +NTSTATUS +DsmpApplyDeviceNameCorrection( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy, + _In_ IN SIZE_T DeviceNameLegacyLen, + _In_reads_(DeviceNameLen) PWSTR DeviceName, + _In_ IN SIZE_T DeviceNameLen + ) +/*++ + +Routine Description: + + If the registry has a key name built with a legacy device name, this + function updates the key name with the current device name. + +Arguements: + + DeviceInfo - Device instance + DeviceNameLegacy - Device name built using legacy methods. + DeviceNameLegacyLen - Number of chars (including NULL) of the DeviceNameLegacy buffer. + DeviceName - Device name built using current methods. + DeviceNameLen - Number of chars (including NULL) of the DeviceName buffer. + +Return Value : + + STATUS_SUCCESS if the device's key was updated successfully. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE lbSettingsKey = NULL; + HANDLE deviceKeyLegacy = NULL; + HANDLE deviceKey = NULL; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + UNICODE_STRING deviceNameLegacy; + UNICODE_STRING deviceName; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(DeviceNameLen); + UNREFERENCED_PARAMETER(DeviceNameLegacyLen); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // First open LoadBalanceSettings key under the service key. + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to open LB Settings key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + RtlInitUnicodeString(&deviceNameLegacy, DeviceNameLegacy); + + InitializeObjectAttributes(&objectAttributes, + &deviceNameLegacy, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Open the old device key under DsmLoadBalanceSettings key. + // The name of this key is the one built using legacy methods - either a + // serial number from VPD page 0x80 or an aliased serial number from VPD + // page 0x83. + // + status = ZwOpenKey(&deviceKeyLegacy, + KEY_ALL_ACCESS, + &objectAttributes); + + if (NT_SUCCESS(status)) { + + ULONG disposition; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name exists.\n", + DeviceInfo)); + + RtlInitUnicodeString(&deviceName, DeviceName); + + InitializeObjectAttributes(&objectAttributes, + &deviceName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Since the old name key exists, create one with the new name. + // + status = ZwCreateKey(&deviceKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + &disposition); + + if (NT_SUCCESS(status)) { + + // + // The new key shouldn't exist if the old one does. + // If it does, it indicates a error occured the previous time + // this was tried, so just copy over the old subtree anyways now. + // + DSM_ASSERT(disposition == REG_CREATED_NEW_KEY); + + // + // Copy over the entire subtree of the old key over to the new key. + // + status = DsmpRegCopyTree(deviceKeyLegacy, deviceKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to copy over the old device key's subtree. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + // + // Delete the old key name. + // + status = DsmpRegDeleteTree(deviceKeyLegacy); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to delete the old device key's subtree. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to create the new device key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + } else if (status == STATUS_INVALID_HANDLE || + status == STATUS_OBJECT_NAME_NOT_FOUND) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name does not exist.\n", + DeviceInfo)); + + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to query key with old device name. Status %x\n", + DeviceInfo, + status)); + } + +__Exit_DsmpApplyDeviceNameCorrection: + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (deviceKeyLegacy) { + ZwClose(deviceKeyLegacy); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryDeviceLBPolicyFromRegistry( + _In_ PDSM_DEVICE_INFO DeviceInfo, + _In_ PWSTR RegistryKeyName, + _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Inout_ PULONGLONG PreferredPath, + _Inout_ PUCHAR ExplicitlySet + ) +/*++ + +Routine Description: + + Query the saved load balance policy and preferred path for this device from + the registry. + Also returns whether this setting was explicitly set via WMI call to SetLBPolicy, + (as opposed to the settings being made based on defaults determined through the + storage's ALUA capabilities). + +Arguements: + + DeviceInfo - The instance of the LUN through a paricular path + RegistryKeyName - DeviceName representing this LUN + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for the device. + ExplicitlySet - Flag reflecting if LB policy was explicitly set. + +Return Value : + + STATUS_SUCCESS if we were able to successfully query the registry for the info. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + UNICODE_STRING subKeyName; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + UNICODE_STRING keyValueName; + ULONG length; + struct _explicitSet { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + UCHAR Data; + } explicitSet; + struct _preferredPath { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } preferredPath; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Query the Load Balance settings for the given device from the registry. + // First open LoadBalanceSettings key under the service key. + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to open LB Settings key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry; + } + + RtlInitUnicodeString(&subKeyName, RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Create or Open the device key under DsmLoadBalanceSettings key. + // The name of this key is the one built in DsmpBuildDeviceName + // + status = ZwCreateKey(&deviceKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = LoadBalanceType; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + deviceKey, + queryTable, + deviceKey, + NULL); + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n", + DeviceInfo, + *LoadBalanceType)); + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // The device key must have been newly created. + // Set the default load balance policy for this device + // + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_LOAD_BALANCE_POLICY, + REG_DWORD, + LoadBalanceType, + sizeof(ULONG)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write LB policy. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry; + } + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_POLICY_EXPLICITLY_SET); + status = ZwQueryValueKey(deviceKey, + &keyValueName, + KeyValuePartialInformation, + &explicitSet, + sizeof(explicitSet), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(explicitSet.KeyValueInfo.DataLength == sizeof(UCHAR)); + + *ExplicitlySet = *((UCHAR UNALIGNED *)&(explicitSet.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): ExplicitlySet is %!bool!.\n", + DeviceInfo, + *ExplicitlySet)); + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + *ExplicitlySet = FALSE; + + // + // The device key must have been newly created. + // Set ExplicitlySet to 0 to indicate that the default was used. + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_POLICY_EXPLICITLY_SET, + REG_BINARY, + ExplicitlySet, + sizeof(UCHAR)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write ExplicitlySet. Status %x.\n", + DeviceInfo, + status)); + } + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + status = ZwQueryValueKey(deviceKey, + &keyValueName, + KeyValuePartialInformation, + &preferredPath, + sizeof(preferredPath), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + + *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n", + DeviceInfo, + *PreferredPath)); + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + *PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + + // + // The device key must have been newly created. + // Set a bogus preferred path as default. + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_PREFERRED_PATH, + REG_BINARY, + PreferredPath, + sizeof(ULONGLONG)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write PreferredPath. Status %x.\n", + DeviceInfo, + status)); + } + } + } + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to create LB policy registry key. Status %x.\n", + DeviceInfo, + status)); + + deviceKey = NULL; + } + +__Exit_DsmpQueryDeviceLBPolicyFromRegistry: + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryTargetLBPolicyFromRegistry( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Query the load balance policy for the VID/PID of the passed in device from + the registry if it has been set. + +Arguements: + + DeviceInfo - Device's whose VID/PID we need to compare against. + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for the device. + +Return Value : + + STATUS_SUCCESS if we were able to successfully query the registry for the info. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE targetsLBSettingKey = NULL; + HANDLE targetKey = NULL; + UNICODE_STRING subKeyName; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status = STATUS_INVALID_PARAMETER; + UNICODE_STRING keyValueName; + ULONG length; + struct _preferredPath { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } preferredPath; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + if (!LoadBalanceType || !PreferredPath) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Invalid parameter.\n", + DeviceInfo)); + + goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; + } + + if (!DeviceInfo->Group->HardwareId) { + + status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Couldn't build hardware id for passed in device.\n", + DeviceInfo)); + + goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; + } + + // + // Query the Load Balance settings for the given target from the registry. + // First open TargetsLoadBalanceSetting key under the service key. + // + status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open Targets LB Setting key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; + } + + RtlInitUnicodeString(&subKeyName, DeviceInfo->Group->HardwareId); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + targetsLBSettingKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Open the VID/PID key under DsmTargetsLoadBalanceSetting key. + // + status = ZwOpenKey(&targetKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = LoadBalanceType; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + targetKey, + queryTable, + targetKey, + NULL); + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n", + DeviceInfo, + *LoadBalanceType)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query LB Policy - error %x.\n", + DeviceInfo, + status)); + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + status = ZwQueryValueKey(targetKey, + &keyValueName, + KeyValuePartialInformation, + &preferredPath, + sizeof(preferredPath), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + + *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n", + DeviceInfo, + *PreferredPath)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query PreferredPath. Status %x.\n", + DeviceInfo, + status)); + } + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open LB policy registry key. Status %x.\n", + DeviceInfo, + status)); + + targetKey = NULL; + } + +__Exit_DsmpQueryTargetLBPolicyFromRegistry: + + if (targetKey) { + ZwClose(targetKey); + } + + if (targetsLBSettingKey) { + ZwClose(targetsLBSettingKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryDsmLBPolicyFromRegistry( + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Query the overall load balance policy for MSDSM controlled devices from + the registry if it has been set. + +Arguements: + + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for the device. + +Return Value : + + STATUS_SUCCESS if we were able to successfully query the registry for the info. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE parametersKey = NULL; + NTSTATUS status = STATUS_INVALID_PARAMETER; + UNICODE_STRING keyValueName; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + ULONG length; + struct _preferredPath { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } preferredPath; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Entering function.\n")); + + if (!LoadBalanceType || !PreferredPath) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Invalid parameter.\n")); + + goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; + } + + // + // Query the overall default Load Balance settings for MSDSM from the registry. + // First open the Parameters key under the service key. + // + status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, ¶metersKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Failed to open Parameters key. Status %x.\n", + status)); + + goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = LoadBalanceType; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + parametersKey, + queryTable, + parametersKey, + NULL); + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: LB Policy is %d.\n", + *LoadBalanceType)); + + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Failed to query LB policy. Status %x.\n", + status)); + + goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + status = ZwQueryValueKey(parametersKey, + &keyValueName, + KeyValuePartialInformation, + &preferredPath, + sizeof(preferredPath), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + + *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: PreferredPath is %I64x.\n", + *PreferredPath)); + + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Failed to query PreferredPath. Status %x.\n", + status)); + } + } + +__Exit_DsmpQueryDsmLBPolicyFromRegistry: + + if (parametersKey) { + ZwClose(parametersKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Exiting function with status %x.\n", + status)); + + return status; +} + + +NTSTATUS +DsmpSetDsmLBPolicyInRegistry( + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Set the overall load balance policy for MSDSM controlled devices in + the registry. + Note: If the policy specified is 0, remove the currently set values + for policy and preferred path. + +Arguements: + + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for devices controlled by DSM. + +Return Value : + + STATUS_SUCCESS if we were able to successfully set the info in the registry. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE parametersKey = NULL; + NTSTATUS status; + UNICODE_STRING lbPolicyValueName; + UNICODE_STRING preferredPathValueName; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Entering function.\n")); + + // + // First open the Parameters key under the service key. + // + status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, ¶metersKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to open Parameters key. Status %x.\n", + status)); + + goto __Exit_DsmpSetDsmLBPolicyInRegistry; + } + + RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY); + RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH); + + // + // If the LB policy is specified as 0, we need to delete the values. + // + if (LoadBalanceType < DSM_LB_FAILOVER) { + + status = ZwDeleteValueKey(parametersKey, &preferredPathValueName); + + if (NT_SUCCESS(status) || status == STATUS_OBJECT_NAME_NOT_FOUND) { + + status = ZwDeleteValueKey(parametersKey, &lbPolicyValueName); + } + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to delete either preferredPath or lbPolicy. Status %x.\n", + status)); + } + } else { + + status = ZwSetValueKey(parametersKey, + &lbPolicyValueName, + 0, + REG_DWORD, + &LoadBalanceType, + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to set LB policy in registry. Status %x.\n", + status)); + + goto __Exit_DsmpSetDsmLBPolicyInRegistry; + } + + status = ZwSetValueKey(parametersKey, + &preferredPathValueName, + 0, + REG_BINARY, + &PreferredPath, + sizeof(ULONGLONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to set preferred path in registry. Status %x.\n", + status)); + } + } + +__Exit_DsmpSetDsmLBPolicyInRegistry: + + if (parametersKey) { + ZwClose(parametersKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Exiting function with status %x.\n", + status)); + + return status; +} + + +NTSTATUS +DsmpSetVidPidLBPolicyInRegistry( + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Set the default load balance policy for MSDSM controlled devices for + a particular target VID/PID in the registry. + Note: If the policy specified is 0, remove the subkey that matches + the passed in TargetHardwareId. + +Arguements: + + TargetHardwareId - The VID/PID for which a default LB policy is being set. + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for devices controlled by DSM. + +Return Value : + + STATUS_SUCCESS if we were able to successfully set the info in the registry. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE targetsLBSettingKey = NULL; + HANDLE targetSubKey = NULL; + NTSTATUS status; + UNICODE_STRING vidPidKeyName; + UNICODE_STRING lbPolicyValueName; + UNICODE_STRING preferredPathValueName; + OBJECT_ATTRIBUTES objectAttributes; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Entering function.\n", + TargetHardwareId)); + + // + // First open the DsmTargetsLoadBalanceSetting key under the service's parameters key. + // + status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open Targets Policy settings key. Status %x.\n", + TargetHardwareId, + status)); + + goto __Exit_DsmpSetVidPidLBPolicyInRegistry; + } + + RtlInitUnicodeString(&vidPidKeyName, TargetHardwareId); + InitializeObjectAttributes(&objectAttributes, + &vidPidKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + targetsLBSettingKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // If the LB policy is specified as 0, we need to delete the values. + // + if (LoadBalanceType < DSM_LB_FAILOVER) { + + // + // Open the VID/PID key under DsmTargetsLoadBalanceSetting key. + // + status = ZwOpenKey(&targetSubKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + status = ZwDeleteKey(targetSubKey); + } + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to either open or delete. Status %x.\n", + TargetHardwareId, + status)); + } + } else { + + RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY); + RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH); + + status = ZwCreateKey(&targetSubKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open/create key in registry. Status %x.\n", + TargetHardwareId, + status)); + + goto __Exit_DsmpSetVidPidLBPolicyInRegistry; + } + + status = ZwSetValueKey(targetSubKey, + &lbPolicyValueName, + 0, + REG_DWORD, + &LoadBalanceType, + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set LB policy in registry. Status %x.\n", + TargetHardwareId, + status)); + + goto __Exit_DsmpSetVidPidLBPolicyInRegistry; + } + + status = ZwSetValueKey(targetSubKey, + &preferredPathValueName, + 0, + REG_BINARY, + &PreferredPath, + sizeof(ULONGLONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set preferred path in registry. Status %x.\n", + TargetHardwareId, + status)); + } + } + +__Exit_DsmpSetVidPidLBPolicyInRegistry: + + if (targetSubKey) { + ZwClose(targetSubKey); + } + + if (targetsLBSettingKey) { + ZwClose(targetsLBSettingKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Exiting function with status %x.\n", + TargetHardwareId, + status)); + + return status; +} + + +NTSTATUS +DsmpOpenLoadBalanceSettingsKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE LoadBalanceSettingsKey + ) +/*++ + +Routine Description: + + Open the device key in the registry. + + NOTE: It is the responsibility of the caller to close the returned handle. + +Arguements: + + AccessMask - Requested access with which to open key + LoadBalanceSettingsKey - handle of the key that is returned to the caller + +Return Value : + + STATUS_SUCCESS if we were able to successfully open the registry key. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING parametersKeyName; + UNICODE_STRING subKeyName; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Entering function.\n", + registryPath)); + + *LoadBalanceSettingsKey = NULL; + + // + // First check if registry path is available for msdsm. + // + if (!registryPath->Buffer) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Registry Path not set.\n", + registryPath)); + + goto __Exit_DsmpOpenLoadBalanceSettingsKey; + } + + // + // Open the service key first + // + InitializeObjectAttributes(&objectAttributes, + registryPath, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + AccessMask, + &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Open Parameters key under the Service key + // + RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + ¶metersKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + serviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(¶metersKey, + AccessMask, + &objectAttributes); + + if (NT_SUCCESS(status)) { + + // + // Open LoadBalanceSettings key under the Parameters key + // + RtlInitUnicodeString(&subKeyName, DSM_LOAD_BALANCE_SETTINGS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + parametersKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(LoadBalanceSettingsKey, + AccessMask, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open/create LBSettings key. Status %x.\n", + registryPath, + status)); + + *LoadBalanceSettingsKey = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open parameters key. Status %x.\n", + registryPath, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open service key %ws. Status %x.\n", + registryPath, + registryPath->Buffer, + status)); + } + +__Exit_DsmpOpenLoadBalanceSettingsKey: + + if (parametersKey) { + ZwClose(parametersKey); + } + + if (serviceKey) { + ZwClose(serviceKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Exiting function with status %x.\n", + registryPath, + status)); + + return status; +} + + +NTSTATUS +DsmpOpenTargetsLoadBalanceSettingKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey + ) +/*++ + +Routine Description: + + Open the target key in the registry. + + NOTE: It is the responsibility of the caller to close the returned handle. + +Arguements: + + AccessMask - Requested access with which to open key + LoadBalanceSettingsKey - handle of the key that is returned to the caller + +Return Value : + + STATUS_SUCCESS if we were able to successfully open the registry key. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING parametersKeyName; + UNICODE_STRING subKeyName; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Entering function.\n", + registryPath)); + + if (!TargetsLoadBalanceSettingKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Invalid parameter.\n", + registryPath)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey; + } + + *TargetsLoadBalanceSettingKey = NULL; + + // + // First check if registry path is available for msdsm. + // + if (!registryPath->Buffer) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Registry Path not set.\n", + registryPath)); + + goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey; + } + + // + // Open the service key first + // + InitializeObjectAttributes(&objectAttributes, + registryPath, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + AccessMask, + &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Open Parameters key under the Service key + // + RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + ¶metersKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + serviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(¶metersKey, + AccessMask, + &objectAttributes); + + if (NT_SUCCESS(status)) { + + // + // Open LoadBalanceSettings key under the Parameters key + // + RtlInitUnicodeString(&subKeyName, DSM_TARGETS_LOAD_BALANCE_SETTING); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + parametersKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(TargetsLoadBalanceSettingKey, + AccessMask, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open/create TargetsLBSetting key. Status %x.\n", + registryPath, + status)); + + *TargetsLoadBalanceSettingKey = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open parameters key. Status %x.\n", + registryPath, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open service key %ws. Status %x.\n", + registryPath, + registryPath->Buffer, + status)); + } + +__Exit_DsmpOpenTargetsLoadBalanceSettingKey: + + if (parametersKey) { + ZwClose(parametersKey); + } + + if (serviceKey) { + ZwClose(serviceKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Exiting function with status %x.\n", + registryPath, + status)); + + return status; +} + + +NTSTATUS +DsmpOpenDsmServicesParametersKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE ParametersKey + ) +/*++ + +Routine Description: + + Open the DSM's Parameters key in the registry. + + NOTE: It is the responsibility of the caller to close the returned handle. + +Arguements: + + AccessMask - Requested access with which to open key + ParametersKey - handle of the key that is returned to the caller + +Return Value : + + STATUS_SUCCESS if we were able to successfully open the registry key. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE serviceKey = NULL; + PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING parametersKeyName; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Entering function.\n", + registryPath)); + + if (!ParametersKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Invalid parameter.\n", + registryPath)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpOpenDsmServicesParametersKey; + } + + *ParametersKey = NULL; + + // + // First check if registry path is available for msdsm. + // + if (!registryPath->Buffer) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Registry Path not set.\n", + registryPath)); + + goto __Exit_DsmpOpenDsmServicesParametersKey; + } + + // + // Open the service key first + // + InitializeObjectAttributes(&objectAttributes, + registryPath, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + AccessMask, + &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Open Parameters key under the Service key + // + RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + ¶metersKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + serviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(ParametersKey, + AccessMask, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open parameters key. Status %x.\n", + registryPath, + status)); + + *ParametersKey = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open service key %ws. Status %x.\n", + registryPath, + registryPath->Buffer, + status)); + } + +__Exit_DsmpOpenDsmServicesParametersKey: + + if (serviceKey) { + ZwClose(serviceKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Exiting function with status %x.\n", + registryPath, + status)); + + return status; +} + +NTSTATUS +DsmpReportTargetPortGroupsSyncCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroupsSyncCompletion: IRP %p, Context %p\n", + Irp, Context)); + + KeSetEvent(Irp->UserEvent, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +_Success_(return==0) +NTSTATUS +DsmpReportTargetPortGroups( + _In_ PDEVICE_OBJECT DeviceObject, + _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo, + _Out_ PULONG TargetPortGroupsInfoLength + ) +/*++ + +Routine Description: + + Helper routine to send down ReportTargetPortGroups request synchronously. + Used if device supports ALUA. + + Note: This routine will allocate memory for the TPG info. It is the + responsibility of the caller to free this buffer, but only if the function + returns STATUS_SUCCESS. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + TargetPortGroupsInfo - buffer containing the returned data. + TargetPortGroupsInfoLength - size of the returned buffer. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb; + NTSTATUS status = STATUS_SUCCESS; + PIRP irp = NULL; + PMDL mdl = NULL; + PSCSI_REQUEST_BLOCK srb = NULL; + PSENSE_DATA_EX senseInfoBuffer = NULL; + UCHAR senseInfoBufferLength = 0; + KEVENT completionEvent; + ULONG targetPortGroupsInfoLength = 0; + PUCHAR targetPortGroupsInfo = NULL; + PIO_STACK_LOCATION irpStack = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Entering function.\n", + DeviceObject)); + + if (TargetPortGroupsInfoLength == NULL || + TargetPortGroupsInfo == NULL) { + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpReportTargetPortGroups; + } + + *TargetPortGroupsInfoLength = 0; + *TargetPortGroupsInfo = NULL; + + senseInfoBuffer = (PSENSE_DATA_EX)DsmpAllocatePool(NonPagedPoolNx, + SENSE_BUFFER_SIZE_EX, + DSM_TAG_SCSI_SENSE_INFO); + if (senseInfoBuffer != NULL) { + + senseInfoBufferLength = SENSE_BUFFER_SIZE_EX; + + srb = (PSCSI_REQUEST_BLOCK)DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + DSM_TAG_SCSI_REQUEST_BLOCK); + if (srb != NULL) { + + SrbSetSenseInfoBufferLength(srb, senseInfoBufferLength); + SrbSetSenseInfoBuffer(srb, senseInfoBuffer); + + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + + targetPortGroupsInfo = (PUCHAR)DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate TPG info.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate SRB.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate Sense Info Buffer.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + irp = IoAllocateIrp(DeviceObject->StackSize + 1, FALSE); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate IRP.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + mdl = IoAllocateMdl(targetPortGroupsInfo, + targetPortGroupsInfoLength, + FALSE, + FALSE, + irp); + + if (mdl == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate MDL.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + MmBuildMdlForNonPagedPool(mdl); + +__Retry_DsmpReportTargetPortGroups: + + irp->MdlAddress = mdl; + + // + // Set up SRB for execute scsi request. Save SRB address in next stack + // for the port driver. + // + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb; + irpStack->DeviceObject = DeviceObject; + + // + // Set the completion event and the completion routine. + // + KeInitializeEvent(&completionEvent, NotificationEvent, FALSE); + irp->UserEvent = &completionEvent; + IoSetCompletionRoutine(irp, + DsmpReportTargetPortGroupsSyncCompletion, + srb, + TRUE, + TRUE, + TRUE); + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS)); + cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb); + cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + REVERSE_BYTES(&(cdb->AllocationLength), &targetPortGroupsInfoLength); + + SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT); + SrbSetDataTransferLength(srb, targetPortGroupsInfoLength); + SrbSetDataBuffer(srb, targetPortGroupsInfo); + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + SrbSetNextSrb(srb, NULL); + SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); + SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); + SrbSetOriginalRequest(srb, irp); + + ObReferenceObject(DeviceObject); + + // + // Finally, send the IRP down and wait for its completion. + // + status = IoCallDriver(DeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&completionEvent, + Executive, + KernelMode, + FALSE, + NULL); + status = irp->IoStatus.Status; + } + + ObDereferenceObject(DeviceObject); + + if ((status == STATUS_BUFFER_OVERFLOW) || + (NT_SUCCESS(status) && (SrbGetScsiStatus(srb) == SCSISTAT_GOOD))) { + + // + // The first 4 bytes of the returned data are the Returned Data Length + // field of the RTPG header. + // + ULONG returnedDataLength = 0; + REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo); + + status = STATUS_SUCCESS; + if (returnedDataLength > SrbGetDataTransferLength(srb)) { + + status = STATUS_BUFFER_OVERFLOW; + } + } + + if (NT_SUCCESS(status) && SrbGetScsiStatus(srb) == SCSISTAT_GOOD) { + + // + // RTPG was successful so return the TPG info to the caller. + // + + // + // The first 4 bytes of the returned data are the Returned Data Length + // field of the RTPG header. We need to return this value plus the header size. + // + ULONG returnedDataLength = 0; + REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo); + *TargetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength; + + *TargetPortGroupsInfo = targetPortGroupsInfo; + + } else if (SrbGetScsiStatus(srb) == SCSISTAT_CHECK_CONDITION) { + + if (DsmpShouldRetryTPGRequest(senseInfoBuffer, senseInfoBufferLength)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Retrying request.\n", + DeviceObject)); + + IoReuseIrp(irp, STATUS_SUCCESS); + + RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); + + goto __Retry_DsmpReportTargetPortGroups; + } + + if (DsmpIsDeviceRemoved(senseInfoBuffer, senseInfoBufferLength)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Device not available.\n", + DeviceObject)); + + // + // Sense key was illegal request. SPC 6.25 says response to TPG should follow Test Unit Ready responses + // + status = STATUS_NO_SUCH_DEVICE; + + } + + // RTPG was unsuccessful + // Here it is possible that status is success, but scsi status is not. + // and there was no RTPG retry. If so, set status to unsuccessful. + if (NT_SUCCESS(status)) { + status = STATUS_UNSUCCESSFUL; + } + + // + // TPG resulted HW to respond with Check Condition but Sense Key indicates it is not for retry or illegal request + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): TPG returned Check Condition, NTStatus 0x%x, ScsiStatus 0x%x.\n", + DeviceObject, + status, + SrbGetScsiStatus(srb))); + } else { + + // RTPG was unsuccessful + // Here it is possible that status is success, but scsi status is not. + // If so, set status to unsuccessful. + if (NT_SUCCESS(status)) { + status = STATUS_UNSUCCESSFUL; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n", + DeviceObject, + status, + SrbGetScsiStatus(srb))); + } + +__Exit_DsmpReportTargetPortGroups: + + // + // The port driver may have allocated its own sense buffer so we need to + // make sure we free that here. + // + if (srb != NULL && + SrbGetSrbFlags(srb) & SRB_FLAGS_PORT_DRIVER_ALLOCSENSE && + SrbGetSrbFlags(srb) & SRB_FLAGS_FREE_SENSE_BUFFER && + SrbGetSenseInfoBuffer(srb) != NULL) { + DsmpFreePool(SrbGetSenseInfoBuffer(srb)); + } + + if (senseInfoBuffer) { + DsmpFreePool(senseInfoBuffer); + } + + if (srb) { + DsmpFreePool(srb); + } + + if (irp) { + if (irp->MdlAddress) { + IoFreeMdl(irp->MdlAddress); + } + IoFreeIrp(irp); + } + + if (!NT_SUCCESS(status) && targetPortGroupsInfo) { + DsmpFreePool(targetPortGroupsInfo); + *TargetPortGroupsInfoLength = 0; + *TargetPortGroupsInfo = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + +NTSTATUS +DsmpReportTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo + ) +/*++ + +Routine Description: + + Helper routine to send down ReportTargetPortGroups request asynchronously. + Used if device supports ALUA. + + NOTE: Caller needs to free Irp, system buffer, and passThrough buffer. + +Arguments: + + DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to. + CompletionRoutine - completion routine passed in by the caller. + CompletionContext - context to be passed to be completion routine. + TargetPortGroupsInfoLength - size of the returned buffer. + TargetPortGroupsInfo - preallocated (by caller) buffer that'll contain the returned data. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext; + PSCSI_REQUEST_BLOCK srb = NULL; + PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb; + NTSTATUS status; + PIRP irp = NULL; + PIO_STACK_LOCATION irpStack; + PMDL mdl = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Entering function.\n", + DeviceInfo)); + + srb = tpgCompletionContext->Srb; + + SrbZeroSrb(srb); + + // + // Allocate an irp. + // + irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE); + if (!irp) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n", + DeviceInfo)); + goto __Exit_DsmpReportTargetPortGroupsAsync; + } + + mdl = IoAllocateMdl(TargetPortGroupsInfo, + TargetPortGroupsInfoLength, + FALSE, + FALSE, + irp); + if (!mdl) { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n", + DeviceInfo)); + goto __Exit_DsmpReportTargetPortGroupsAsync; + } + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + // + // It is possible that if an implicit access state transition took place, + // each I_T nexus will return UA for asymmetric access state changed. So + // set the number of retries to be one more than the total number of paths. + // Worst case scenario is the it is sent down each path once (assuming every + // is a different I_T nexus) and then one more for a retry one one of the + // paths. + // + tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1; + + // + // Set-up the completion routine. + // + IoSetCompletionRoutine(irp, + CompletionRoutine, + (PVOID)CompletionContext, + TRUE, + TRUE, + TRUE); + + // + // Get the recipient's irpstack location. + // + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->Parameters.Scsi.Srb = srb; + irpStack->DeviceObject = DeviceInfo->TargetObject; + + // + // Set the major function code to IRP_MJ_SCSI. + // + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + + // + // Set the minor function, or many requests will get kicked by by port. + // + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS)); + cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb); + cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->AllocationLength); + + SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT); + SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer); + SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength); + SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength); + SrbSetDataBuffer(srb, TargetPortGroupsInfo); + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + SrbSetNextSrb(srb, NULL); + SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); + SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); + SrbSetOriginalRequest(srb, irp); + + irp->UserBuffer = TargetPortGroupsInfo; + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext, + DeviceInfo->TargetObject, + irp, + (PVOID)DeviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + +__Exit_DsmpReportTargetPortGroupsAsync: + + if (status != STATUS_PENDING) { + + // + // This indicates Irp was never sent down to stack (completion routine was never called). + // We need to clean up. + // + if (irp) { + + if (irp->MdlAddress) { + IoFreeMdl(irp->MdlAddress); + } + + IoFreeIrp(irp); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x\n.", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryLBPolicyForDevice( + _In_ IN PWSTR RegistryKeyName, + _In_ IN ULONGLONG PathId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONG PrimaryPath, + _Out_ OUT PULONG OptimizedPath, + _Out_ OUT PULONG PathWeight + ) +/*++ + +Routine Description: + + This routine opens the device's registry subkey, builds the path subkey from + the passed in PathId, then queries that subkey for the value of PrimaryPath, + OptimizedPath and PathWeight. + +Arguments: + + RegistryKeyName - The device's registry subkey name. + PathId - The pathId for this instance of the device. + LoadBalanceType - The current load balance policy. + PrimaryPath - Output of the queried PrimaryPath value. + OptimizedPath - Output of the queried OptimizedPath value. + PathWeight - Output of the queried PathWeight value. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + HANDLE dsmPathKey = NULL; + UNICODE_STRING subKeyName; + WCHAR dsmPathName[128] = {0}; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + NTSTATUS pathWeightQueryStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Entering function.\n", + RegistryKeyName)); + + // + // Query PrimaryPath and PathWeight for the given path. + // These values are stored under DsmPath#Suffix key for + // this path. If this key doesn't exist create it and + // create PrimaryPath and PathWeight values - use the + // values passed in PrimaryPath and PathWeight in this case. + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open LB Settings key. Status %x.\n", + RegistryKeyName, + status)); + + goto __Exit_DsmpQueryLBPolicyForDevice; + } + + RtlInitUnicodeString(&subKeyName, RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Create or open DsmPath#Suffix key for this path + // + DsmpGetDSMPathKeyName(PathId, dsmPathName, 128); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Will query %ws for PrimaryPath, OptimizedPath and PathWeight.\n", + RegistryKeyName, + dsmPathName)); + + RtlInitUnicodeString(&subKeyName, dsmPathName); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + deviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(&dsmPathKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + // + // Query the Path Weight value. + // + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_PATH_WEIGHT; + queryTable[0].EntryContext = PathWeight; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + pathWeightQueryStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + dsmPathKey, + queryTable, + dsmPathKey, + NULL); + + if (!NT_SUCCESS(pathWeightQueryStatus)) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PathWeight. Status %x.\n", + RegistryKeyName, + pathWeightQueryStatus)); + } + + // + // Query the Primary Path value. + // + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_PRIMARY_PATH; + queryTable[0].EntryContext = PrimaryPath; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + dsmPathKey, + queryTable, + dsmPathKey, + NULL); + if (NT_SUCCESS(status)) { + + // + // Query the Optimized Path value. + // + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED | + RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_OPTIMIZED_PATH; + queryTable[0].EntryContext = OptimizedPath; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + dsmPathKey, + queryTable, + dsmPathKey, + NULL); + if (!NT_SUCCESS(status)) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query OptimizedPath. Status %x.\n", + RegistryKeyName, + status)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PrimaryPath. Status %x.\n", + RegistryKeyName, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to create DSM Path key %ws. Status %x.\n", + RegistryKeyName, + dsmPathName, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open key. Status %x.\n", + RegistryKeyName, + status)); + } + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): PrimaryPath %d, OptmizedPath %d, PathWeight %d.\n", + RegistryKeyName, + *PrimaryPath, + *OptimizedPath, + *PathWeight)); + } + +__Exit_DsmpQueryLBPolicyForDevice: + + if (dsmPathKey) { + ZwClose(dsmPathKey); + } + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + // + // If the load balance policy is Weighted Paths and we failed to read in + // the path weight value, we need to return the failure status from the + // path weight value query. + // + if (LoadBalanceType == DSM_LB_WEIGHTED_PATHS && !NT_SUCCESS(pathWeightQueryStatus)) { + status = pathWeightQueryStatus; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Exiting function with status %x.\n", + RegistryKeyName, + status)); + + return status; +} + + +VOID +DsmpGetDSMPathKeyName( + _In_ ULONGLONG DSMPathId, + _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName, + _In_ ULONG DsmPathKeyNameSize + ) +/*++ + +Routine Description: + + This routine builds the string that corresponds to the device's Path subkey + name in the registry. + +Arguments: + + DSMPathId - The pathId of this instance of the device. + DsmPathKeyName - Output buffer in which the subkey name for path is returned. + DsmPathKeyNameSize - size of the output buffer in WCHARs. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PWCHAR pathPtr; + SIZE_T wcharsLeft; + SIZE_T size; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDSMPathKeyName (PathId %I64x): Entering function.\n", + DSMPathId)); + + // + // This routine will build a name for a given DSM Path. + // The name is of the format DsmPath#Suffix, where Suffix + // is derived from the PathId + // + pathPtr = DsmPathKeyName; + + wcharsLeft = DsmPathKeyNameSize; + + size = wcslen(DSM_PATH); + + if (size < wcharsLeft) { + + // + // First copy the string DsmPath# + // + if (NT_SUCCESS(RtlStringCchCopyNW(pathPtr, wcharsLeft, DSM_PATH, wcslen(DSM_PATH)))) { + + wcharsLeft -= size; + pathPtr += size; + + if (wcharsLeft > 2) { + + RtlStringCchCatW(pathPtr, wcharsLeft, L"#"); + wcharsLeft--; + pathPtr++; + + // + // Each nibble in the path id would need 1 WCHAR + // upon conversion to WCHAR string. So we'll need + // 2 WCHARs for each byte. Include the NULL char also + // + size = (sizeof(PVOID) + 1) * 2; + if (size <= wcharsLeft) { + + PVOID pathId; + PUCHAR pathIdPtr; + ULONG inx; + UCHAR tmpChar; + + // + // Convert the ULONGLONG path id to a string and + // append that to DsmPath# + // + pathId = (PVOID) DSMPathId; + + pathIdPtr = (PUCHAR) &pathId; + + for (inx = 0; inx < sizeof(PVOID); inx++) { + + tmpChar = (*pathIdPtr & 0xF0) >> 4; + *pathPtr++ = DsmpGetAsciiForBinary(tmpChar); + + tmpChar = (*pathIdPtr & 0x0F); + *pathPtr++ = DsmpGetAsciiForBinary(tmpChar); + + pathIdPtr++; + } + + *pathPtr = WNULL; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDSMPathKeyName (PathId %I64x): Exiting function.\n", + DSMPathId)); + + return; +} + + +UCHAR +DsmpGetAsciiForBinary( + _In_ UCHAR BinaryChar + ) +/*++ + +Routine Description: + + This routine converts the passed in binary value into ASCII equivalent. + +Arguments: + + BinaryChar - The binary value that needs to be converted. + +Return Value: + + Corresponding ASCII value. + +--*/ +{ + UCHAR outChar = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAsciiForBinary (BinaryChar %d): Entering function.\n", + BinaryChar)); + + // + // Convert a binary nibble into an ASCII character. + // + if ((BinaryChar >= 0) && (BinaryChar <= 9)) { + outChar = BinaryChar + '0'; + } else { + outChar = BinaryChar + 'A' - 10; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAsciiForBinary (BinaryChar %d): Exiting function with outChar %c.\n", + BinaryChar, + outChar)); + + return outChar; +} + + +NTSTATUS +DsmpGetDeviceIdList( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor + ) +/*++ + +Routine Description: + + This routine will perform a query for the StorageDeviceIdProperty and will + allocate a non-paged buffer to store the data in. + IMPORTANT: It is the responsibility of the caller to ensure that this buffer is freed. + +Arguments: + + DeviceObject - the device to query + Descriptor - a location to store a pointer to the buffer we allocate + +Return Value: + + status. + +--*/ +{ + STORAGE_PROPERTY_QUERY query; + PIO_STATUS_BLOCK ioStatus = NULL; + PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL; + ULONG length; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Entering function.\n", + DeviceObject)); + + if (!DeviceObject) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpGetDeviceIdList; + } + + // + // Poison the passed in descriptor. + // + *Descriptor = NULL; + + // + // Setup the query buffer. + // + query.PropertyId = StorageDeviceIdProperty; + query.QueryType = PropertyStandardQuery; + query.AdditionalParameters[0] = 0; + + ioStatus = DsmpAllocatePool(NonPagedPoolNx, sizeof(IO_STATUS_BLOCK), DSM_TAG_IO_STATUS_BLOCK); + + if (!ioStatus) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Failed to allocate an IO_STATUS_BLOCK.\n", + DeviceObject)); + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpGetDeviceIdList; + } + + ioStatus->Status = 0; + ioStatus->Information = 0; + + // + // On the first call, just need to get the length of the descriptor. + // + descriptor = (PVOID)&query; + DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY, + DeviceObject, + &query, + &query, + sizeof(STORAGE_PROPERTY_QUERY), + sizeof(STORAGE_DESCRIPTOR_HEADER), + FALSE, + ioStatus); + + status = ioStatus->Status; + + if(!NT_SUCCESS(status)) { + + descriptor = NULL; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Query failed (%x) on attempt 1.\n", + DeviceObject, + ioStatus->Status)); + + goto __Exit_DsmpGetDeviceIdList; + } + + NT_ASSERT(descriptor->Size); + if (descriptor->Size == 0) { + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpGetDeviceIdList; + } + + // + // This time we know how much data there is so we can + // allocate a buffer of the correct size + // + length = descriptor->Size; + + descriptor = DsmpAllocatePool(NonPagedPoolNx, length, DSM_TAG_DEVICE_ID_LIST); + + if(!descriptor) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Couldn't allocate descriptor of %ld.\n", + DeviceObject, + length)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpGetDeviceIdList; + } + + // + // setup the query again. + // + query.PropertyId = StorageDeviceIdProperty; + query.QueryType = PropertyStandardQuery; + query.AdditionalParameters[0] = 0; + + // + // copy the input to the new outputbuffer + // + RtlCopyMemory(descriptor, + &query, + sizeof(STORAGE_PROPERTY_QUERY)); + + DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY, + DeviceObject, + descriptor, + descriptor, + sizeof(STORAGE_PROPERTY_QUERY), + length, + 0, + ioStatus); + + status = ioStatus->Status; + + if(!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Query Failed (%x) on attempt 2.\n", + DeviceObject, + ioStatus->Status)); + + goto __Exit_DsmpGetDeviceIdList; + } + +__Exit_DsmpGetDeviceIdList: + + if (ioStatus) { + DsmpFreePool(ioStatus); + } + + if (!NT_SUCCESS(status)) { + + if (descriptor) { + DsmpFreePool(descriptor); + } + + } else { + *Descriptor = descriptor; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + + +NTSTATUS +DsmpSetTargetPortGroups( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ) +/*++ + +Routine Description: + + Helper routine to send down SetTargetPortGroups request. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + TargetPortGroupsInfo - buffer containing the TPG data. + TargetPortGroupsInfoLength - size of the TPG buffer. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER passThrough; + PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb; + IO_STATUS_BLOCK ioStatus; + ULONG alignmentMask = DeviceObject->AlignmentRequirement; + PUCHAR dataBuffer = NULL; + SIZE_T allocatedLength = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): Entering function.\n", + DeviceObject)); + + NT_ASSERT(TargetPortGroupsInfoLength && TargetPortGroupsInfo); + + // + // Build request. + // + RtlZeroMemory(&passThrough, sizeof(passThrough)); + + dataBuffer = DsmpAllocateAlignedPool(NonPagedPoolNx, + TargetPortGroupsInfoLength, + alignmentMask, + DSM_TAG_PASS_THRU, + &allocatedLength); + if (!dataBuffer) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): Failed to allocate mem for passthrough's databuffer.\n", + DeviceObject)); + goto __Exit_DsmpSetTargetPortGroups; + } + +__Retry_Request: + + // + // Build the cdb. + // + cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)passThrough.ScsiPassThroughDirect.Cdb; + + cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength); + + passThrough.ScsiPassThroughDirect.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + passThrough.ScsiPassThroughDirect.CdbLength = 12; + passThrough.ScsiPassThroughDirect.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough.ScsiPassThroughDirect.DataIn = 0; + passThrough.ScsiPassThroughDirect.DataTransferLength = TargetPortGroupsInfoLength; + passThrough.ScsiPassThroughDirect.TimeOutValue = 20; + passThrough.ScsiPassThroughDirect.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseInfoBuffer); + passThrough.ScsiPassThroughDirect.DataBuffer = dataBuffer; + RtlCopyMemory(dataBuffer, + TargetPortGroupsInfo, + TargetPortGroupsInfoLength); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT, + DeviceObject, + &passThrough, + &passThrough, + sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), + sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), + FALSE, + &ioStatus); + + if ((passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(ioStatus.Status))) { + + status = STATUS_SUCCESS; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): STPG succeeded.\n", + DeviceObject)); + + } else if (NT_SUCCESS(ioStatus.Status) && + passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_CHECK_CONDITION && + DsmpShouldRetryTPGRequest((PSENSE_DATA)&passThrough.SenseInfoBuffer, passThrough.ScsiPassThroughDirect.SenseInfoLength)) { + + // + // Retry the request + // + RtlZeroMemory(dataBuffer, TargetPortGroupsInfoLength); + goto __Retry_Request; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + DeviceObject, + ioStatus.Status, + passThrough.ScsiPassThroughDirect.ScsiStatus)); + + status = ioStatus.Status; + } + +__Exit_DsmpSetTargetPortGroups: + + // + // Free the passthrough + data buffer. + // + if (dataBuffer) { + DsmpFreePool(dataBuffer); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + + +NTSTATUS +DsmpSetTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo + ) +/*++ + +Routine Description: + + Helper routine to send down SetTargetPortGroups request asynchronously. + + IMPORTANT: Caller needs to free the IRP and allocated system buffer. + +Arguments: + + DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to. + CompletionRoutine - completion routine provided by the caller. + CompletionContext - context passed into the completion routine. + TargetPortGroupsInfoLength - size of the TPG buffer. + TargetPortGroupsInfo - buffer containing the TPG data. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext; + PSCSI_REQUEST_BLOCK srb; + PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb; + NTSTATUS status; + PIRP irp = NULL; + PIO_STACK_LOCATION irpStack; + PMDL mdl = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Entering function.\n", + DeviceInfo)); + + srb = tpgCompletionContext->Srb; + + SrbZeroSrb(srb); + + // + // Allocate an irp. + // + irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE); + if (!irp) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n", + DeviceInfo)); + goto __Exit_DsmpSetTargetPortGroupsAsync; + } + + mdl = IoAllocateMdl(TargetPortGroupsInfo, + TargetPortGroupsInfoLength, + FALSE, + FALSE, + irp); + if (!mdl) { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n", + DeviceInfo)); + goto __Exit_DsmpSetTargetPortGroupsAsync; + } + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + // + // It is possible that an implicit state transition may have occurred which + // will cause every I_T nexus to return an UA (for asymmetric access state + // changed). So set the number of retries to number of paths (worst case of + // every path being a separate I_T nexus) plus one for a retry down one of + // paths. + // + tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1; + + // + // Set-up the completion routine. + // + IoSetCompletionRoutine(irp, + CompletionRoutine, + (PVOID)CompletionContext, + TRUE, + TRUE, + TRUE); + + // + // Get the recipient's irpstack location. + // + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->Parameters.Scsi.Srb = srb; + irpStack->DeviceObject = DeviceInfo->TargetObject; + + // + // Set the major function code to IRP_MJ_SCSI. + // + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + + // + // Set the minor function, or many requests will get kicked by by port. + // + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + SrbSetCdbLength(srb, sizeof(SPC3_CDB_SET_TARGET_PORT_GROUPS)); + cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)SrbGetCdb(srb); + cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength); + + SrbSetTimeOutValue(srb, SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT); + SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer); + SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength); + SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength); + SrbSetDataBuffer(srb, TargetPortGroupsInfo); + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + SrbSetNextSrb(srb, NULL); + SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); + SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); + SrbSetOriginalRequest(srb, irp); + + irp->UserBuffer = TargetPortGroupsInfo; + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext, + DeviceInfo->TargetObject, + irp, + DeviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + + +__Exit_DsmpSetTargetPortGroupsAsync: + + if (status != STATUS_PENDING) { + + // + // This indicates Irp was never sent down to stack (completion routine was never called). + // We need to clean up. + // + if (irp) { + + if (irp->MdlAddress) { + IoFreeMdl(irp->MdlAddress); + } + + IoFreeIrp(irp); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +PDSM_LOAD_BALANCE_POLICY_SETTINGS +DsmpCopyLoadBalancePolicies( + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ) +/*+++ + +Routine Description: + + This routine copies the LB Policies that needs to be persisted in registry. + This is done because registry routines can be called at PASSIVE IRQL only. + So a spinlock cannot be held while accessing registry. So hold a spinlock, + save the values in a temp buffer, release spinlock, and save data to registry + from the temp buffer. + + NOTE: This routine MUST be called with DSM_CONTEXT lock held. + +Arguements: + + GroupEntry - Group entry + DsmWmiVersion - version of the MPIO_DSM_Path class to use + SupportedLBPolicies - LB policy for the group + + Return Value: + + Pointer to LOAD_BALANCE_POLICY_SETTINGS if successful. Else, NULL +--*/ +{ + PDSM_LOAD_BALANCE_POLICY_SETTINGS lbSettings = NULL; + ULONG sizeNeeded; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): Entering function.\n", + GroupEntry)); + + if (((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): No paths specified in Set LB policies.\n", + GroupEntry)); + + goto __Exit_DsmpCopyLoadBalancePolicies; + } + + sizeNeeded = sizeof(DSM_LOAD_BALANCE_POLICY_SETTINGS) + + ((((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount - 1) * sizeof(MPIO_DSM_Path_V2));; + + lbSettings = DsmpAllocatePool(NonPagedPoolNx, + sizeNeeded, + DSM_TAG_LB_POLICY); + + if (!lbSettings) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): Failed to allocate memory for LBSettings.\n", + GroupEntry)); + goto __Exit_DsmpCopyLoadBalancePolicies; + } + + // + // Copy the registry key name used to store the LB policies. + // + RtlStringCchCopyNW(lbSettings->RegistryKeyName, + sizeof(lbSettings->RegistryKeyName) / sizeof(lbSettings->RegistryKeyName[0]), + GroupEntry->RegistryKeyName, + ((sizeof(lbSettings->RegistryKeyName) - sizeof(WCHAR))/sizeof(WCHAR))); + + // + // Copy the Load Balance settings for this group + // + lbSettings->LoadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->LoadBalancePolicy; + + lbSettings->PathCount = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; + + for (inx = 0; inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + RtlCopyMemory(&(lbSettings->DsmPath[inx]), + &(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]), + sizeof(MPIO_DSM_Path)); + + // + // DSM_WMI_VERSION_1 supports only active and standby states + // + (lbSettings->DsmPath[inx]).OptimizedPath = TRUE; + + } else { + + RtlCopyMemory(&(lbSettings->DsmPath[inx]), + &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]), + sizeof(MPIO_DSM_Path_V2)); + } + } + +__Exit_DsmpCopyLoadBalancePolicies: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): Exiting function with lbSettings %p.\n", + GroupEntry, + lbSettings)); + + return lbSettings; +} + + +NTSTATUS +DsmpPersistLBSettings( + _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings + ) +/*+++ + +Routine Description: + + This routine will save the Load Balance settings from LoadBalanceSettings + to registry. + + NOTE: This routine MUST be called at PASSIVE IRQL + + The format of the registry tree is : + + Services\MSDSM\LoadBalanceSettings -> + + DeviceName -> LoadBalancePolicy REG_DWORD + + DsmPath#Suffix -> PrimaryPath REG_DWORD + OptimizedPath REG_DWORD + PathWeight REG_DWORD + + The device name is the one built in DsmpBuildDeviceName + + The Suffix in DsmPath#Suffix is built from the PathId. It is built in + the routine DsmpGetDSMPathKeyName. + +Arguements: + + LoadBalanceSettings - Load Balance settings to be persisted in registry + +Return Value: + + STATUS_SUCCESS if the data could be successfully stored in the registry + Appropriate NT Status code on failure. +--*/ +{ + PMPIO_DSM_Path_V2 dsmPath; + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + HANDLE dsmPathKey = NULL; + UNICODE_STRING subKeyName; + WCHAR dsmPathName[128]; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + ULONG inx; + PMPIO_DSM_Path_V2 preferredPath = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Entering function.\n", + LoadBalanceSettings->RegistryKeyName)); + + // + // First open LoadBalanceSettings key under the Service key + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to open LB Settings key. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + + goto __Exit_DsmpPersistLBSettings; + } + + // + // Now open the key under which the LB settings for the given device is stored + // + RtlInitUnicodeString(&subKeyName, LoadBalanceSettings->RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + // + // Remove all LB policy information as we are going to rewrite it. + // We do this in case there is stale information about a path that + // no longer exists + // + status = DsmpRegDeleteTree(deviceKey); + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Deleted key along with its subkeys.\n", + LoadBalanceSettings->RegistryKeyName)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to delete key. Status %x\n", + LoadBalanceSettings->RegistryKeyName, + status)); + + } + + ZwClose(deviceKey); + deviceKey = NULL; + + } + + status = ZwCreateKey(&deviceKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + PDSM_DEVICE_INFO devInfo; + + for (inx = 0; inx < LoadBalanceSettings->PathCount; inx++) { + + dsmPath = &(LoadBalanceSettings->DsmPath[inx]); + + if (dsmPath->DsmPathId == 0) { + + continue; + } + + RtlZeroMemory(dsmPathName, sizeof(dsmPathName)); + + // + // Get the sub key name under which the LB settings for + // the given path is stored. + // + DsmpGetDSMPathKeyName(dsmPath->DsmPathId, dsmPathName, 128); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Will open subkey %ws.\n", + LoadBalanceSettings->RegistryKeyName, + dsmPathName)); + + RtlInitUnicodeString(&subKeyName, dsmPathName); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + deviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(&dsmPathKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + if (dsmPath->PreferredPath) { + + preferredPath = dsmPath; + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + // + // Save PrimaryPath, PathWeight and OptimizedPath values for this path + // + if (devInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + dsmPathKey, + DSM_PRIMARY_PATH, + REG_DWORD, + &(dsmPath->PrimaryPath), + sizeof(ULONG)); + + if (NT_SUCCESS(status)) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + dsmPathKey, + DSM_OPTIMIZED_PATH, + REG_DWORD, + &(dsmPath->OptimizedPath), + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save OptimizedPath. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save Primary Path. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + } + + if (NT_SUCCESS(status)) { + + if (LoadBalanceSettings->LoadBalancePolicy == DSM_LB_WEIGHTED_PATHS) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + dsmPathKey, + DSM_PATH_WEIGHT, + REG_DWORD, + &(dsmPath->PathWeight), + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save PathWeight. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + } + } + + ZwClose(dsmPathKey); + dsmPathKey = NULL; + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to open DSM Path key. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + + if (!NT_SUCCESS(status)) { + break; + } + } + + if (NT_SUCCESS(status)) { + + // + // Save the new Load Balance Policy value, + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_LOAD_BALANCE_POLICY, + REG_DWORD, + &(LoadBalanceSettings->LoadBalancePolicy), + sizeof(ULONG)); + if (NT_SUCCESS(status)) { + + UCHAR explicitlySet = TRUE; + + // + // Write out that the policy has been explicitly set + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_POLICY_EXPLICITLY_SET, + REG_BINARY, + &explicitlySet, + sizeof(UCHAR)); + + if (NT_SUCCESS(status)) { + + // + // If FailOver-Only policy, set the PreferredPath, if specified + // + if (preferredPath) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_PREFERRED_PATH, + REG_BINARY, + &(preferredPath->DsmPathId), + sizeof(ULONGLONG)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (ES).\n", + LoadBalanceSettings->RegistryKeyName)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (LBP).\n", + LoadBalanceSettings->RegistryKeyName)); + } + } + } + +__Exit_DsmpPersistLBSettings: + + if (dsmPathKey) { + ZwClose(dsmPathKey); + } + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Exiting function with status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + + return status; +} + + +NTSTATUS +DsmpSetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN DSM_DEVICE_STATE DevState + ) +/*++ + +Routine Description: + + Helper routine to build the STPG info and send it down to modify the passed in + devInfo's state. + +Arguments: + + DsmContext - DSM context. + DeviceInfo - DevInfo whose state needs to be changed. + DevState - New state to be set. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength; + PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Send down SetTPG to set the appropriate access state + // (The TPG block will contain the header and a SetTPG descriptor). + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); + tpgDescriptor->AsymmetricAccessState = DevState; + REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &DeviceInfo->TargetPortGroup->Identifier); + + status = DsmpSetTargetPortGroups(DeviceInfo->TargetObject, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + if (NT_SUCCESS(status)) { + + // + // An explicit transition may cause changes to some other TPGs. + // So we need to query for the states of all the TPGs and update + // our internal list and its elements. + // + status = DsmpGetDeviceALUAState(DsmContext, + DeviceInfo, + NULL); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Failed to SetTPG with %x.\n", + DeviceInfo, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Failed to allocate TPG.\n", + DeviceInfo)); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (targetPortGroupsInfo) { + DsmpFreePool(targetPortGroupsInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpAdjustDeviceStatesALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + Helper routine to build the adjust every device state in the group taking + the following into consideration: + 1. PreferredActiveDeviceInfo + 2. DeviceInfo's TPG state + 3. Preferred Path + 4. LB Policy + +Arguments: + + Group - Pseudo-LUN whose path states need to be adjusted. + PreferredActiveDeviceInfo - DevInfo whose state needs to preferrably made + A/O, if possible. This parameter is optional. + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + ULONG index; + PDSM_DEVICE_INFO deviceInfo; + PDSM_DEVICE_INFO activeDevice = NULL; + DSM_DEVICE_STATE devState; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): Entering function with preferred active devInfo %p.\n", + Group, + PreferredActiveDeviceInfo)); + + // + // Ensure that: + // 1. All devices match their ALUA state. + // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it. + // 3. For FOO there must be only one A/O device. Preferably the preferred path. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + deviceInfo = Group->DeviceList[index]; + + if (deviceInfo) { + + devState = deviceInfo->State; + + if (!DsmpIsDeviceFailedState(deviceInfo->State) && + DsmpIsDeviceInitialized(deviceInfo) && + DsmpIsDeviceUsable(deviceInfo) && + DsmpIsDeviceUsablePR(deviceInfo)) { + + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->ALUAState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (its ALUA state).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + + if (deviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // In FOO and RRWS, we need to mask states. + // + switch (Group->LoadBalanceType) { + case DSM_LB_FAILOVER: { + + // + // Cache the first available devInfo that is in A/O + // + if (!activeDevice) { + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p choosen as the active device.\n", + Group, + activeDevice)); + + break; + } + + // + // Check if this deviceInfo is the preferred path. If yes, + // mask the active device's state and make this the new + // active device. + // + if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)deviceInfo->FailGroup->PathId)) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): Previous active devInfo %p transitioning from %u to %u.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (preferred path).\n", + Group, + activeDevice)); + + break; + } + + // + // If active device's desired state is not A/O but this + // deviceInfo's is, then mask the active device's state + // and make this one the new active device. + // + if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { + + // + // The exception though is if the current active device + // is the preferred path + // + if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || + deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active DI is PrefPath).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // If this is the devInfo that is preferred to be A/O, make it such + // + if (PreferredActiveDeviceInfo && + PreferredActiveDeviceInfo == deviceInfo) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (found a preferred active DI).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active DI (preferred).\n", + Group, + activeDevice)); + } else { + + // + // Check if this devInfo desires to be in A/O, since the currently + // active one doesn't want to be. + // + if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + // + // This deviceInfo's desire is also not to be in A/O, + // so just leave the current one active. + // + if (devState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // Exception is if we're processing the device whose state before + // RTPG was sent was already A/O, it is best to leave this device + // in A/O state. + // + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Found a DI that was previously active.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (previously A/O).\n", + Group, + activeDevice)); + } else { + + // + // This device wasn't in A/O state before, so just leave + // the currently selected active device as is. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active device already exists).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } + } else { + + // + // Current devInfo wants (or doesn't) mind being in + // A/O, whereas the current active device doesn't, so + // mask the active device and make this devInfo the + // active device. + // + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Current DI prefers being A/O.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (desired state).\n", + Group, + activeDevice)); + } + } + } + } else { + + // + // The single overriding factor is always the preferred path. + // Everything else is secondary, so first check if the currently + // active device can even be overridden by another one. + // + if (Group->PreferredPath != (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) { + + // + // It can't be overridden, so we're done. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || + deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (current active DI is PrefPath).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // Active device's desired state is A/O but it isn't the preferred + // path. Check if this devInfo is preferred as A/O. + // + if (PreferredActiveDeviceInfo && + PreferredActiveDeviceInfo == deviceInfo) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. New DI is preferred active.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (this DI is preferred active).\n", + Group, + activeDevice)); + } else { + + // + // Active device's desired state is A/O but it isn't the + // preferred path. Check if this devInfo's desired state + // is also A/O. If yes, we'll need to make certain decisions. + // + if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + // + // Since this device doesn't desire to be in + // A/O and we already have an active device, just + // mask its state. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (prefers being in non-A/O).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // Active device is in A/O and this device desires to be in + // A/O too. Make this the new active device only if its state + // before the RTPG was already A/O. + // + if (devState == DSM_DEV_ACTIVE_OPTIMIZED) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI was already in A/O previously).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is new active device (since it was in A/O previously too).\n", + Group, + activeDevice)); + } else { + + // + // Just leave the currently active one alone. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || + deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (leave current active DI alone).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } + } + } + } + } + break; + } + + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + // + // At least one path needs to be in A/O state, so + // cache the first available devInfo that is in A/O + // + if (!activeDevice) { + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): We have atleast one A/O DevInfo %p.\n", + Group, + activeDevice)); + + break; + } + + // + // Check if this device is preferred to be in A/O + // + if (PreferredActiveDeviceInfo && + PreferredActiveDeviceInfo == deviceInfo) { + + // + // If the currently active device, doesn't desire to be in + // A/O state, mask its state. + // + if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desired state non-A/O).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + } + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (preferred active DI).\n", + Group, + activeDevice)); + } else { + + // + // If this device's desired state is specified and not A/O, + // mask its path state. + // + if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desires to be in non-A/O).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // Since this devInfo desires to be in A/O, we are assured + // of at least one path in A/O. So check to see if the + // currently active device doesn't desire to be in A/O. + // + if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI desires to be in A/O).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active DI (desires to be in A/O).\n", + Group, + activeDevice)); + } + } + } + + break; + } + + default: { + + // + // For RR, LQD and WP, paths must be in the same + // state as their corresponding TPG. Preferably + // all should be A/O. + // + if (deviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { + DSM_ASSERT(deviceInfo->State == deviceInfo->ALUAState); + } + + break; + } + } + } + } + } + } + + // + // There may have been a change to the device states. + // DsmpGetPath() will pick these changes for RR, RRWS and LQD. + // However, it won't for FOO and WP, so update PTBU if needed. + // + if (Group->LoadBalanceType == DSM_LB_FAILOVER || + Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) { + + deviceInfo = DsmpGetActivePathToBeUsed(Group, FALSE, SpecialHandlingFlag); + + if (deviceInfo) { + + InterlockedExchangePointer(&(Group->PathToBeUsed), deviceInfo->FailGroup); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): Exiting function with status %x\n", + Group, + status)); + + return status; +} + + +PDSM_WORKITEM +DsmpAllocateWorkItem( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PVOID Context + ) +/*++ + +Routine Description: + + Allocates a work item to handle reservation failover. + +Arguments: + + DeviceObject - Target device. + Context - Workitem context + +Return Value: + + Allocated workitem or NULL (if low memory). + +--*/ +{ + PDSM_WORKITEM dsmWorkItem = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpAllocateWorkItem (DevObj %p): Entering function.\n", + DeviceObject)); + + dsmWorkItem = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_WORKITEM), + DSM_TAG_WORKITEM); + if (dsmWorkItem != NULL) { + + dsmWorkItem->WorkItem = IoAllocateWorkItem(DeviceObject); + if (dsmWorkItem->WorkItem != NULL) { + + dsmWorkItem->Context = Context; + } else { + + DsmpFreePool(dsmWorkItem); + dsmWorkItem = NULL; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpAllocateWorkItem (DevObj %p): Exiting function. dsmWorkItem %p.\n", + DeviceObject, + dsmWorkItem)); + + return dsmWorkItem; +} + + +VOID +DsmpFreeWorkItem( + _In_ IN PDSM_WORKITEM DsmWorkItem + ) +{ + PVOID temp = DsmWorkItem; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpFreeWorkItem (WorkItem %p): Entering function.\n", + DsmWorkItem)); + + if (DsmWorkItem != NULL) { + + if (DsmWorkItem->WorkItem != NULL) { + IoFreeWorkItem(DsmWorkItem->WorkItem); + } + + DsmpFreePool(DsmWorkItem); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpFreeWorkItem (WorkItem %p): Exiting function.\n", + temp)); + + return; +} + + +VOID +DsmpFreeZombieGroupList( + _In_ IN PDSM_FAILOVER_GROUP FailGroup + ) +{ + PLIST_ENTRY zombieEntry = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreeZombieGroupList (FailGroup %p): Entering function.\n", + FailGroup)); + + while (!IsListEmpty(&FailGroup->ZombieGroupList)) { + + zombieEntry = RemoveHeadList(&FailGroup->ZombieGroupList); + + if (zombieEntry) { + + DsmpFreePool(zombieEntry); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreeZombieGroupList (FailGroup %p): Exiting function.\n", + FailGroup)); +} + + +NTSTATUS +DsmpGetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_opt_ IN PDSM_DEVICE_STATE DevState + ) +/*++ + +Routine Description: + + Helper routine to build the RTPG info and send it down to retrieve the + devInfo's current state. + +Arguments: + + DsmContext - DSM context. + DeviceInfo - DevInfo whose state needs to be changed. + DevState - Current state of passed in DeviceInfo. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; + KIRQL irql; + NTSTATUS status; + ULONG index; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetDeviceALUAState (DevInfo %p): Entering function.\n", + DeviceInfo)); + + status = DsmpReportTargetPortGroups(DeviceInfo->TargetObject, + &targetPortGroupsInfo, + &targetPortGroupsInfoLength); + + + if (NT_SUCCESS(status) && targetPortGroupsInfo != NULL) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + status = DsmpParseTargetPortGroupsInformation(DsmContext, + DeviceInfo->Group, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroup = DeviceInfo->Group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (DevState) { + + *DevState = DeviceInfo->State; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpGetDeviceALUAState (DevInfo %p): ReportTPG failed with %x.\n", + DeviceInfo, + status)); + } + + if (targetPortGroupsInfo) { + + DsmpFreePool(targetPortGroupsInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetDeviceALUAState (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpRegCopyTree( + _In_ IN HANDLE SourceKey, + _In_ IN HANDLE DestKey + ) +/*++ + +Routine Description: + + Copies a reg subtree from source key to destination key. + This routine will first copy over all the key's values, and then + copy the subkeys, each time recursively handling the subkey's + values and its subtree. + +Arguments: + + SourceKey - Handle to the root of the subtree to copy over. + DestKey - Handle to the root of the new tree. + +Return Value: + + STATUS_SUCCESS upon successfully coping over the tree. + Appropriate NT error code in case of failure. + +--*/ +{ + ULONG numValues = 0; + ULONG numSubKeys = 0; + ULONG lengthOfValueName = 0; + ULONG lengthOfValueData = 0; + ULONG lengthOfKeyName = 0; + LPWSTR valueBuf = NULL; + BYTE *valueDataBuf = NULL; + ULONG valueDataType; + ULONG titleIndex; + HANDLE srcSubKey = NULL; + HANDLE destSubKey = NULL; + LPWSTR subKey = NULL; + NTSTATUS status; + PKEY_FULL_INFORMATION keyFullInfo = NULL; + ULONG length = sizeof(KEY_FULL_INFORMATION); + ULONG index = 0; + PKEY_VALUE_FULL_INFORMATION keyValueFullInfo = NULL; + PKEY_BASIC_INFORMATION keyBasicInfo = NULL; + OBJECT_ATTRIBUTES objectAttributes; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Entering function.\n", + SourceKey)); + + if (!SourceKey || !DestKey) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpRegCopyTree; + } + + // + // Query the source key for information about number of subkeys, number of values, etc. + // + do { + if (keyFullInfo) { + + DsmpFreePool(keyFullInfo); + } + + keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key full info.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + status = ZwQueryKey(SourceKey, + KeyFullInformation, + keyFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to query key. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + numSubKeys = keyFullInfo->SubKeys; + numValues = keyFullInfo->Values; + lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR); + lengthOfValueName = keyFullInfo->MaxValueNameLen + sizeof(WCHAR); + lengthOfValueData = keyFullInfo->MaxValueDataLen + sizeof(WCHAR); + + // + // Allocate a buffer for the name of the value + // + valueBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfValueName, + DSM_TAG_REG_KEY_RELATED); + if (!valueBuf) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value name.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Allocate a buffer for the value data + // + valueDataBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfValueData, + DSM_TAG_REG_KEY_RELATED); + + if (!valueDataBuf) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value's data.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // First enumerate all of the values + // + status = STATUS_SUCCESS; + for (index = 0; index < numValues && NT_SUCCESS(status); index++) { + + UNICODE_STRING valueName; + + length = sizeof(KEY_VALUE_FULL_INFORMATION); + + do { + + if (keyValueFullInfo) { + + DsmpFreePool(keyValueFullInfo); + } + + keyValueFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyValueFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value full info.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Get the information of the index'th value + // + status = ZwEnumerateValueKey(SourceKey, + index, + KeyValueFullInformation, + keyValueFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to enumerate key's value information. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + // + // Capture the data type, data value, and value name. + // + titleIndex = keyValueFullInfo->TitleIndex; + valueDataType = keyValueFullInfo->Type; + + RtlZeroMemory(valueDataBuf, lengthOfValueData); + RtlCopyMemory(valueDataBuf, + (PUCHAR)keyValueFullInfo + keyValueFullInfo->DataOffset, + keyValueFullInfo->DataLength); + + RtlZeroMemory(valueBuf, lengthOfValueName); + RtlStringCbCopyNW(valueBuf, lengthOfValueName, keyValueFullInfo->Name, keyValueFullInfo->NameLength); + RtlInitUnicodeString(&valueName, valueBuf); + + // + // Copy the value over to the new key + // + status = ZwSetValueKey(DestKey, + &valueName, + titleIndex, + valueDataType, + valueDataBuf, + keyValueFullInfo->DataLength); + } + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate set new key's value information. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + // + // Allocate buffer for subkey name + // + subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfKeyName, + DSM_TAG_REG_KEY_RELATED); + + if(!subKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for sub key name.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Now Enumerate all of the subkeys + // + length = sizeof(KEY_BASIC_INFORMATION); + for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) { + + UNICODE_STRING subKeyName; + + do { + if (keyBasicInfo) { + + DsmpFreePool(keyBasicInfo); + } + + keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + length, + DSM_TAG_REG_KEY_RELATED); + + if (!keyBasicInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key basic info.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Enumerate the index'th subkey + // + status = ZwEnumerateKey(SourceKey, + index, + KeyBasicInformation, + keyBasicInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to enumerate sub key's info. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + RtlZeroMemory(subKey, lengthOfKeyName); + RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength); + RtlInitUnicodeString(&subKeyName, subKey); + + // + // Open a handle to the the subkey on the old device. + // + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + SourceKey, + (PSECURITY_DESCRIPTOR) NULL); + + if (srcSubKey) { + ZwClose(srcSubKey); + srcSubKey = NULL; + } + + status = ZwOpenKey(&srcSubKey, + KEY_ALL_ACCESS, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to open reg key %ws. Status %x.\n", + SourceKey, + subKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + DestKey, + (PSECURITY_DESCRIPTOR) NULL); + + if (destSubKey) { + ZwClose(destSubKey); + destSubKey = NULL; + } + + // + // Create the subkey on the new device. + // + status = ZwCreateKey(&destSubKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to create reg key %ws. Status %x.\n", + SourceKey, + subKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + // + // That's it. We've got everything we need (ie. handles to the two new + // subtrees' roots. Call recursively. + // + status = DsmpRegCopyTree(srcSubKey, destSubKey); + } + +__Exit_DsmpRegCopyTree: + + if (keyFullInfo) { + DsmpFreePool(keyFullInfo); + } + + if (valueBuf) { + DsmpFreePool(valueBuf); + } + + if (valueDataBuf) { + DsmpFreePool(valueDataBuf); + } + + if (keyValueFullInfo) { + DsmpFreePool(keyValueFullInfo); + } + + if (subKey) { + DsmpFreePool(subKey); + } + + if (keyBasicInfo) { + DsmpFreePool(keyBasicInfo); + } + + if (srcSubKey) { + ZwClose(srcSubKey); + } + + if (destSubKey) { + ZwClose(destSubKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Exiting function with status %x.\n", + SourceKey, + status)); + + return status; +} + + +NTSTATUS +DsmpRegDeleteTree( + _In_ IN HANDLE KeyRoot + ) +/*++ +Routine Description: + + This routine is a recursive worker that enumerates the subkeys + of a given key, applies itself to each one, then deletes itself. + +Arguments: + + KeyRoot - Supplies a handle to the root of subtree to be deleted. + +Return Value: + + STATUS_SUCCESS - upon successful deletion of subtree. + Appropriate NT error code upon failure. + +--*/ +{ + NTSTATUS status; + PKEY_FULL_INFORMATION keyFullInfo = NULL; + ULONG length = sizeof(KEY_FULL_INFORMATION); + ULONG numSubKeys; + ULONG lengthOfKeyName; + LPWSTR subKey = NULL; + PKEY_BASIC_INFORMATION keyBasicInfo = NULL; + ULONG index = 0; + HANDLE srcSubKey = NULL; + OBJECT_ATTRIBUTES objectAttributes; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Entering function.\n", + KeyRoot)); + + if (!KeyRoot) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpRegDeleteTree; + } + + // + // Query the source key for information about number of subkeys and max + // length needed for subkey name. + // + do { + if (keyFullInfo) { + + DsmpFreePool(keyFullInfo); + } + + keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key full info.\n", + KeyRoot)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegDeleteTree; + } + + status = ZwQueryKey(KeyRoot, + KeyFullInformation, + keyFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to query key. Status %x.\n", + KeyRoot, + status)); + + goto __Exit_DsmpRegDeleteTree; + } + + numSubKeys = keyFullInfo->SubKeys; + lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR); + + if (numSubKeys) { + + // + // Allocate buffer for subkey name + // + subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfKeyName, + DSM_TAG_REG_KEY_RELATED); + + if(!subKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for sub key.\n", + KeyRoot)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegDeleteTree; + } + + // + // Now Enumerate all of the subkeys + // + index = numSubKeys - 1; + length = sizeof(KEY_BASIC_INFORMATION); + do { + + UNICODE_STRING subKeyName; + + do { + if (keyBasicInfo) { + + DsmpFreePool(keyBasicInfo); + } + + keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + length, + DSM_TAG_REG_KEY_RELATED); + + if (!keyBasicInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key basic info.\n", + KeyRoot)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegDeleteTree; + } + + // + // Enumerate the index'th subkey + // + status = ZwEnumerateKey(KeyRoot, + index, + KeyBasicInformation, + keyBasicInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (NT_SUCCESS(status)) { + + RtlZeroMemory(subKey, lengthOfKeyName); + RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength); + RtlInitUnicodeString(&subKeyName, subKey); + + // + // Open a handle to the the current root's subkey. + // + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + KeyRoot, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&srcSubKey, + KEY_ALL_ACCESS, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to open key %ws. Status %x.\n", + KeyRoot, + subKey, + status)); + + goto __Exit_DsmpRegDeleteTree; + } + + // + // Delete this key's subtree (recursively). + // + status = DsmpRegDeleteTree(srcSubKey); + + ZwClose(srcSubKey); + srcSubKey = NULL; + } + + index--; + + } while (status != STATUS_NO_MORE_ENTRIES && (LONG)index >= 0); + + if (status == STATUS_NO_MORE_ENTRIES) { + + status = STATUS_SUCCESS; + } + } + + ZwDeleteKey(KeyRoot); + +__Exit_DsmpRegDeleteTree: + + if (srcSubKey) { + ZwClose(srcSubKey); + } + + if (keyFullInfo) { + DsmpFreePool(keyFullInfo); + } + + if (subKey) { + DsmpFreePool(subKey); + } + + if (keyBasicInfo) { + DsmpFreePool(keyBasicInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Exiting function with status %x.\n", + KeyRoot, + status)); + + return status; +} + + +#if defined (_WIN64) +VOID +DsmpPassThroughPathTranslate32To64( + _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64 + ) +/*++ + +Routine Description: + + On WIN64, the SCSI_PASS_THROUGH field of the MPIO_PASS_THROUGH_PATH structure + sent down by a 32-bit application must be marshaled into a 64-bit version + of the structure. This function performs that marshaling. + +Arguments: + + MpioPassThroughPath32 - Supplies a pointer to a 32-bit MPIO_PASS_THROUGH_PATH + struct. + + MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH + structure, into which we'll copy the marshaled + 32-bit data. + +Return Value: + + None. + +--*/ +{ + // + // Copy the first set of fields out of the 32-bit structure. These + // fields all line up between the 32 & 64 bit versions. + // + // Note that we do NOT adjust the length in the SrbControl. This is to + // allow the calling routine to compare the length of the actual + // control area against the offsets embedded within. If we adjusted the + // length then requests with the sense area backed against the control + // area would be rejected because the 64-bit control area is 4 bytes + // longer. + // + RtlCopyMemory(MpioPassThroughPath64, + MpioPassThroughPath32, + FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); + + // + // Copy over the CDB. + // + RtlCopyMemory(MpioPassThroughPath64->PassThrough.Cdb, + MpioPassThroughPath32->PassThrough.Cdb, + 16 * sizeof(UCHAR) + ); + + // + // Copy over the rest of the fields of the structure. + // + MpioPassThroughPath64->Version = MpioPassThroughPath32->Version; + MpioPassThroughPath64->Length = MpioPassThroughPath32->Length; + MpioPassThroughPath64->Flags = MpioPassThroughPath32->Flags; + MpioPassThroughPath64->PortNumber = MpioPassThroughPath32->PortNumber; + MpioPassThroughPath64->MpioPathId = MpioPassThroughPath32->MpioPathId; + + // + // Copy the fields that follow the ULONG_PTR. + // + MpioPassThroughPath64->PassThrough.DataBufferOffset = (ULONG_PTR)MpioPassThroughPath32->PassThrough.DataBufferOffset; + MpioPassThroughPath64->PassThrough.SenseInfoOffset = MpioPassThroughPath32->PassThrough.SenseInfoOffset; + + return; +} + + +VOID +DsmpPassThroughPathTranslate64To32( + _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32 + ) +/*++ + +Routine Description: + + On WIN64, the SCSI_PASS_THROUGH field of MPIO_PASS_THROUGH_PATH structure + sent down by a 32-bit application must be marshaled into a 64-bit version + of the structure. This function marshals a 64-bit version of the structure + back into a 32-bit version. + +Arguments: + + MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH + struct. + + MpioPassThroughPath32 - Supplies the address of a pointer to a 32-bit + MPIO_PASS_THROUGH_PATH structure, into which we'll + copy the marshaled 64-bit data. + +Return Value: + + None. + +--*/ +{ + // + // Copy back the fields through the data offsets. + // + RtlCopyMemory(MpioPassThroughPath32, + MpioPassThroughPath64, + FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); + + + // + // Copy over the CDB. + // + RtlCopyMemory(MpioPassThroughPath32->PassThrough.Cdb, + MpioPassThroughPath64->PassThrough.Cdb, + 16 * sizeof(UCHAR) + ); + + // + // Copy over the rest of the fields of the structure. + // + MpioPassThroughPath32->Version = MpioPassThroughPath64->Version; + MpioPassThroughPath32->Length = MpioPassThroughPath64->Length; + MpioPassThroughPath32->Flags = MpioPassThroughPath64->Flags; + MpioPassThroughPath32->PortNumber = MpioPassThroughPath64->PortNumber; + MpioPassThroughPath32->MpioPathId = MpioPassThroughPath64->MpioPathId; + + return; +} +#endif + + +NTSTATUS +DsmpGetMaxPRRetryTime( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG RetryTime + ) +/*++ + +Routine Description: + + This routine is used to get the max time period for which a PR request failing + with a retry-able unit attention should be retried before failing back to MSCS. + The value is determined by querying the value found at + "msdsm\Parameters\DsmMaximumStateTransitionTime" + +Arguments: + + Context - The DSM Context value. + RetryTime - The output parameter that will receive the value to be used. + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + WCHAR registryKeyName[56] = {0}; + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetMaxPRRetryTime (DsmCtxt %p): Entering function.\n", + Context)); + + NT_ASSERT(RetryTime); + *RetryTime = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME; + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for the state transition time and + // the second which is the 'NULL' terminator. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME; + queryTable[0].EntryContext = RetryTime; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetMaxPRRetryTime (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryCacheInformationFromRegistry( + _In_ IN PDSM_CONTEXT DsmContext, + _Out_ OUT PBOOLEAN UseCacheForLeastBlocks, + _Out_ OUT PULONGLONG CacheSizeForLeastBlocks + ) +/*++ + +Routine Description: + + This routine is used to get the information about whether sequential IO + should use the same path when employing Least Blocks policy. + It also queries the size of cache set by the administrator. + The value is determined by querying the value found at + "msdsm\Parameters\DsmUseCacheForLeastBlocks" and + "msdsm\Parameters\DsmCacheSizeForLeastBlocks" + +Arguments: + + Context - The DSM Context value. + UseCacheForLeastBlocks - Returns the flag that indicates whether or not to + use same path for sequential IO when LB policy + is Least Blocks. + CacheSizeForLeastBlocks - Returns the size of the cache (in bytes) set by + the Admin to indicate the amount of sequential + data that should be use the same path when LB + policy is Least Blocks. + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; + WCHAR registryKeyName[56] = {0}; + HANDLE parametersKey = NULL; + UNICODE_STRING keyValueName; + NTSTATUS status; + struct _cacheSizeForLeastBlocks { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } cacheSizeForLeastBlocks; + ULONG length = 0; + BOOLEAN useCacheForLeastBlocksDefault = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Entering function.\n", + DsmContext)); + + NT_ASSERT(UseCacheForLeastBlocks); + NT_ASSERT(CacheSizeForLeastBlocks); + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for whether to use cache, and + // and the second which is the 'NULL' terminator. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_USE_CACHE_FOR_LEAST_BLOCKS; + queryTable[0].EntryContext = UseCacheForLeastBlocks; + queryTable[0].DefaultType = (REG_BINARY << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_BINARY; + queryTable[0].DefaultLength = sizeof(BOOLEAN); + queryTable[0].DefaultData = &useCacheForLeastBlocksDefault; + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + + if (NT_SUCCESS(status)) { + + status = DsmpOpenDsmServicesParametersKey(KEY_QUERY_VALUE, ¶metersKey); + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_CACHE_SIZE_FOR_LEAST_BLOCKS); + + status = ZwQueryValueKey(parametersKey, + &keyValueName, + KeyValuePartialInformation, + &cacheSizeForLeastBlocks, + sizeof(cacheSizeForLeastBlocks), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(cacheSizeForLeastBlocks.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + *CacheSizeForLeastBlocks = *((ULONGLONG UNALIGNED *)&(cacheSizeForLeastBlocks.KeyValueInfo.Data)); + } + } + + if (parametersKey) { + ZwClose(parametersKey); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + +BOOLEAN +DsmpConvertSharedSpinLockToExclusive( + _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock + ) +/*++ + +Routine Description: + + This routine is a wrapper around ExTryConvertSharedSpinLockExclusive() that + guarantees the given EX_SPIN_LOCK will be acquired in Exclusive mode once + this function returns. + + It's possible the lock may be released and re-acquired within this function + so the caller should be very careful about the use of this function. + + N.B. The caller MUST have acquired the given lock in Shared mode before + calling this function. + +Arguments: + + SpinLock - The EX_SPIN_LOCK to convert from Shared to Exclusive mode. + +Return Value: + + Status of the ExTryConvertSharedSpinLockExclusive() call. This function + will always return with the lock acquired in Exclusive mode. The FALSE is + returned, then the lock had to be released and re-acquired. + +--*/ +{ + BOOLEAN converted = FALSE; + + converted = (BOOLEAN)ExTryConvertSharedSpinLockExclusive(SpinLock); + + // + // If the conversion attempt failed, then we should release the lock from + // Shared mode and try to pick it back up in Exclusive mode to guarantee + // this function will always return with the lock in Exclusive mode. + // + if (converted == FALSE) { + ExReleaseSpinLockSharedFromDpcLevel(SpinLock); + ExAcquireSpinLockExclusiveAtDpcLevel(SpinLock); + } + + return converted; +} + + diff --git a/tests/projects/wdk/wdm/msdsm/wmi.c b/tests/projects/wdk/wdm/msdsm/wmi.c new file mode 100644 index 000000000..0ee882192 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/wmi.c @@ -0,0 +1,3822 @@ + +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + wmi.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM). + It exports behaviours that mpio.sys will use to determine how to + multipath SPC-3 compliant devices. + + This file contains WMI related functions. + +Environment: + + kernel mode only + +Notes: + +--*/ + + + +#include "precomp.h" +#include "msdsmwmi.h" +#include "msdsmdsm.h" + +#ifdef DEBUG_USE_WPP +#include "wmi.tmh" +#endif + +#pragma warning (disable:4305) + +extern BOOLEAN DoAssert; + +#define USE_BINARY_MOF_RESOURCE + +#define DSM_INVALID_LOAD_BALANCE_POLICY STATUS_INVALID_PARAMETER +#define DSM_UNSUPPORTED_VERSION STATUS_NOT_SUPPORTED + +// +// Max length for each of the DeviceId strings (supported device list) +// NOTE: This must be kept in sync with msdsmdsm.mof +// +#define MSDSM_MAX_DEVICE_ID_LENGTH 31 +#define MSDSM_MAX_DEVICE_ID_SIZE (MSDSM_MAX_DEVICE_ID_LENGTH * sizeof(WCHAR)) + +// +// List of supported DSM-centric guids +// +GUID MSDSM_SUPPORTED_DEVICES_LISTGUID = MSDSM_SUPPORTED_DEVICES_LISTGuid; +GUID MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID = MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGuid; +GUID MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID = MSDSM_DEFAULT_LOAD_BALANCE_POLICYGuid; + +// +// Symbolic names for the DSM-centric guid indexes +// +#define MSDSM_SUPPORTED_DEVICES_LISTGUID_Index 0 +#define MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index 1 +#define MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index 2 + +WMIGUIDREGINFO MSDsmGuidList[] = { + { + &MSDSM_SUPPORTED_DEVICES_LISTGUID, + 1, + 0 + }, + + { + &MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID, + 1, + 0 + }, + + { + &MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID, + 1, + 0 + } +}; + +#define MSDsmGuidCount (sizeof(MSDsmGuidList) / sizeof(WMIGUIDREGINFO)) + +// +// List of supported Device-centric guids +// +GUID DSM_LBOperationsGUID = DSM_LB_OperationsGuid; +GUID DSM_QueryLBPolicyGUID = DSM_QueryLBPolicyGuid; +GUID DSM_QuerySupportedLBPoliciesGUID = DSM_QuerySupportedLBPoliciesGuid; +GUID DSM_QueryDsmUniqueIdGUID = DSM_QueryUniqueIdGuid; +GUID DSM_QueryLBPolicyV2GUID = DSM_QueryLBPolicy_V2Guid; +GUID DSM_QuerySupportedLBPoliciesV2GUID = DSM_QuerySupportedLBPolicies_V2Guid; +GUID MSDSM_DEVICE_PERFGUID = MSDSM_DEVICE_PERFGuid; +GUID MSDSM_WMI_METHODSGUID = MSDSM_WMI_METHODSGuid; + +// +// Symbolic names for the Device-centric guid indexes +// +#define DSM_LBOperationsGUID_Index 0 +#define DSM_QueryLBPolicyGUID_Index 1 +#define DSM_QuerySupportedLBPoliciesGUID_Index 2 +#define DSM_QueryDsmUniqueIdGUID_Index 3 +#define DSM_QueryLBPolicyV2GUID_Index 4 +#define DSM_QuerySupportedLBPoliciesV2GUID_Index 5 +#define MSDSM_DEVICE_PERFGuidIndex 6 +#define MSDSM_WMI_METHODSGuidIndex 7 + +WMIGUIDREGINFO DsmGuidList[] = { + { + &DSM_LBOperationsGUID, + 1, + 0 + }, + + { + &DSM_QueryLBPolicyGUID, + 1, + 0 + }, + + { + &DSM_QuerySupportedLBPoliciesGUID, + 1, + 0 + }, + + { + &DSM_QueryDsmUniqueIdGUID, + 1, + 0 + }, + + { + &DSM_QueryLBPolicyV2GUID, + 1, + 0 + }, + + { + &DSM_QuerySupportedLBPoliciesV2GUID, + 1, + 0 + }, + + { + &MSDSM_DEVICE_PERFGUID, + 1, + 0 + }, + + { + &MSDSM_WMI_METHODSGUID, + 1, + 0 + } +}; + +#define DsmGuidCount (sizeof(DsmGuidList) / sizeof(WMIGUIDREGINFO)) + +VOID +DsmpDsmWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiGlobalInfo, + _In_ IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine intializes the DSM-specific WmiGlobalInfo structure that is passed + back to MPIO during DriverEntry. + +Arguments: + + WmiGlobalInfo - WMI information structure to initialize. + RegistryPath - Registry path to the service key for this driver. + +Return Value: + + None + +--*/ +{ + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpDsmWmiInitialize (RegPath %ws): Entering function.\n", + RegistryPath->Buffer)); + + RtlZeroMemory(WmiGlobalInfo, sizeof(DSM_WMILIB_CONTEXT)); + + // + // Build the mof resource name. This tells wmi via the busdriver, + // where to find the mof data. This is found in the .rc. + // + RtlInitUnicodeString(&WmiGlobalInfo->MofResourceName, L"DsmMofResourceName"); + + // + // This will jam in the entry points and guids for supported WMI + // operations. SetDataBlock, SetDataItem, ExecuteMethod and FunctionControl are + // currently not needed, so leave them set to zero. + // + WmiGlobalInfo->GuidCount = MSDsmGuidCount; + WmiGlobalInfo->GuidList = MSDsmGuidList; + + WmiGlobalInfo->QueryWmiDataBlockEx = DsmGlobalQueryData; + WmiGlobalInfo->SetWmiDataBlockEx = DsmGlobalSetData; + + // + // Allocate a buffer for the reg. path. + // + WmiGlobalInfo->RegistryPath.Buffer = DsmpAllocatePool(NonPagedPoolNx, + RegistryPath->MaximumLength, + DSM_TAG_REG_PATH); + if (WmiGlobalInfo->RegistryPath.Buffer) { + + // + // Set maximum length of the new string and copy it. + // + WmiGlobalInfo->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + + RtlCopyUnicodeString(&WmiGlobalInfo->RegistryPath, RegistryPath); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "DsmpDsmWmiInitialize (RegPath %ws): Failed to allocate memory for Registry path in WmiGlobalInfo.\n", + RegistryPath->Buffer)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpDsmWmiInitialize (RegPath %ws): Exiting function.\n", + RegistryPath->Buffer)); + + return; +} + + +NTSTATUS +DsmGlobalQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _Out_writes_to_(BufferAvail, *DataLength) OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ) +/*++ + +Routine Description: + + This is the WMI query entry point for DSM-specific GUIDs. The index into the + GUID array is found and assuming the buffer is large enough, the data will be + copied over. + +Arguments: + + DsmContext - Global DSM Context + DsmIds - Dsm Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index of the data instance + InstanceCount - Number of instances + InstanceLengthArray - Array of ULONGs that indicate per-instance data lengths. + BufferAvail - Size of the buffer in which data is returned. + Buffer - Buffer in which the data is returned. + DataLength - Storage for the actual data length written. + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to + to return all the available data. + STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry + in the reginfo array. + STATUS_SUCCESS - On success. + +--*/ +{ + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(InstanceLengthArray); + UNREFERENCED_PARAMETER(InstanceCount); + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(DsmIds); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalQueryData (DsmContext %p): Entering function - GuidIndex %u.\n", + DsmContext, + GuidIndex)); + + // + // Check the GuidIndex - the index into the DsmGuildList array - to see + // whether this is a supported GUID or not. + // + switch(GuidIndex) { + + case MSDSM_SUPPORTED_DEVICES_LISTGUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQuerySupportedDevicesList(DsmContext, + BufferAvail, + DataLength, + Buffer); + + break; + } + + case MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQueryTargetsDefaultPolicy(DsmContext, + BufferAvail, + DataLength, + Buffer); + + break; + } + + case MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQueryDsmDefaultPolicy(DsmContext, + BufferAvail, + DataLength, + Buffer); + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalQueryData (DsmContext %p): Unknown GuidIndex %d.\n", + DsmContext, + GuidIndex)); + + *DataLength = 0; + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalQueryData (DsmContext %p): Exiting function with status 0x%x.\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmGlobalSetData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG BufferAvail, + _In_reads_bytes_(BufferAvail) IN PUCHAR Buffer, + ... + ) +/*++ + +Routine Description: + + This is the WMI set entry point for DSM-specific GUIDs. The index into the + GUID array is found and the contents of the buffer are set to the passed in + instance index. + +Arguments: + + DsmContext - Global DSM Context + DsmIds - Dsm Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index of the data instance + BufferAvail - Size of the buffer in which data is returned. + Buffer - Buffer in which the data is returned. + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to + to return all the available data. + STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry + in the reginfo array. + STATUS_SUCCESS - On success. + +--*/ +{ + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + ULONG dataLength; + PDSM_CONTEXT dsmContext = (PDSM_CONTEXT)DsmContext; + + UNREFERENCED_PARAMETER(DsmIds); + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(InstanceIndex); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): Entering function - GuidIndex %u.\n", + DsmContext, + GuidIndex)); + + switch (GuidIndex) { + + case MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY targetsPolicyInfo = (PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + PMSDSM_TARGET_DEFAULT_POLICY_INFO targetPolicyInfo; + PWSTR vidpidIndex; + DSM_LOAD_BALANCE_TYPE loadBalancePolicy; + ULONGLONG preferredPath; + DWORD index; + NTSTATUS errorStatus = STATUS_SUCCESS; + + // + // Determine the correct buffer size. + // + dataLength = AlignOn8Bytes(FIELD_OFFSET(MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY, TargetDefaultPolicyInfo)); + + if (BufferAvail < dataLength) { + + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size. Status %x\n", + DsmContext, + GuidIndex, + status)); + break; + } + + dataLength += targetsPolicyInfo->NumberDevices * sizeof(MSDSM_TARGET_DEFAULT_POLICY_INFO); + + if (BufferAvail < dataLength) { + + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size for %u targets. Status %x\n", + DsmContext, + GuidIndex, + targetsPolicyInfo->NumberDevices, + status)); + break; + } + + targetPolicyInfo = targetsPolicyInfo->TargetDefaultPolicyInfo; + + for (index = 0; index < targetsPolicyInfo->NumberDevices; index++, targetPolicyInfo++) { + + size_t stringLength = 0; + + // + // First ensure that these values make sense. The VID/PID should be + // a string of 8+16 chars and the LB policy must be one that MSDSM + // supports. + // + // The WMI string is like a unicode string with the first USHORT + // containing the size. + // + vidpidIndex = targetPolicyInfo->HardwareId; + vidpidIndex++; + + if (!NT_SUCCESS(RtlStringCchLengthW(vidpidIndex, DSM_VENDPROD_ID_LEN + 1, &stringLength)) || (stringLength != DSM_VENDPROD_ID_LEN)) { + + errorStatus = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Ignoring incorrect VID/PID %ws. Status %x\n", + DsmContext, + GuidIndex, + vidpidIndex, + errorStatus)); + continue; + } + + if (targetPolicyInfo->LoadBalancePolicy >= DSM_LB_VENDOR_SPECIFIC) { + + errorStatus = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Ignoring policy %u for %ws. Status %x\n", + DsmContext, + GuidIndex, + targetPolicyInfo->LoadBalancePolicy, + vidpidIndex, + errorStatus)); + continue; + } + + loadBalancePolicy = targetPolicyInfo->LoadBalancePolicy; + preferredPath = (ULONGLONG)((ULONG_PTR)targetPolicyInfo->PreferredPath); + + // + // Now update/create the key in the registry with the LB policy info. + // If the LB policy is specified as 0, delete the key. + // + status = DsmpSetVidPidLBPolicyInRegistry(vidpidIndex, loadBalancePolicy, preferredPath); + + // + // If above was successful, find the group that corresponds to this + // targetId and update its LB policy as well as the states of the paths + // + if (NT_SUCCESS(status)) { + + DsmpSetLBForVidPidPolicyAdjustment(dsmContext, vidpidIndex, loadBalancePolicy, preferredPath); + } else { + errorStatus = status; + } + } + + // + // If any error occurred, return the last error. + // + if (!NT_SUCCESS(errorStatus)) { + status = errorStatus; + } + + break; + } + + case MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + PMSDSM_DEFAULT_LOAD_BALANCE_POLICY dsmPolicyInfo = (PMSDSM_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + DSM_LOAD_BALANCE_TYPE loadBalancePolicy; + ULONGLONG preferredPath; + + // + // Determine the correct buffer size. + // + dataLength = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); + + if (BufferAvail < dataLength) { + + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size. Status %x\n", + DsmContext, + GuidIndex, + status)); + break; + } + + loadBalancePolicy = dsmPolicyInfo->LoadBalancePolicy; + preferredPath = (ULONGLONG)((ULONG_PTR)dsmPolicyInfo->PreferredPath); + + // + // First ensure that the values make sense. + // + if (loadBalancePolicy >= DSM_LB_VENDOR_SPECIFIC) { + + status = STATUS_INVALID_PARAMETER; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Invalid policy %u specified. Status %x\n", + DsmContext, + GuidIndex, + loadBalancePolicy, + status)); + + } else { + + // + // Update/create the values in the registry with the LB policy info. + // If the LB policy is specified as 0, delete the values. + // + status = DsmpSetDsmLBPolicyInRegistry(loadBalancePolicy, preferredPath); + + // + // If above is successful, find the groups that haven't had their LB policy + // explicitly set or haven't had their policy set in accordance with target + // hardware id. For each of these, adjust the states of the paths as well. + // + if (NT_SUCCESS(status)) { + + DsmpSetLBForDsmPolicyAdjustment(dsmContext, loadBalancePolicy, preferredPath); + } + } + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): Unknown GuidIndex %d.\n", + DsmContext, + GuidIndex)); + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): Exiting function with status 0x%x.\n", + DsmContext, + status)); + + return status; +} + + +VOID +DsmpWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiInfo, + _In_ IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine intializes the Device-specific WmiInfo structure that is passed + back to MPIO during DriverEntry. + +Arguments: + + WmiInfo - WMI information structure to initialize. + RegistryPath - Registry path to the service key for this driver. + +Return Value: + + None + +--*/ +{ + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpWmiInitialize (RegPath %ws): Entering function.\n", + RegistryPath->Buffer)); + + RtlZeroMemory(WmiInfo, sizeof(DSM_WMILIB_CONTEXT)); + + // + // Build the mof resource name. This tells wmi via the busdriver, + // where to find the mof data. This is found in the .rc. + // + RtlInitUnicodeString(&WmiInfo->MofResourceName, L"MofResourceName"); + + // + // This will jam in the entry points and guids for supported WMI + // operations. SetDataBlock, SetDataItem, and FunctionControl are + // currently not needed, so leave them set to zero. + // + WmiInfo->GuidCount = DsmGuidCount; + WmiInfo->GuidList = DsmGuidList; + + WmiInfo->QueryWmiDataBlockEx = DsmQueryData; + WmiInfo->ExecuteWmiMethodEx = DsmExecuteMethod; + + // + // Allocate a buffer for the reg. path. + // + WmiInfo->RegistryPath.Buffer = DsmpAllocatePool(NonPagedPoolNx, + RegistryPath->MaximumLength, + DSM_TAG_REG_PATH); + if (WmiInfo->RegistryPath.Buffer) { + + // + // Set maximum length of the new string and copy it. + // + WmiInfo->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + + RtlCopyUnicodeString(&WmiInfo->RegistryPath, RegistryPath); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "DsmpWmiInitialize (RegPath %ws): Failed to allocate memory for Registry path in WMIInfo.\n", + RegistryPath->Buffer)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpWmiInitialize (RegPath %ws): Exiting function.\n", + RegistryPath->Buffer)); + + return; +} + + +NTSTATUS +DsmQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _When_(GuidIndex == DSM_LBOperationsGUID_Index || GuidIndex == MSDSM_WMI_METHODSGuidIndex, _Pre_notnull_ _Const_) + _When_(!(GuidIndex == DSM_LBOperationsGUID_Index || GuidIndex == MSDSM_WMI_METHODSGuidIndex), _Out_writes_to_(BufferAvail, *DataLength)) + OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ) +/*++ + +Routine Description: + + This is the main WMI query entry point. The index into the GUID array is found + and assuming the buffer is large enough, the data will be copied over. + +Arguments: + + DsmContext - Global DSM Context + DsmIds - Dsm Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index of the data instance + InstanceCount - Number of instances + InstanceLengthArray - Array of ULONGs that indicate per-instance data lengths. + BufferAvail - Size of the buffer in which data is returned. + Buffer - Buffer in which the data is returned. + DataLength - Storage for the actual data length written. + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to + to return all the available data. + STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry + in the reginfo array. + STATUS_SUCCESS - On success. + +--*/ +{ + ULONG sizeNeeded; + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(InstanceCount); + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(Irp); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Entering function - GuidIndex %u.\n", + DsmIds, + GuidIndex)); + + // + // Check the GuidIndex - the index into the DsmGuildList array - to see + // whether this is a supported GUID or not. + // + switch(GuidIndex) { + + case DSM_LBOperationsGUID_Index: { + + // + // Even though this class only has methods, we need to respond + // to any queries for it since WMI expects that there is an actual + // instance of the class on which to execute the method + // + + sizeNeeded = sizeof(ULONG); + + *DataLength = sizeNeeded; + + if (BufferAvail >= sizeNeeded) { + + *InstanceLengthArray = sizeNeeded; + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Buffer too small in query data. Needed %d, Given %d.\n", + DsmIds, + sizeNeeded, + BufferAvail)); + + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case DSM_QueryLBPolicyGUID_Index: + case DSM_QueryLBPolicyV2GUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQueryLoadBalancePolicy(DsmContext, + DsmIds, + ((GuidIndex == DSM_QueryLBPolicyGUID_Index) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2), + BufferAvail, + DataLength, + Buffer); + break; + } + + case DSM_QuerySupportedLBPoliciesGUID_Index: + case DSM_QuerySupportedLBPoliciesV2GUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQuerySupportedLBPolicies(DsmContext, + DsmIds, + BufferAvail, + ((GuidIndex == DSM_QuerySupportedLBPoliciesGUID_Index) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2), + DataLength, + Buffer); + break; + } + + case DSM_QueryDsmUniqueIdGUID_Index: { + + PDSM_QueryUniqueId dsmQueryUniqueId; + + *DataLength = sizeof(DSM_QueryUniqueId); + + if (BufferAvail >= sizeof(DSM_QueryUniqueId)) { + + dsmQueryUniqueId = (PDSM_QueryUniqueId) Buffer; + dsmQueryUniqueId->DsmUniqueId = (ULONGLONG)((ULONG_PTR)DsmContext); + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Buffersize %d too small for Query Unique Id.\n", + DsmIds, + BufferAvail)); + + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case MSDSM_DEVICE_PERFGuidIndex: { + + *DataLength = BufferAvail; + + status = DsmpQueryDevicePerf(DsmContext, + DsmIds, + BufferAvail, + DataLength, + Buffer); + + break; + } + + case MSDSM_WMI_METHODSGuidIndex: { + + // + // Even though this class only has methods, we need to respond + // to any queries for it since WMI expects that there is an actual + // instance of the class on which to execute the method + // + + sizeNeeded = sizeof(ULONG); + + *DataLength = sizeNeeded; + + if (BufferAvail >= sizeNeeded) { + + *InstanceLengthArray = sizeNeeded; + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Buffer too small in query data. Needed %d, Given %d.\n", + DsmIds, + sizeNeeded, + BufferAvail)); + + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Unknown GuidIndex %d in DsmQueryData.\n", + DsmIds, + GuidIndex)); + + *DataLength = 0; + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Out_writes_bytes_(*OutBufferSize) OUT PVOID Buffer + ) +/*+++ + +Routine Description: + + This routine returns the current Load Balance policy settings + for the given device. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + DsmWmiVersion - version of the MPIO_DSM_Path class to use + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current Load Balance policy settings + is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + PDSM_GROUP_ENTRY groupEntry; + PDSM_DEVICE_INFO devInfo; + PDSM_DEVICE_INFO rtpgDeviceInfo = NULL; + ULONG inx; + ULONG sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PDSM_Load_Balance_Policy_V2 supportedLBPolicies; + PMPIO_DSM_Path_V2 dsmPath; + PDSM_FAILOVER_GROUP foGroup; + ULONG SpecialHandlingFlag = 0; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): No DSM Ids given in DsmpQueryLoadBalancePolicy.\n", + DsmIds)); + + *OutBufferSize = 0; + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpQueryLoadBalancePolicy; + } + + // + // Compute the size needed for returning LoadBalance policy information + // + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)); + sizeNeeded += (DsmIds->Count) * sizeof(MPIO_DSM_Path); + + } else { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)); + sizeNeeded += (DsmIds->Count * sizeof(MPIO_DSM_Path_V2)); + } + + if (*OutBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): Output buffer too small for QueryLBPolicy.\n", + DsmIds)); + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQueryLoadBalancePolicy; + } + + // + // Set the size of the data returned to user + // + *OutBufferSize = sizeNeeded; + + // + // Zero out the output buffer first + // + RtlZeroMemory(Buffer, sizeNeeded); + + devInfo = DsmIds->IdList[0]; + DSM_ASSERT(devInfo && devInfo->DeviceSig == DSM_DEVICE_SIG); + groupEntry = devInfo->Group; + + // + // Send down an RTPG to get the current state info if implicit transitions + // are supported, since the states may have changed from under us. + // Storages that support both implicit and explicit transitions that haven't + // allowed us to turn OFF their implicit transitions, may have also changed + // TPG states from under us. So do this for such storages also. + // + if (!DsmpIsSymmetricAccess(devInfo) && + devInfo->ALUASupport != DSM_DEVINFO_ALUA_EXPLICIT) { + + rtpgDeviceInfo = DsmpGetActivePathToBeUsed(groupEntry, FALSE, SpecialHandlingFlag); + + if (!rtpgDeviceInfo) { + + BOOLEAN sendTPG = FALSE; + + rtpgDeviceInfo = DsmpFindStandbyPathToActivateALUA(groupEntry, &sendTPG, SpecialHandlingFlag); + } + + if (rtpgDeviceInfo) { + + status = DsmpGetDeviceALUAState(DsmContext, rtpgDeviceInfo, NULL); + } + } + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // If an RTPG was sent down, update all the devInfo states. + // + if (NT_SUCCESS(status) && rtpgDeviceInfo) { + + DsmpAdjustDeviceStatesALUA(groupEntry, NULL, SpecialHandlingFlag); + } + + supportedLBPolicies = &(((PDSM_QueryLBPolicy_V2)Buffer)->LoadBalancePolicy); + supportedLBPolicies->Version = DSM_WMI_VERSION; + supportedLBPolicies->LoadBalancePolicy = groupEntry->LoadBalanceType; + supportedLBPolicies->DSMPathCount = DsmIds->Count; + dsmPath = supportedLBPolicies->DSM_Paths; + + // + // Indicate which path is active and which path(s) are standby paths + // + inx = 0; + while (inx < DsmIds->Count) { + + devInfo = (PDSM_DEVICE_INFO)DsmIds->IdList[inx]; + + dsmPath->PathWeight = devInfo->PathWeight; + dsmPath->Reserved = DSM_STATE_ACTIVE_OPTIMIZED_SUPPORTED; + + if (devInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->TargetPortGroup_State = DSM_DEV_NOT_USED_STATE; + } + + dsmPath->Reserved |= DSM_STATE_STANDBY_SUPPORTED; + + } else { + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->TargetPortGroup_State = devInfo->TargetPortGroup->AsymmetricAccessState; + dsmPath->TargetPortGroup_Preferred = devInfo->TargetPortGroup->Preferred; + dsmPath->TargetPortGroup_Identifier = devInfo->TargetPortGroup->Identifier; + + if (devInfo->TargetPort) { + + dsmPath->TargetPort_Identifier = devInfo->TargetPort->Identifier; + } + + if (groupEntry->Symmetric) { + + // + // For certain policies like FOO and RRWS, we need to be able to put + // path in standby. + // + dsmPath->Reserved |= DSM_STATE_STANDBY_SUPPORTED; + } + } + + dsmPath->Reserved |= devInfo->TargetPortGroup->ActiveUnoptimizedSupported ? DSM_STATE_ACTIVE_UNOPTIMIZED_SUPPORTED : 0; + dsmPath->Reserved |= devInfo->TargetPortGroup->StandBySupported ? DSM_STATE_STANDBY_SUPPORTED : 0; + dsmPath->Reserved |= devInfo->TargetPortGroup->UnavailableSupported ? DSM_STATE_UNAVAILABLE_SUPPORTED : 0; + } + + groupEntry = devInfo->Group; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->SymmetricLUA = groupEntry->Symmetric; + dsmPath->ALUASupport = devInfo->ALUASupport; + + } + + if (DsmpIsDeviceFailedState(devInfo->State) || !DsmpIsDeviceInitialized(devInfo)) { + + dsmPath->PrimaryPath = FALSE; + dsmPath->DsmPathId = 0; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->OptimizedPath = dsmPath->PreferredPath = FALSE; + dsmPath->FailedPath = TRUE; + } + + } else { + + foGroup = devInfo->FailGroup; + dsmPath->DsmPathId = (ULONGLONG)((ULONG_PTR)foGroup->PathId); + + if (DsmpIsDeviceStateActive(devInfo->State)) { + + dsmPath->PrimaryPath = TRUE; + } + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED || + devInfo->State == DSM_DEV_STANDBY) { + + dsmPath->OptimizedPath = TRUE; + } + + if (((ULONGLONG)((ULONG_PTR)(foGroup->PathId))) == (devInfo->Group->PreferredPath)) { + + dsmPath->PreferredPath = TRUE; + } + } + } + +#if DBG + if (!dsmPath->PrimaryPath && + !dsmPath->FailedPath) { + NT_ASSERT(groupEntry->LoadBalanceType != DSM_LB_ROUND_ROBIN && + groupEntry->LoadBalanceType != DSM_LB_WEIGHTED_PATHS && + groupEntry->LoadBalanceType != DSM_LB_DYN_LEAST_QUEUE_DEPTH && + groupEntry->LoadBalanceType != DSM_LB_LEAST_BLOCKS); + } +#endif + + dsmPath = DsmWmiVersion == DSM_WMI_VERSION_1 ? + (PVOID)((PUCHAR)dsmPath + sizeof(MPIO_DSM_Path)) : + (PVOID)((PUCHAR)dsmPath + sizeof(MPIO_DSM_Path_V2)); + + inx++; + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + +__Exit_DsmpQueryLoadBalancePolicy: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): Exiting with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpQuerySupportedLBPolicies( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG BufferAvail, + _In_ IN ULONG DsmWmiVersion, + _Out_ OUT PULONG OutBufferSize, + _Out_writes_to_(BufferAvail, *OutBufferSize) OUT PUCHAR Buffer + ) +/*+++ + +Routine Description: + + This routine returns the load balance policies supported by this DSM for the + given LUN (specified by the DsmIds). + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + BufferAvail - Size of buffer available. + DsmWmiVersion - Indicates which version of MPIO_DSMPath to use. + OutBufferSize - Size of the output buffer. + Buffer - Buffer in which the supported Load Balance policies are + returned, if the buffer is big enough. + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + PDSM_QuerySupportedLBPolicies_V2 supportedLBPolicies; + PDSM_Load_Balance_Policy_V2 dsmLBPolicy; + ULONG sizeNeeded; + ULONG policyCount; + ULONG inx; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN skipRR = FALSE; + PDSM_DEVICE_INFO devInfo = NULL; + PUCHAR endOfBuffer; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI,"DsmpQuerySupportedLBPolicies (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedLBPolicies (DsmIds %p): No DSM Ids given in DsmpQuerySupportedLBPolicies.\n", + DsmIds)); + + *OutBufferSize = 0; + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpQuerySupportedLBPolicies; + } + + devInfo = DsmIds->IdList[0]; + DSM_ASSERT(devInfo && devInfo->DeviceSig == DSM_DEVICE_SIG); + + policyCount = DSM_NUMBER_OF_LB_POLICIES; + + // + // Round Robin policy is not supported for arrays that are AAA. + // + if (!DsmpIsSymmetricAccess(devInfo)) { + + skipRR = TRUE; + policyCount--; + } + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_QuerySupportedLBPolicies, Supported_LB_Policies)); + sizeNeeded += policyCount * AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)); + + } else { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_QuerySupportedLBPolicies_V2, Supported_LB_Policies)); + sizeNeeded += policyCount * AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)); + } + + // + // Set the size of the data returned to user or needed but not provided. + // + *OutBufferSize = sizeNeeded; + + if (sizeNeeded > BufferAvail) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedLBPolicies (Buffer %p): Output buffer too small. Size needed = %u.\n", + Buffer, + sizeNeeded)); + + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQuerySupportedLBPolicies; + } + + endOfBuffer = Buffer + sizeNeeded - 1; + + // + // Zero out the output buffer first + // + supportedLBPolicies = (PDSM_QuerySupportedLBPolicies_V2)Buffer; + RtlZeroMemory(Buffer, sizeNeeded); + + supportedLBPolicies->SupportedLBPoliciesCount = policyCount; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmLBPolicy = &(supportedLBPolicies->Supported_LB_Policies[0]); + + } else { + + dsmLBPolicy = (PVOID)&(((PDSM_QuerySupportedLBPolicies)supportedLBPolicies)->Supported_LB_Policies[0]); + } + + // + // All Load Balance policies are supported in Windows Server 2003 + // and above. + // + for (inx = 0; inx < DSM_NUMBER_OF_LB_POLICIES; inx++) { + + // + // Skip reporting Round Robin for AAA arrays. + // + if (((inx + 1) == DSM_LB_ROUND_ROBIN) && skipRR) { + + continue; + } + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if ((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)) - 1 > endOfBuffer) { + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + } else { + + if ((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)) - 1 > endOfBuffer) { + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + } + + dsmLBPolicy->Version = DSM_WMI_VERSION; + + // + // The value set for LoadBalancePolicy is based on + // the #define for LB policies in LBPolicy.h + // + dsmLBPolicy->LoadBalancePolicy = inx + 1; + + // + // Point to the next DSM_Load_Balance_Policy area + // + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmLBPolicy = (PDSM_Load_Balance_Policy_V2)((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths))); + + } else { + + dsmLBPolicy = (PVOID)((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths))); + } + } + +__Exit_DsmpQuerySupportedLBPolicies: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQuerySupportedLBPolicies (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmExecuteMethod( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG MethodId, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Inout_ IN OUT PUCHAR Buffer, + ... + ) +/*++ + +Routine Description: + + This routine handles the invocation of WMI methods defined in the DSM mof. + +Arguments: + + DsmContext - Global DSM context + DsmIds - DSM Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index value indicating for which instance data should be returned. + MethodId - Specifies which method to invoke. + InBufferSize - Buffer size, in bytes, of input parameter data. + OutBufferSize - Buffer size, in bytes, of output data. + Buffer - Buffer to which the data is read/written. + +Return Value: + + Status of the method, or STATUS_WMI_ITEMID_NOT_FOUND + +--*/ +{ + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(Irp); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // This should be the index for ExecMethod Index + // + if (GuidIndex == DSM_LBOperationsGUID_Index) { + + switch (MethodId) { + + case DsmSetLoadBalancePolicy: + case DsmSetLoadBalancePolicyALUA: { + + status = DsmpSetLoadBalancePolicy(DsmContext, + DsmIds, + (MethodId == DsmSetLoadBalancePolicy) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2, + InBufferSize, + OutBufferSize, + Buffer); + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Unknown MethodId %d in DsmExecuteMethod.\n", + DsmIds, + MethodId)); + + status = STATUS_WMI_ITEMID_NOT_FOUND; + + break; + } + } + } else if (GuidIndex == MSDSM_WMI_METHODSGuidIndex) { + + if (MethodId == MSDsmClearCounters) { + + status = DsmpClearPerfCounters(DsmContext, DsmIds); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Unknown MethodId %d for GuidIndex %d in DsmExecuteMethod.\n", + DsmIds, + MethodId, + GuidIndex)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Unknown GuidIndex %d in DsmExecuteMethod.\n", + DsmIds, + GuidIndex)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + +NTSTATUS +DsmpClearLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ) +/*++ + +Routine Description: + + This routine is called to clear the LUN-specific load balance policy for the given device. + + First, the routine will try to clear the "explicitly set" registry key for the device. If + this fails, the whole routine is aborted. + + If the registry key is successfully cleared, the following happens: + 1. Check to see if there is a target-wide load balance policy set for this device's VID/PID. + If yes, we set the device's load balance policy accordingly and return. + 2. Check to see if there is an MSDSM-wide load balance policy set. + If yes, we set the device's load balance policy accordingly and return. + 3. If steps 1 and 2 fall through, we set the device's load balance policy to RR, or RRWS if + ALUA is enabled. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + +Return Value: + + Appropriate status indicating the error if the input is malformed or + if the function was unable to clear the load balance policy. + STATUS_SUCCESS on success + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_DEVICE_INFO deviceInfo = NULL; + PDSM_GROUP_ENTRY group = NULL; + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + UNICODE_STRING subKeyName; + OBJECT_ATTRIBUTES objectAttributes; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + ULONG devInfoIndex; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // There should be at least one device + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpClearLoadBalancePolicy; + } + + deviceInfo = (PDSM_DEVICE_INFO)DsmIds->IdList[0]; + group = deviceInfo->Group; + + // + // First open LoadBalanceSettings key under the Services key + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DevName %ws): Failed to open LB Settings key. Status %x.\n", + group->RegistryKeyName, + status)); + + goto __Exit_DsmpClearLoadBalancePolicy; + } + + // + // Now open the key under which the LB settings for the given device is stored + // and clear the DsmLoadBalancePolicyExplicitlySet key. + // + RtlInitUnicodeString(&subKeyName, group->RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + UCHAR explicitlySet = FALSE; + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_POLICY_EXPLICITLY_SET, + REG_BINARY, + &explicitlySet, + sizeof(UCHAR)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DevName %ws): Failed to clear DsmLoadBalancePolicyExplicitlySet key.\n", + group->RegistryKeyName)); + + goto __Exit_DsmpClearLoadBalancePolicy; + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DevName %ws): Failed to open device subkey.\n", + group->RegistryKeyName)); + + goto __Exit_DsmpClearLoadBalancePolicy; + } + + + + // + // Set the defaults. These will be used if no target-wide or MSDSM-wide + // load balance policies are set. + // + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + loadBalanceType = DSM_LB_ROUND_ROBIN; + preferredPath = 0; + + // + // Check to see if target-wide (VID/PID) LB policy is set for this device. + // + status = DsmpQueryTargetLBPolicyFromRegistry(deviceInfo, + &loadBalanceType, + &preferredPath); + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // Since the policy hasn't been set for this VID/PID, check if + // overall MSDSM-wide policy has been set. + // + status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, + &preferredPath); + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpClearLoadBalancePolicy (DevInfo %p): Failed to query Dsm overall LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpClearLoadBalancePolicy (DevInfo %p): Failed to query VID/PID LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + + + // + // If the storage is ALUA enabled and we specified Round Robin, change + // it to Round Robin with Subset instead. + // + if (!DsmpIsSymmetricAccess(deviceInfo) && loadBalanceType == DSM_LB_ROUND_ROBIN) { + + loadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + // + // Finally set the load balance policy and the preferred path. + // + group->LoadBalanceType = loadBalanceType; + group->PreferredPath = preferredPath; + + // + // Update the path states in accordance with the new policy. + // + for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { + + DsmpSetNewDefaultLBPolicy(DsmContext, + group->DeviceList[devInfoIndex], + group->LoadBalanceType, + SpecialHandlingFlag); + } + +__Exit_DsmpClearLoadBalancePolicy: + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _In_ IN PVOID Buffer + ) +/*++ + +Routine Description: + + This routine is called to set the load balance policy for the given device. + + If zero is passed in as the load balance policy, the LUN-specific load balance + policy will attempt to be cleared. See DsmpClearLoadBalancePolicy for more details. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + DsmWmiVersion - version of the MPIO_DSM_Path class to use + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer for input\output data + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If the input buffer is too small + Appropriate status indicating the error if the input is malformed. + STATUS_SUCCESS on success + +--*/ + +{ + PDsmSetLoadBalancePolicyALUA_IN setLoadBalancePolicyIN = (PDsmSetLoadBalancePolicyALUA_IN) Buffer; + PDsmSetLoadBalancePolicyALUA_OUT setLoadBalancePolicyOUT = (PDsmSetLoadBalancePolicyALUA_OUT) Buffer; + PVOID supportedLBPolicies; + PMPIO_DSM_Path_V2 dsmPath; + ULONG inx = 0; + ULONG jnx; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN lengthOkay = TRUE; + PDSM_DEVICE_INFO devInfo = NULL; + PDSM_DEVICE_INFO tempDevInfo = NULL; + PDSM_GROUP_ENTRY groupEntry; + PDSM_LOAD_BALANCE_POLICY_SETTINGS savedLBSettings = NULL; + KIRQL irql; + BOOLEAN optimized = TRUE; + BOOLEAN preferred = FALSE; + ULONG activePaths = 0; + ULONG activeTPGs = 0; + ULONG numberDevInfoChanged = 0; + ULONG numberPreferredPaths = 0; + DSM_LOAD_BALANCE_TYPE loadBalancePolicy; + BOOLEAN sendSTPG = FALSE; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // There should be at least one device + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpSetLoadBalancePolicy; + } + + groupEntry = ((PDSM_DEVICE_INFO)DsmIds->IdList[0])->Group; + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + if (*OutBufferSize < sizeof(DsmSetLoadBalancePolicy_OUT)) { + + *OutBufferSize = sizeof(DsmSetLoadBalancePolicy_OUT); + lengthOkay = FALSE; + } + } else { + + if (*OutBufferSize < sizeof(DsmSetLoadBalancePolicyALUA_OUT)) { + + *OutBufferSize = sizeof(DsmSetLoadBalancePolicyALUA_OUT); + lengthOkay = FALSE; + } + } + + if (!lengthOkay) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Buffer too small for SetLBPolicy.\n", + DsmIds)); + + status = STATUS_BUFFER_TOO_SMALL; + goto __Exit_DsmpSetLoadBalancePolicy; + } + + *OutBufferSize = (DsmWmiVersion == DSM_WMI_VERSION_1) ? sizeof(DsmSetLoadBalancePolicy_OUT) : sizeof(DsmSetLoadBalancePolicyALUA_OUT); + + // + // If the user specified zero as the load balance policy, we need to clear the + // LUN-specific load balance policy. + // + if (setLoadBalancePolicyIN->LoadBalancePolicy.LoadBalancePolicy == 0) { + status = DsmpClearLoadBalancePolicy(DsmContext, DsmIds); + goto __Exit_DsmpSetLoadBalancePolicy; + } + + status = DsmpValidateSetLBPolicyInput(DsmContext, + DsmIds, + DsmWmiVersion, + Buffer, + InBufferSize); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Failed to validate input. Status %x.\n", + DsmIds, + status)); + + goto __Exit_DsmpSetLoadBalancePolicy; + } + + // + // At this point the Reserved field in each MPIO_DSM_Path should + // contain the respective Device Info + // + supportedLBPolicies = &(setLoadBalancePolicyIN->LoadBalancePolicy); + loadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->LoadBalancePolicy; + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Cache each DeviceInfo's current state. + // This will be used to rollback in case of errors. + // + DsmpSaveDeviceState(supportedLBPolicies, DsmWmiVersion); + + while (inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + optimized = TRUE; + preferred = FALSE; + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); + + optimized = dsmPath->OptimizedPath ? TRUE : FALSE; + preferred = dsmPath->PreferredPath ? TRUE : FALSE; + + if (preferred && loadBalancePolicy == DSM_LB_FAILOVER) { + + preferredPath = dsmPath->DsmPathId; + + if (preferredPath != 0) { + + numberPreferredPaths++; + } + + if (numberPreferredPaths > 1) { + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + } + + // + // Reserved field in MPIO_DSM_Path is set to DeviceInfo in + // DsmpValidateSetLBPolicyInput routine. + // + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (!devInfo) { + + inx++; + continue; + + } else { + + if (!tempDevInfo) { + + tempDevInfo = devInfo; + + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), NULL); + } + } + } + + if (!DsmpIsDeviceFailedState(devInfo->State)) { + + if (devInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + activeTPGs++; + } + + if (dsmPath->PrimaryPath) { + + // + // Optimized flag decides between AO and AU + // + if (optimized) { + + // + // For implicit-only ALUA, state cannot be explicitly changed to A/O + // + if (!DsmpIsSymmetricAccess(devInfo) && devInfo->ALUASupport == DSM_DEVINFO_ALUA_IMPLICIT) { + + // + // While we can mask off acutal A/O to be A/U, there is no + // way to explicitly make non-A/O state A/O + // + if (devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Can't make non-AO path A/O for Implicit-only transitions.\n", + DsmIds)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + + numberDevInfoChanged++; + + devInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + activePaths++; + + // + // Check to see if the actual making of this path state A/O + // will require an STPG to be sent down. + // + if (devInfo->TargetPortGroup && + devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED) { + + sendSTPG = TRUE; + } + + if (loadBalancePolicy == DSM_LB_FAILOVER) { + + // + // Only ONE path can be specified as AO for FailOverOnly policy. + // + if (activePaths > 1) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): More than one AO node given for FO Only.\n", + DsmIds)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + if (!groupEntry->PathToBeUsed) { + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)devInfo->FailGroup); + } + } + } else { + + // + // This is an ActiveUnoptimized path + // + devInfo->State = DSM_DEV_ACTIVE_UNOPTIMIZED; + + // + // For LB policy RR, WP, LB and LQD, all paths must be in A/O + // state. However, this is not possible for ALUA storages. + // For these storages, A/U is allowable only if that is the + // access state that the TPG is in. + // + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_WEIGHTED_PATHS || + loadBalancePolicy == DSM_LB_DYN_LEAST_QUEUE_DEPTH || + loadBalancePolicy == DSM_LB_LEAST_BLOCKS) { + + if (devInfo->TargetPortGroup && devInfo->ALUAState != DSM_DEV_ACTIVE_UNOPTIMIZED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) specified in A/U state when TPG is in %u state (for LB %u).\n", + DsmIds, + inx, + devInfo->ALUAState, + loadBalancePolicy)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + } + } else { + + if (optimized) { + + // + // This is a standby path + // + devInfo->State = DSM_DEV_STANDBY; + + } else { + + // + // This is unavailable path + // + devInfo->State = DSM_DEV_UNAVAILABLE; + } + + // + // For RR, LQD, LB and WP, all paths must be in A/O state for non-ALUA + // storage. For ALUA storage, the only time path states can be in + // S/B or U/A is if the TPG itself is in that state. + // + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_WEIGHTED_PATHS || + loadBalancePolicy == DSM_LB_DYN_LEAST_QUEUE_DEPTH || + loadBalancePolicy == DSM_LB_LEAST_BLOCKS) { + + if ((!devInfo->TargetPortGroup) || + (devInfo->TargetPortGroup && devInfo->State != devInfo->ALUAState)) { + + // + // No paths can be in SB or UA unless its TPG is in that state. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) specified in non-active state for LB %u.\n", + DsmIds, + inx, + loadBalancePolicy)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } else if (loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + // + // It is okay to set a path to be in S/B or U/A state in RRWS + // if either the storage is non-ALUA, or if the storage is + // ALUA but the TPG is in A/O (where it can be masked) or the + // TPG is in the state that the path is being set to. + // + if ((devInfo->TargetPortGroup) && + (devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED && devInfo->State != devInfo->ALUAState)) { + + // + // No paths can be in SB or UA unless its TPG is in that state. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) (in TPG state %u) can't be specified in non-active state for LB %u.\n", + DsmIds, + inx, + devInfo->ALUAState, + loadBalancePolicy)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + } + } + + inx++; + } + + if (NT_SUCCESS(status)) { + + + // + // If we arrive here, that means DsmpValidateSetLBPolicyInput already returned success. + // The device info is found. + // + _Analysis_assume_(tempDevInfo != NULL); + + // + // There must be at least one AO path. Unless there are no A/O TPGs. + // eg. During a controller failover, it is possible that the TPG through + // the TPG through other controller is still in non-A/O state and the + // storage supports implicit transitions and is still in the midst of + // making the transition of the non-A/O TPG to A/O. During such windows + // the states for all paths will be non-A/O and there's nothing that can + // be done about it. This is not an error condition. + // + if (!activePaths) { + + if ((tempDevInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) || + (tempDevInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED && activeTPGs)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): No active node given for LB %u.\n", + DsmIds, + loadBalancePolicy)); + + // + // Roll back to DeviceState to the state it was before + // processing this SetLB policy request + // + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + + status = STATUS_INVALID_PARAMETER; + } + } + } + + if (NT_SUCCESS(status)) { + + // + // If we arrive here, that means DsmpValidateSetLBPolicyInput already returned success. + // The device info is found. + // + _Analysis_assume_(tempDevInfo != NULL); + + // + // If device supports explicit transitions, we need to send down an + // STPG to enforce A/O path selection if we need to make a path in a + // non-A/O TPG active/optimized. + // + if (tempDevInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT && sendSTPG) { + + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; + + // + // Build the target port groups info to set the new states. + // Send down an STPG for TPG descriptors for those devInfos' TPGs + // that need to be in AO state. If this causes side-effects in + // state transitions (these can't be considered implicit according + // to the spec), fake the devInfo states to what was selected. + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + activePaths * sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + PDSM_DEVICE_INFO devInfoToUse = NULL; + + // + // Set the new asymmetric access states for the the devices' target port groups + // + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); + + for (inx = 0, jnx = 0; + inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount; + inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (!devInfo) { + + continue; + } + + if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + tpgDescriptor->AsymmetricAccessState = devInfo->State; + REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &devInfo->TargetPortGroup->Identifier); + + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)((PUCHAR)tpgDescriptor + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)); + + jnx++; + } + + if (devInfo->TempPreviousStateForLB == DSM_DEV_ACTIVE_OPTIMIZED) { + + devInfoToUse = devInfo; + } + } + + NT_ASSERT(jnx == numberDevInfoChanged); + NT_ASSERT(devInfoToUse); + + if (devInfoToUse) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + status = DsmpSetTargetPortGroups(devInfoToUse->TargetObject, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + if (NT_SUCCESS(status)) { + + DsmpFreePool(targetPortGroupsInfo); + targetPortGroupsInfo = NULL; + targetPortGroupsInfoLength = 0; + status = DsmpReportTargetPortGroups(devInfoToUse->TargetObject, + &targetPortGroupsInfo, + &targetPortGroupsInfoLength); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): STPG failed with status %x.\n", + DsmIds, + status)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + } + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + if (NT_SUCCESS(status)) { + + ULONG index; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; + + status = DsmpParseTargetPortGroupsInformation(DsmContext, + groupEntry, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + NT_ASSERT(NT_SUCCESS(status)); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroup = groupEntry->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + // + // Update TPGs with new state + // + for (inx = 0; + inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount; + inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (devInfo) { + + // + // An explicit state transition can cause TPGs that were not specified + // in the parameter list to also change (this is not considered to be + // an implicit transition. It is SPC3 behavior and we must take + // this into consideration and update the devInfo states. + // This is an unfortunate side-effect in that the Admin may not get + // the paths to be in the exact states that he has set. + // + if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + if (devInfo->ALUAState == DSM_DEV_ACTIVE_UNOPTIMIZED || + devInfo->ALUAState == DSM_DEV_STANDBY || + devInfo->ALUAState == DSM_DEV_UNAVAILABLE) { + + // + // An A/O TPG's devInfos can be masked as A/U. + // However, the reverse the is not true (ie. we can't + // mark a non-A/O TPG's devInfo(s) to be in A/O state. + // + devInfo->State = devInfo->ALUAState; + } + } + + // + // The devInfo->State has already been set. Update its previous state. + // + devInfo->PreviousState = devInfo->TempPreviousStateForLB; + } + } + + NT_ASSERT(jnx == numberDevInfoChanged); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Failed to allocate targetPortGroupsInfo.\n", + DsmIds)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + groupEntry->LoadBalanceType = loadBalancePolicy; + + if (loadBalancePolicy == DSM_LB_FAILOVER) { + + groupEntry->PreferredPath = preferredPath; + } + + savedLBSettings = DsmpCopyLoadBalancePolicies(groupEntry, + DsmWmiVersion, + supportedLBPolicies); + + } else { + + // + // Roll back to DeviceState to the state it was before + // processing this SetLB policy request + // + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + } + } + + if (NT_SUCCESS(status)) { + + // + // LUN's LB policy has been explicitly set by Admin + // + groupEntry->LBPolicySelection = DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT; + + // + // Update the states and if appropriate, the path weight + // + DsmpUpdateDesiredStateAndWeight(groupEntry, + DsmWmiVersion, + supportedLBPolicies); + + // + // Update the next path to be used for the group + // + devInfo = DsmpGetActivePathToBeUsed(groupEntry, + DsmpIsSymmetricAccess(tempDevInfo), + SpecialHandlingFlag); + if (devInfo != NULL) { + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)devInfo->FailGroup); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): After setting LB policy No FOG available for group %p\n", + DsmIds, + groupEntry)); + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), NULL); + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (NT_SUCCESS(status) && savedLBSettings) { + + DsmpPersistLBSettings(savedLBSettings); + + DsmpFreePool(savedLBSettings); + } + +__Exit_DsmpSetLoadBalancePolicy: + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + ((PDsmSetLoadBalancePolicy_OUT)setLoadBalancePolicyOUT)->Status = status; + + } else { + + setLoadBalancePolicyOUT->Status = status; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpValidateSetLBPolicyInput( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SetLoadBalancePolicyIN, + _In_ IN ULONG InBufferSize + ) +/*++ + +Routine Description: + + This routine validates the input buffer given for setting + Load Balance policy + +Arguements: + + DsmContext - DSM Global Context + DsmIds - DSM Ids for the given device + DsmWmiVersion - version of the MPIO_DSM_Path class to use + SetLoadBalancePolicyIN - Describes the load balance policy to be set + InBufferSize - Number of bytes in SetLoadBalancePolicyIN + +Return Value: + + STATUS_SUCCESS - if the input buffer is well formed + Appropriate error status if the input buffer is malformed. + +--*/ +{ + PDSM_Load_Balance_Policy_V2 supportedLBPolicies; + PMPIO_DSM_Path_V2 dsmPath0; + PMPIO_DSM_Path_V2 dsmPath1; + NTSTATUS status = STATUS_SUCCESS; + ULONG inx; + ULONG jnx; + ULONG sizeNeeded; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Validate the input buffer for setting Load Balance policy + // + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + sizeNeeded = FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths); + + } else { + + sizeNeeded = FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths); + } + + if (InBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Insufficient buffer in SetLB. Expected %d, Given %d.\n", + DsmIds, + sizeNeeded, + InBufferSize)); + + status = STATUS_BUFFER_TOO_SMALL; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + supportedLBPolicies = (PVOID)&(((PDsmSetLoadBalancePolicy_IN)SetLoadBalancePolicyIN)->LoadBalancePolicy); + + sizeNeeded += supportedLBPolicies->DSMPathCount * sizeof(MPIO_DSM_Path); + + } else { + + supportedLBPolicies = &(((PDsmSetLoadBalancePolicyALUA_IN)SetLoadBalancePolicyIN)->LoadBalancePolicy); + + sizeNeeded += supportedLBPolicies->DSMPathCount * sizeof(MPIO_DSM_Path_V2); + } + + if (InBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Insufficient buffer in SetLB. Expected %d, Given %d.\n", + DsmIds, + sizeNeeded, + InBufferSize)); + + status = STATUS_BUFFER_TOO_SMALL; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + if (supportedLBPolicies->Version > DSM_WMI_VERSION) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): WMI Version mismatch. Expected %d, Given %d.\n", + DsmIds, + DSM_WMI_VERSION, + supportedLBPolicies->Version)); + + status = DSM_UNSUPPORTED_VERSION; + goto __Exit_DsmpValidateSetLBPolicyInput; + + } else if (supportedLBPolicies->Version < DSM_WMI_VERSION) { + + ULONG dsmWmiVersion = DSM_WMI_VERSION; + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Use of older management app (WMI-Version %x) with newer DSM (WMI-Version %x).\n", + DsmIds, + supportedLBPolicies->Version, + dsmWmiVersion)); + + NT_ASSERT(supportedLBPolicies->Version == DSM_WMI_VERSION); + } + + if ((supportedLBPolicies->LoadBalancePolicy < DSM_LB_FAILOVER) || + (supportedLBPolicies->LoadBalancePolicy > DSM_LB_LEAST_BLOCKS)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Invalid LB Policy %d.\n", + DsmIds, + supportedLBPolicies->LoadBalancePolicy)); + + status = DSM_INVALID_LOAD_BALANCE_POLICY; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + // + // It is expected that the user provide LB policy settings + // for all the paths and not just a subset of the paths. + // + if (supportedLBPolicies->DSMPathCount != DsmIds->Count) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Path Count %d not equal to DSM IDs count %d.\n", + DsmIds, + supportedLBPolicies->DSMPathCount, + DsmIds->Count)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + // + // Make sure user did not provide duplicate path ids + // + for (inx = 0; inx < supportedLBPolicies->DSMPathCount && NT_SUCCESS(status); inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath0 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath0 = &(supportedLBPolicies->DSM_Paths[inx]); + } + + dsmPath0->Reserved = 0; + + for (jnx = 0; jnx < supportedLBPolicies->DSMPathCount; jnx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath1 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[jnx]); + + } else { + + dsmPath1 = &(supportedLBPolicies->DSM_Paths[jnx]); + } + + if ((inx != jnx) && + ((dsmPath0->DsmPathId == dsmPath1->DsmPathId) && (dsmPath1->DsmPathId != 0))) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Duplicate path id %I64x at %d and %d.\n", + DsmIds, + dsmPath0->DsmPathId, + inx, + jnx)); + + status = STATUS_INVALID_PARAMETER; + + break; + } + } + } + + if (NT_SUCCESS(status)) { + + PDSM_DEVICE_INFO devInfo; + PDSM_FAILOVER_GROUP foGroup; + PVOID pathId; + BOOLEAN foundPath; + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Make sure the user has provided path id corresponding + // to all the DSM IDs given to us. + // + for (inx = 0; inx < DsmIds->Count; inx++) { + + devInfo = DsmIds->IdList[inx]; + + if (!DsmpIsDeviceInitialized(devInfo)) { + + continue; + } + + foGroup = devInfo->FailGroup; + if (!foGroup) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): FO Group NULL for %p at index %d.\n", + DsmIds, + devInfo, + inx)); + + status = STATUS_INVALID_PARAMETER; + + break; + } + + foundPath = FALSE; + + for (jnx = 0; jnx < supportedLBPolicies->DSMPathCount; jnx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath0 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[jnx]); + + } else { + + dsmPath0 = &(supportedLBPolicies->DSM_Paths[jnx]); + } + + pathId = (PVOID) dsmPath0->DsmPathId; + if (foGroup->PathId == pathId) { + + // + // Found the device info corresponding to the given path. + // Use the reserved field in MPIO_DSM_Path to store + // the pointer to the device info. Device Info is used + // later on to set the load balance policy for the device. + // + foundPath = TRUE; + + dsmPath0->Reserved = (ULONG_PTR) devInfo; + + // + // If ALUA, RoundRobin is not an allowed LB policy since not all paths can + // be in A/O state. RRWS must be used instead. + // + if (supportedLBPolicies->LoadBalancePolicy == DSM_LB_ROUND_ROBIN && !DsmpIsSymmetricAccess(devInfo)) { + + status = DSM_INVALID_LOAD_BALANCE_POLICY; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Invalid LB policy for ALUA. Status %x.\n", + DsmIds, + status)); + } + + break; + } + } + + if (!foundPath) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Failed to find path %p for %p at index %d.\n", + DsmIds, + foGroup->PathId, + devInfo, + inx)); + + status = STATUS_INVALID_PARAMETER; + + break; + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + +__Exit_DsmpValidateSetLBPolicyInput: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +VOID +DsmpSaveDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ) +/*+++ + +Routine Description: + + This routine saves the current Load Balance policy settings. + If there is any error while setting the new policy given + by the user, the saved values will be used to restore + the old state. + + Note: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguements: + + SupportedLBPolicies - New Load Balance policy values + DsmWmiVersion - version of the MPIO_DSM_Path class to use + +Return Value: + + None +--*/ +{ + PDSM_DEVICE_INFO devInfo; + PMPIO_DSM_Path_V2 dsmPath; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSaveDeviceState (LBP %p): Entering function.\n", + SupportedLBPolicies)); + + inx = 0; + + while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (devInfo) { + + devInfo->TempPreviousStateForLB = devInfo->State; + } + + inx++; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSaveDeviceState (LBP %p): Exiting function.\n", + SupportedLBPolicies)); + + return; +} + + +VOID +DsmpRestorePreviousDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ) +/*++ + +Routine Description: + + This routine restores the old Load Balance policy settings. + If there is any error while setting the new policy given + by the user, the old state is restored from the saved state. + + Note: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguements: + + SupportedLBPolicies - New Load Balance policy values + DsmWmiVersion - version of the MPIO_DSM_Path class to use + +Return Value: + + None +--*/ +{ + PDSM_DEVICE_INFO devInfo; + PMPIO_DSM_Path_V2 dsmPath; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpRestorePreviousDeviceState (LBP %p): Entering function.\n", + SupportedLBPolicies)); + + inx = 0; + + while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (devInfo) { + + devInfo->State = devInfo->TempPreviousStateForLB; + } + + inx++; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpRestorePreviousDeviceState (LBP %p): Exiting function.\n", + SupportedLBPolicies)); + + return; +} + + +VOID +DsmpUpdateDesiredStateAndWeight( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ) +/*++ + +Routine Description: + + This routine updates the desired state and path weights + based on admin's LB selection. + + Note: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguements: + + Group - The group entry correponding to the pseudo-LUN. + SupportedLBPolicies - New Load Balance policy values + DsmWmiVersion - version of the MPIO_DSM_Path class to use + +Return Value: + + None +--*/ +{ + PMPIO_DSM_Path_V2 dsmPath; + PDSM_DEVICE_INFO devInfo; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpUpdatedDesiredState (Group %p): Entering function.\n", + Group)); + + inx = 0; + while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO) dsmPath->Reserved; + + if (!devInfo) { + + inx++; + continue; + } + + DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); + NT_ASSERT(devInfo->Group == Group); + + // + // We'll honor the chosen path for FOO for ALUA storage + // since we know for a fact that the Admin has chosen the path. + // We'll also honor path state in RRWS if it is different from TPG state + // as that too is an indication that it was explicitly selected. + // + if ((DsmpIsSymmetricAccess(devInfo)) || + (Group->LoadBalanceType == DSM_LB_FAILOVER) || + (!DsmpIsSymmetricAccess(devInfo) && Group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && devInfo->State != devInfo->ALUAState)) { + + // + // Check if this is the primary path or a standby path + // + if (dsmPath->PrimaryPath) { + + devInfo->DesiredState = DSM_DEV_ACTIVE_OPTIMIZED; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if (!dsmPath->OptimizedPath) { + + devInfo->DesiredState = DSM_DEV_ACTIVE_UNOPTIMIZED; + } + } + + } else { + + devInfo->DesiredState = DSM_DEV_STANDBY; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if (!dsmPath->OptimizedPath) { + + devInfo->DesiredState = DSM_DEV_UNAVAILABLE; + } + } + } + } else { + + devInfo->DesiredState = DSM_DEV_UNDETERMINED; + } + + if (Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) { + + devInfo->PathWeight = dsmPath->PathWeight; + } + + inx++; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpUpdatedDesiredState (Group %p): Exiting function.\n", + Group)); + + return; +} + + +NTSTATUS +DsmpQueryDevicePerf( + _In_ PDSM_CONTEXT DsmContext, + _In_ PDSM_IDS DsmIds, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine returns the perf counters for each path for the + device that corresponds to the passed in DsmIds. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current Load Balance policy settings + is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_DEVICE_INFO devInfo; + ULONG sizeNeeded; + PMSDSM_DEVICE_PERF devicePerf; + ULONG i; + PMSDSM_DEVICEPATH_PERF pathPerf; + KIRQL irql; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + *OutBufferSize = 0; + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpQueryDevicePerf; + } + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_DEVICE_PERF, PerfInfo)); + sizeNeeded += (DsmIds->Count * sizeof(MSDSM_DEVICEPATH_PERF)); + + if (*OutBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): Output buffer too small for QueryLBPolicy.\n", + DsmIds)); + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQueryDevicePerf; + } + + // + // Zero out the output buffer first + // + RtlZeroMemory(Buffer, sizeNeeded); + +#if DBG + devInfo = DsmIds->IdList[0]; + DSM_ASSERT(devInfo); + DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); +#endif + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + devicePerf = (PMSDSM_DEVICE_PERF)Buffer; + devicePerf->NumberPaths = DsmIds->Count; + + // + // For each path, get the stats info + // + for (i = 0; i < DsmIds->Count; i++) { + + pathPerf = &devicePerf->PerfInfo[i]; + devInfo = DsmIds->IdList[i]; + + if (DsmpIsDeviceInitialized(devInfo)) { + + pathPerf->PathId = (ULONGLONG)((ULONG_PTR)((devInfo->FailGroup)->PathId)); + pathPerf->NumberReads = (devInfo->DeviceStats).NumberReads; + pathPerf->NumberWrites = (devInfo->DeviceStats).NumberWrites; + pathPerf->BytesRead = (devInfo->DeviceStats).BytesRead; + pathPerf->BytesWritten = (devInfo->DeviceStats).BytesWritten; + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + *OutBufferSize = sizeNeeded; + +__Exit_DsmpQueryDevicePerf: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpClearPerfCounters( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ) +/*++ + +Routine Description: + + This routine clears the perf counters for each path for the + device that corresponds to the passed in DsmIds. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_DEVICE_INFO devInfo; + KIRQL irql; + ULONG i; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearPerfCounters (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearPerfCounters (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpClearPerfCounters; + } + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + for (i = 0; i < DsmIds->Count; i++) { + + devInfo = DsmIds->IdList[i]; + DSM_ASSERT(devInfo); + DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); + + if (devInfo) { + (devInfo->DeviceStats).BytesRead = 0; + (devInfo->DeviceStats).BytesWritten = 0; + (devInfo->DeviceStats).NumberReads = 0; + (devInfo->DeviceStats).NumberWrites = 0; + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + +__Exit_DsmpClearPerfCounters: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearPerfCounters (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpQuerySupportedDevicesList( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine returns the list of devices that are supported by MSDSM. + +Arguements: + + DsmContext - Global DSM context + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current Load Balance policy settings + is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + NTSTATUS status; + ULONG sizeNeeded; + PMSDSM_SUPPORTED_DEVICES_LIST supportedDeviceIds; + PWSTR szIndex; + PWSTR deviceIdIndex; + ULONG numberDeviceIds = 0; + ULONG index = 0; + KIRQL oldIrql; + PWSTR tempBuffer = NULL; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Entering function.\n", + DsmContext)); + + // + // It is possible that manually changes to the registry weren't yet picked up, + // so query for the list in its current state. Failure to get this list is not + // fatal, so ignore errors. + // +#if DBG + status = DsmpGetDeviceList(DsmContext); + NT_ASSERT(NT_SUCCESS(status)); +#else + DsmpGetDeviceList(DsmContext); +#endif + + // + // Since it is possible that this list may change if a new device arrival + // gets processed at the same time as this query being processed, we need + // to protect it. + // + KeAcquireSpinLock(&DsmContext->SupportedDevicesListLock, &oldIrql); + + tempBuffer = DsmpAllocatePool(NonPagedPoolNx, DsmContext->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED); + + if (tempBuffer) { + + RtlCopyMemory(tempBuffer, DsmContext->SupportedDevices.Buffer, DsmContext->SupportedDevices.Length); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Failed to allocate temporary list.\n", + DsmContext)); + + status = STATUS_INSUFFICIENT_RESOURCES; + KeReleaseSpinLock(&DsmContext->SupportedDevicesListLock, oldIrql); + + goto __Exit_DsmpQuerySupportedDevicesList; + } + + KeReleaseSpinLock(&DsmContext->SupportedDevicesListLock, oldIrql); + + status = STATUS_SUCCESS; + szIndex = tempBuffer; + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_SUPPORTED_DEVICES_LIST, DeviceId)); + + if (szIndex) { + + while (*szIndex) { + + szIndex += wcslen(szIndex) + 1; + numberDeviceIds++; + } + + sizeNeeded += numberDeviceIds * (MSDSM_MAX_DEVICE_ID_SIZE + sizeof(WNULL)); + } + + if (*OutBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Output buffer too small for QuerySupportedDevicesList.\n", + DsmContext)); + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQuerySupportedDevicesList; + } + + // + // Zero out the output buffer first + // + RtlZeroMemory(Buffer, sizeNeeded); + + *OutBufferSize = sizeNeeded; + + supportedDeviceIds = (PMSDSM_SUPPORTED_DEVICES_LIST)Buffer; + supportedDeviceIds->NumberDevices = numberDeviceIds; + + for (index = 0, szIndex = tempBuffer, deviceIdIndex = supportedDeviceIds->DeviceId; + index < numberDeviceIds; + index++, szIndex += wcslen(szIndex) + 1, deviceIdIndex += MSDSM_MAX_DEVICE_ID_LENGTH) { + + *((PUSHORT)deviceIdIndex) = MSDSM_MAX_DEVICE_ID_SIZE; + deviceIdIndex++; + + RtlStringCchCopyW(deviceIdIndex, + MSDSM_MAX_DEVICE_ID_LENGTH - 1, + szIndex); + } + +__Exit_DsmpQuerySupportedDevicesList: + + if (tempBuffer) { + DsmpFreePool(tempBuffer); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryTargetsDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is used to build the target list (for which the override default LB policy + was explicitly set), by querying the services key for the subkeys under + "msdsm\Parameters\DsmTargetsLoadBalanceSetting" + +Arguements: + + Context - The DSM Context value. It contains storage for the target hardware ids and their + default policy info. + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current targets whose default policy settings is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + ULONG sizeNeeded; + PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY targetsPolicyInfo = (PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + PMSDSM_TARGET_DEFAULT_POLICY_INFO targetPolicyInfo; + HANDLE targetsLBSettingKey = NULL; + NTSTATUS status; + PKEY_FULL_INFORMATION keyFullInfo = NULL; + ULONG length = sizeof(KEY_FULL_INFORMATION); + ULONG numSubKeys = 0; + WCHAR vidPid[25] = {0}; + PKEY_BASIC_INFORMATION keyBasicInfo = NULL; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE targetKey = NULL; + ULONG index = 0; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + PWCHAR policyInfoIndex; + UNICODE_STRING keyValueName; + PKEY_VALUE_PARTIAL_INFORMATION keyValueInfo = NULL; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Entering function.\n", + DsmContext)); + + status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to open Targets LB Setting key. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + // + // Query for number of subkeys + // + do { + if (keyFullInfo) { + + DsmpFreePool(keyFullInfo); + } + + keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for key full info.\n", + DsmContext)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + status = ZwQueryKey(targetsLBSettingKey, + KeyFullInformation, + keyFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query key. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + // + // Calculate total buffer size required + // + numSubKeys = keyFullInfo->SubKeys; + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY, TargetDefaultPolicyInfo)); + sizeNeeded += numSubKeys * sizeof(MSDSM_TARGET_DEFAULT_POLICY_INFO); + + if (*OutBufferSize < sizeNeeded) { + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Buffer insufficient. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + *OutBufferSize = sizeNeeded; + RtlZeroMemory(Buffer, *OutBufferSize); + + targetsPolicyInfo->NumberDevices = numSubKeys; + targetPolicyInfo = targetsPolicyInfo->TargetDefaultPolicyInfo; + + // + // Now Enumerate all of the subkeys + // + for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) { + + UNICODE_STRING targetName; + + if (targetKey) { + ZwClose(targetKey); + targetKey = NULL; + } + + length = sizeof(KEY_BASIC_INFORMATION); + + do { + if (keyBasicInfo) { + + DsmpFreePool(keyBasicInfo); + } + + keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + length, + DSM_TAG_REG_KEY_RELATED); + + if (!keyBasicInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for key basic info.\n", + DsmContext)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + // + // Enumerate the index'th subkey + // + status = ZwEnumerateKey(targetsLBSettingKey, + index, + KeyBasicInformation, + keyBasicInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + // + // Ignore errors - this is a best case effort. + // + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to enumerate sub key's info. Status %x.\n", + DsmContext, + status)); + + status = STATUS_SUCCESS; + continue; + } + + RtlZeroMemory(vidPid, sizeof(vidPid)); + RtlStringCbCopyNW(vidPid, sizeof(vidPid), keyBasicInfo->Name, keyBasicInfo->NameLength); + RtlInitUnicodeString(&targetName, vidPid); + + // + // Open a handle to the the target subkey. + // + InitializeObjectAttributes(&objectAttributes, + &targetName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + targetsLBSettingKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&targetKey, + KEY_ALL_ACCESS, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to open reg key %ws. Status %x.\n", + DsmContext, + vidPid, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = &loadBalanceType; + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + targetKey, + queryTable, + targetKey, + NULL); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query LB Policy for %ws - error %x.\n", + DsmContext, + vidPid, + status)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): LB Policy for %ws is %d.\n", + DsmContext, + vidPid, + loadBalanceType)); + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + + length = sizeof(KEY_VALUE_PARTIAL_INFORMATION); + + do { + DsmpFreePool(keyValueInfo); + keyValueInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + if (!keyValueInfo) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for keyValueInfo (PP). Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + status = ZwQueryValueKey(targetKey, + &keyValueName, + KeyValuePartialInformation, + keyValueInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(keyValueInfo->DataLength == sizeof(ULONGLONG)); + + preferredPath = *((ULONGLONG UNALIGNED *)keyValueInfo->Data); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): PreferredPath for %ws is %I64x.\n", + DsmContext, + vidPid, + preferredPath)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query PreferredPath for %ws. Status %x.\n", + DsmContext, + vidPid, + status)); + } + + // + // Copy over this target's policy info. + // + policyInfoIndex = targetPolicyInfo->HardwareId; + *((PUSHORT)policyInfoIndex) = MSDSM_MAX_DEVICE_ID_SIZE; + policyInfoIndex++; + RtlStringCchCopyW((PWSTR)policyInfoIndex, MSDSM_MAX_DEVICE_ID_LENGTH - 1, vidPid); + targetPolicyInfo->LoadBalancePolicy = loadBalanceType; + targetPolicyInfo->PreferredPath = preferredPath; + + targetPolicyInfo++; + } + } + +__Exit_DsmpQueryTargetsDefaultPolicy: + + if (targetKey) { + ZwClose(targetKey); + } + + if (targetsLBSettingKey) { + ZwClose(targetsLBSettingKey); + } + + if (keyBasicInfo) { + DsmpFreePool(keyBasicInfo); + } + + if (keyValueInfo) { + DsmpFreePool(keyValueInfo); + } + + if (keyFullInfo) { + DsmpFreePool(keyFullInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryDsmDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is used to return the override MSDSM-wide default LB policy + if it was explicitly set, by querying the services key at "msdsm\Parameters" + +Arguements: + + Context - The DSM Context value. It contains storage for the target hardware ids and their + default policy info. + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current MSDSM-wide default policy is returned, if the buffer + is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + PMSDSM_DEFAULT_LOAD_BALANCE_POLICY dsmPolicyInfo = (PMSDSM_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + NTSTATUS status; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Entering function.\n", + DsmContext)); + + if (*OutBufferSize < sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY)) { + + *OutBufferSize = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Buffer insufficient. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryDsmDefaultPolicy; + } + + *OutBufferSize = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); + RtlZeroMemory(Buffer, *OutBufferSize); + + status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, &preferredPath); + + if (NT_SUCCESS(status)) { + + dsmPolicyInfo->LoadBalancePolicy = loadBalanceType; + dsmPolicyInfo->PreferredPath = (ULONGLONG)((ULONG_PTR)preferredPath); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): LB policy = %u, Preferred path = %I64x.\n", + DsmContext, + dsmPolicyInfo->LoadBalancePolicy, + dsmPolicyInfo->PreferredPath)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Query for MSDSM-wide policy, status %x.\n", + DsmContext, + status)); + } + +__Exit_DsmpQueryDsmDefaultPolicy: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + diff --git a/tests/projects/wdk/wdm/msdsm/xmake.lua b/tests/projects/wdk/wdm/msdsm/xmake.lua new file mode 100644 index 000000000..66f09a9b1 --- /dev/null +++ b/tests/projects/wdk/wdm/msdsm/xmake.lua @@ -0,0 +1,27 @@ + +-- add modes: debug and release +add_rules("mode.debug", "mode.release") + +-- add target +target("msdsm") + + -- add rules + add_rules("wdk.wdm.driver") + + -- add flags for rule: wdk.tracewpp + add_values("wdk.tracewpp.flags", "-func:TracePrint((LEVEL,FLAGS,MSG,...))") + + -- add files + add_files("*.c", {rule = "wdk.tracewpp"}) + add_files("*.rc", "*.inf") + add_files("*.mof|msdsm.mof") + + -- add file msdsm.mof and modify default wdk.mof.header for this file + add_files("msdsm.mof", {values = {wdk_mof_header = "msdsmwmi.h"}}) + + -- set precompiled header + set_pcheader("precomp.h") + + -- add links + add_links("mpio", "ucrt") + diff --git a/tests/projects/wdk/wdm/perfcounters/kcs.c b/tests/projects/wdk/wdm/perfcounters/kcs.c new file mode 100644 index 000000000..9996addcb --- /dev/null +++ b/tests/projects/wdk/wdm/perfcounters/kcs.c @@ -0,0 +1,407 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + kcs.c + +Abstract: + + This module contains sample code to demonstrate how to provide + counter data from a kernel driver. + +Environment: + + Kernel mode only. + +--*/ + + +#include +#include "kcs.h" +#include "kcsCounters.h" + +#pragma code_seg("PAGE") + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD KcsUnload; + +NTSTATUS +KcsAddGeometricInstance ( + _In_ PPCW_BUFFER Buffer, + _In_ PCWSTR Name, + _In_ ULONG MinimalValue, + _In_ ULONG Amplitude + ) + +/*++ + +Routine Description: + + This utility function adds instance to the callback buffer. + +Arguments: + + Buffer - Data will be returned in this buffer. + + Name - Name of instances to be added. + + MinimalValue - Minimum value of the wave. + + Amplitude - Amplitude of the wave. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + ULONG Index; + LARGE_INTEGER Timestamp; + UNICODE_STRING UnicodeName; + GEOMETRIC_WAVE_VALUES Values; + + PAGED_CODE(); + + KeQuerySystemTime(&Timestamp); + + Index = (Timestamp.QuadPart / 10000000) % 10; + + Values.Triangle = MinimalValue + Amplitude * abs(5 - Index) / 5; + Values.Square = MinimalValue + Amplitude * (Index < 5); + + RtlInitUnicodeString(&UnicodeName, Name); + + return KcsAddGeometricWave(Buffer, &UnicodeName, 0, &Values); +} + +NTSTATUS NTAPI +KcsGeometricWaveCallback ( + _In_ PCW_CALLBACK_TYPE Type, + _In_ PPCW_CALLBACK_INFORMATION Info, + _In_opt_ PVOID Context + ) + +/*++ + +Routine Description: + + This function returns the list of counter instances and counter data. + +Arguments: + + Type - Request type. + + Info - Buffer for returned data. + + Context - Not used. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UnicodeName; + + UNREFERENCED_PARAMETER(Context); + + PAGED_CODE(); + + switch (Type) { + case PcwCallbackEnumerateInstances: + + // + // Instances are being enumerated, so we add them without values. + // + + RtlInitUnicodeString(&UnicodeName, L"Small Wave"); + Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + RtlInitUnicodeString(&UnicodeName, L"Medium Wave"); + Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + RtlInitUnicodeString(&UnicodeName, L"Large Wave"); + Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + break; + + case PcwCallbackCollectData: + + // + // Add values for 3 instances of Geometric Wave Counter Set. + // + + Status = KcsAddGeometricInstance(Info->CollectData.Buffer, + L"Small Wave", + 40, + 20); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = KcsAddGeometricInstance(Info->CollectData.Buffer, + L"Medium Wave", + 30, + 40); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = KcsAddGeometricInstance(Info->CollectData.Buffer, + L"Large Wave", + 20, + 60); + if (!NT_SUCCESS(Status)) { + return Status; + } + + break; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +KcsAddTrignometricInstance ( + _In_ PPCW_BUFFER Buffer, + _In_ PCWSTR Name, + _In_ ULONG MinimalValue, + _In_ ULONG Amplitude + ) + +/*++ + +Routine Description: + + This utility function adds instance to the callback buffer. + +Arguments: + + Buffer - Data will be returned in this buffer. + + Name - Name of instances to be added. + + MinimalValue - Minimum value of the wave. + + Amplitude - Amplitude of the wave. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + double Angle; + KFLOATING_SAVE FloatSave; + NTSTATUS Status; + LARGE_INTEGER Timestamp; + UNICODE_STRING UnicodeName; + TRIGNOMETRIC_WAVE_VALUES Values; + + PAGED_CODE(); + + Status = KeSaveFloatingPointState(&FloatSave); + if (!NT_SUCCESS(Status)) { + return Status; + } + + KeQuerySystemTime(&Timestamp); + + Angle = (double)(Timestamp.QuadPart / 400000) * (22/7) / 180; + + Values.Constant = MinimalValue; + Values.Cosine = (ULONG)(MinimalValue + Amplitude * cos(Angle)); + Values.Sine = (ULONG)(MinimalValue + Amplitude * sin(Angle)); + + KeRestoreFloatingPointState(&FloatSave); + + // + // Add instance name & values to the caller's buffer. + // + + RtlInitUnicodeString(&UnicodeName, Name); + + return KcsAddTrignometricWave(Buffer, &UnicodeName, 0, &Values); +} + +NTSTATUS NTAPI +KcsTrignometricWaveCallback ( + _In_ PCW_CALLBACK_TYPE Type, + _In_ PPCW_CALLBACK_INFORMATION Info, + _In_opt_ PVOID Context + ) + +/*++ + +Routine Description: + + This function returns the list of counter instances and counter data. + +Arguments: + + Type - Request type. + + Info - Buffer for returned data. + + Context - Not used. + +Return Value: + + NTSTATUS indicating if the function succeeded. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING UnicodeName; + + UNREFERENCED_PARAMETER(Context); + + PAGED_CODE(); + + switch (Type) { + case PcwCallbackEnumerateInstances: + RtlInitUnicodeString(&UnicodeName, L"default"); + Status = KcsAddTrignometricWave(Info->EnumerateInstances.Buffer, + &UnicodeName, + 0, + NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + break; + + case PcwCallbackCollectData: + + // + // Add values for Single Instance of Trignometirc Wave Counter Set. + // + + return KcsAddTrignometricInstance(Info->CollectData.Buffer, + L"default", + 50, + 30); + } + + return STATUS_SUCCESS; +} + +VOID +KcsUnload ( + _In_ PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This function unregisters countersets + +Arguments: + + DriverObject - Not used. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(DriverObject); + + PAGED_CODE(); + + // + // Unregister Countersets. + // + + KcsUnregisterGeometricWave(); + KcsUnregisterTrignometricWave(); +} + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This function registers countersets on initial loading of the driver. + +Arguments: + + DriverObject - Supplies the driver object of the driver being loaded. + + RegistryPath - Not used. + +Return Value: + + NTSTATUS indicating if driver was properly loaded. + +--*/ + +{ + NTSTATUS Status; + + UNREFERENCED_PARAMETER(RegistryPath); + + PAGED_CODE(); + + // + // Register Countersets. + // + + Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL); + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = KcsRegisterTrignometricWave(KcsTrignometricWaveCallback, NULL); + if (!NT_SUCCESS(Status)) { + KcsUnregisterTrignometricWave(); + return Status; + } + + // + // Success path - set up unload routine and return success. + // + + DriverObject->DriverUnload = KcsUnload; + + return STATUS_SUCCESS; +} + diff --git a/tests/projects/wdk/wdm/perfcounters/kcs.h b/tests/projects/wdk/wdm/perfcounters/kcs.h new file mode 100644 index 000000000..f575b816d --- /dev/null +++ b/tests/projects/wdk/wdm/perfcounters/kcs.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + kcs.h + +Abstract: + + This module contains sample code to demonstrate how to provide + counter data from a kernel driver. + +Environment: + + Kernel mode only. + +--*/ + +typedef struct _GEOMETRIC_WAVE_VALUES { + ULONG Square; + ULONG Triangle; +} GEOMETRIC_WAVE_VALUES, *PGEOMETRIC_WAVE_VALUES; + +typedef struct _TRIGNOMETRIC_WAVE_VALUES { + ULONG Constant; + ULONG Cosine; + ULONG Sine; +} TRIGNOMETRIC_WAVE_VALUES, *PTRIGNOMETRIC_WAVE_VALUES; \ No newline at end of file diff --git a/tests/projects/wdk/wdm/perfcounters/kcs.man b/tests/projects/wdk/wdm/perfcounters/kcs.man new file mode 100644 index 000000000..a5a16ffbd --- /dev/null +++ b/tests/projects/wdk/wdm/perfcounters/kcs.man @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/projects/wdk/wdm/perfcounters/kcs.rc b/tests/projects/wdk/wdm/perfcounters/kcs.rc new file mode 100644 index 000000000..7ebb6b97b --- /dev/null +++ b/tests/projects/wdk/wdm/perfcounters/kcs.rc @@ -0,0 +1 @@ +#include "kcsCounters.rc" diff --git a/tests/projects/wdk/wdm/perfcounters/xmake.lua b/tests/projects/wdk/wdm/perfcounters/xmake.lua new file mode 100644 index 000000000..fb0bc3d54 --- /dev/null +++ b/tests/projects/wdk/wdm/perfcounters/xmake.lua @@ -0,0 +1,19 @@ + +-- add modes: debug and release +add_rules("mode.debug", "mode.release") + +-- add target +target("kcs") + + -- add rules + add_rules("wdk.wdm.driver") + + -- add flags for rule: wdk.man + add_values("wdk.man.prefix", "Kcs") + add_values("wdk.man.resource", "kcsCounters.rc") + add_values("wdk.man.header", "kcsCounters.h") + add_values("wdk.man.counter_header", "kcsCounters_counters.h") + + -- add files + add_files("*.c", "*.rc", "*.man") + diff --git a/xmake/rules/wdk/inf/xmake.lua b/xmake/rules/wdk/inf/xmake.lua index 83d46db8e..7c3e37790 100644 --- a/xmake/rules/wdk/inf/xmake.lua +++ b/xmake/rules/wdk/inf/xmake.lua @@ -72,7 +72,7 @@ rule("wdk.inf") end local wdk = target:data("wdk") if wdk then - if wdk.kmdfver and (target:rule("wdk.kmdf.driver") or target:rule("wdk.kmdf.binary")) then + if wdk.kmdfver and (target:rule("wdk.kmdf.driver") or target:rule("wdk.kmdf.binary") or target:rule("wdk.wdm.driver") or target:rule("wdk.wdm.binary")) then table.insert(args, "-k") table.insert(args, wdk.kmdfver) elseif wdk.umdfver then diff --git a/xmake/rules/wdk/load.lua b/xmake/rules/wdk/load.lua index 3e8b54a9c..6af486a12 100644 --- a/xmake/rules/wdk/load.lua +++ b/xmake/rules/wdk/load.lua @@ -197,3 +197,92 @@ function kmdf_binary(target) target:add("links", "kernel32", "user32", "gdi32", "winspool", "comdlg32") target:add("links", "advapi32", "shell32", "ole32", "oleaut32", "uuid", "odbc32", "odbccp32", "setupapi") end + +-- load for wdm driver +function wdm_driver(target) + + -- get wdk + local wdk = target:data("wdk") + + -- set kind + target:set("kind", "binary") + + -- set filename: xxx.sys + target:set("filename", target:basename() .. ".sys") + + -- add defines + local arch = config.arch() + local kmdfver = wdk.kmdfver:split('%.') + if arch == "x64" then + target:add("defines", "_WIN64", "_AMD64_", "AMD64") + else + target:add("cxflags", "-Gz", {force = true}) + target:add("defines", "_X86_=1", "i386=1", "STD_CALL") + end + target:add("defines", "WIN32_LEAN_AND_MEAN=1", "_WIN32_WINNT=0x0A00", "WINVER=0x0A00", "WINNT=1", "NTDDI_VERSION=0x0A000004", "_WINDLL") + target:add("defines", "KMDF_VERSION_MAJOR=" .. kmdfver[1], "KMDF_VERSION_MINOR=" .. kmdfver[2], "KMDF_USING_NTSTATUS") + + -- add include directories + target:add("includedirs", path.join(wdk.includedir, wdk.sdkver, "km")) + target:add("includedirs", path.join(wdk.includedir, "wdf", "kmdf", wdk.kmdfver)) + + -- add link directories + target:add("linkdirs", path.join(wdk.libdir, wdk.sdkver, "km", arch)) + target:add("linkdirs", path.join(wdk.libdir, "wdf", "kmdf", arch, wdk.kmdfver)) + + -- add links + target:add("links", "BufferOverflowFastFailK", "ntoskrnl", "hal", "wmilib", "ntstrsafe") + + -- compile as kernel driver + target:add("cxflags", "-kernel", {force = true}) + target:add("ldflags", "-kernel", "-driver", {force = true}) + + -- set subsystem: native, TODO 10.00 + target:add("ldflags", "-subsystem:native,10.00", {force = true}) + + -- set default driver entry if does not exist + local entry = false + for _, ldflag in ipairs(target:get("ldflags")) do + ldflag = ldflag:lower() + if ldflag:find("[/%-]entry:") then + entry = true + break + end + end + if not entry then + target:add("ldflags", "-entry:GsDriverEntry" .. (is_arch("x86") and "@8" or ""), {force = true}) + end +end + +-- load for wdm binary +function wdm_binary(target) + + -- get wdk + local wdk = target:data("wdk") + + -- set kind + target:set("kind", "binary") + + -- add defines + local arch = config.arch() + local kmdfver = wdk.kmdfver:split('%.') + if arch == "x64" then + target:add("defines", "_WIN64", "_AMD64_", "AMD64") + else + target:add("defines", "_X86_=1", "i386=1", "STD_CALL") + end + target:add("defines", "WIN32_LEAN_AND_MEAN=1", "_WIN32_WINNT=0x0A00", "WINVER=0x0A00", "WINNT=1", "NTDDI_VERSION=0x0A000004", "_WINDLL") + target:add("defines", "KMDF_VERSION_MAJOR=" .. kmdfver[1], "KMDF_VERSION_MINOR=" .. kmdfver[2]) + + -- add include directories + target:add("includedirs", path.join(wdk.includedir, wdk.sdkver, "km")) + target:add("includedirs", path.join(wdk.includedir, "wdf", "kmdf", wdk.kmdfver)) + + -- add link directories + target:add("linkdirs", path.join(wdk.libdir, wdk.sdkver, "km", arch)) + target:add("linkdirs", path.join(wdk.libdir, "wdf", "kmdf", arch, wdk.kmdfver)) + + -- add links + target:add("links", "kernel32", "user32", "gdi32", "winspool", "comdlg32") + target:add("links", "advapi32", "shell32", "ole32", "oleaut32", "uuid", "odbc32", "odbccp32", "setupapi") +end diff --git a/xmake/rules/wdk/tracewpp/xmake.lua b/xmake/rules/wdk/tracewpp/xmake.lua index 382669584..1d86d3751 100644 --- a/xmake/rules/wdk/tracewpp/xmake.lua +++ b/xmake/rules/wdk/tracewpp/xmake.lua @@ -72,7 +72,7 @@ rule("wdk.tracewpp") -- init args local args = {} - if target:rule("wdk.kmdf.driver") then + if target:rule("wdk.kmdf.driver") or target:rule("wdk.wdm.driver") then table.insert(args, "-km") table.insert(args, "-gen:{km-WdfDefault.tpl}*.tmh") end diff --git a/xmake/rules/wdk/xmake.lua b/xmake/rules/wdk/xmake.lua index 2415e17e7..95e9b2a78 100644 --- a/xmake/rules/wdk/xmake.lua +++ b/xmake/rules/wdk/xmake.lua @@ -82,3 +82,26 @@ rule("wdk.kmdf.binary") target:data_add("wdk.cleanfiles", os.files(path.join(target:targetdir(), "*.dll"))) end) + +-- define rule: wdm driver +rule("wdk.wdm.driver") + + -- add rules + add_deps("wdk.inf", "wdk.man", "wdk.mc", "wdk.mof") + + -- on load + on_load(function (target) + import("load").wdm_driver(target) + end) + +-- define rule: wdm binary +rule("wdk.wdm.binary") + + -- add rules + add_deps("wdk.inf", "wdk.man", "wdk.mc", "wdk.mof") + + -- on load + on_load(function (target) + import("load").wdm_binary(target) + end) + -- cgit v1.3.1