/*++ Copyright (C) 1999 Microsoft Corporation Module Name: mpdev.c Abstract: This driver acts as a lower-filter over the device pdo's and provides the support necessary for multipathing. It's main function is keep mpctl.sys (main multipath module) informed of system state. Author: Environment: kernel mode only Notes: Revision History: --*/ #include #include #include #include "mpdevf.h" #include "scsi.h" #include "mplib.h" typedef struct _DEVICE_EXTENSION { // // Backpointer to our device object. // PDEVICE_OBJECT DeviceObject; // // Pointer to the lower object. // PDEVICE_OBJECT TargetDevice; // // The PDisk device object information. // MPIO_PDO_INFO PdoInfo; // // MpCtl's Control device Object. // PDEVICE_OBJECT ControlDevice; // // State flags of this (and the lower device). // ULONG DeviceState; // // General purpose event. // KEVENT Event; // // Counters for IOs // ULONG NumberReads; ULONG NumberWrites; BOOLEAN FailureSimulation; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; // // Entry point decls // NTSTATUS MpDevDefaultDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MpDevInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MpDevDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MpDevPnPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MpDevPowerDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS MpDevAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ); NTSTATUS MpDevUnload( IN PDRIVER_OBJECT DriverObject ); // // Internal function decls // NTSTATUS MpDevStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); // // The code. // 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 --*/ { ULONG i; MPDebugPrint((0, "MpDev: DriverEntry\n")); // // Setup forwarder for ALL Irp functions. // for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = MpDevDefaultDispatch; } // // Specify those that we are interested in. // DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MpDevInternalDeviceControl; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MpDevDeviceControl; DriverObject->MajorFunction[IRP_MJ_PNP] = MpDevPnPDispatch; DriverObject->MajorFunction[IRP_MJ_POWER] = MpDevPowerDispatch; DriverObject->DriverUnload = MpDevUnload; DriverObject->DriverExtension->AddDevice = MpDevAddDevice; return STATUS_SUCCESS; } NTSTATUS MpDevDefaultDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine sends the Irp to the next driver in line when the Irp is not processed by this driver. Arguments: DeviceObject Irp Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(deviceExtension->TargetDevice, Irp); } NTSTATUS MpDevClaimDevice( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Claim ) /*++ Routine Description: This is used to claim the underlying port pdo, so that only the mpdisk will be used by the class drivers. Arguments: DeviceObject - Supplies the scsiport device object. Claim - Indica0tes whether the device should be claimed or released. Return Value: Returns a status indicating success or failure of the operation. --*/ { IO_STATUS_BLOCK ioStatus; PIRP irp; PIO_STACK_LOCATION irpStack; KEVENT event; NTSTATUS status; SCSI_REQUEST_BLOCK srb; RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); // // Set the event object to the unsignaled state. // It will be used to signal request completion // KeInitializeEvent(&event, SynchronizationEvent, FALSE); // // Build synchronous request with no transfer. // irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE, DeviceObject, NULL, 0, NULL, 0, TRUE, &event, &ioStatus); if (irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } irpStack = IoGetNextIrpStackLocation(irp); // // Save SRB address in next stack for port driver. // irpStack->Parameters.Scsi.Srb = &srb; // // Setup the SRB. // srb.Length = SCSI_REQUEST_BLOCK_SIZE; srb.Function = Claim ? SRB_FUNCTION_CLAIM_DEVICE : SRB_FUNCTION_RELEASE_DEVICE; srb.OriginalRequest = irp; // // Call the port driver with the request and wait for it to complete. // status = IoCallDriver(DeviceObject, irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; } return status; } NTSTATUS MpDevInternalDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine handles IRM_MJ_INTERNAL_DEVICE_CONTROL and IRP_MJ_SCSI requests. It's main function is to simulate failed devices or adapters, based on IOCTLs sent from test applications. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb; return MpDevDefaultDispatch(DeviceObject, Irp); } NTSTATUS MpDevDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This device control routine handles only the interactions between this driver and mpctl. The rest are passed down to scsiport. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IO request packet. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { default: return MpDevDefaultDispatch(DeviceObject, Irp); } return status; } NTSTATUS MpDevPowerDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Dispatch routine for Power Irps. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); // // TODO: Snoop these, and if the device is changing state notify mpctl.sys // PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(deviceExtension->TargetDevice, Irp); } NTSTATUS MpDevPnPDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Dispatch routine for PNP Irps. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the I/O request packet. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; KeClearEvent(&deviceExtension->Event); MPDebugPrint((2, "MpDevPnPDispatch: Minor Function (%x) for (%x)\n", irpStack->MinorFunction, DeviceObject)); // // TODO: signal the associated pdisk of all power and pnp events. // switch (irpStack->MinorFunction) { case IRP_MN_QUERY_REMOVE_DEVICE: MPDebugPrint((2, "MPDevPnP: QueryRemove (%x)\n", DeviceObject)); status = STATUS_DEVICE_BUSY; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; case IRP_MN_STOP_DEVICE: MPDebugPrint((2, "MpDevPnP: StopDevice (%x)\n", DeviceObject)); break; case IRP_MN_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: MPDebugPrint((2, "MpDevPnP: RemoveDevice (%x)\n", DeviceObject)); //status = STATUS_SUCCESS; //Irp->IoStatus.Status = status; //IoCompleteRequest(Irp, IO_NO_INCREMENT); //return status; break; case IRP_MN_START_DEVICE: return MpDevStartDevice(DeviceObject, Irp); case IRP_MN_QUERY_CAPABILITIES: case IRP_MN_QUERY_ID: default: return MpDevDefaultDispatch(DeviceObject, Irp); } return MpDevDefaultDispatch(DeviceObject, Irp); } NTSTATUS MpDevAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: Creates and initializes a new filter device object for the corresponding PDO. Then it attaches the device object to the device stack of the drivers for the device. Arguments: DriverObject PhysicalDeviceObject - Physical Device Object from the underlying layered driver Return Value: NTSTATUS --*/ { PDEVICE_OBJECT deviceObject; PDEVICE_OBJECT controlDeviceObject; PDEVICE_EXTENSION deviceExtension; IO_STATUS_BLOCK ioStatus; UNICODE_STRING unicodeName; PFILE_OBJECT fileObject; WCHAR dosDeviceName[40]; MPIO_REG_INFO ctlRegInfo; NTSTATUS status; PIRP irp; PDEVICE_OBJECT attachedDevice; // // Build the mpctl name. // swprintf(dosDeviceName, L"\\DosDevices\\MPathControl"); RtlInitUnicodeString(&unicodeName, dosDeviceName); // // Get mpctl's deviceObject. // status = IoGetDeviceObjectPointer(&unicodeName, FILE_READ_ATTRIBUTES, &fileObject, &controlDeviceObject); if (NT_SUCCESS(status)) { MPIO_PDO_QUERY pdoQuery; pdoQuery.DeviceObject = PhysicalDeviceObject; // // Call mpctl to determine whether PhysicalDeviceObject is // one of it's children or a 'real' scsiport pdo. // MPLIBSendDeviceIoControlSynchronous(IOCTL_MPDEV_QUERY_PDO, controlDeviceObject, &pdoQuery, NULL, sizeof(MPIO_PDO_QUERY), 0, TRUE, &ioStatus); status = ioStatus.Status; MPDebugPrint((2, "MPDevAddDevice: Query on (%x). Status (%x)\n", PhysicalDeviceObject, status)); #if 0 if (status == STATUS_SUCCESS) { // // This indicates that PhysicalDeviceObject is really an MPDisk. // Don't want to load on this. // status = STATUS_NO_SUCH_DEVICE; } else { // // This indicates that PhysicalDeviceObject is a real one. // status = STATUS_SUCCESS; } #endif } if (!NT_SUCCESS(status)) { // // This indicates that PhysicalDeviceObject is really an MPDisk object or // that getting the control object failed. // return status; } if (DontLoad) { return STATUS_NO_SUCH_DEVICE; } status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FILE_DEVICE_DISK, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { MPDebugPrint((0, "MpDevAddDevice: Couldn't create device object (%x)\n", status)); return status; } // // Start building the deviceExtension and attach to the lower device. // deviceExtension = deviceObject->DeviceExtension; deviceExtension->DeviceObject = deviceObject; deviceExtension->TargetDevice = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); if (deviceExtension->TargetDevice == NULL) { MPDebugPrint((0, "MpDevAddDevice: Couldn't attach to device stack\n")); IoDeleteDevice(deviceObject); return STATUS_NO_SUCH_DEVICE; } // // Build IOCTL_MPDEV_REGISTER. This gives the port pdo to // mpctl to match up with one of it's children. // Mpctl then returns the PdoRegistration routine // ctlRegInfo.FilterObject = deviceObject; ctlRegInfo.LowerDevice = deviceExtension->TargetDevice; MPLIBSendDeviceIoControlSynchronous(IOCTL_MPDEV_REGISTER, controlDeviceObject, &ctlRegInfo, &ctlRegInfo, sizeof(MPIO_REG_INFO), sizeof(MPIO_REG_INFO), TRUE, &ioStatus); status = ioStatus.Status; // // Save off mpctls deviceObject. // deviceExtension->ControlDevice = controlDeviceObject; // // Update the flags by ORing in all of the set flags below. // deviceObject->Flags |= deviceExtension->TargetDevice->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE | DO_POWER_INRUSH); // // Sync up other devObj stuff. // deviceObject->DeviceType = deviceExtension->TargetDevice->DeviceType; deviceObject->Characteristics = deviceExtension->TargetDevice->Characteristics; if (status == STATUS_SUCCESS) { // // Register with the MPDisk PDO. It will give back some notification // functions and update it's own internal structures to match // this filter with it's knowledge of scsiport's children. // status = ctlRegInfo.DevicePdoRegister(ctlRegInfo.MPDiskObject, deviceObject, PhysicalDeviceObject, &deviceExtension->PdoInfo); MPDebugPrint((2, "MpDevAddDevice: Status of PdoRegister (%x)\n", status)); if (status == STATUS_SUCCESS) { status = MpDevClaimDevice(deviceExtension->TargetDevice, TRUE); if (status == STATUS_SUCCESS) { MPDebugPrint((2, "MpDevAddDevice: Claim of (%x) successful.\n", deviceExtension->TargetDevice)); } else { MPDebugPrint((1, "MpDevAddDevice: Claim of (%x) not successful. Status (%x)\n", deviceExtension->TargetDevice, status)); } } } // // Indicate that we are ready. // deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; MPDebugPrint((2, "MPDevAddDevice: Returning (%x) on D.O. (%x)\n", status, PhysicalDeviceObject)); return status; } NTSTATUS MpDevUnload( IN PDRIVER_OBJECT DriverObject ) { return STATUS_SUCCESS; } NTSTATUS MpDevSyncCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Completion routine for sync forwarding of Irps. Arguments: DeviceObject Irp Context Return Value: STATUS_MORE_PROCESSING_REQUIRED --*/ { PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION)Context; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; UNREFERENCED_PARAMETER(DeviceObject); // // Set the event on which the dispatch handler is waiting. // KeSetEvent(&deviceExtension->Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS MpDevStartDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Handles Start requests by sending the start down to scsiport. The completion will signal the event and then device object flags and characteristics are updated. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the I/O request packet. Return Value: Status of the operations. --*/ { PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status; // // Setup initial status. // Irp->IoStatus.Status = STATUS_SUCCESS; // // Clone the stack location. // IoCopyCurrentIrpStackLocationToNext(Irp); // // Setup the completion routine. It will set the event that we wait on below. // IoSetCompletionRoutine(Irp, MpDevSyncCompletion, deviceExtension, TRUE, TRUE, TRUE); KeInitializeEvent(&deviceExtension->Event, NotificationEvent, FALSE); // // Call port with the request. // status = IoCallDriver(deviceExtension->TargetDevice, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&deviceExtension->Event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } if (NT_SUCCESS(status)) { // // Sync up our stuff with scsiport's. // DeviceObject->Flags |= deviceExtension->TargetDevice->Flags; DeviceObject->Characteristics |= deviceExtension->TargetDevice->Characteristics; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; }