|
|
#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; }
|