windows-nt/Source/XPSP1/NT/drivers/storage/mpath/device/mpdev.c

829 lines
19 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 <ntddk.h>
#include <stdio.h>
#include <stdarg.h>
#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;
}