#include "mpio.h" VOID MPIOTimer( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION)Context; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PFLTR_ENTRY fltrEntry; ULONG tickDiff; ULONG numberDevices; PMPIO_REQUEST_INFO requestInfo; PREAL_DEV_INFO deviceInfo; PDEVICE_OBJECT currentAdapter; ULONG i; ULONG j; PDEVICE_OBJECT controlObject = diskExtension->ControlObject; PDEVICE_EXTENSION devExt = controlObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = devExt->TypeExtension; PLIST_ENTRY listEntry; // // Inc current tickcount. // diskExtension->TickCount++; if ((deviceExtension->State != MPIO_STATE_IN_FO) && (deviceExtension->State != MPIO_STATE_WAIT1)) { // // Check for any pending work items to start. // if (diskExtension->PendingItems) { PLIST_ENTRY listEntry; // // Yank the packet from the pending list... // listEntry = ExInterlockedRemoveHeadList(&diskExtension->PendingWorkList, &diskExtension->WorkListLock); if (listEntry) { // // ...and jam it on the work list. // ExInterlockedInsertTailList(&diskExtension->WorkList, listEntry, &diskExtension->WorkListLock); InterlockedDecrement(&diskExtension->PendingItems); // // Signal the thread to initiate the F.O. // KeSetEvent(&diskExtension->ThreadEvent, 8, FALSE); } else { MPDebugPrint((0, "MPIOTimer: PendingItems set, but entry == NULL\n")); ASSERT(FALSE); } } } return; } BOOLEAN MPIOFindMatchingDevice( IN PDEVICE_OBJECT MPDiskObject, IN PADP_DEVICE_INFO DeviceInfo, IN PDSM_ENTRY DsmEntry, IN PVOID DsmId ) { PDEVICE_EXTENSION deviceExtension = MPDiskObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; ULONG i; // // Hand off each real pdo and the new one to the DSM to see // if they are really the same device (accessed via different paths) // for (i = 0; i < diskExtension->TargetInfoCount; i++) { if (DsmEntry->CompareDevices(DsmEntry->DsmContext, diskExtension->TargetInfo[i].DsmID, DsmId)) { // // Have a match. // return TRUE; } } return FALSE; } NTSTATUS MPIOCreateDevice( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT FilterObject, IN PDEVICE_OBJECT PortObject, IN PADP_DEVICE_INFO DeviceInfo, IN PDSM_ENTRY DsmEntry, IN PVOID DsmId, IN OUT PDEVICE_OBJECT *NewDeviceObject ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCONTROL_EXTENSION controlExtension = deviceExtension->TypeExtension; PDEVICE_OBJECT diskObject; PDEVICE_EXTENSION newExtension; PMPDISK_EXTENSION diskExtension; UNICODE_STRING unicodeDeviceName; PUNICODE_STRING regString; PREAL_DEV_INFO targetInfo; PMPIO_THREAD_CONTEXT threadContext; WCHAR deviceName[30]; NTSTATUS status; ULONG i; ULONG numberDevices = controlExtension->NumberDevices; // // Build the new name. // swprintf(deviceName, L"\\Device\\MPathDisk%0d", numberDevices); RtlInitUnicodeString(&unicodeDeviceName, deviceName); // // Create the device object. // status = IoCreateDevice(deviceExtension->DriverObject, sizeof(DEVICE_EXTENSION), &unicodeDeviceName, FILE_DEVICE_MASS_STORAGE, FILE_DEVICE_SECURE_OPEN, FALSE, &diskObject); if (status == STATUS_SUCCESS) { MPDebugPrint((0, "MPIOCreateDevice: New PDO %x\n", diskObject)); // // TODO, break this up into some helper functions that // can be used by both this routine and UpdateDevice // // // Setup the various devObj stuff to be like the real PDO. // diskObject->StackSize = DeviceInfo->DeviceObject->StackSize + 1; diskObject->Flags = DeviceInfo->DeviceObject->Flags; diskObject->Flags |= DO_DEVICE_INITIALIZING; diskObject->AlignmentRequirement = DeviceInfo->DeviceObject->AlignmentRequirement; // // Allocate the type extension. // diskExtension = ExAllocatePool(NonPagedPool, sizeof(MPDISK_EXTENSION)); RtlZeroMemory(diskExtension, sizeof(MPDISK_EXTENSION)); // // Allocate storage for the device descriptor. // diskExtension->DeviceDescriptor = ExAllocatePool(NonPagedPool, DeviceInfo->DeviceDescriptor->Size); // // Set-up the device extension. // newExtension = diskObject->DeviceExtension; newExtension->TypeExtension = diskExtension; newExtension->DeviceObject = diskObject; newExtension->Pdo = diskObject; newExtension->LowerDevice = DeviceObject; newExtension->DriverObject = deviceExtension->DriverObject; newExtension->Type = MPIO_MPDISK; // // Since there is only one path, set the original state to DEGRADED, as we // can't fail-over yet. // newExtension->State = MPIO_STATE_NORMAL; newExtension->LastState = MPIO_STATE_NORMAL; newExtension->CompletionState = MPIO_STATE_NORMAL; diskExtension->CheckState = TRUE; // // Set-up the Lookaside List of context structs. // ExInitializeNPagedLookasideList(&newExtension->ContextList, NULL, NULL, 0, sizeof(MPIO_CONTEXT), 'oCPM', 0); // // Set-up emergency buffers // KeInitializeSpinLock(&newExtension->EmergencySpinLock); for (i = 0; i < MAX_EMERGENCY_CONTEXT; i++) { newExtension->EmergencyContext[i] = ExAllocatePool(NonPagedPool, sizeof(MPIO_CONTEXT)); } // // Copy the reg. path from the control object. // regString = &deviceExtension->RegistryPath; newExtension->RegistryPath.Buffer = ExAllocatePool(NonPagedPool, regString->MaximumLength); newExtension->RegistryPath.MaximumLength = regString->MaximumLength; RtlCopyUnicodeString(&newExtension->RegistryPath, regString); // // Save off the important DSM Info. // RtlCopyMemory(&diskExtension->DsmInfo, DsmEntry, sizeof(DSM_ENTRY)); // // Prepare this D.O. to handle WMI requests. // MPIOSetupWmi(diskObject); // // Set-up the new DO's type extension. // diskExtension->ControlObject = DeviceObject; diskExtension->DeviceOrdinal = numberDevices; // // Copy the device descriptor. Used for PnP ID stuff. // RtlCopyMemory(diskExtension->DeviceDescriptor, DeviceInfo->DeviceDescriptor, DeviceInfo->DeviceDescriptor->Size); // // Prepare all of the queues. // MPIOInitQueue(&diskExtension->ResubmitQueue, 1); MPIOInitQueue(&diskExtension->FailOverQueue, 2); // // Set-up the thread stuff. // KeInitializeSpinLock(&diskExtension->SpinLock); KeInitializeSpinLock(&diskExtension->WorkListLock); InitializeListHead(&diskExtension->WorkList); InitializeListHead(&diskExtension->PendingWorkList); KeInitializeEvent(&diskExtension->ThreadEvent, NotificationEvent, FALSE); threadContext = ExAllocatePool(NonPagedPool, sizeof(MPIO_THREAD_CONTEXT)); threadContext->DeviceObject = diskObject; threadContext->Event = &diskExtension->ThreadEvent; status = PsCreateSystemThread(&diskExtension->Handle, (ACCESS_MASK)0, NULL, NULL, NULL, MPIORecoveryThread, threadContext); if (status != STATUS_SUCCESS) { diskExtension->Handle = NULL; } status = IoInitializeTimer(diskObject, MPIOTimer, newExtension); // // Add in the info for the first target device. // Note that the DevFilter and PathId fields are // set to the Port PDO and Adapter Filter. // When the DevFilter registers, these get copied over // to the correct values and DSMInit flag is set. // targetInfo = diskExtension->TargetInfo; targetInfo->AdapterFilter = FilterObject; targetInfo->PathId = FilterObject; targetInfo->PortPdo = DeviceInfo->DeviceObject; targetInfo->PortFdo = PortObject; targetInfo->DsmID = DsmId; targetInfo->DevFilter = DeviceInfo->DeviceObject; targetInfo->DSMInit = FALSE; diskExtension->DsmIdList.IdList[0] = DsmId; diskExtension->DsmIdList.Count = 1; diskExtension->TargetInfoCount = 1; diskExtension->MaxPaths = 1; diskExtension->HasName = FALSE; diskExtension->PdoName.Buffer = ExAllocatePool(NonPagedPool, PDO_NAME_LENGTH); RtlZeroMemory(diskExtension->PdoName.Buffer, PDO_NAME_LENGTH); diskExtension->PdoName.MaximumLength = PDO_NAME_LENGTH; // // Indicate that this slot is taken. // diskExtension->DeviceMap |= 1; *NewDeviceObject = diskObject; } else { MPDebugPrint((0, "MPIOCreateDevice: Error creating new PDO %x\n", status)); } return status; } NTSTATUS MPIOUpdateDevice( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_OBJECT AdapterFilter, IN PDEVICE_OBJECT PortObject, IN PADP_DEVICE_INFO DeviceInfo, IN PVOID DsmId ) /*++ Routine Description: This routine updates the PDO extension to include a newly arrived device. Arguments: Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PREAL_DEV_INFO devInfo; ULONG allocatedMap = diskExtension->DeviceMap; ULONG i; ULONG j; ULONG currentState; ULONG newState; WCHAR componentName[64]; // // Find a free spot in the array. // for (i = 0; i < MAX_NUMBER_PATHS; i++) { if (!(allocatedMap & (1 << i))) { diskExtension->DeviceMap |= (1 << i); break; } } // // Indicate that one more path is available. // diskExtension->DsmIdList.IdList[diskExtension->DsmIdList.Count] = DsmId; diskExtension->DsmIdList.Count++; diskExtension->TargetInfoCount++; ASSERT(diskExtension->DsmIdList.Count <= MAX_NUMBER_PATHS); // // See if we are transitioning from degraded to normal. // currentState = deviceExtension->State; if ((diskExtension->TargetInfoCount == diskExtension->MaxPaths) && (currentState == MPIO_STATE_DEGRADED)) { // // This indicates that a path that went missing, has returned. // diskExtension->PathBackOnLine = TRUE; } // // Update MaxPaths, if necessary. It's the count of the most // paths we have seen. // if (diskExtension->TargetInfoCount > diskExtension->MaxPaths) { diskExtension->MaxPaths++; } // // Determine the state info. // newState = MPIOHandleStateTransition(DeviceObject); if ((currentState == MPIO_STATE_DEGRADED) && (newState == MPIO_STATE_NORMAL)) { // // Did make that tranistion. Fire an event to notify any listeners. // MPDebugPrint((0, "MPIOUpdateDevice: Moving to NORMAL (%x)\n", DeviceObject)); swprintf(componentName, L"MPIO Disk(%02d)", diskExtension->DeviceOrdinal); MPIOFireEvent(DeviceObject, componentName, L"Moved to STATE_NORMAL", MPIO_INFORMATION); } // // Get the structure pertaining to the device being updated. // devInfo = &diskExtension->TargetInfo[i]; RtlZeroMemory(devInfo, sizeof(REAL_DEV_INFO)); // // Add the FilterObject as being the PathID for the new device. // devInfo->AdapterFilter = AdapterFilter; devInfo->PathId = AdapterFilter; // // Point the DevFilter entry to the Port PDO for now. // It is set when the device filter registers. // devInfo->DevFilter = DeviceInfo->DeviceObject; // // Add the Scsiport PDO. // devInfo->PortPdo = DeviceInfo->DeviceObject; devInfo->PortFdo = PortObject; for (j = 0; j < diskExtension->TargetInfoCount; j++) { if (diskExtension->TargetInfo[j].DsmID == DsmId) { MPDebugPrint((0, "UpdateDevice: Matching DSM IDs\n")); DbgBreakPoint(); } } // // Add the DSM Id that corresponds to the Scsiport PDO. // devInfo->DsmID = DsmId; devInfo->DSMInit = FALSE; return STATUS_SUCCESS; } NTSTATUS MPIOPdoPowerNotification( IN PDEVICE_OBJECT MPDiskObject, IN PDEVICE_OBJECT FilterObject, IN PIRP Irp ) { return STATUS_SUCCESS; } NTSTATUS MPIOPdoPnPNotification( IN PDEVICE_OBJECT MPDiskObject, IN PDEVICE_OBJECT FilterObject, IN PIRP Irp ) { return STATUS_SUCCESS; } NTSTATUS MPIODsmFinalInit( IN PMPDISK_EXTENSION DiskExtension, IN PDEVICE_OBJECT DevFilter, IN PREAL_DEV_INFO TargetInfo ) /*++ Routine Description: This routine handles giving the dsm the remaining info. that it needs (PathId, it's own DSMID, and target object matchup). Also, determines whether the path is active. Arguments: DiskExtension - The type extension for the pseudo-disk DevFilter - MPDev's object - the target object for I/O TargetInfo - Collection of DSM related info. Return Value: NTSTATUS --*/ { PDSM_ENTRY dsm = &DiskExtension->DsmInfo; ULONGLONG controllerId; NTSTATUS status; BOOLEAN active; // // Need to give info about this to the DSM // TargetInfo->PathId = TargetInfo->AdapterFilter; status = dsm->SetDeviceInfo(dsm->DsmContext, DevFilter, TargetInfo->DsmID, &TargetInfo->PathId); if (!NT_SUCCESS(status)) { // // LOG // NOTE: This is really a fatal error, as there is no mapping // in the DSM for Path, DsmID, and the target D.O. // Figure out how to go into limp-home mode. // } else { // // If the DSM updated "pathID" need to encapsulate this and // the AdapterFilter. Be sure and use // if (TargetInfo->PathId != TargetInfo->AdapterFilter) { MPDebugPrint((2, "MPIODsmFinalInit: DSM updated PathID (%x) -> (%x)\n", TargetInfo->AdapterFilter, TargetInfo->PathId)); } // // Have everything needed now, so create a path entry. // status = MPIOCreatePathEntry(DiskExtension->ControlObject, TargetInfo->AdapterFilter, TargetInfo->PortFdo, TargetInfo->PathId); if (NT_SUCCESS(status)) { // // See if there is already an ID for this path. // This routine will create it, if not. // TargetInfo->Identifier = MPIOCreateUID(DiskExtension->ControlObject, TargetInfo->PathId); if (TargetInfo->Identifier != 0) { TargetInfo->PathUIDValue = TRUE; } } else { MPDebugPrint((0, "MPIODsmFinalInit: Couldn't create the path entry (%x)\n", status)); // // LOG // } // // Verify the path before going on. // status = dsm->PathVerify(dsm->DsmContext, TargetInfo->DsmID, TargetInfo->PathId); if (status != STATUS_SUCCESS) { MPDebugPrint((0, "MPIODsmFinalInit: PathVerify failed. Dsm (%x). TargetInfo (%x)\n", dsm, TargetInfo)); DbgBreakPoint(); // // Can't use this. Tear down this entry. // //TODO // status = STATUS_SUCCESS; } // // Determine if this path is active. // active = dsm->IsPathActive(dsm->DsmContext, TargetInfo->PathId); if (active) { // // Indicate this. // TargetInfo->PathActive = TRUE; } // // Get the controller info for the DSM ID on this device. // controllerId = MPIOBuildControllerInfo(DiskExtension->ControlObject, dsm, TargetInfo->DsmID); TargetInfo->ControllerId = controllerId; MPDebugPrint((1, "DsmFinalInit: TargetInfo (%x) now fully init'ed\n", TargetInfo)); TargetInfo->DSMInit = TRUE; } return status; } NTSTATUS MPIOPdoRegistration( IN PDEVICE_OBJECT MPDiskObject, IN PDEVICE_OBJECT FilterObject, IN PDEVICE_OBJECT LowerDevice, IN OUT PMPIO_PDO_INFO PdoInformation ) /*++ Routine Description: This routine handles setting up communication and passing of info. between the device filter and the mpdisk. Arguments: MPDiskObject - Pseudo-disk that contains the real device. FilterObject - MPDev's object LowerDevice - The real pdo. PdoInformation - Entry points for the filter to call. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = MPDiskObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; ULONG i; NTSTATUS status; // // Ensure that the passed in MPDiskObject and LowerDevice // are correct. // The DsmIdList is the list of scisport pdo's. This is just // to ensure that the device id'ed by LowerDevice is in the list // and to get it's index for use below. // for (i = 0; i < diskExtension->TargetInfoCount; i++) { if (diskExtension->TargetInfo[i].PortPdo == LowerDevice) { break; } } if (i == diskExtension->TargetInfoCount) { return STATUS_NO_SUCH_DEVICE; } // // Capture the dev filter's object. This is the target for // I/O's. // diskExtension->TargetInfo[i].DevFilter = FilterObject; // // Now indicate that we are ready. // MPDiskObject->Flags &= ~DO_DEVICE_INITIALIZING; // // Finish setting up the DSM. // status = MPIODsmFinalInit(diskExtension, FilterObject, &diskExtension->TargetInfo[i]); ASSERT(status == STATUS_SUCCESS); // // Fill in the routines that the dev filter can use for // power and pnp. // PdoInformation->DevicePowerNotify = MPIOPdoPowerNotification; PdoInformation->DevicePnPNotify = MPIOPdoPnPNotification; PdoInformation->PdoObject = MPDiskObject; return STATUS_SUCCESS; } NTSTATUS MPIOPdoDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; PDEVICE_OBJECT targetObject; // // These should just be IOCTL_STORAGE_XXX and IOCTL_SCSI_XXX // if ((controlCode == IOCTL_STORAGE_QUERY_PROPERTY) || (controlCode == IOCTL_SCSI_GET_ADDRESS) || (controlCode == IOCTL_SCSI_GET_ADDRESS) || (controlCode == IOCTL_SCSI_PASS_THROUGH) || (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) || (controlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) || (controlCode == IOCTL_DISK_GET_MEDIA_TYPES)) { } else { MPDebugPrint((0, "PdoDeviceControl: Unhandled IOCTL Code (%x)\n", controlCode)); } IoMarkIrpPending(Irp); // // Set-up the completion routine. // TODO: Yank this and setting up of context into single routine. // IoSetCompletionRoutine(Irp, MPPdoGlobalCompletion, context, TRUE, TRUE, TRUE); // // Categorization of these should be unneccesary. // NOTE: Verify this assertion, and also ensure that the only requests // coming here are the ones noted above. // // Send these to the first device in the multi-path group. // targetObject = diskExtension->TargetInfo[0].DevFilter; // // Save the real pdo in the context. // context->TargetInfo = (diskExtension->TargetInfo); MPDebugPrint((3, "MPIOPdoDeviceControl: Context (%x) TargetInfo (%x)\n", context, context->TargetInfo)); // // Set-up our context info. // NOTE: Validate whether the DSM will need the opportunity to set it's // completion. // context->DsmCompletion.DsmCompletionRoutine = NULL; context->DsmCompletion.DsmContext = NULL; // // Indicate that a request is outstanding to this device. // InterlockedIncrement(&diskExtension->TargetInfo[0].Requests); // // Send the request to the device filter. // IoCallDriver(targetObject, Irp); return STATUS_PENDING; } NTSTATUS MPIOReadWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; PREAL_DEV_INFO targetInfo = NULL; PDSM_ENTRY dsm; NTSTATUS status; PVOID pathId; ULONG i; IoMarkIrpPending(Irp); // // Set-up the completion routine. // IoSetCompletionRoutine(Irp, MPPdoGlobalCompletion, context, TRUE, TRUE, TRUE); dsm = &diskExtension->DsmInfo; targetInfo = diskExtension->TargetInfo; // // Ensure that the DSM has been fully init'ed. // if (targetInfo->DSMInit) { // // Call into the DSM to find the path to which the request should be sent. // pathId = dsm->GetPath(dsm->DsmContext, srb, &diskExtension->DsmIdList, diskExtension->CurrentPath, &status); if (pathId == NULL) { // // LOG // This is fatal. Figure out what to do. // Attempt a fail-over, though this probably won't fix anything (all the paths // are dead according to the DSM. TODO // // BUGBUG need to CompleteRequest with an error to cause a fail-over. // Will first have to setup Context. // and remove the following // Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, 8); return status; } else { // // Determine which target info the path is in. // targetInfo = MPIOGetTargetInfo(diskExtension, pathId, NULL); } } else { pathId = targetInfo->AdapterFilter; // // Determine which target info the path is in. // targetInfo = MPIOGetTargetInfo(diskExtension, NULL, pathId); } // // If this path isn't marked active, this indicates a fail-over. // if (targetInfo->PathActive == FALSE) { // // LOG - Run the F.O. Handler. // } if (targetInfo->PathFailed) { MPDebugPrint((0, "MPIOReadWrite: Got back a failed path. (%x)\n", targetInfo)); ASSERT(targetInfo->PathFailed == FALSE); } // // Indicate the new path that is active. // diskExtension->CurrentPath = targetInfo->PathId; // // Save the real pdo in the context. // context->TargetInfo = targetInfo; // // Get a copy of the original srb. // RtlCopyMemory(&context->Srb, srb, sizeof(SCSI_REQUEST_BLOCK)); if (targetInfo->DSMInit) { // // Now have determined the correct path, allow the DSM // to set-up it's context and completion. // dsm->SetCompletion(dsm->DsmContext, targetInfo->DsmID, Irp, srb, &context->DsmCompletion); } // // Indicate that a request is outstanding to this device. // InterlockedIncrement(&targetInfo->Requests); // // Call the device Filter with the request. // IoCallDriver(targetInfo->DevFilter, Irp); return STATUS_PENDING; } NTSTATUS MPIOPdoHandleRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; PREAL_DEV_INFO targetInfo; PDSM_ENTRY dsm; NTSTATUS status; KEVENT event; UCHAR opCode; PVOID pathId; ULONG action; KIRQL irql; // // Categorize the I/O // opCode = srb->Cdb[0]; if ((opCode == SCSIOP_READ) || (opCode == SCSIOP_WRITE) || (opCode == SCSIOP_VERIFY)) { // // Call the Read/Write Handler. // return MPIOReadWrite(DeviceObject, Irp); } // // The request is something other than a read/write request. // Get the DSM information. // dsm = &diskExtension->DsmInfo; targetInfo = diskExtension->TargetInfo; if (targetInfo->DSMInit == FALSE) { // // Not fully initialized yet. // context->DsmCompletion.DsmCompletionRoutine = NULL; context->DsmCompletion.DsmContext = NULL; // // Indicate the current path. // diskExtension->CurrentPath = targetInfo->PathId; // // Save the real pdo in the context. // context->TargetInfo = targetInfo; IoMarkIrpPending(Irp); // // Set-up the completion routine. // IoSetCompletionRoutine(Irp, MPPdoGlobalCompletion, context, TRUE, TRUE, TRUE); // // Get a copy of the original srb. // RtlCopyMemory(&context->Srb, srb, sizeof(SCSI_REQUEST_BLOCK)); // // Indicate that a request is outstanding to this device. // InterlockedIncrement(&targetInfo->Requests); // // Issue request to pathId specified. // return IoCallDriver(targetInfo->DevFilter, Irp); } action = dsm->CategorizeRequest(dsm->DsmContext, &diskExtension->DsmIdList, Irp, srb, diskExtension->CurrentPath, &pathId, &status); if (action == DSM_PATH_SET) { IoMarkIrpPending(Irp); // // Set-up the completion routine. // IoSetCompletionRoutine(Irp, MPPdoGlobalCompletion, context, TRUE, TRUE, TRUE); // // DSM has set the path to which the request should be sent. // // Get the target info corresponding to the pathId returned // by the DSM. // targetInfo = MPIOGetTargetInfo(diskExtension, pathId, NULL); if (targetInfo->PathFailed) { MPDebugPrint((0, "MPIOHandleRequest: Got back a failed path. (%x)\n", targetInfo)); ASSERT(targetInfo->PathFailed == FALSE); } // // Save the real pdo in the context. // context->TargetInfo = targetInfo; // // Get a copy of the original srb. // RtlCopyMemory(&context->Srb, srb, sizeof(SCSI_REQUEST_BLOCK)); MPDebugPrint((2, "Categorize: Context (%x). TargetInfo (%x)\n", context, context->TargetInfo)); // // Allow the DSM to set-up completion info. // The DSM has the responsibility for the allocation and free // of it's own context. // dsm->SetCompletion(dsm->DsmContext, targetInfo->DsmID, Irp, srb, &context->DsmCompletion); // // Indicate the current path. // diskExtension->CurrentPath = targetInfo->PathId; // // Indicate that a request is outstanding to this device. // InterlockedIncrement(&targetInfo->Requests); // // Issue request to pathId specified. // status = IoCallDriver(targetInfo->DevFilter, Irp); } else { // // DSM wants to handle it internally. // // Init the event. We wait below, and the DSM // sets it when it's finished handling the request. // KeInitializeEvent(&event, NotificationEvent, FALSE); if (action == DSM_BROADCAST) { // // Give it to the DSM's Broadcast routine. The request // will be sent down all paths, and possibly be modified // from the original OpCode. // status = dsm->BroadcastSrb(dsm->DsmContext, &diskExtension->DsmIdList, Irp, srb, &event); } else if (action == DSM_WILL_HANDLE) { // // Hand the request off to the DSM. // status = dsm->SrbDeviceControl(dsm->DsmContext, &diskExtension->DsmIdList, Irp, srb, &event); } else { // // It's broken (DSM_ERROR). // // TODO handle this // Call InterpretError to see if it's a fail-over. // Set-up a bogus assert for the time being, until // this case is actually handled. // // Status is set by the DSM. Go ahead and propogate that // back. // ASSERT(action == DSM_BROADCAST); } if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } else { // // Ensure that the DSM is returning status // correctly. // ASSERT(status == Irp->IoStatus.Status); } // // The calling routine expects these to be completed here. // IoCompleteRequest(Irp, IO_NO_INCREMENT); InterlockedDecrement(&diskExtension->OutstandingRequests); } return status; } NTSTATUS MPIOExecuteNone( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; NTSTATUS status; PREAL_DEV_INFO targetInfo = diskExtension->TargetInfo; // // If the path UID isn't set, try again. // if (targetInfo->PathUIDValue == FALSE) { targetInfo->Identifier = MPIOCreateUID(diskExtension->ControlObject, targetInfo->PathId); if (targetInfo->Identifier != 0) { targetInfo->PathUIDValue = TRUE; } } // // Set-up NULL context info as none of these requires // a special completion routine. // context->DsmCompletion.DsmCompletionRoutine = NULL; context->DsmCompletion.DsmContext = NULL; switch (srb->Function) { // // If it's a Claim ore Release, handle it here. // TODO: Lock these accesses. // case SRB_FUNCTION_CLAIM_DEVICE: // // Determine if a claim has already been made. // if (diskExtension->IsClaimed) { // // Already been claimed. Return that the device // is busy. // srb->SrbStatus = SRB_STATUS_BUSY; status = STATUS_DEVICE_BUSY; } else { // // Indicate that the claim has been made // and return this deviceObject. // diskExtension->IsClaimed = TRUE; srb->DataBuffer = DeviceObject; status = STATUS_SUCCESS; } break; case SRB_FUNCTION_RELEASE_DEVICE: // // This is always successful. // srb->SrbStatus = SRB_STATUS_SUCCESS; status = STATUS_SUCCESS; break; default: // // These aren't handled - check to // see whether any do end up here. // LOG the error. // srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STATUS_INVALID_DEVICE_REQUEST; MPDebugPrint((1, "MPIOInternalDevControl: Unhandled Srb Function (%x)\n", srb->Function)); DbgBreakPoint(); break; } return status; } NTSTATUS MPIOPdoInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; NTSTATUS status; BOOLEAN completeRequest = TRUE; // // Determine the what the request is. // The only requests handled here are EXECUTE_NONE // switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SCSI_EXECUTE_NONE: // // Handle all of the EXECUTE_NONE requests here. // This routine won't complete the request, so // do it below. // status = MPIOExecuteNone(DeviceObject, Irp); break; default: // // This routine will categorise the I/O and execute // the appropriate handler. // status = MPIOPdoHandleRequest(DeviceObject, Irp); // // Don't complete these // completeRequest = FALSE; break; } if (completeRequest) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); InterlockedDecrement(&diskExtension->OutstandingRequests); } return status; } NTSTATUS MPIOPdoUnhandled( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; // // See what IRP_MJ the request is. // MPDebugPrint((1, "MPIOPdoUnhandled: Major %x, Minor %x\n", irpStack->MajorFunction, irpStack->MinorFunction)); context->DsmCompletion.DsmCompletionRoutine = NULL; context->DsmCompletion.DsmContext = NULL; Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); InterlockedDecrement(&diskExtension->OutstandingRequests); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS MPIOPdoPnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; NTSTATUS status; context->DsmCompletion.DsmCompletionRoutine = NULL; context->DsmCompletion.DsmContext = NULL; // // Determine the MinorFunction and call the appropriate // helper function. // switch (irpStack->MinorFunction) { case IRP_MN_QUERY_DEVICE_RELATIONS: // // Call the Qdr handler. // status = MPIOPdoQdr(DeviceObject, Irp); break; case IRP_MN_QUERY_ID: // // Call the QueryId routine. // status = MPIOPdoQueryId(DeviceObject, Irp); break; case IRP_MN_DEVICE_USAGE_NOTIFICATION: // // Adjust the various counters depending upon the // Type of Usage Notification. // // Invalidate the device state // // complete the request. // //status = MPIODeviceUsage(DeviceObject, // Irp); status = STATUS_INVALID_DEVICE_REQUEST; break; case IRP_MN_QUERY_DEVICE_TEXT: // // Get the device text or location. // status = MPIOQueryDeviceText(DeviceObject, Irp); break; case IRP_MN_QUERY_CAPABILITIES: { PDEVICE_CAPABILITIES capabilities; capabilities = irpStack->Parameters.DeviceCapabilities.Capabilities; // // Indicate no function driver is really necessary, suppress PnP Pop-ups // and the MPDisk's Id // capabilities->RawDeviceOK = 1; capabilities->SilentInstall = 1; capabilities->Address = diskExtension->DeviceOrdinal; status = STATUS_SUCCESS; break; } case IRP_MN_QUERY_PNP_DEVICE_STATE: { PPNP_DEVICE_STATE deviceState = (PPNP_DEVICE_STATE) &(Irp->IoStatus.Information); status = STATUS_SUCCESS; // // TODO: This depends on whether this is in the hibernate/page path. // Implement the check, and implement the IRP_MN_DEVICE_USAGE_NOTIFICATION // *deviceState |= PNP_DEVICE_NOT_DISABLEABLE; break; } case IRP_MN_QUERY_RESOURCES: case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: // // Don't need resources and don't have a boot config. // Complete it successfully with a NULL buffer. // status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR)NULL; break; case IRP_MN_STOP_DEVICE: status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR) NULL; break; case IRP_MN_START_DEVICE: // // Register as a WMI provider. // IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER); // // Start the per-disk timer. // IoStartTimer(DeviceObject); status = STATUS_SUCCESS; break; case IRP_MN_QUERY_REMOVE_DEVICE: MPDebugPrint((1, "MPIOPdoPnp: QueryRemove on (%x)\n", DeviceObject)); status = STATUS_DEVICE_BUSY; break; case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: case IRP_MN_CANCEL_REMOVE_DEVICE: case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_REMOVE_DEVICE: // // TODO // status = STATUS_SUCCESS; break; // // Send these down // case IRP_MN_READ_CONFIG: case IRP_MN_WRITE_CONFIG: case IRP_MN_EJECT: case IRP_MN_SET_LOCK: case IRP_MN_QUERY_BUS_INFORMATION: InterlockedDecrement(&diskExtension->OutstandingRequests); return MPIOForwardRequest(DeviceObject, Irp); break; // // The following requests are completed without status // being updated. // case IRP_MN_QUERY_LEGACY_BUS_INFORMATION: status = Irp->IoStatus.Status; break; case IRP_MN_QUERY_INTERFACE: MPDebugPrint((2, "MPIOPdoPnP: Query Interface\n")); default: MPDebugPrint((2, "MPIOPdoPnP: Not handled - (%x)\n", irpStack->MinorFunction)); DbgBreakPoint(); status = Irp->IoStatus.Status; break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); InterlockedDecrement(&diskExtension->OutstandingRequests); return status; } NTSTATUS MPIOPdoPowerCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } PoStartNextPowerIrp(Irp); InterlockedDecrement(&diskExtension->OutstandingRequests); return STATUS_SUCCESS; } NTSTATUS MPIOPdoPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PMPDISK_EXTENSION diskExtension = deviceExtension->TypeExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PMPIO_CONTEXT context = irpStack->Parameters.Others.Argument4; MPDebugPrint((2, "MPIOPdoPower: Got a %x power irp\n", irpStack->MinorFunction)); context->DsmCompletion.DsmCompletionRoutine = NULL; context->DsmCompletion.DsmContext = NULL; IoSetCompletionRoutine(Irp, MPIOPdoPowerCompletion, context, TRUE, TRUE, TRUE); Irp->IoStatus.Status = STATUS_SUCCESS; PoCallDriver(deviceExtension->LowerDevice, Irp); return STATUS_PENDING; }