Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

712 lines
19 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
FspDisp.c
Abstract:
This module implements the main dispatch procedure/thread for the RDBSS Fsp
Author:
Joe Linn [JoeLinn] 1-aug-1994
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "NtDspVec.h"
#include <ntddnfs2.h>
#include <ntddmup.h>
//
// Define our local debug trace level
//
#define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
#ifndef MONOLITHIC_MINIRDR
PIO_WORKITEM RxIoWorkItem;
#endif
//
// Internal support routine, spinlock wrapper.
//
PRX_CONTEXT
RxRemoveOverflowEntry (
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
IN WORK_QUEUE_TYPE Type
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxFspDispatch)
#endif
#ifdef RDBSSLOG
//this stuff must be in nonpaged memory
//// 1 2 3 4 5 6 7
char RxFsp_SurrogateFormat[] = "%S%S%N%N%N%N%N";
//// 2 3 4 5 6 7
char RxFsp_ActualFormat[] = "Fsp %s/%lx %08lx irp %lx thrd %lx #%lx";
#endif //ifdef RDBSSLOG
#ifndef MONOLITHIC_MINIRDR
VOID
RxFspDispatchEx (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
/*++
Routine Description:
This routine calls RxFspDispatch which is the main FSP thread routine that
is executed to receive and dispatch IRP requests.
Arguments:
Context - The RxContext being queued to the FSP.
Notes:
None.
--*/
{
RxFspDispatch( Context );
return;
}
#endif
VOID
RxFspDispatch (
IN PVOID Context
)
/*++
Routine Description:
This is the main FSP thread routine that is executed to receive
and dispatch IRP requests. Each FSP thread begins its execution here.
There is one thread created at system initialization time and subsequent
threads created as needed.
Arguments:
Context - The RxContext being queued to the FSP.
Notes:
This routine never exits
--*/
{
NTSTATUS Status;
PRX_CONTEXT RxContext = (PRX_CONTEXT)Context;
RX_TOPLEVELIRP_CONTEXT TopLevelContext;
PRDBSS_DEVICE_OBJECT RxDeviceObject;
WORK_QUEUE_TYPE WorkQueueType;
DWORD CurrentIrql;
PIRP Irp = RxContext->CurrentIrp;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
PFILE_OBJECT FileObject = IrpSp->FileObject;
PAGED_CODE();
CurrentIrql = KeGetCurrentIrql();
//
// If this request has an associated volume device object, remember it.
//
if (FileObject != NULL ) {
RxDeviceObject = CONTAINING_RECORD( IrpSp->DeviceObject, RDBSS_DEVICE_OBJECT, DeviceObject );
//
// currently, we use the wrapper's device object for all throttling.....
//
RxDeviceObject = RxFileSystemDeviceObject;
} else {
RxDeviceObject = NULL;
}
if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_FSP_DELAYED_OVERFLOW_QUEUE )) {
WorkQueueType = DelayedWorkQueue;
} else if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_FSP_CRITICAL_OVERFLOW_QUEUE )) {
WorkQueueType = CriticalWorkQueue;
} else {
ASSERT(!"Valid RXCONTEXT Work Queue Type");
}
//
// We'll do all of the work within an exception handler that
// will be invoked if ever some underlying operation gets into
// trouble.
//
while (TRUE) {
//
// Grab the current irp
//
Irp = RxContext->CurrentIrp;
RxDbgTrace( 0, Dbg, ("RxFspDispatch: IrpC = 0x%08lx\n", RxContext) );
ASSERT( RxContext->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION );
ASSERT( RxContext->PostRequest == FALSE );
RxContext->LastExecutionThread = PsGetCurrentThread();
RxLog(( RxFsp_SurrogateFormat, RxFsp_ActualFormat, RXCONTX_OPERATION_NAME( RxContext->MajorFunction, TRUE ), RxContext->MinorFunction, RxContext, Irp, RxContext->LastExecutionThread, RxContext->SerialNumber ));
RxWmiLog( LOG,
RxFspDispatch,
LOGPTR( RxContext)
LOGPTR( Irp )
LOGPTR( RxContext->LastExecutionThread)
LOGULONG( RxContext->SerialNumber)
LOGUCHAR( RxContext->MinorFunction)
LOGARSTR( RXCONTX_OPERATION_NAME( RxContext->MajorFunction, TRUE ) ) );
//
// Now because we are the Fsp we will force the RxContext to
// indicate true on Wait.
//
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_WAIT | RX_CONTEXT_FLAG_IN_FSP );
//
// If this Irp was top level, note it in our thread local storage.
//
FsRtlEnterFileSystem();
if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_RECURSIVE_CALL )) {
RxTryToBecomeTheTopLevelIrp( &TopLevelContext,
(PIRP)FSRTL_FSP_TOP_LEVEL_IRP,
RxContext->RxDeviceObject,
TRUE ); // force
} else {
RxTryToBecomeTheTopLevelIrp( &TopLevelContext,
Irp,
RxContext->RxDeviceObject,
TRUE ); // force
}
try {
ASSERT( RxContext->ResumeRoutine != NULL );
if (FlagOn( RxContext->MinorFunction, IRP_MN_DPC ) && (Irp->Tail.Overlay.Thread == NULL)) {
ASSERT( (RxContext->MajorFunction == IRP_MJ_WRITE) ||
(RxContext->MajorFunction == IRP_MJ_READ) );
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
}
do {
BOOLEAN NoCompletion = BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_NO_COMPLETE_FROM_FSP );
Status = RxContext->ResumeRoutine( RxContext, Irp );
if (NoCompletion) {
NOTHING;
} else if ((Status != STATUS_PENDING) && (Status != STATUS_RETRY)) {
Status = RxCompleteRequest( RxContext, Status );
}
} while (Status == STATUS_RETRY);
} except( RxExceptionFilter( RxContext, GetExceptionInformation() )) {
//
// We had some trouble trying to perform the requested
// operation, so we'll abort the I/O request with
// the error status that we get back from the
// execption code.
//
(VOID) RxProcessException( RxContext, GetExceptionCode() );
}
RxUnwindTopLevelIrp( &TopLevelContext );
FsRtlExitFileSystem();
//
// If there are any entries on this volume's overflow queue, service
// them.
//
if (RxDeviceObject != NULL) {
//
// We have a volume device object so see if there is any work
// left to do in its overflow queue.
//
RxContext = RxRemoveOverflowEntry( RxDeviceObject,WorkQueueType );
//
// There wasn't an entry, break out of the loop and return to
// the Ex Worker thread.
//
if (RxContext == NULL) {
break;
}
continue;
} else {
break;
}
}
#ifdef DBG
if(KeGetCurrentIrql() >= APC_LEVEL) {
DbgPrint( "High Irql RxContext=%x Irql On Entry=%x\n", RxContext, CurrentIrql);
// DbgBreakPoint();
}
#endif
return;
}
//
// Internal support routine, spinlock wrapper.
//
PRX_CONTEXT
RxRemoveOverflowEntry (
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
IN WORK_QUEUE_TYPE WorkQueueType)
{
PRX_CONTEXT RxContext;
KIRQL SavedIrql;
KeAcquireSpinLock( &RxDeviceObject->OverflowQueueSpinLock, &SavedIrql );
if (RxDeviceObject->OverflowQueueCount[WorkQueueType] > 0) {
PVOID Entry;
//
// There is overflow work to do in this volume so we'll
// decrement the Overflow count, dequeue the IRP, and release
// the Event
//
RxDeviceObject->OverflowQueueCount[WorkQueueType] -= 1;
Entry = RemoveHeadList( &RxDeviceObject->OverflowQueue[WorkQueueType] );
//
// Extract the RxContext, Irp, and IrpSp, and loop.
//
RxContext = CONTAINING_RECORD( Entry,
RX_CONTEXT,
OverflowListEntry );
RxContext->OverflowListEntry.Flink = NULL;
ClearFlag( RxContext->Flags, RX_CONTEXT_FLAG_FSP_CRITICAL_OVERFLOW_QUEUE | RX_CONTEXT_FLAG_FSP_DELAYED_OVERFLOW_QUEUE );
} else {
RxContext = NULL;
InterlockedDecrement( &RxDeviceObject->PostedRequestCount[WorkQueueType] );
}
KeReleaseSpinLock( &RxDeviceObject->OverflowQueueSpinLock, SavedIrql );
return RxContext;
}
BOOLEAN
RxCancelOperationInOverflowQueue (
PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine cancels the operation in the overflow queue
Arguments:
RxContext The context of the operation being synchronized
--*/
{
BOOLEAN CancelledRequest = FALSE;
PRDBSS_DEVICE_OBJECT RxDeviceObject;
KIRQL SavedIrql;
//
// currently, we use the wrapper's device object for all throttling.....
//
RxDeviceObject = RxFileSystemDeviceObject;
KeAcquireSpinLock( &RxDeviceObject->OverflowQueueSpinLock, &SavedIrql );
if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_FSP_CRITICAL_OVERFLOW_QUEUE | RX_CONTEXT_FLAG_FSP_DELAYED_OVERFLOW_QUEUE) &&
(RxContext->OverflowListEntry.Flink != NULL)) {
//
// Remove the entry from the overflow queue
//
RemoveEntryList( &RxContext->OverflowListEntry );
RxContext->OverflowListEntry.Flink = NULL;
if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_FSP_CRITICAL_OVERFLOW_QUEUE )) {
RxDeviceObject->OverflowQueueCount[CriticalWorkQueue] -= 1;
} else {
RxDeviceObject->OverflowQueueCount[DelayedWorkQueue] -= 1;
}
ClearFlag( RxContext->Flags, RX_CONTEXT_FLAG_FSP_CRITICAL_OVERFLOW_QUEUE | RX_CONTEXT_FLAG_FSP_DELAYED_OVERFLOW_QUEUE );
CancelledRequest = TRUE;
}
KeReleaseSpinLock( &RxDeviceObject->OverflowQueueSpinLock, SavedIrql );
if (CancelledRequest) {
RxRemoveOperationFromBlockingQueue( RxContext );
RxCompleteRequest( RxContext, STATUS_CANCELLED );
}
return CancelledRequest;
}
//
// The following constant is the maximum number of ExWorkerThreads that we
// will allow to be servicing a particular target device at any one time.
//
#define FSP_PER_DEVICE_THRESHOLD (1)
VOID
RxPrePostIrp (
IN PVOID Context,
IN PIRP Irp
)
/*++
Routine Description:
This routine performs any neccessary work before RxStatus(PENDING) is
returned with the Fsd thread. This routine is called within the
filesystem and by the oplock package. The main issue is that we are about
to leave the user's process so we need to get a systemwide address for
anything in his address space that we require.
Arguments:
Context - Pointer to the RxContext to be queued to the Fsp
Irp - I/O Request Packet.
Return Value:
None.
--*/
{
PRX_CONTEXT RxContext = (PRX_CONTEXT) Context;
PIO_STACK_LOCATION IrpSp;
//
// If there is no Irp, we are done.
//
if (Irp == NULL) {
return;
}
IrpSp = IoGetCurrentIrpStackLocation( Irp );
if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED )) {
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED );
//
// We need to lock the user's buffer, unless this is an MDL-read,
// in which case there is no user buffer.
//
// **** we need a better test than non-MDL (read or write)!
//
if ((RxContext->MajorFunction == IRP_MJ_READ) || (RxContext->MajorFunction == IRP_MJ_WRITE)) {
//
// If not an Mdl request, lock the user's buffer.
//
if (!FlagOn( RxContext->MinorFunction, IRP_MN_MDL )) {
RxLockUserBuffer( RxContext,
Irp,
(RxContext->MajorFunction == IRP_MJ_READ) ? IoWriteAccess : IoReadAccess,
IrpSp->Parameters.Write.Length );
}
//
// We also need to check whether this is a query file operation.
//
} else if ((RxContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) &&
(RxContext->MinorFunction == IRP_MN_QUERY_DIRECTORY)) {
RxLockUserBuffer( RxContext,
Irp,
IoWriteAccess,
IrpSp->Parameters.QueryDirectory.Length );
//
// We also need to check whether this is a query ea operation.
//
} else if (RxContext->MajorFunction == IRP_MJ_QUERY_EA) {
RxLockUserBuffer( RxContext,
Irp,
IoWriteAccess,
IrpSp->Parameters.QueryEa.Length );
//
// We also need to check whether this is a set ea operation.
//
} else if (RxContext->MajorFunction == IRP_MJ_SET_EA) {
RxLockUserBuffer( RxContext,
Irp,
IoReadAccess,
IrpSp->Parameters.SetEa.Length );
}
//
// Mark that we've already returned pending to the user
//
IoMarkIrpPending( Irp );
}
return;
}
#ifdef RDBSSLOG
//this stuff must be in nonpaged memory
//// 1 2 3 4 5 6 7
char RxFsdPost_SurrogateFormat[] = "%S%S%N%N%N%N%N";
//// 2 3 4 5 6 7
char RxFsdPost_ActualFormat[] = "POST %s/%lx %08lx irp %lx thrd %lx #%lx";
#endif //ifdef RDBSSLOG
NTSTATUS
RxFsdPostRequest (
IN PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine enqueues the request packet specified by RxContext to the
Ex Worker threads. This is a FSD routine.
Arguments:
RxContext - Pointer to the RxContext to be queued to the Fsp
Return Value:
RxStatus(PENDING)
--*/
{
PIRP Irp = RxContext->CurrentIrp;
if(!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED )) {
RxPrePostIrp( RxContext, Irp );
}
RxLog(( RxFsdPost_SurrogateFormat,
RxFsdPost_ActualFormat,
RXCONTX_OPERATION_NAME( RxContext->MajorFunction, TRUE ),
RxContext->MinorFunction,
RxContext,
RxContext->CurrentIrp,
RxContext->LastExecutionThread,
RxContext->SerialNumber ));
RxWmiLog( LOG,
RxFsdPostRequest,
LOGPTR( RxContext )
LOGPTR( RxContext->CurrentIrp )
LOGPTR( RxContext->LastExecutionThread )
LOGULONG( RxContext->SerialNumber )
LOGUCHAR( RxContext->MinorFunction )
LOGARSTR( RXCONTX_OPERATION_NAME( RxContext->MajorFunction,TRUE ) ) );
RxAddToWorkque( RxContext, Irp );
return STATUS_PENDING;
}
VOID
RxAddToWorkque (
IN PRX_CONTEXT RxContext,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called to acually store the posted Irp to the Fsp
workque.
Arguments:
RxContext - Pointer to the RxContext to be queued to the Fsp
Irp - I/O Request Packet.
Return Value:
None.
--*/
{
KIRQL SavedIrql;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
PFILE_OBJECT FileObject = IrpSp->FileObject;
WORK_QUEUE_TYPE WorkQueueType;
BOOLEAN PostToWorkerThread = FALSE;
ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
//
// Send it off.....
//
RxContext->PostRequest = FALSE;
//
// Check if this request has an associated file object, and thus volume
// device object.
//
if ((RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) &&
(IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_REDIR_QUERY_PATH)) {
WorkQueueType = DelayedWorkQueue;
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_FSP_DELAYED_OVERFLOW_QUEUE );
} else {
WorkQueueType = CriticalWorkQueue;
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_FSP_CRITICAL_OVERFLOW_QUEUE );
}
if (FileObject != NULL) {
PRDBSS_DEVICE_OBJECT RxDeviceObject;
LONG RequestCount;
RxDeviceObject = CONTAINING_RECORD( IrpSp->DeviceObject, RDBSS_DEVICE_OBJECT, DeviceObject );
//
// currently, we use the wrapper's device object for all throttling.....
//
RxDeviceObject = RxFileSystemDeviceObject;
//
// Check to see if this request should be sent to the overflow
// queue. If not, then send it off to an exworker thread.
//
KeAcquireSpinLock( &RxDeviceObject->OverflowQueueSpinLock, &SavedIrql );
RequestCount = InterlockedIncrement(&RxDeviceObject->PostedRequestCount[WorkQueueType]);
PostToWorkerThread = (RequestCount > FSP_PER_DEVICE_THRESHOLD);
if (PostToWorkerThread) {
//
// We cannot currently respond to this IRP so we'll just enqueue it
// to the overflow queue on the volume.
//
InterlockedDecrement( &RxDeviceObject->PostedRequestCount[WorkQueueType] );
InsertTailList( &RxDeviceObject->OverflowQueue[WorkQueueType],
&RxContext->OverflowListEntry );
RxDeviceObject->OverflowQueueCount[WorkQueueType] += 1;
KeReleaseSpinLock( &RxDeviceObject->OverflowQueueSpinLock, SavedIrql );
return;
} else {
KeReleaseSpinLock( &RxDeviceObject->OverflowQueueSpinLock, SavedIrql );
}
} else {
PostToWorkerThread = TRUE;
}
#ifndef MONOLITHIC_MINIRDR
IoQueueWorkItem( RxIoWorkItem, RxFspDispatchEx, WorkQueueType, RxContext );
#else
ExInitializeWorkItem( &RxContext->WorkQueueItem, RxFspDispatch, RxContext );
ExQueueWorkItem( (PWORK_QUEUE_ITEM)&RxContext->WorkQueueItem, WorkQueueType );
#endif
return;
}