windows-nt/Source/XPSP1/NT/drivers/storage/mpath/lib/mplib.c
2020-09-26 16:20:57 +08:00

945 lines
22 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <ntddk.h>
#include "mplib.h"
#include <stdio.h>
#include <stdarg.h>
#include <ntddscsi.h>
#include <scsi.h>
typedef struct _MPLIB_COMPLETION_CONTEXT {
PDEVICE_OBJECT DeviceObject;
KEVENT Event;
NTSTATUS Status;
PSCSI_REQUEST_BLOCK Srb;
PSENSE_DATA SenseBuffer;
} MPLIB_COMPLETION_CONTEXT, *PMPLIB_COMPLETION_CONTEXT;
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
ULONG DontLoad = 0;
NTSTATUS
MPLIBSignalCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This completion routine will signal the event given as context and then
return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
the responsibility of the routine waiting on the event to complete the
request and free the event.
Arguments:
DeviceObject - a pointer to the device object
Irp - a pointer to the irp
Event - a pointer to the event to signal
Return Value:
STATUS_MORE_PROCESSING_REQUIRED
--*/
{
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
MPLIBSendIrpSynchronous(
IN PDEVICE_OBJECT TargetDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine issues an irp synchronously.
Arguments:
TargetDeviceObject - Recepient of the request.
Irp - Irp to send.
Return Value:
Status of the request
--*/
{
KEVENT event;
NTSTATUS status;
//
// Ensure enough stack locations are available.
//
ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
//
// Initialize the event that will be set by the completion
// routine.
//
KeInitializeEvent(&event,
SynchronizationEvent,
FALSE);
//
// Set the completion routine - the event is the context.
//
IoSetCompletionRoutine(Irp,
MPLIBSignalCompletion,
&event,
TRUE,
TRUE,
TRUE);
//
// Submit the request.
//
status = IoCallDriver(TargetDeviceObject, Irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE,
NULL);
status = Irp->IoStatus.Status;
}
return status;
}
VOID
MPLIBSendDeviceIoControlSynchronous(
IN ULONG IoControlCode,
IN PDEVICE_OBJECT TargetDeviceObject,
IN PVOID InputBuffer OPTIONAL,
IN OUT PVOID OutputBuffer OPTIONAL,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength,
IN BOOLEAN InternalDeviceIoControl,
OUT PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
This routine builds and sends the IOCTL specified by IoControlCode to
TargetDeviceObject.
Arguments:
IoControlCode - IOCTL to send.
TargetDeviceObject - Recepient of the request.
Buffer - Input/output buffer.
InputBufferLength - Size, in bytes of the inputbuffer.
OutputBufferLength - Size, in bytes of the output buffer.
InternalDeviceIocontrol - Specifies whethers it's internal or not.
IoStatus - Pointer to caller's iostatus block.
Return Value:
Status of the request
--*/
{
PIRP irp = NULL;
PIO_STACK_LOCATION irpStack;
ASSERT((IoControlCode & 3) == METHOD_BUFFERED);
ASSERT(ARGUMENT_PRESENT(IoStatus));
//
// Ensure that if either buffer length is set, that there is actually
// a buffer.
//
if (InputBufferLength) {
if (InputBuffer == NULL) {
(*IoStatus).Status = STATUS_BUFFER_TOO_SMALL;
(*IoStatus).Information = 0;
return;
}
}
if (OutputBufferLength) {
if (OutputBuffer == NULL) {
(*IoStatus).Status = STATUS_BUFFER_TOO_SMALL;
(*IoStatus).Information = 0;
return;
}
}
//
// Allocate an irp.
//
irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
if (irp == NULL) {
(*IoStatus).Information = 0;
(*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
return;
}
//
// Get the recipient's irpstack location.
//
irpStack = IoGetNextIrpStackLocation(irp);
//
// Set the major function code based on the type of device I/O control
// function the caller has specified.
//
if (InternalDeviceIoControl) {
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
} else {
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
}
//
// Fill in the ioControl parameters.
//
irpStack->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
irpStack->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
irpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
//
// Allocate the SystemBuffer, if needed.
//
if (InputBufferLength || OutputBufferLength) {
irp->AssociatedIrp.SystemBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
max(InputBufferLength, OutputBufferLength));
if (irp->AssociatedIrp.SystemBuffer == NULL) {
IoFreeIrp(irp);
(*IoStatus).Information = 0;
(*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
return;
}
//
// If passing info to TargetDevice, copy the caller's data
// into the system buffer.
//
if (InputBufferLength) {
RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
InputBuffer,
InputBufferLength);
}
}
irp->UserBuffer = OutputBuffer;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
//
// send the irp synchronously
//
MPLIBSendIrpSynchronous(TargetDeviceObject, irp);
//
// copy the iostatus block for the caller
//
*IoStatus = irp->IoStatus;
//
// If there's an ouputbuffer, copy the results.
//
if (OutputBufferLength && (IoControlCode != IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
RtlCopyMemory(OutputBuffer,
irp->AssociatedIrp.SystemBuffer,
OutputBufferLength
);
}
//
// Free the allocations.
//
if (InputBufferLength || OutputBufferLength) {
ExFreePool(irp->AssociatedIrp.SystemBuffer);
irp->AssociatedIrp.SystemBuffer = NULL;
}
IoFreeIrp(irp);
irp = (PIRP) NULL;
return;
}
NTSTATUS
MPLIBGetDescriptor(
IN PDEVICE_OBJECT DeviceObject,
IN PSTORAGE_PROPERTY_ID PropertyId,
OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
)
/*++
Routine Description:
This routine will perform a query for the specified property id and will
allocate a non-paged buffer to store the data in. It is the responsibility
of the caller to ensure that this buffer is freed.
Arguments:
DeviceObject - the device to query
DeviceInfo - a location to store a pointer to the buffer we allocate
Return Value:
status.
--*/
{
STORAGE_PROPERTY_QUERY query;
PIO_STATUS_BLOCK ioStatus;
PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
ULONG length;
//
// Poison the passed in descriptor.
//
*Descriptor = NULL;
//
// Setup the query buffer.
//
query.PropertyId = *PropertyId;
query.QueryType = PropertyStandardQuery;
query.AdditionalParameters[0] = 0;
ioStatus = ExAllocatePool(NonPagedPool, sizeof(IO_STATUS_BLOCK));
ASSERT(ioStatus);
ioStatus->Status = 0;
ioStatus->Information = 0;
//
// On the first call, just need to get the length of the descriptor.
//
descriptor = (PVOID)&query;
MPLIBSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY,
DeviceObject,
&query,
&query,
sizeof(STORAGE_PROPERTY_QUERY),
sizeof(STORAGE_DESCRIPTOR_HEADER),
FALSE,
ioStatus);
if(!NT_SUCCESS(ioStatus->Status)) {
MPDebugPrint((0,
"MPLIBGetDescriptor: Query failed (%x) on attempt 1\n",
ioStatus->Status));
return ioStatus->Status;
}
ASSERT(descriptor->Size);
if (descriptor->Size == 0) {
return STATUS_UNSUCCESSFUL;
}
//
// This time we know how much data there is so we can
// allocate a buffer of the correct size
//
length = descriptor->Size;
descriptor = ExAllocatePoolWithTag(NonPagedPool, length, 'BLPM');
if(descriptor == NULL) {
MPDebugPrint((0,
"MPLIBGetDescriptor: Couldn't allocate descriptor of %ld\n",
length));
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// setup the query again.
//
query.PropertyId = *PropertyId;
query.QueryType = PropertyStandardQuery;
query.AdditionalParameters[0] = 0;
//
// copy the input to the new outputbuffer
//
RtlCopyMemory(descriptor,
&query,
sizeof(STORAGE_PROPERTY_QUERY));
MPLIBSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY,
DeviceObject,
descriptor,
descriptor,
sizeof(STORAGE_PROPERTY_QUERY),
length,
0,
ioStatus);
if(!NT_SUCCESS(ioStatus->Status)) {
MPDebugPrint((0,
"MPLIBGetDescriptor: Query Failed (%x) on attempt 2\n",
ioStatus->Status));
ExFreePool(descriptor);
return ioStatus->Status;
}
//
// return the memory we've allocated to the caller
//
*Descriptor = descriptor;
return ioStatus->Status;
}
NTSTATUS
MPLibReleaseQueueCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
/*++
Routine Description:
Arguments:
DeviceObject - The device object for the logical unit; however since this
is the top stack location the value is NULL.
Irp - Supplies a pointer to the Irp to be processed.
Context - Supplies the context to be used to process this request.
Return Value:
None.
--*/
{
PKEVENT event = Context;
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
KeSetEvent(event, 0 , FALSE);
//
// Free the Irp.
//
IoFreeIrp(Irp);
//
// Indicate the I/O system should stop processing the Irp completion.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
MPLibReleaseQueue(
IN PDEVICE_OBJECT ChildDevice
)
/*++
Routine Description:
This routine issues a Release Queue to the port driver for Child device.
Arguments:
ChildDevice - Device Object for a scsiport child returned in QDR.
Return Value:
Status of the request
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpStack;
KEVENT event;
NTSTATUS status;
PSCSI_REQUEST_BLOCK srb;
//
// Set the event object to the unsignaled state.
// It will be used to signal request completion
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
irp = IoAllocateIrp((CCHAR)(ChildDevice->StackSize),
FALSE);
if (irp) {
srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
if (srb) {
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
//
// Construct the IRP stack for the lower level driver.
//
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
irpStack->Parameters.Scsi.Srb = srb;
IoSetCompletionRoutine(irp,
MPLibReleaseQueueCompletion,
&event,
TRUE,
TRUE,
TRUE);
//
// Setup the SRB.
//
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
srb->OriginalRequest = irp;
} else {
IoFreeIrp(irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
} else {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Call the port driver with the request and wait for it to complete.
//
status = IoCallDriver(ChildDevice, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
}
ExFreePool(srb);
return status;
}
NTSTATUS
MPLibTURCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
/*++
Routine Description:
This routine is called when an asynchronous I/O request
which was issused by the dsm completes. Examples of such requests
are release queue or test unit ready. This routine releases the queue if
necessary. It then frees the context and the IRP.
Arguments:
DeviceObject - The device object for the logical unit; however since this
is the top stack location the value is NULL.
Irp - Supplies a pointer to the Irp to be processed.
Context - Supplies the context to be used to process this request.
Return Value:
None.
--*/
{
PMPLIB_COMPLETION_CONTEXT context = Context;
PSCSI_REQUEST_BLOCK srb;
srb = context->Srb;
//
// If this is an execute srb, then check the return status and make sure.
// the queue is not frozen.
//
if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
//
// Check for a frozen queue.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
//
// Unfreeze the queue getting the device object from the context.
//
MPDebugPrint((2,
"DsmCompletion: Queue is frozen!!!!\n"));
MPLibReleaseQueue(context->DeviceObject);
}
}
context->Status = Irp->IoStatus.Status;
//
// Free the Irp.
//
IoFreeIrp(Irp);
KeSetEvent(&context->Event, 0, FALSE);
//
// Indicate the I/O system should stop processing the Irp completion.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
InterpretSenseInfo(
IN PSENSE_DATA SenseData,
IN ULONG SenseLength
)
{
UCHAR senseKey = SenseData->SenseKey & 0xF;
UCHAR asc = SenseData->AdditionalSenseCode;
UCHAR ascq = SenseData->AdditionalSenseCodeQualifier;
switch (senseKey) {
case SCSI_SENSE_NOT_READY:
return STATUS_DEVICE_NOT_READY;
case SCSI_SENSE_DATA_PROTECT:
return STATUS_MEDIA_WRITE_PROTECTED;
case SCSI_SENSE_MEDIUM_ERROR:
return STATUS_DEVICE_DATA_ERROR;
case SCSI_SENSE_ILLEGAL_REQUEST:
return STATUS_INVALID_DEVICE_REQUEST;
case SCSI_SENSE_BLANK_CHECK:
return STATUS_NO_DATA_DETECTED;
case SCSI_SENSE_RECOVERED_ERROR:
return STATUS_SUCCESS;
case SCSI_SENSE_HARDWARE_ERROR:
case SCSI_SENSE_UNIT_ATTENTION:
case SCSI_SENSE_NO_SENSE:
case SCSI_SENSE_ABORTED_COMMAND:
default:
return STATUS_IO_DEVICE_ERROR;
}
return STATUS_IO_DEVICE_ERROR;
}
NTSTATUS
InterpretScsiStatus(
IN PUCHAR SenseBuffer,
IN ULONG SenseLength,
IN UCHAR ScsiStatus
)
{
NTSTATUS status;
switch (ScsiStatus) {
case SCSISTAT_CHECK_CONDITION: {
if (SenseBuffer && SenseLength) {
status = InterpretSenseInfo((PSENSE_DATA)SenseBuffer,
SenseLength);
} else {
status = STATUS_IO_DEVICE_ERROR;
}
break;
}
case SCSISTAT_BUSY:
status = STATUS_DEVICE_NOT_READY;
break;
case SCSISTAT_RESERVATION_CONFLICT:
status = STATUS_DEVICE_BUSY;
break;
default:
status = STATUS_IO_DEVICE_ERROR;
break;
}
return status;
}
NTSTATUS
MPLibSendPassThroughDirect(
IN PDEVICE_OBJECT DeviceObject,
IN PSCSI_PASS_THROUGH_DIRECT ScsiPassThrough,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
{
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
UCHAR scsiStatus;
MPLIBSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT,
DeviceObject,
ScsiPassThrough,
ScsiPassThrough->DataBuffer,
InputBufferLength,
OutputBufferLength,
FALSE,
&ioStatus);
//
// Check the iostatus. STATUS_SUCCESS really only means that the request
// was sent successfully, not that it did what it was supposed to.
//
status = ioStatus.Status;
if (NT_SUCCESS(status)) {
//
// Get the scsi status.
//
scsiStatus = ScsiPassThrough->ScsiStatus;
if (scsiStatus == SCSISTAT_GOOD) {
status = STATUS_SUCCESS;
} else {
PUCHAR senseBuffer;
ULONG senseLength;
senseBuffer = (PUCHAR)ScsiPassThrough;
(ULONG_PTR)senseBuffer += ScsiPassThrough->SenseInfoOffset;
senseLength = ScsiPassThrough->SenseInfoLength;
status = InterpretScsiStatus(senseBuffer, senseLength, scsiStatus);
}
}
return status;
}
NTSTATUS
MPLibSendTUR(
IN PDEVICE_OBJECT TargetDevice
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
PIO_STACK_LOCATION irpStack;
PMPLIB_COMPLETION_CONTEXT completionContext;
PIRP irp;
PSCSI_REQUEST_BLOCK srb;
PSENSE_DATA senseData;
NTSTATUS status;
PCDB cdb;
ULONG retry = 4;
//
// Allocate an srb, the sense buffer, and context block for the request.
//
srb = ExAllocatePool(NonPagedPool,sizeof(SCSI_REQUEST_BLOCK));
senseData = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(SENSE_DATA));
completionContext = ExAllocatePool(NonPagedPool, sizeof(MPLIB_COMPLETION_CONTEXT));
if ((srb == NULL) || (senseData == NULL) || (completionContext == NULL)) {
if (srb) {
ExFreePool(srb);
}
if (senseData) {
ExFreePool(senseData);
}
if (completionContext) {
ExFreePool(completionContext);
}
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Setup the context.
//
completionContext->DeviceObject = TargetDevice;
completionContext->Srb = srb;
completionContext->SenseBuffer = senseData;
retryRequest:
KeInitializeEvent(&completionContext->Event, NotificationEvent, FALSE);
//
// Zero out srb and sense data.
//
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
RtlZeroMemory(senseData, sizeof(SENSE_DATA));
//
// Build the srb.
//
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->TimeOutValue = 4;
srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
srb->SenseInfoBufferLength = sizeof(SENSE_DATA);
srb->SenseInfoBuffer = senseData;
//
// Build the TUR CDB.
//
srb->CdbLength = 6;
cdb = (PCDB)srb->Cdb;
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
//
// Build the asynchronous request to be sent to the port driver.
// Since this routine is called from a DPC the IRP should always be
// available.
//
irp = IoAllocateIrp(TargetDevice->StackSize + 1, FALSE);
if(irp == NULL) {
ExFreePool(srb);
ExFreePool(senseData);
ExFreePool(completionContext);
return STATUS_INSUFFICIENT_RESOURCES;
}
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_SCSI;
srb->OriginalRequest = irp;
//
// Store the SRB address in next stack for port driver.
//
irpStack->Parameters.Scsi.Srb = srb;
IoSetCompletionRoutine(irp,
(PIO_COMPLETION_ROUTINE)MPLibTURCompletion,
completionContext,
TRUE,
TRUE,
TRUE);
//
// Call the port driver with the IRP.
//
status = IoCallDriver(TargetDevice, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&completionContext->Event,
Executive,
KernelMode,
FALSE,
NULL);
status = completionContext->Status;
}
if ((status != STATUS_SUCCESS) && retry--) {
goto retryRequest;
}
//
// Free the allocations.
//
ExFreePool(completionContext);
ExFreePool(srb);
ExFreePool(senseData);
return status;
}
#if 1
UCHAR DebugBuffer[DEBUG_BUFFER_LENGTH + 1];
ULONG MPathDebug = 1;
VOID
MPathDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print for multi-path components.
Arguments:
Return Value:
None
--*/
{
va_list ap;
va_start(ap, DebugMessage);
if (DebugPrintLevel <= MPathDebug) {
_vsnprintf(DebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
DbgPrint(DebugBuffer);
}
va_end(ap);
}
#else
VOID
MPathDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
{
}
#endif