/*++ Copyright (C) 1999-2000 Microsoft Corporation Module Name: mpath.c Abstract: The main multi-path module. Provides fault tolerance across adapter/path failures. Author: Environment: kernel mode only Notes: Revision History: --*/ #include #include "stdarg.h" #include "stdio.h" #include "mpio.h" // // Entry point decl's. // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS MPIOGlobalDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MPIOAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ); NTSTATUS MPIOCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MPIOClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MPIOWmiDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MPIOUnload( IN PDRIVER_OBJECT DriverObject ); typedef NTSTATUS (*PMPIO_DISPATCH) ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PMPIO_CONTEXT Context ); // PMPIO_DISPATCH PdoDispatch[IRP_MJ_MAXIMUM_FUNCTION]; PDRIVER_DISPATCH PdoDispatch[IRP_MJ_MAXIMUM_FUNCTION]; PDRIVER_DISPATCH FdoDispatch[IRP_MJ_MAXIMUM_FUNCTION]; NTSTATUS MPIOPdoDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG State ); BOOLEAN DoASSERT = TRUE; 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 --*/ { PDEVICE_OBJECT deviceObject; PDEVICE_EXTENSION deviceExtension; NTSTATUS status; PDEVICE_OBJECT pdo; ULONG i; MPDebugPrint((0, "MPIO: Driver Entry\n")); if (DontLoad) { return STATUS_NO_SUCH_DEVICE; } // // Register the entry points for all IRP_MJ's. // DriverObject->MajorFunction[IRP_MJ_CREATE] = MPIOCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = MPIOClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MPIOGlobalDispatch; DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MPIOGlobalDispatch; DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = MPIOGlobalDispatch; DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = MPIOGlobalDispatch; DriverObject->MajorFunction[IRP_MJ_PNP] = MPIOGlobalDispatch; DriverObject->MajorFunction[IRP_MJ_POWER] = MPIOGlobalDispatch; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = MPIOWmiDispatch; // // Register the Unload. // DriverObject->DriverUnload = MPIOUnload; ObReferenceObject(DriverObject); pdo = NULL; // // Notify PnP that this 'device' was found. // Indicate no resources, no bus location, and lie that // resources were reported (as we need none). // status = IoReportDetectedDevice(DriverObject, InterfaceTypeUndefined, -1, -1, NULL, NULL, TRUE, &pdo); if (!NT_SUCCESS(status)) { MPDebugPrint((0, "Multipath DriverEntry: IoReportDetectedDevice returned (%x)\n", status)); ObDereferenceObject(DriverObject); return status; } // // Create the fdo. Not really an AddDevice routine // but name it as such. // status = MPIOAddDevice(DriverObject, pdo); if (!NT_SUCCESS(status)) { MPDebugPrint((0, "MPIO DriverEntry: AddDevice failed (%x)\n", status)); ObDereferenceObject(DriverObject); return status; } // // Get deviceObject (fdo) and deviceExtension. // deviceObject = IoGetAttachedDeviceReference(pdo); ObDereferenceObject(deviceObject); deviceExtension = deviceObject->DeviceExtension; // // Setup the device extension. // deviceExtension->Pdo = pdo; deviceExtension->RegistryPath.Buffer = ExAllocatePool(NonPagedPool, RegistryPath->MaximumLength); deviceExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength; RtlCopyUnicodeString(&deviceExtension->RegistryPath, RegistryPath); // // Setup the Pdo's dispatch routines. // for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { PdoDispatch[i] = MPIOPdoUnhandled; } PdoDispatch[IRP_MJ_DEVICE_CONTROL] = MPIOPdoDeviceControl; PdoDispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MPIOPdoInternalDeviceControl; PdoDispatch[IRP_MJ_PNP] = MPIOPdoPnp; PdoDispatch[IRP_MJ_POWER] = MPIOPdoPower; MPathDebug = 1; return status; } NTSTATUS MPIOAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine creates and initializes a new FDO for the corresponding PDO. Arguments: DriverObject - Supplies the driver object for mpath control. PhysicalDeviceObject - Supplies the physical device object. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension; PCONTROL_EXTENSION controlExtension; UNICODE_STRING deviceName; UNICODE_STRING dosName; PDEVICE_OBJECT deviceObject; NTSTATUS status; RtlInitUnicodeString(&deviceName, DD_MULTIPATH_CONTROL_DEVICE_NAME); // // Create the fdo. // status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &deviceName, FILE_DEVICE_CONTROLLER, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { return status; } controlExtension = ExAllocatePool(NonPagedPool, sizeof(CONTROL_EXTENSION)); if (controlExtension == NULL) { IoDeleteDevice(deviceObject); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(controlExtension, sizeof(CONTROL_EXTENSION)); // // Create the symbolic link. // RtlInitUnicodeString(&dosName, DD_MULTIPATH_CONTROL_DOS_NAME); IoCreateSymbolicLink( &dosName, &deviceName); // // Save off devObj and build the device extension. // deviceExtension = deviceObject->DeviceExtension; deviceExtension->Type = MPIO_CONTROL; deviceExtension->DeviceObject = deviceObject; deviceExtension->DriverObject = DriverObject; // // Attach to the pdo. // deviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); if (deviceExtension->LowerDevice == NULL) { IoDeleteSymbolicLink(&dosName); IoDeleteDevice(deviceObject); return STATUS_NO_SUCH_DEVICE; } deviceExtension->Pdo = PhysicalDeviceObject; deviceExtension->TypeExtension = controlExtension; // // Initialize the 'control' portion of the device extension. // KeInitializeSpinLock(&controlExtension->SpinLock); InitializeListHead(&controlExtension->FilterList); InitializeListHead(&controlExtension->DeviceList); InitializeListHead(&controlExtension->DsmList); InitializeListHead(&controlExtension->IdList); InitializeListHead(&controlExtension->ControllerList); InitializeListHead(&controlExtension->FailPacketList); // // Set-up the WMILIB Context. // MPIOSetupWmi(deviceObject); // // Register as a WMI provider. // IoWMIRegistrationControl(deviceObject, WMIREG_ACTION_REGISTER); // // Indicate that this device is ready. // deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return status; } NTSTATUS MPIOCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_CREATE. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request block. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS MPIOClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for IRP_MJ_CLOSE Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request block. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS MPIOUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine unloads. Arguments: DriverObject - Supplies the driver object. Return Value: None. --*/ { // // TODO: Release all allocations. // TODO: Ensure pdo's/DSMs have been torndown. // ObDereferenceObject(DriverObject); return STATUS_SUCCESS; } NTSTATUS MPPdoGlobalCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: All requests to the pdo's have this as the completion. It handles calling out to the function-specific completions and deals with errors. Arguments: Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PDSM_ENTRY dsm = &diskExtension->DsmInfo; PMPIO_CONTEXT context = Context; PSCSI_REQUEST_BLOCK srb = NULL; PMPIO_COMPLETION_ROUTINE specificCompletion; PKDEVICE_QUEUE_ENTRY entry; PREAL_DEV_INFO targetInfo; PREAL_DEV_INFO newTargetInfo; PDEVICE_OBJECT targetObject; NTSTATUS status; NTSTATUS status2; PVOID dsmId; PVOID dsmContext; DSM_COMPLETION_ROUTINE dsmCompletion; ULONG errorMask = 0; ULONG completionState = context->CurrentState; ULONG state; ULONG state2; LONG outstandingRequests; LONG deviceRequests; BOOLEAN fatal = FALSE; BOOLEAN retry = FALSE; BOOLEAN needsRemoval = FALSE; BOOLEAN currentRequestHandled; if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } // // Get the targetInfo. This describes the device. // targetInfo = context->TargetInfo; // // Get the number of requests on THIS device (vs. the entire disk). // deviceRequests = InterlockedDecrement(&targetInfo->Requests); // // One less request below us. // outstandingRequests = InterlockedDecrement(&diskExtension->OutstandingRequests); ASSERT(diskExtension->OutstandingRequests >= 0); if ((outstandingRequests == 0) && (deviceRequests != 0)) { // // If there are no requests for the disk, then there shouldn't be any of this // deviceInfo. // if (DoASSERT) { MPDebugPrint((0, "GlobalCompletion: DeviceRequests (%x)\n", deviceRequests)); ASSERT(deviceRequests == 0); } } // // Get the config. spinlock. // KeAcquireSpinLockAtDpcLevel(&diskExtension->SpinLock); // // See whether there is a remove pending on this targetInfo. // if (targetInfo->NeedsRemoval) { needsRemoval = TRUE; } KeReleaseSpinLockFromDpcLevel(&diskExtension->SpinLock); // // For scsi requests, need to ensure that frozen queues // get released. // if (irpStack->MajorFunction == IRP_MJ_SCSI) { // // Get the srb. // srb = irpStack->Parameters.Scsi.Srb; // // Handle any frozen queue stuff. // if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { // // Get the target object for this request. // NOTE: This assumes that the Function-Specific handler // has set this up correctly. // targetObject = targetInfo->PortPdo; MPLibReleaseQueue(targetObject); } } // // Extract the DSM-Specific stuff from the context. // dsmId = targetInfo->DsmID; dsmContext = context->DsmCompletion.DsmContext; dsmCompletion = context->DsmCompletion.DsmCompletionRoutine; // // // Determine the current state. // state = deviceExtension->State; if ((deviceExtension->CompletionState == MPIO_STATE_NORMAL) || (deviceExtension->CompletionState == MPIO_STATE_DEGRADED)) { deviceExtension->CompletionState = state; } // // Indicate that this request hasn't been dealt with yet. // currentRequestHandled = FALSE; // // This call will update state information, but the request coming in // needs to be first handled with the current state. // state2 = MPIOHandleStateTransition(DeviceObject); runState: if ((state != MPIO_STATE_NORMAL) && (state != MPIO_STATE_DEGRADED)) { switch (state) { case MPIO_STATE_IN_FO: // // The fail-over is being executed. Put on resubmit Queue. (unless // status == success. // status2 = MPIOQueueRequest(DeviceObject, Irp, Context, &diskExtension->ResubmitQueue); state = MPIOHandleStateTransition(DeviceObject); if (state != MPIO_STATE_IN_FO) { MPDebugPrint((0, "GlobalCompletion: Going from IN_FO to (%x)\n", state)); // // See if we've gone into WAIT2 directly. // if (state == MPIO_STATE_WAIT2) { // // Get the targetInfo corresponding to the path set-up in the // fail-over handler. Whatever was in the completion context is // probably not valid. // NOTE: This assumes that no-one is updating the CurrentPath // in the diskExtension. Probably not a good assumption. // TODO: Validate. // targetInfo = MPIOGetTargetInfo(diskExtension, diskExtension->CurrentPath, NULL); // // Submit the queued requests. These Resubmit requests all go // down "newPath". TODO: Indicate this behaviour in dsm.h // status = MPIOIssueQueuedRequests(targetInfo, &diskExtension->ResubmitQueue, MPIO_STATE_WAIT2, &diskExtension->OutstandingRequests); } else { // // Went into WAIT1. // currentRequestHandled = TRUE; goto runState; } } if (needsRemoval && (deviceRequests == 0)) { MPDebugPrint((0, "MPIOGlobalCompletion (IN_FO): Removing %x\n", targetInfo)); // // This routine will queue a work item to handle the device removal. // MPIOHandleRemoveAsync(DeviceObject, targetInfo); } return STATUS_MORE_PROCESSING_REQUIRED; break; case MPIO_STATE_WAIT1: { // // This indicates that the fail-over was successful, but there // are still outstanding requests (on the failed path). // Status should be flushed, or some error condition, though // conceivable, a successful completion could have occurred. // One less request below us. // if (currentRequestHandled == FALSE) { // // Enqueue this request on the Resubmit Queue. // status2 = MPIOQueueRequest(DeviceObject, Irp, Context, &diskExtension->ResubmitQueue); if (needsRemoval && (deviceRequests == 0)) { MPDebugPrint((0, "MPIOGlobalCompletion (WAIT1): Removing %x\n", targetInfo)); // // This routine will queue a work item to handle the device removal. // MPIOHandleRemoveAsync(DeviceObject, targetInfo); } } // // See if the has caused a move into a different state. // state = MPIOHandleStateTransition(DeviceObject); if (state != MPIO_STATE_WAIT1) { MPDebugPrint((0, "GlobalCompletion: Going from WAIT1 to (%x)\n", state)); // // See if we've gone into WAIT2 directly. // if (state == MPIO_STATE_WAIT2) { // // Get the targetInfo corresponding to the path set-up in the // fail-over handler. Whatever was in the completion context is // probably not valid. // NOTE: This assumes that no-one is updating the CurrentPath // in the diskExtension. Probably not a good assumption. // TODO: Validate. // targetInfo = MPIOGetTargetInfo(diskExtension, diskExtension->CurrentPath, NULL); // // Submit the queued requests. These Resubmit requests all go // down "newPath". TODO: Indicate this behaviour in dsm.h // status = MPIOIssueQueuedRequests(targetInfo, &diskExtension->ResubmitQueue, MPIO_STATE_WAIT2, &diskExtension->OutstandingRequests); } else { currentRequestHandled = TRUE; goto runState; } } return STATUS_MORE_PROCESSING_REQUIRED; break; } case MPIO_STATE_WAIT2: { // // Dec the # of resubmit requests when zero, we go into WAIT3. // New requests on FOQ. // outstandingRequests = InterlockedDecrement(&diskExtension->ResubmitRequests); ASSERT(diskExtension->ResubmitRequests >= 0); state = MPIOHandleStateTransition(DeviceObject); if (state != MPIO_STATE_WAIT2) { MPDebugPrint((0, "GlobalCompletion: Going from WAIT2 to (%x)\n", state)); if (state == MPIO_STATE_WAIT3) { // // Get the targetInfo corresponding to CurrentPath. // targetInfo = MPIOGetTargetInfo(diskExtension, diskExtension->CurrentPath, NULL); // // Process the Fail-Over queue. // status2 = MPIOIssueQueuedRequests(targetInfo, &diskExtension->FailOverQueue, MPIO_STATE_WAIT3, &diskExtension->OutstandingRequests); } } if (!NT_SUCCESS(Irp->IoStatus.Status)) { MPDebugPrint((0, "MPIOCompletion: !Success while in WAIT2. Irp (%x) Status (%x)\n", Irp, Irp->IoStatus.Status)); } // // Now, handle the request normally. // break; } case MPIO_STATE_WAIT3: { // // Dec the # of fail-over requests. When zero, we go into DEGRADED. // outstandingRequests = InterlockedDecrement(&diskExtension->FailOverRequests); ASSERT(diskExtension->FailOverRequests >= 0); state = MPIOHandleStateTransition(DeviceObject); // // Continue down and handle the request normally. // break; } default: // // Invalid state. // MPDebugPrint((0, "MPIOCompletion: Unknown State (%x)\n", state)); DbgBreakPoint(); break; } } else { if (needsRemoval && (deviceRequests == 0)) { MPDebugPrint((0, "MPIOGlobalCompletion (WAIT1): Removing %x\n", targetInfo)); // // This routine will queue a work item to handle the device removal. // MPIOHandleRemoveAsync(DeviceObject, targetInfo); } } status = Irp->IoStatus.Status; if (!NT_SUCCESS(status)) { fatal = FALSE; retry = FALSE; if (irpStack->MajorFunction == IRP_MJ_SCSI) { // // Invoke the DSM's InterpretError with preset fatal and retry // values. // errorMask = dsm->InterpretError(dsm->DsmContext, dsmId, srb, &status, &retry); if (errorMask & DSM_FATAL_ERROR) { fatal = TRUE; } } } if (fatal) { // if ((deviceExtension->LastState == MPIO_STATE_NORMAL) || // (deviceExtension->LastState == MPIO_STATE_DEGRADED)) { diskExtension->FailOver = TRUE; state = MPIOHandleStateTransition(DeviceObject); // } // // Put the request on the resubmit queue. // This routine will also clean up the request and // prepare it for resubmission. // // BUGBUG: If we queue this, and the FailOver is unsuccessful, the request // will remain in the queue. // status2 = MPIOQueueRequest(DeviceObject, Irp, Context, &diskExtension->ResubmitQueue); MPDebugPrint((1, "MPIOCompletion: Irp (%x) Srb (%x) on resubmit queue of (%x). Status (%x)\n", Irp, srb, DeviceObject, status)); // // Call the Fail-over Handler. // status2 = MPIOFailOverHandler(DeviceObject, errorMask, targetInfo); // // If the fail-over was successful or pending, don't let Io get rid of this // request. // If it wasn't successful, pass the original status back. // if ((status2 == STATUS_SUCCESS) || (status2 == STATUS_PENDING)) { status = STATUS_MORE_PROCESSING_REQUIRED; } else { ASSERT(FALSE); } return status; } else if (retry) { // // If the DSM has requested a retry, clean-up the request and call PdoDispatch. // if (irpStack->MajorFunction == IRP_MJ_SCSI) { // // Need to rebuild the Srb. // RtlCopyMemory(irpStack->Parameters.Scsi.Srb, &context->Srb, sizeof(SCSI_REQUEST_BLOCK)); } // // Re-do status and information. // Irp->IoStatus.Status = 0; Irp->IoStatus.Information = 0; // // Rebuild port's stack location. // IoCopyCurrentIrpStackLocationToNext(Irp); // // Resend. // status2 = MPIOPdoDispatch(DeviceObject, Irp, deviceExtension->State); if (status2 == STATUS_PENDING) { status = STATUS_MORE_PROCESSING_REQUIRED; } else { status = status2; } return status; } // // If the DSM specified a completion routine, invoke it now. // if (dsmCompletion) { dsmCompletion(dsmId, Irp, srb, dsmContext); } // // Free the context structure. // MPIOFreeContext(deviceExtension, Context); return status; } NTSTATUS MPIOPdoDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG State ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; ULONG sortKey = deviceExtension->SequenceNumber++; PMPIO_CONTEXT context; NTSTATUS status = STATUS_PENDING; KIRQL irql; UCHAR opCode = 0; ASSERT(deviceExtension->Type == MPIO_MPDISK); KeAcquireSpinLock(&diskExtension->SpinLock, &irql); // // Allocate the request context. // context = MPIOAllocateContext(deviceExtension); RtlZeroMemory(context, sizeof(MPIO_CONTEXT)); // // Indicate this I/O's sequence number. Used in tracking // requests during fail-over and also as a key into the device queue. // context->CurrentState = State; context->OriginalState = State; context->Irp = Irp; context->Allocated = TRUE; // // Clone the irpstack location. // IoCopyCurrentIrpStackLocationToNext(Irp); // // Set the context in our stack. // irpStack->Parameters.Others.Argument4 = (PVOID)context; switch (State) { case MPIO_STATE_IN_FO: case MPIO_STATE_WAIT1: { // // As no requests will be submitted, release the lock now. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); // // If in FO, or WAIT1 simply queue to the // Fail-Over Queue. Once the Fail-Over is complete and the // resubmit queue has been handled, the F.O.Q. will be dealt // with. // status = MPIOQueueRequest(DeviceObject, Irp, context, &diskExtension->FailOverQueue); // // This routine will only return success or insufficient resources. // if (status == STATUS_INSUFFICIENT_RESOURCES) { MPDebugPrint((1, "MPIOQueueRequest returned (%x). State (%x)\n", status, State)); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // // Set pending. // IoMarkIrpPending(Irp); // // Return pending. // status = STATUS_PENDING; break; } case MPIO_STATE_WAIT2: { // // As no requests will be submitted, release the lock now. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); // // The resubmit queue is being handled. These (like IN_FO and WAIT1) // go onto the Fail-Over queue. // This is seperated from the above States merely for administrative // reasons. (Unless it gets changed...) // status = MPIOQueueRequest(DeviceObject, Irp, context, &diskExtension->FailOverQueue); // // This routine will only return success or insufficient resources. // if (status == STATUS_INSUFFICIENT_RESOURCES) { MPDebugPrint((1, "MPIOQueueRequest returned (%x). State (%x)\n", status, State)); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // // Set pending. // IoMarkIrpPending(Irp); // // Return pending. // status = STATUS_PENDING; break; } case MPIO_STATE_WAIT3: { // // Draining the Fail-Over queue. Complete these requests // with busy, if they are Srb's (build sense data, so that they // are retried). Otherwise, just return BUSY and hope for the best. // BUGBUG: Revisit handling of non-Srb requests. Maybe put these // on the F.O.Q... // MPDebugPrint((0, "PdoDispatch: Setting Irp(%x) Srb (%x) BUSY\n", Irp, srb)); // // As no requests will be submitted, release the lock now. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); if (irpStack->MajorFunction == IRP_MJ_SCSI) { RtlCopyMemory(&context->Srb, srb, sizeof(SCSI_REQUEST_BLOCK)); // // This will build the appropriate sense data that indicates // to the class driver that the device is busy. // MPIOSetRequestBusy(srb); } // // All requests will be completed as BUSY. // status = STATUS_DEVICE_BUSY; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } case MPIO_STATE_NORMAL: case MPIO_STATE_DEGRADED: { // // All failure queues are empty and the new // path is functioning (DEGRADED) or all is working // fine (NORMAL). // Submit these requests normally. // // Indicate another request has been submitted. // InterlockedIncrement(&diskExtension->OutstandingRequests); // // Now it's safe to release the lock as the config. code checks // for outstanding requests. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); // // Call FunctionSpecific handler. // The function-specific handler will set-up it's private // completion routine, along with the DSM information. // status = PdoDispatch[irpStack->MajorFunction](DeviceObject, Irp); break; } default: // // As no requests will be submitted, release the lock now. // KeReleaseSpinLock(&diskExtension->SpinLock, irql); // // TODO: Here only for test. Remove it. // MPDebugPrint((0, "MPIOPdoDispatch: Unknown State (%x)\n", State)); DbgBreakPoint(); break; } return status; } NTSTATUS MPIOFdoDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG State ) { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; // // State is less important for the FDO: Perhaps of no importance. // Maybe: // Set Sequence # // Set completion. // Call FunctionSpecific Handler. // switch (irpStack->MajorFunction) { case IRP_MJ_PNP: status = MPIOFdoPnP(DeviceObject, Irp); break; case IRP_MJ_POWER: status = MPIOFdoPower(DeviceObject, Irp); break; case IRP_MJ_INTERNAL_DEVICE_CONTROL: status = MPIOFdoInternalDeviceControl(DeviceObject, Irp); break; case IRP_MJ_SYSTEM_CONTROL: status = MPIOFdoWmi(DeviceObject, Irp); break; case IRP_MJ_DEVICE_CONTROL: case IRP_MJ_SHUTDOWN: case IRP_MJ_FLUSH_BUFFERS: default: status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } return status; } NTSTATUS MPIOWmiDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status; // // Determine whether this is for the FDO or one of the PDOs. // if (deviceExtension->Type == MPIO_MPDISK) { // // Call the Pdo-specific handler // status = MPIOPdoWmi(DeviceObject, Irp); } else { // // Call the Fdo-specific handler (MPIO Control). // status = MPIOFdoDispatch(DeviceObject, Irp, deviceExtension->State); } return status; } NTSTATUS MPIOGlobalDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the global dispatch routine for all requests. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request block. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status; // // Determine whether this is for the FDO or one of the PDOs. // if (deviceExtension->Type == MPIO_MPDISK) { // // Call the Pdo-specific handler (MPDISK). The routines under this will do // everything needed, so just return the status. // status = MPIOPdoDispatch(DeviceObject, Irp, deviceExtension->State); } else { // // Call the Fdo-specific handler (MPIO Control). // status = MPIOFdoDispatch(DeviceObject, Irp, deviceExtension->State); } return status; } // // Routines exported to the DSMs // VOID DsmSendDeviceIoControlSynchronous( IN ULONG IoControlCode, IN PDEVICE_OBJECT TargetDeviceObject, IN PVOID InputBuffer OPTIONAL, IN PVOID OutputBuffer OPTIONAL, IN ULONG InputBufferLength, IN ULONG OutputBufferLength, IN BOOLEAN InternalDeviceIoControl, OUT PIO_STATUS_BLOCK IoStatus ) { MPLIBSendDeviceIoControlSynchronous(IoControlCode, TargetDeviceObject, InputBuffer, OutputBuffer, InputBufferLength, OutputBufferLength, InternalDeviceIoControl, IoStatus); } PDSM_IDS DsmGetAssociatedDevice( IN PVOID MPIOContext, IN PDEVICE_OBJECT PortFdo, IN UCHAR DeviceType ) /*++ Routine Description: If the DSM needs to acquire information from other devices (such as a controller), this routine can be used to get a list of the PDO's associated with PortFdo. Arguments: PortFdo - Port driver FDO passed to InquireDriver. DeviceType - Indicates the SCSI DeviceType to return. Return Value: Pointer to a DSM_ID structure, where IdList entries are PDEVICE_OBJECT. It is the reponsibility of the DSM to free the buffer. --*/ { PDEVICE_OBJECT deviceObject = MPIOContext; PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PADP_ASSOCIATED_DEVICES filterDevices; PDSM_IDS deviceList; ULONG numberMatched = 0; ULONG i; ULONG j; ULONG length; PFLTR_ENTRY fltrEntry; PLIST_ENTRY entry; for (entry = controlExtension->FilterList.Flink; entry != &controlExtension->FilterList; entry = entry->Flink) { fltrEntry = CONTAINING_RECORD(entry, FLTR_ENTRY, ListEntry); if (fltrEntry->PortFdo == PortFdo) { // // Get the current list from the fdo filter. // filterDevices = fltrEntry->FltrGetDeviceList(fltrEntry->FilterObject); if (filterDevices) { for (i = 0; i < filterDevices->NumberDevices; i++) { if (filterDevices->DeviceInfo[i].DeviceType == DeviceType) { numberMatched++; } } // // Allocate the dsm list. // length = sizeof(DSM_IDS) + (sizeof(PDEVICE_OBJECT) * (numberMatched - 1)); deviceList = ExAllocatePool(NonPagedPool, length); deviceList->Count = numberMatched; for (j = 0, i = 0; i < filterDevices->NumberDevices; i++) { if (filterDevices->DeviceInfo[i].DeviceType == DeviceType) { deviceList->IdList[j] = filterDevices->DeviceInfo[i].DeviceObject; j++; } } } else { deviceList = NULL; } ExFreePool(filterDevices); return deviceList; } } return NULL; } NTSTATUS DsmReleaseQueue( IN PDEVICE_OBJECT ChildDevice ) { return MPLibReleaseQueue(ChildDevice); } NTSTATUS DsmSendTUR( IN PDEVICE_OBJECT TargetDevice ) { return MPLibSendTUR(TargetDevice); } NTSTATUS DsmSendPassThroughDirect( IN PDEVICE_OBJECT DeviceObject, IN PSCSI_PASS_THROUGH_DIRECT ScsiPassThrough, IN ULONG InputBufferLength, IN ULONG OutputBufferLength ) { return MPLibSendPassThroughDirect(DeviceObject, ScsiPassThrough, InputBufferLength, OutputBufferLength); } NTSTATUS DsmGetDescriptor( IN PDEVICE_OBJECT DeviceObject, IN PSTORAGE_PROPERTY_ID PropertyId, OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor ) { return MPLIBGetDescriptor(DeviceObject, PropertyId, Descriptor); } VOID DsmNotification( IN PVOID MPIOContext, IN DSM_NOTIFICATION_TYPE Type, IN PVOID AdditionalParameter ) { return; } NTSTATUS DsmWriteEvent( IN PVOID MPIOContext, IN PWCHAR ComponentName, IN PWCHAR EventDescription, IN ULONG Severity ) { NTSTATUS status; PDEVICE_OBJECT deviceObject = MPIOContext; status = MPIOFireEvent(deviceObject, ComponentName, EventDescription, Severity); return status; } VOID DsmDebugPrint( ULONG DebugPrintLevel, PCCHAR DebugMessage, ... ) { MPathDebugPrint(DebugPrintLevel, DebugMessage); }