|
|
/*++
Copyright (C) 2000-2001 Microsoft Corporation
Module Name:
hpdsm.c
Abstract:
This driver is the DSM for HP XP-256/512
Author:
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include <ntddk.h>
#include <stdio.h>
#include <stdarg.h>
#include "dsm.h"
#include "hpdsm.h"
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++
Routine Description:
This routine is called when the driver loads loads.
Arguments:
DriverObject - Supplies the driver object. RegistryPath - Supplies the registry path.
Return Value:
NTSTATUS
--*/ { DSM_INIT_DATA initData; WCHAR dosDeviceName[40]; UNICODE_STRING mpUnicodeName; PDEVICE_OBJECT deviceObject; PFILE_OBJECT fileObject; NTSTATUS status; PDSM_CONTEXT dsmContext; PDSM_MPIO_CONTEXT mpctlContext; PVOID buffer;
//
// Build the init data structure.
//
dsmContext = ExAllocatePool(NonPagedPool, sizeof(DSM_CONTEXT)); if (dsmContext == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(dsmContext, sizeof(DSM_CONTEXT));
buffer = &initData;
//
// Set-up the init data
//
initData.DsmContext = (PVOID)dsmContext; initData.InitDataSize = sizeof(DSM_INIT_DATA);
initData.DsmInquireDriver = HPInquire; initData.DsmCompareDevices = HPCompareDevices; initData.DsmSetDeviceInfo = HPSetDeviceInfo; initData.DsmGetControllerInfo = HPGetControllerInfo; initData.DsmIsPathActive = HPIsPathActive; initData.DsmPathVerify = HPPathVerify; initData.DsmInvalidatePath = HPInvalidatePath; initData.DsmRemoveDevice = HPRemoveDevice; initData.DsmRemovePath = HPRemovePath; initData.DsmReenablePath = HPBringPathOnLine;
initData.DsmCategorizeRequest = HPCategorizeRequest; initData.DsmBroadcastSrb = HPBroadcastRequest; initData.DsmSrbDeviceControl = HPSrbDeviceControl; initData.DsmSetCompletion = HPSetCompletion; initData.DsmLBGetPath = HPLBGetPath; initData.DsmInterpretError = HPInterpretError; initData.DsmUnload = HPUnload;
//
// Need to also set-up the WMI info. TODO
//
//
// Set the DriverObject. Used by MPIO for Unloading.
//
initData.DriverObject = DriverObject; RtlInitUnicodeString(&initData.DisplayName, L"HP FC-12 Device-Specific Module");
//
// Initialize the context objects.
//
KeInitializeSpinLock(&dsmContext->SpinLock); InitializeListHead(&dsmContext->GroupList); InitializeListHead(&dsmContext->DeviceList); InitializeListHead(&dsmContext->FailGroupList); ExInitializeNPagedLookasideList(&dsmContext->ContextList, NULL, NULL, 0, sizeof(COMPLETION_CONTEXT), 'MSDG', 0); //
// Build the mpctl name.
//
swprintf(dosDeviceName, L"\\DosDevices\\MPathControl"); RtlInitUnicodeString(&mpUnicodeName, dosDeviceName); //
// Get mpctl's deviceObject.
//
status = IoGetDeviceObjectPointer(&mpUnicodeName, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject); if (NT_SUCCESS(status)) { KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatus;
//
// Send the IOCTL to mpctl.sys to register ourselves.
//
DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_REGISTER, deviceObject, &initData, &initData, sizeof(DSM_INIT_DATA), sizeof(DSM_MPIO_CONTEXT), TRUE, &ioStatus); status = ioStatus.Status; ObDereferenceObject(fileObject); }
if (status == STATUS_SUCCESS) {
//
// Grab the context value passed back by mpctl.
//
mpctlContext = buffer; dsmContext->MPIOContext = mpctlContext->MPIOContext;
} else { DebugPrint((0, "HPDsm: Failed to register (%x)\n", status)); //
// Stay loaded, perhaps mpctl will come up later.
// Will need to implement a mechanism to poll for mpio to arrive.
//
status = STATUS_SUCCESS; }
return status; }
NTSTATUS HPInquire ( IN PVOID DsmContext, IN PDEVICE_OBJECT TargetDevice, IN PDEVICE_OBJECT PortObject, IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, OUT PVOID *DsmIdentifier ) { PDEVICE_INFO deviceInfo; PGROUP_ENTRY group; NTSTATUS status; ULONG deviceState; ULONG allocationLength; PHP_ENQUIRY enquiry; PHP_DAC_STATUS dacStatus; UCHAR majorRev; UCHAR minorRev; ULONG loadBal; PUCHAR vendorIndex; PUCHAR productIndex; BOOLEAN needInquiry = FALSE; UCHAR nativeSlot; UCHAR portNumber; UCHAR logicalPort; PUCHAR vendorId = "HP "; PUCHAR productId = "FCArray";
//
// Ensure that the device's serial number is present. If not, can't claim
// support for this drive.
//
if ((Descriptor->SerialNumberOffset == (ULONG)-1) || (Descriptor->SerialNumberOffset == 0)) {
// TODO : remove after FW update...
//
//return STATUS_NOT_SUPPORTED;
needInquiry = TRUE; } vendorIndex = (PUCHAR)Descriptor; productIndex = (PUCHAR)Descriptor; (ULONG_PTR)vendorIndex += Descriptor->VendorIdOffset; (ULONG_PTR)productIndex += Descriptor->ProductIdOffset; //
// Determine if the device is supported.
//
if ((!RtlEqualMemory(vendorId, vendorIndex, 8)) || (!RtlEqualMemory(productId, productIndex, 7))) { return STATUS_NOT_SUPPORTED; } //
// Allocate the descriptor. This is also used as DsmId.
//
allocationLength = sizeof(DEVICE_INFO); allocationLength += Descriptor->Size - sizeof(STORAGE_DEVICE_DESCRIPTOR); deviceInfo = ExAllocatePool(NonPagedPool, allocationLength); if (deviceInfo == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(deviceInfo, allocationLength); //
// Copy over the StorageDescriptor.
//
RtlCopyMemory(&deviceInfo->Descriptor, Descriptor, Descriptor->Size);
//
// Save the PortPdo Object.
//
deviceInfo->PortPdo = TargetDevice;
//
// Send the enquire command to get the FW revs. Based on the revision
// we can either do fail-over only or active lb.
//
enquiry = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(HP_ENQUIRY)); if (enquiry == NULL) { ExFreePool(deviceInfo); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(enquiry, sizeof(HP_ENQUIRY)); status = HPSendDirectCommand(TargetDevice, (PUCHAR)enquiry, sizeof(HP_ENQUIRY), DCMD_ENQUIRY);
if (NT_SUCCESS(status)) {
majorRev = enquiry->FwMajorRev; minorRev = enquiry->FwMinorRev;
DebugPrint((0, "HPDSM: FirmWare Major (%u) Minor (%u)\n", majorRev, minorRev)); //
// Free the buffer.
//
ExFreePool(enquiry); if (majorRev > 5 || (majorRev == 5 && minorRev >= 46)) {
//
// Supports dual active/active.
//
loadBal = LB_MIN_QUEUE;
} else if (majorRev == 5 && minorRev >= 41) {
//
// Fail-over only.
//
loadBal = LB_ACTIVE_PASSIVE; } else {
//
// Not supported. LOG.
//
ExFreePool(deviceInfo); return STATUS_NOT_SUPPORTED; } } else {
ExFreePool(enquiry); ExFreePool(deviceInfo); return status; } //
// Send the Get Controller Status command so that the port
// on which this device lives can be determined.
//
dacStatus = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(HP_DAC_STATUS)); if (dacStatus == NULL) { ExFreePool(deviceInfo); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(dacStatus, sizeof(HP_DAC_STATUS)); status = HPSendDirectCommand(TargetDevice, (PUCHAR)dacStatus, sizeof(HP_DAC_STATUS), DCMD_GET_DAC_STATUS);
if (NT_SUCCESS(status)) {
//
// Build a logical port value (1-4) based on the NativeSlot and PortNumber
// values. NativeSlot 0 or 1, and portNumber 0 or 1 for 4 possibilities.
// NativeSlot refers to the controller and portNumber to the port.
//
nativeSlot = dacStatus->DACInfo[0] >> 4; portNumber = (dacStatus->DACInfo[1] >> 5) & 0x01;
logicalPort = ((nativeSlot & 1) << 1) + (portNumber + 1);
//
// Set the port number for this device.
// Used to help create the FOGroups and as the index into
// the path array.
//
deviceInfo->Controller = logicalPort; } else {
ExFreePool(deviceInfo); return status; }
//
// Build the controller serial number.
// Bytes 40-47 of the inquiry data have the Node Name of Controller 0.
// Use this as a 64-bit identifier.
//
if (needInquiry) { CDB cdb; PINQUIRYDATA inquiryBuffer;
RtlZeroMemory(&cdb, sizeof(CDB)); cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; cdb.START_STOP.Start = 1; status = HPSendScsiCommand(TargetDevice, NULL, 0, 6, &cdb, 1); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; cdb.CDB6INQUIRY.AllocationLength = 56; inquiryBuffer = ExAllocatePool(NonPagedPoolCacheAligned, 56);
status = HPSendScsiCommand(TargetDevice, (PUCHAR)inquiryBuffer, 56, 6, &cdb, 1); if (NT_SUCCESS(status)) { UCHAR controllerSerialNumber[9]; UCHAR driveNumber[11];
RtlZeroMemory(controllerSerialNumber, 9); RtlZeroMemory(driveNumber, 11);
//
// Copy the serial number over into the deviceInfo.
// SerialNumber is built from the Node Name of the Controller +
// the SystemDrive Number.
//
RtlCopyMemory(controllerSerialNumber, &inquiryBuffer->VendorSpecific[4], 8); //
// Get the drive Number.
//
driveNumber[0] = inquiryBuffer->VendorSpecific[0]; driveNumber[1] = inquiryBuffer->VendorSpecific[1];
//
// cat the driveNumber & controller serial number into 10-byte binary value.
//
RtlCopyMemory(&driveNumber[2], controllerSerialNumber, 8);
//
// Convert to ascii.
//
HPConvertHexToAscii(driveNumber, deviceInfo->SerialNumber, 10);
DebugPrint((0, "HPInquiry: SerialNumber: %s\n", deviceInfo->SerialNumber));
}
} //
// See if there is an existing Muli-path group to which this belongs.
// (same serial number).
//
group = FindDevice(DsmContext, deviceInfo);
if (group == NULL) { //
// Build a multi-path group entry.
//
group = BuildGroupEntry(DsmContext, deviceInfo); if (group == NULL) { ExFreePool(deviceInfo); return STATUS_INSUFFICIENT_RESOURCES; }
//
// This is the first in the group, so make it the active
// device. The actual active/passive devices will be set-up
// later when the first call to LBGetPath is made.
//
deviceState = DEV_ACTIVE;
} else { //
// Already something active, this will be the fail-over
// device until the load-balance groups are set-up.
//
deviceState = DEV_PASSIVE; } //
// Add it to the list.
//
status = AddDeviceEntry(DsmContext, group, deviceInfo, deviceState); *DsmIdentifier = deviceInfo; return status; }
VOID HPConvertHexToAscii( IN PUCHAR HexString, IN OUT PUCHAR AsciiString, IN ULONG Count ) { ULONG i; ULONG j; UCHAR value; DebugPrint((0, "ConvertHexToAscii: ")); for (i = 0, j = 0; i < Count; i++, j++) { value = HexString[i]; DebugPrint((0, "%x ", value)); if (value <= 9) { AsciiString[j] = value + '0'; } else { AsciiString[j] = value - 10 + 'A'; } } DebugPrint((0,"\n")); }
BOOLEAN HPCompareDevices( IN PVOID DsmContext, IN PVOID DsmId1, IN PVOID DsmId2 ) { PDEVICE_INFO deviceInfo = DsmId1; PDEVICE_INFO comparedDevice = DsmId2; ULONG length; PUCHAR serialNumber; PUCHAR comparedSerialNumber;
//
// If this is an RS12, then no serial number in the device descriptor.
//
if (deviceInfo->Descriptor.SerialNumberOffset == (ULONG)-1) {
//
// Use the one's built from inquiry Data.
//
serialNumber = deviceInfo->SerialNumber; comparedSerialNumber = comparedDevice->SerialNumber; } else {
//
// Get the two serial numbers.
// They reside at SNOffset from the front of the
// descriptor buffer.
//
serialNumber = (PUCHAR)&deviceInfo->Descriptor; serialNumber += deviceInfo->Descriptor.SerialNumberOffset;
comparedSerialNumber = (PUCHAR)&comparedDevice->Descriptor; comparedSerialNumber += comparedDevice->Descriptor.SerialNumberOffset; }
//
// Get the length of the base-device Serial Number.
//
length = strlen(serialNumber);
//
// If the lengths match, compare the contents.
//
if (length == strlen(comparedSerialNumber)) { if (RtlEqualMemory(serialNumber, comparedSerialNumber, length)) { return TRUE; } } return FALSE; }
NTSTATUS HPSetDeviceInfo( IN PVOID DsmContext, IN PDEVICE_OBJECT TargetObject, IN PVOID DsmId, IN OUT PVOID *PathId ) { PDEVICE_INFO deviceInfo = DsmId; PGROUP_ENTRY group = deviceInfo->Group; PFAILOVER_GROUP failGroup; NTSTATUS status;
//
// TargetObject is the destination for any requests created by this driver.
// Save this for future reference.
//
deviceInfo->TargetObject = TargetObject;
//
// 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 = FindFOGroup(DsmContext, *PathId); //
// if not found, create a new f.o. group
//
if (failGroup == NULL) { failGroup = BuildFOGroup(DsmContext, DsmId, *PathId); if (failGroup == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } }
//
// add this deviceInfo to the f.o. group.
//
status = UpdateFOGroup(DsmContext, failGroup, deviceInfo); return status; }
NTSTATUS HPGetControllerInfo( IN PVOID DsmContext, IN PVOID DsmId, IN ULONG Flags, IN OUT PCONTROLLER_INFO *ControllerInfo ) { PCONTROLLER_INFO controllerInfo;
if (Flags & DSM_CNTRL_FLAGS_ALLOCATE) { controllerInfo = ExAllocatePool(NonPagedPool, sizeof(CONTROLLER_INFO)); if (controllerInfo == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(controllerInfo, sizeof(CONTROLLER_INFO));
//
// TODO Get the ID etc.
//
controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; *ControllerInfo = controllerInfo;
} else {
controllerInfo = *ControllerInfo;
//
// TODO Get the state.
//
controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; } return STATUS_SUCCESS; }
BOOLEAN HPIsPathActive( IN PVOID DsmContext, IN PVOID PathId ) { PFAILOVER_GROUP group; //
// NOTE: Internal callers of this assume certain behaviours. If it's changed,
// those functions need to be updated appropriately.
//
//
// Get the F.O. Group information.
//
group = FindFOGroup(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 ((group->Count >= 1) && (group->State == FG_NORMAL)) { return TRUE; } return FALSE; }
NTSTATUS HPPathVerify( IN PVOID DsmContext, IN PVOID DsmId, IN PVOID PathId ) { PDEVICE_INFO deviceInfo = DsmId; PFAILOVER_GROUP group; NTSTATUS status; ULONG i;
//
// Get the F.O. group
//
group = FindFOGroup(DsmContext, PathId); if (group == NULL) { return STATUS_DEVICE_NOT_CONNECTED; } //
// Check the Path state to ensure all is normal.
// Should be in FAILBACK state. This indicates that either
// an admin utility told us we are O.K. or the AutoRecovery detected
// the error was transitory.
// BUGBUG: Need to implement both of the above assumptions.
//
if ((group->Count >= 1) && group->State == FG_FAILBACK) { //
// Ensure that the device is still there
//
for (i = 0; i < group->Count; i++) { if (group->DeviceList[i] == deviceInfo) {
//
// Send it a TUR.
//
status = DsmSendTUR(deviceInfo->TargetObject); } } } else {
//
// What really has to happen:
// Ensure the device is in our structs
// Send it a TUR.
// Depending upon prior state - update to the new, appropriate state.
// return status.
//
status = STATUS_UNSUCCESSFUL; for (i = 0; i < group->Count; i++) { if (group->DeviceList[i] == deviceInfo) { status = DsmSendTUR(deviceInfo->TargetObject); } }
if (!NT_SUCCESS(status)) {
//
// Either the device is not in the group, or the TUR was not successful.
// TODO - Something.
//
} } //
// Update the group State, depending upon the outcome.
// TODO
//
ASSERT(status == STATUS_SUCCESS); if (status == STATUS_SUCCESS) {
//
// This lets the LBInit run to properly set-up this device.
//
deviceInfo->NeedsVerification = FALSE; }
return status; }
NTSTATUS HPInvalidatePath( IN PVOID DsmContext, IN ULONG ErrorMask, IN PVOID PathId, IN OUT PVOID *NewPathId ) { PFAILOVER_GROUP failGroup; PFAILOVER_GROUP hintPath; PGROUP_ENTRY group; PDEVICE_INFO deviceInfo; NTSTATUS status; ULONG i;
ASSERT((ErrorMask & DSM_FATAL_ERROR) || (ErrorMask & DSM_ADMIN_FO)); failGroup = FindFOGroup(DsmContext, PathId); //
// Mark the path as failed.
//
failGroup->State = FG_FAILED; //
// First interation, the hint will be NULL. This allows the
// GetNewPath routine the opportunity to select the best new path
// Subsequent calls will be fed the updated value.
//
hintPath = NULL;
//
// Process each device in the fail-over group
//
for (i = 0; i < failGroup->Count; i++) {
//
// Get the deviceInfo.
//
deviceInfo = failGroup->DeviceList[i];
//
// Set the state of the Failing Devicea
//
deviceInfo->State = DEV_FAILED;
//
// Get it's Multi-Path Group entry.
//
group = deviceInfo->Group;
//
// Get a new path for this failed device.
//
hintPath = SetNewPath(DsmContext, group, deviceInfo, hintPath); }
if (hintPath == NULL) {
//
// This indicates that no acceptable paths
// were found. Return the error to mpctl.
//
status = STATUS_NO_SUCH_DEVICE; *NewPathId = NULL;
} else {
//
// return the new path.
//
*NewPathId = hintPath->PathId; status = STATUS_SUCCESS; }
return status; }
NTSTATUS HPRemoveDevice( IN PVOID DsmContext, IN PVOID DsmId, IN PVOID PathId ) { PDSM_CONTEXT dsmContext = DsmContext; PDEVICE_INFO deviceInfo; PFAILOVER_GROUP failGroup; PGROUP_ENTRY group; ULONG state; WCHAR buffer[64];
//
// DsmId is our deviceInfo structure.
//
deviceInfo = DsmId; //
// Get it's Multi-Path Group entry.
//
group = deviceInfo->Group;
//
// Get the Fail-over group.
//
failGroup = deviceInfo->FailGroup;
//
// If it's active, need to 'Fail-Over' to another device in
// the group.
//
state = deviceInfo->State;
//
// Set the state of the Failing Devicea
//
deviceInfo->State = DEV_FAILED; if (state == DEV_ACTIVE) {
//
// Find the next available device.
// This is basically a fail-over for just
// this device.
//
SetNewPath(DsmContext, group, deviceInfo, NULL); }
//
// Remove it's entry from the Fail-Over Group.
//
RemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo);
//
// 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.
//
RemoveDeviceEntry(DsmContext, group, deviceInfo); swprintf(buffer, L"Removing Device"); DsmWriteEvent(dsmContext->MPIOContext, L"HpDsm", buffer, 2); return STATUS_SUCCESS; }
NTSTATUS HPRemovePath( IN PDSM_CONTEXT DsmContext, IN PVOID PathId ) { PFAILOVER_GROUP failGroup; KIRQL irql; failGroup = FindFOGroup(DsmContext, PathId);
if (failGroup == NULL) {
//
// It's already been removed.
// LOG though.
//
return STATUS_SUCCESS; }
//
// The claim is that a path won't be removed, until all
// the devices on it are.
//
ASSERT(failGroup->Count == 0);
KeAcquireSpinLock(&DsmContext->SpinLock, &irql); //
// Yank it from the list.
//
RemoveEntryList(&failGroup->ListEntry); DsmContext->NumberFOGroups--;
//
// Zero the entry.
//
RtlZeroMemory(failGroup, sizeof(FAILOVER_GROUP)); KeReleaseSpinLock(&DsmContext->SpinLock, irql);
//
// Free the allocation.
//
ExFreePool(failGroup);
return STATUS_SUCCESS; }
NTSTATUS HPBringPathOnLine( IN PVOID DsmContext, IN PVOID PathId, OUT PULONG DSMError ) { PFAILOVER_GROUP failGroup;
//
// PathVerify has been called already, so if
// it came back successfully, then this is O.K.
//
failGroup = FindFOGroup(DsmContext, PathId);
if (failGroup == NULL) {
//
// LOG
//
*DSMError = 0; return STATUS_DEVICE_NOT_CONNECTED; }
//
// Should be in FG_PENDING
//
ASSERT(failGroup->State == FG_PENDING);
//
// Indicate that it's ready to go.
//
failGroup->State = FG_NORMAL;
return STATUS_SUCCESS; }
PVOID HPLBGetPath( IN PVOID DsmContext, IN PSCSI_REQUEST_BLOCK Srb, IN PDSM_IDS DsmList, IN PVOID CurrentPath, OUT NTSTATUS *Status ) { PDEVICE_INFO deviceInfo; PGROUP_ENTRY group; PFAILOVER_GROUP failGroup = NULL; ULONG i;
//
// Up-front checking to minimally validate
// the list of DsmId's being passed in.
//
ASSERT(DsmList->Count); ASSERT(DsmList->IdList[0]);
//
// Grab the first device from the list.
//
deviceInfo = DsmList->IdList[0];
//
// Get the multi-path group.
//
group = deviceInfo->Group; //
// See if Load-Balancing has been initialized.
//
if (group->LoadBalanceInit == FALSE) { PDEVICE_INFO lbDevice; BOOLEAN doInit = TRUE; //
// Check to see whether we are really ready to run
// the LBInit. If any of the list aren't verified, then
// we will hold off.
//
for (i = 0; i < DsmList->Count; i++) { lbDevice = DsmList->IdList[i]; if (lbDevice->NeedsVerification) { DebugPrint((0, "LBGetPath: (%x) needs verify\n", lbDevice)); doInit = FALSE; break; } }
if (doInit) {
//
// Set-up the load-balancing. This routine
// builds a static assignment of multi-path group to
// a particular path.
//
LBInit(DsmContext, group); } } //
// Ensure that mpctl and this dsm are in sync.
//
ASSERT(DsmList->Count == group->NumberDevices); //
// Find the active device.
//
for (i = 0; i < DsmList->Count; i++) {
//
// Get each of the DsmId's, in reality the deviceInfo.
//
deviceInfo = DsmList->IdList[i]; //
// Ensure that the device is in our list.
//
ASSERT(FindDevice(DsmContext, deviceInfo)); //
// NOTE: This assumes 'static' Load-Balancing. Once others
// are implemented, this section will have to be updated.
//
// Return the path on which the ACTIVE device resides.
//
if (deviceInfo->State == DEV_ACTIVE) {
//
// Get the F.O.Group, as it contains the
// correct PathId for this device.
//
failGroup = deviceInfo->FailGroup;
*Status = STATUS_SUCCESS; return failGroup->PathId; } }
//
// Should never have gotten here.
//
DebugPrint((0, "LBGetPath: Returning STATUS_DEVICE_NOT_CONNECTED\n")); DbgBreakPoint(); ASSERT(failGroup); *Status = STATUS_DEVICE_NOT_CONNECTED; return NULL; }
ULONG HPCategorizeRequest( IN PVOID DsmContext, IN PDSM_IDS DsmIds, IN PIRP Irp, IN PSCSI_REQUEST_BLOCK Srb, IN PVOID CurrentPath, OUT PVOID *PathId, OUT NTSTATUS *Status ) { ULONG dsmStatus; NTSTATUS status;
//
// Requests to broadcast
// Reset
// Reserve
// Release
//
// Requests to Handle
// None for now.
//
//
// For all other requests, punt it back to the bus-driver.
// Need to get a path for the request first, so call the Load-Balance
// function.
//
*PathId = HPLBGetPath(DsmContext, Srb, DsmIds, CurrentPath, &status); if (NT_SUCCESS(status)) {
//
// 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.
//
Srb->SrbStatus = SRB_STATUS_NO_DEVICE; } //
// Pass back status info to mpctl.
//
*Status = status; return dsmStatus; }
NTSTATUS HPBroadcastRequest( IN PVOID DsmContext, IN PDSM_IDS DsmIds, IN PIRP Irp, IN PSCSI_REQUEST_BLOCK Srb, IN PKEVENT Event ) { //
// BUGBUG: Need to handle Reset, Reserve, and Release.
//
return STATUS_INVALID_DEVICE_REQUEST; }
NTSTATUS HPSrbDeviceControl( IN PVOID DsmContext, IN PDSM_IDS DsmIds, IN PIRP Irp, IN PSCSI_REQUEST_BLOCK Srb, IN PKEVENT Event ) { //
// BUGBUG: Need to handle ??
//
return STATUS_INVALID_DEVICE_REQUEST; }
VOID HPCompletion( IN PVOID DsmId, IN PIRP Irp, IN PSCSI_REQUEST_BLOCK Srb, IN PVOID DsmContext ) { PCOMPLETION_CONTEXT completionContext = DsmContext; PDEVICE_INFO deviceInfo; PDSM_CONTEXT dsmContext; UCHAR opCode;
//
// If it's read or write, save stats.
// Categorize set-up the Context to have path, target info.
// TODO
//
ASSERT(DsmContext);
dsmContext = completionContext->DsmContext; deviceInfo = completionContext->DeviceInfo; opCode = Srb->Cdb[0]; //
// Indicate one less request on this device.
//
InterlockedDecrement(&deviceInfo->Requests);
//
// TODO: Use the timestamp.
// Path/Device up-time, ave. time/request...
//
//
// If it's a read or a write, update the stats.
//
if (opCode == SCSIOP_READ) {
deviceInfo->Stats.NumberReads++; deviceInfo->Stats.BytesRead.QuadPart += Srb->DataTransferLength; } else if (opCode == SCSIOP_WRITE) { deviceInfo->Stats.NumberWrites++; deviceInfo->Stats.BytesWritten.QuadPart += Srb->DataTransferLength; } //
// Release the allocation.
//
ExFreeToNPagedLookasideList(&dsmContext->ContextList, DsmContext); }
VOID HPSetCompletion( IN PVOID DsmContext, IN PVOID DsmId, IN PIRP Irp, IN PSCSI_REQUEST_BLOCK Srb, IN OUT PDSM_COMPLETION_INFO DsmCompletion ) { PCOMPLETION_CONTEXT completionContext; PDSM_CONTEXT dsmContext = DsmContext; PDEVICE_INFO deviceInfo = DsmId; //
// Save the DeviceInfo as being the target for this request.
// Get a timestamp
// TODO Determine other data.
//
completionContext = ExAllocateFromNPagedLookasideList(&dsmContext->ContextList); if (completionContext == NULL) {
//
// LOG
//
}
//
// Time stamp this.
//
KeQueryTickCount(&completionContext->TickCount);
//
// Indicate the target for this request.
//
completionContext->DeviceInfo = deviceInfo; completionContext->DsmContext = DsmContext;
//
// Indicate one more request on this device.
// LB may use this.
//
InterlockedIncrement(&deviceInfo->Requests); DsmCompletion->DsmCompletionRoutine = HPCompletion; DsmCompletion->DsmContext = completionContext; return; }
ULONG HPInterpretError( IN PVOID DsmContext, IN PVOID DsmId, IN PSCSI_REQUEST_BLOCK Srb, IN OUT NTSTATUS *Status, OUT PBOOLEAN Retry ) { ULONG errorMask = 0; BOOLEAN failover = FALSE; BOOLEAN retry = FALSE; BOOLEAN handled = FALSE; //
// 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:
//
// The port pdo has either been removed or is
// very broken. A fail-over is necessary.
//
handled = TRUE; failover = TRUE; break; default: break; }
if (handled == FALSE) {
if (Srb) {
//
// 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.
//
failover = TRUE; break; default: break;
} } } if (failover) { DebugPrint((0, "InterpretError: Marking Fatal. Srb (%x). *Status (%x)\n", Srb, *Status)); errorMask = DSM_FATAL_ERROR; }
//
// TODO: Gather a list of status that indicate a retry is necessary.
// Look at InterpretSenseInfo.
//
*Retry = retry; return errorMask; }
NTSTATUS HPUnload( IN PVOID DsmContext ) {
//
// 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.
//
ExFreePool(DsmContext); return STATUS_SUCCESS; }
//
// Utility functions.
//
NTSTATUS HPSendDirectCommand( IN PDEVICE_OBJECT DeviceObject, IN PUCHAR Buffer, IN ULONG BufferSize, IN UCHAR Opcode ) { PSCSI_PASS_THROUGH_DIRECT passThrough; ULONG length; NTSTATUS status; PCDB cdb; IO_STATUS_BLOCK ioStatus;
DsmSendTUR(DeviceObject);
//
// Allocate the pass through plus sense info buffer.
//
length = sizeof(SCSI_PASS_THROUGH_DIRECT) + sizeof(SENSE_DATA); passThrough = ExAllocatePool(NonPagedPool, length); if (passThrough == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(passThrough, length); //
// These are always 10-byte CDB, guess on the timeout.
// Buffer is allocated by the caller and is it's responsibility to be correctly
// sized and aligned.
//
passThrough->Length = sizeof(SCSI_PASS_THROUGH_DIRECT); passThrough->CdbLength = 10; passThrough->SenseInfoLength = sizeof(SENSE_DATA); passThrough->DataIn = 1; passThrough->DataTransferLength = BufferSize; passThrough->TimeOutValue = 20; passThrough->DataBuffer = Buffer; passThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT); cdb = (PCDB)passThrough->Cdb;
//
// These are always 0x20.
//
cdb->CDB10.OperationCode = 0x20;
//
// The sub-code (DCMD OpCode)
//
cdb->CDB10.LogicalBlockByte0 = Opcode;
//
// Allocation length.
//
cdb->CDB10.TransferBlocksLsb = (UCHAR)(BufferSize & 0xFF); cdb->CDB10.TransferBlocksMsb = (UCHAR)(BufferSize >> 8);
//
// Submit the command.
//
DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT, DeviceObject, passThrough, passThrough, length, length, FALSE, &ioStatus);
status = ioStatus.Status;
// status = DsmSendPassThroughDirect(DeviceObject,
// passThrough,
// length,
// BufferSize);
if (!NT_SUCCESS(status)) {
//
// The call above has already 'interpreted' the senseInfo, but check
// to see if it is correct.
//
if (Opcode == 0x5) {
//
// No error conditions reported with this command, so trust the interpretation.
//
//
} }
if (passThrough->ScsiStatus) { DebugPrint((0, "SendDirect: ScsiStatus (%x)\n", passThrough->ScsiStatus)); } ExFreePool(passThrough); return status; }
NTSTATUS HPSendScsiCommand( IN PDEVICE_OBJECT DeviceObject, IN PUCHAR Buffer, IN ULONG BufferSize, IN ULONG CdbLength, IN PCDB Cdb, IN BOOLEAN DataIn ) { PSCSI_PASS_THROUGH_DIRECT passThrough; ULONG length; NTSTATUS status; IO_STATUS_BLOCK ioStatus;
//
// Allocate the pass through plus sense info buffer.
//
length = sizeof(SCSI_PASS_THROUGH_DIRECT) + sizeof(SENSE_DATA); passThrough = ExAllocatePool(NonPagedPool, length); if (passThrough == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(passThrough, length); //
// These are always 10-byte CDB, guess on the timeout.
// Buffer is allocated by the caller and is it's responsibility to be correctly
// sized and aligned.
//
passThrough->Length = sizeof(SCSI_PASS_THROUGH_DIRECT); passThrough->CdbLength = (UCHAR)CdbLength; passThrough->SenseInfoLength = sizeof(SENSE_DATA); passThrough->DataIn = DataIn; passThrough->DataTransferLength = BufferSize; passThrough->TimeOutValue = 20; passThrough->DataBuffer = Buffer; passThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT); RtlCopyMemory(passThrough->Cdb, Cdb, CdbLength);
//
// Submit the command.
//
DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT, DeviceObject, passThrough, passThrough, length, length, FALSE, &ioStatus);
status = ioStatus.Status;
// status = DsmSendPassThroughDirect(DeviceObject,
// passThrough,
// length,
// BufferSize);
return status; }
PGROUP_ENTRY FindDevice( IN PDSM_CONTEXT DsmContext, IN PDEVICE_INFO DeviceInfo ) { PDEVICE_INFO deviceInfo; PLIST_ENTRY entry; ULONG i;
//
// 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, DEVICE_INFO, ListEntry); ASSERT(deviceInfo);
//
// Call the Serial Number compare routine.
//
if (HPCompareDevices(DsmContext, DeviceInfo, deviceInfo)) {
return deviceInfo->Group; } } DebugPrint((0, "DsmFindDevice: DsmContext (%x), DeviceInfo (%x)\n", DsmContext, DeviceInfo)); return NULL; }
PGROUP_ENTRY BuildGroupEntry( IN PDSM_CONTEXT DsmContext, IN PDEVICE_INFO DeviceInfo ) { PGROUP_ENTRY group;
//
// Allocate the memory for the multi-path group.
//
group = ExAllocatePool(NonPagedPool, sizeof(GROUP_ENTRY)); if (group == NULL) { return NULL; }
RtlZeroMemory(group, sizeof(GROUP_ENTRY)); //
// Add it to the list of multi-path groups.
//
ExInterlockedInsertTailList(&DsmContext->GroupList, &group->ListEntry, &DsmContext->SpinLock);
group->GroupNumber = InterlockedIncrement(&DsmContext->NumberGroups);
ASSERT(group->GroupNumber >= 1); return group; }
NTSTATUS AddDeviceEntry( IN PDSM_CONTEXT DsmContext, IN PGROUP_ENTRY Group, IN PDEVICE_INFO DeviceInfo, IN ULONG DeviceState ) { ULONG numberDevices; ULONG i; KIRQL irql; //
// Ensure that this is a valid config - namely, it hasn't
// exceeded the number of paths supported.
//
numberDevices = Group->NumberDevices; if (numberDevices >= MAX_PATHS) { return STATUS_UNSUCCESSFUL; } KeAcquireSpinLock(&DsmContext->SpinLock, &irql); #if DBG
//
// 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) { DebugPrint((0, "DsmAddDeviceEntry: Received same PDO twice\n")); DbgBreakPoint(); } } #endif
//
// Indicate one device is present in
// this group.
//
Group->DeviceList[numberDevices] = DeviceInfo;
//
// Indicate one more in the list.
//
Group->NumberDevices++;
//
// Set-up this device's group id.
//
DeviceInfo->Group = Group;
//
// Set-up whether this is an active/passive member of the
// group.
//
DeviceInfo->State = DeviceState; //
// One more deviceInfo entry.
//
DsmContext->NumberDevices++; //
// Finally, add it to the global list of devices.
//
InsertTailList(&DsmContext->DeviceList, &DeviceInfo->ListEntry);
KeReleaseSpinLock(&DsmContext->SpinLock, irql); return STATUS_SUCCESS; }
VOID RemoveDeviceEntry( IN PDSM_CONTEXT DsmContext, IN PGROUP_ENTRY Group, IN PDEVICE_INFO DeviceInfo ) { KIRQL irql; NTSTATUS status; ULONG i; ULONG j; BOOLEAN freeGroup = FALSE;
KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
//
// 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.
//
Group->NumberDevices--;
//
// Collapse the array.
//
// BUGBUG: If any requests come in during this time, it's
// possible to either bugcheck or get an incorrect deviceInfo
// structure.
//
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; } }
//
// See if anything is left in the Group.
//
if (Group->NumberDevices == 0) {
//
// Yank it from the Group list.
//
RemoveEntryList(&Group->ListEntry); DsmContext->NumberGroups--;
//
// Zero it.
//
RtlZeroMemory(Group, sizeof(GROUP_ENTRY)); freeGroup = TRUE; }
//
// Yank the device out of the Global list.
//
RemoveEntryList(&DeviceInfo->ListEntry); DsmContext->NumberDevices--; //
// Zero it.
//
RtlZeroMemory(DeviceInfo, sizeof(DEVICE_INFO));
KeReleaseSpinLock(&DsmContext->SpinLock, irql); //
// Free the allocation.
//
ExFreePool(DeviceInfo);
if (freeGroup) {
//
// Free the allocation.
//
ExFreePool(Group); } }
PFAILOVER_GROUP FindFOGroup( IN PDSM_CONTEXT DsmContext, IN PVOID PathId ) { PFAILOVER_GROUP failOverGroup; PLIST_ENTRY entry; ULONG i;
//
// 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, FAILOVER_GROUP, ListEntry); ASSERT(failOverGroup);
//
// Check for a match of the PathId.
//
if (failOverGroup->PathId == PathId) { return failOverGroup; } } return NULL; }
PFAILOVER_GROUP BuildFOGroup( IN PDSM_CONTEXT DsmContext, IN PDEVICE_INFO DeviceInfo, IN PVOID PathId ) { PFAILOVER_GROUP failOverGroup; KIRQL irql; ULONG numberGroups; //
// Allocate an entry.
//
failOverGroup = ExAllocatePool(NonPagedPool, sizeof(FAILOVER_GROUP)); if (failOverGroup == NULL) { return NULL; }
RtlZeroMemory(failOverGroup, sizeof(FAILOVER_GROUP));
KeAcquireSpinLock(&DsmContext->SpinLock, &irql);
//
// Get the current number of groups, and add the one that's
// being created.
//
numberGroups = DsmContext->NumberFOGroups++; //
// Set the PathId - All devices on the same PathId will
// failover together.
//
failOverGroup->PathId = PathId; //
// Set the initial state to NORMAL.
//
failOverGroup->State = FG_NORMAL;
//
// Add it to the global list.
//
InsertTailList(&DsmContext->FailGroupList, &failOverGroup->ListEntry);
KeReleaseSpinLock(&DsmContext->SpinLock, irql);
return failOverGroup; }
NTSTATUS UpdateFOGroup( IN PDSM_CONTEXT DsmContext, IN PFAILOVER_GROUP FailGroup, IN PDEVICE_INFO DeviceInfo ) { PGROUP_ENTRY group; ULONG count; KIRQL irql;
KeAcquireSpinLock(&DsmContext->SpinLock, &irql); //
// Add the device to the list of devices that are on this path.
//
count = FailGroup->Count++; FailGroup->DeviceList[count] = DeviceInfo;
//
// Get the MultiPath group for this device.
//
group = DeviceInfo->Group;
//
// Indicate that the L.B. policy needs to be updated.
// The next call to LBGetPath will cause the re-shuffle to
// take place.
//
group->LoadBalanceInit = FALSE; DeviceInfo->NeedsVerification = TRUE;
//
// Set the device's F.O. Group.
//
DeviceInfo->FailGroup = FailGroup; KeReleaseSpinLock(&DsmContext->SpinLock, irql);
return STATUS_SUCCESS; }
VOID RemoveDeviceFailGroup( IN PDSM_CONTEXT DsmContext, IN PFAILOVER_GROUP FailGroup, IN PDEVICE_INFO DeviceInfo ) { ULONG count; KIRQL irql; ULONG i; ULONG j;
KeAcquireSpinLock(&DsmContext->SpinLock, &irql); //
// Find it's offset in the array of devices.
//
for (i = 0; i < FailGroup->Count; i++) {
if (FailGroup->DeviceList[i] == DeviceInfo) {
//
// Zero out it's entry.
//
FailGroup->DeviceList[i] = NULL;
//
// Reduce the number in the group.
//
FailGroup->Count--;
//
// Collapse the array.
//
for (j = i; j < FailGroup->Count; j++) {
//
// Shuffle all entries down to fill the hole.
//
FailGroup->DeviceList[j] = FailGroup->DeviceList[j + 1]; } //
// Zero out the last one.
//
FailGroup->DeviceList[j] = NULL; break; } }
KeReleaseSpinLock(&DsmContext->SpinLock, irql); return; }
PFAILOVER_GROUP SetNewPath( IN PDSM_CONTEXT DsmContext, IN PGROUP_ENTRY Group, IN PDEVICE_INFO FailingDevice, IN PFAILOVER_GROUP SelectedPath ) { PFAILOVER_GROUP failGroup; PGROUP_ENTRY group; PDEVICE_INFO device; ULONG i; NTSTATUS status; BOOLEAN matched = FALSE;
if (SelectedPath) {
//
// This indicates that a new path has already been selected
// for at least one device in the Fail-Over Group.
// Run the list of new devices and find the matching
// multi-path group.
//
for (i = 0; i < SelectedPath->Count; i++) {
//
// Get the device from the newly selected Path.
//
device = SelectedPath->DeviceList[i];
//
// Determine if the device's group matches the failing
// device's group.
//
if (device->Group == Group) {
//
// The new device should be either ACTIVE or PASSIVE
//
if ((device->State == DEV_ACTIVE) || (device->State == DEV_PASSIVE)) {
//
// Set it to ACTIVE.
//
device->State = DEV_ACTIVE;
//
// Ensure that it's ready.
//
status = DsmSendTUR(device->TargetObject); ASSERT(status == STATUS_SUCCESS); matched = TRUE; break; } } }
//
// When the first call was made and a path selected, all devices
// on the path were checked for validity.
//
ASSERT(matched == TRUE);
//
// Just return the SelectedPath
//
failGroup = SelectedPath; } else { //
// Go through Group, looking for an available device.
//
for (i = 0; i < Group->NumberDevices; i++) { //
// Look for any that are Passive. They are the best
// choice. This would indicate either an ActiveN/PassiveN arrangement.
//
device = Group->DeviceList[i]; if (device->State == DEV_PASSIVE) { matched = TRUE; break; } } if (matched) {
//
// Mark the device as active.
//
device->State = DEV_ACTIVE;
//
// Ensure that it's ready.
//
status = DsmSendTUR(device->TargetObject); ASSERT(status == STATUS_SUCCESS);
//
// Get the Fail-Over group from the selected device.
//
failGroup = device->FailGroup; } else {
//
// No passive devices. This indicates either an Active/Active arrangement,
// or everything is failed.
// Look for active devices.
//
for (i = 0; i < Group->NumberDevices; i++) { device = Group->DeviceList[i]; if (device->State == DEV_ACTIVE) { matched = TRUE; break; } } if (matched) { //
// The device is already active, just return the
// new path info.
//
failGroup = device->FailGroup; } else {
//
// Everything has failed. Should try to do something?? TODO
//
failGroup = NULL; } }
if (failGroup) {
//
// Run through all the devices to ensure that they are
// in a reasonable state.
//
for (i = 0; i < failGroup->Count; i++) { device = failGroup->DeviceList[i]; if ((device->State != DEV_ACTIVE) && (device->State != DEV_PASSIVE)) {
//
// Really need to find a new fail-over group.
// TODO.
// This isn't necessarily a valid assert. If static lb is in
// effect and this is one of the first to fail-over, others
// could be considered bad.
//
ASSERT(device->State == DEV_ACTIVE); } } } } return failGroup; }
VOID LBInit( IN PDSM_CONTEXT DsmContext, IN PGROUP_ENTRY Group ) { PFAILOVER_GROUP failGroup; PDEVICE_INFO device; PLIST_ENTRY entry; ULONG numberPaths; ULONG assignedPath; ULONG i; BOOLEAN found; //
// TODO: Once the Wmi support is here, this will be configurable
// Need to add code to handle each of the different policies.
//
//
// Doing 'static' LB. Out of each Multi-Path Group, one
// device will be active and assigned to a particular path.
// The assignment is based on the group ordinal modulus the total
// number of paths.
//
numberPaths = DsmContext->NumberFOGroups; assignedPath = Group->GroupNumber % numberPaths;
DebugPrint((0, "DsmLBInit: NumberFOGs (%x), Group Number (%x), assignedPath (%x)\n", DsmContext->NumberFOGroups, Group->GroupNumber, assignedPath)); //
// Get the Fail-Over Group with the correct path.
//
i = 0; found = FALSE; //
// Get the first entry.
//
entry = DsmContext->FailGroupList.Flink; do {
//
// Extract the F.O. Group entry.
//
failGroup = CONTAINING_RECORD(entry, FAILOVER_GROUP, ListEntry); ASSERT(failGroup);
DebugPrint((0, "DsmLBInit: Trying %x. at (%x) of (%x)\n", failGroup, i, assignedPath)); if (i == assignedPath) {
//
// This is the one.
//
found = TRUE; } else {
//
// Advance to the next entry.
//
entry = entry->Flink; i++; } //
// BUGBUG: Need to terminate this loop based on #of FG's.
//
} while (found == FALSE); DebugPrint((0, "DsmLBInit: Using FOG (%x)\n", failGroup)); //
// It may occur that though there are multiple paths/groups, not
// all devices have been put into the DeviceList.
// If there is only 1, special case this. It will get fixed up
// when the second device arrives.
//
if (Group->NumberDevices == 1) {
//
// LOG. Indicates something "might" be wrong - definitely
// not multi-pathing this device, so could lead to disaster
//
//
// Grab device 0 and set it active.
//
device = Group->DeviceList[0]; device->State = DEV_ACTIVE; //
// Go ahead state that this is init'ed. If/when another
// device shows up, we will re-do this.
//
Group->LoadBalanceInit = TRUE; Group->LoadBalanceType = LB_STATIC; DebugPrint((0, "DsmLBInit: Only One Device (%x) currently in group. Setting it Active\n", device)); return; } //
// Find the device with the same F.O. Group
// in the mulit-path group.
//
for (i = 0; i < Group->NumberDevices; i++) { //
// Get the device info.
//
device = Group->DeviceList[i];
//
// See if there is a match.
//
if (device->FailGroup == failGroup) { //
// Set the device to active.
//
device->State = DEV_ACTIVE;
DebugPrint((0, "DsmLBInit: Marking (%x) as active device\n", device));
//
// Done setting up this multi-path group.
// Indicate that it's so, and that we are using
// STATIC Load-Balancing.
//
Group->LoadBalanceInit = TRUE; Group->LoadBalanceType = LB_STATIC; return; } else {
DebugPrint((0, "DsmLBInit: Marking (%x) as stand-by device\n", device)); device->State = DEV_PASSIVE; } } }
VOID DsmDebugPrint( ULONG DebugPrintLevel, PCCHAR DebugMessage, ... )
/*++
Routine Description:
Debug print for the DSM
Arguments:
Return Value:
None
--*/
{ va_list ap;
va_start(ap, DebugMessage);
if (DebugPrintLevel <= HPDSMDebug) {
_vsnprintf(DebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
DbgPrint(DebugBuffer); }
va_end(ap);
}
|