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.
2325 lines
70 KiB
2325 lines
70 KiB
/*++ BUILD Version: 0009 // Increment this if a change has global effects
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
buffring.c
|
|
|
|
Abstract:
|
|
|
|
This module defines the implementation for changing buffering states in the RDBSS
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman (SethuR) 11-Nov-95 Created
|
|
|
|
Notes:
|
|
|
|
The RDBSS provides a mechanism for providing distributed cache coherency in conjunction with
|
|
the various mini redirectors. This service is encapsulated in the BUFFERING_MANAGER which
|
|
processes CHANGE_BUFFERING_STATE_REQUESTS.
|
|
|
|
In the SMB protocol OPLOCK's ( Oppurtunistic Locks ) provide the necessary infrastructure for
|
|
cache coherency.
|
|
|
|
There are three components in the implementation of cache coherency protocol's in any mini
|
|
redirector.
|
|
|
|
1) The first constitutes the modifications to the CREATE/OPEN path. In this path the
|
|
type of buffering to be requested is determined and the appropriate request is made to the
|
|
server. On the return path the buffering state associated with the FCB is updated based
|
|
on the result of the CREATE/OPEN.
|
|
|
|
2) The receive indication code needs to modified to handle change buffering state notifications
|
|
from the server. If such a request is detected then the local mechanism to coordinate the
|
|
buffering states needs to be triggered.
|
|
|
|
3) The mechanism for changing the buffering state which is implemented as part of the
|
|
RDBSS.
|
|
|
|
Any change buffering state request must identify the SRV_OPEN to which the request applies.
|
|
The amount of computational effort involved in identifying the SRV_OPEN depends upon the
|
|
protocol. In the SMB protocol the Server gets to pick the id's used for identifying files
|
|
opened at the server. These are relative to the NET_ROOT(share) on which they are opened.
|
|
Thus every change buffering state request is identified by two keys, the NetRootKey and the
|
|
SrvOpenKey which need to be translated to the appropriate NET_ROOT and SRV_OPEN instance
|
|
respectively. In order to provide better integration with the resource acquisition/release
|
|
mechanism and to avoid duplication of this effort in the various mini redirectors the RDBSS
|
|
provides this service.
|
|
|
|
There are two mechanisms provided in the wrapper for indicating buffering state
|
|
changes to SRV_OPEN's. They are
|
|
|
|
1) RxIndicateChangeOfBufferingState
|
|
|
|
2) RxIndicateChangeOfBufferingStateForSrvOpen.
|
|
|
|
The mini rediretors that need an auxillary mechanism for establishing the mapping
|
|
from the id's to the SRV_OPEN instance employ (1) while the mini redirectors that
|
|
do not require this assistance employ (2).
|
|
|
|
The buffering manager processes these requests in different stages. It maintains the
|
|
requests received from the various underlying mini redirectors in one of three lists.
|
|
|
|
The Dispatcher list contains all the requests for which the appropriate mapping to a
|
|
SRV_OPEN instance has not been established. The Handler list contains all the requests
|
|
for which the appropriate mapping has been established and have not yet been processed.
|
|
The LastChanceHandlerList contains all the requests for which the initial processing was
|
|
unsuccessful.
|
|
|
|
This typically happens when the FCB was accquired in a SHARED mode at the time the
|
|
change buffering state request was received. In such cases the Oplock break request
|
|
can only be processed by a delayed worker thread.
|
|
|
|
The Change buffering state request processing in the redirector is intertwined with
|
|
the FCB accqusition/release protocol. This helps in ensuring shorter turn around times.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxTearDownBufferingManager)
|
|
#pragma alloc_text(PAGE, RxIndicateChangeOfBufferingStateForSrvOpen)
|
|
#pragma alloc_text(PAGE, RxPrepareRequestForHandling)
|
|
#pragma alloc_text(PAGE, RxPrepareRequestForReuse)
|
|
#pragma alloc_text(PAGE, RxpDiscardChangeBufferingStateRequests)
|
|
#pragma alloc_text(PAGE, RxProcessFcbChangeBufferingStateRequest)
|
|
#pragma alloc_text(PAGE, RxPurgeChangeBufferingStateRequestsForSrvOpen)
|
|
#pragma alloc_text(PAGE, RxProcessChangeBufferingStateRequestsForSrvOpen)
|
|
#pragma alloc_text(PAGE, RxInitiateSrvOpenKeyAssociation)
|
|
#pragma alloc_text(PAGE, RxpLookupSrvOpenForRequestLite)
|
|
#pragma alloc_text(PAGE, RxChangeBufferingState)
|
|
#pragma alloc_text(PAGE, RxFlushFcbInSystemCache)
|
|
#pragma alloc_text(PAGE, RxPurgeFcbInSystemCache)
|
|
#endif
|
|
|
|
//
|
|
// The Bug check file id for this module
|
|
//
|
|
|
|
#define BugCheckFileId (RDBSS_BUG_CHECK_CACHESUP)
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_CACHESUP)
|
|
|
|
//
|
|
// Forward declarations
|
|
//
|
|
|
|
NTSTATUS
|
|
RxRegisterChangeBufferingStateRequest (
|
|
PSRV_CALL SrvCall,
|
|
PSRV_OPEN SrvOpen,
|
|
PVOID SrvOpenKey,
|
|
PVOID MRxContext
|
|
);
|
|
|
|
VOID
|
|
RxDispatchChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall
|
|
);
|
|
|
|
VOID
|
|
RxpDispatchChangeBufferingStateRequests (
|
|
IN OUT PSRV_CALL SrvCall,
|
|
IN OUT PSRV_OPEN SrvOpen,
|
|
OUT PLIST_ENTRY DiscardedRequests
|
|
);
|
|
|
|
VOID
|
|
RxpDiscardChangeBufferingStateRequests (
|
|
IN OUT PLIST_ENTRY DiscardedRequests
|
|
);
|
|
|
|
VOID
|
|
RxLastChanceHandlerForChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall
|
|
);
|
|
|
|
NTSTATUS
|
|
RxpLookupSrvOpenForRequestLite (
|
|
IN PSRV_CALL SrvCall,
|
|
IN OUT PCHANGE_BUFFERING_STATE_REQUEST Request
|
|
);
|
|
|
|
VOID
|
|
RxGatherRequestsForSrvOpen (
|
|
IN OUT PSRV_CALL SrvCall,
|
|
IN PSRV_OPEN SrvOpen,
|
|
IN OUT PLIST_ENTRY RequestsListHead
|
|
);
|
|
|
|
NTSTATUS
|
|
RxInitializeBufferingManager (
|
|
PSRV_CALL SrvCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the buffering manager associated with a SRV_CALL
|
|
instance.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
Notes:
|
|
|
|
The buffering manager consists of three lists .....
|
|
|
|
1) the dispatcher list which contains all the requests that need to be
|
|
processed.
|
|
|
|
2) the handler list contains all the requests for which the SRV_OPEN
|
|
instance has been found and referenced.
|
|
|
|
3) the last chance handler list contains all the requests for which
|
|
an unsuccessful attempt to process the request was made, i.e., the
|
|
FCB could not be acquired exclusively.
|
|
|
|
The manipulation of these lists are done under the control of the spin lock
|
|
associated with the buffering manager. A Mutex will not suffice since these
|
|
lists are manipulated at DPC level.
|
|
|
|
All buffering manager operations at non DPC level are serialized using the
|
|
Mutex associated with the buffering manager.
|
|
|
|
--*/
|
|
{
|
|
PRX_BUFFERING_MANAGER BufferingManager;
|
|
|
|
BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
KeInitializeSpinLock( &BufferingManager->SpinLock );
|
|
|
|
InitializeListHead( &BufferingManager->HandlerList );
|
|
InitializeListHead( &BufferingManager->LastChanceHandlerList );
|
|
InitializeListHead( &BufferingManager->DispatcherList );
|
|
|
|
BufferingManager->HandlerInactive = FALSE;
|
|
BufferingManager->LastChanceHandlerActive = FALSE;
|
|
BufferingManager->DispatcherActive = FALSE;
|
|
|
|
BufferingManager->NumberOfOutstandingOpens = 0;
|
|
|
|
InitializeListHead( &BufferingManager->SrvOpenLists[0] );
|
|
ExInitializeFastMutex( &BufferingManager->Mutex );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxTearDownBufferingManager (
|
|
PSRV_CALL SrvCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tears down the buffering manager associated with a SRV_CALL
|
|
instance.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Note: All the work items in the buffering manager should not be in use.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
RxIndicateChangeOfBufferingState (
|
|
PMRX_SRV_CALL SrvCall,
|
|
PVOID SrvOpenKey,
|
|
PVOID MRxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers an oplock break indication.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
SrvOpenKey - the key for the SRV_OPEN instance.
|
|
|
|
MRxContext - the context to be passed back to the mini rdr during callbacks for
|
|
processing the oplock break.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Notes:
|
|
|
|
This is an instance in which the buffering state change request from the
|
|
server identifies the SRV_OPEN instance using the key generated by the server
|
|
|
|
This implies that the key needs to be mapped onto the SRV_OPEN instance locally.
|
|
|
|
--*/
|
|
{
|
|
RxRegisterChangeBufferingStateRequest( (PSRV_CALL)SrvCall,
|
|
NULL,
|
|
SrvOpenKey,
|
|
MRxContext );
|
|
}
|
|
|
|
|
|
VOID
|
|
RxIndicateChangeOfBufferingStateForSrvOpen (
|
|
PMRX_SRV_CALL SrvCall,
|
|
PMRX_SRV_OPEN MRxSrvOpen,
|
|
PVOID SrvOpenKey,
|
|
PVOID MRxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers an oplock break indication. If the necessary preconditions
|
|
are satisfied the oplock is processed further.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
MRxSrvOpen - the SRV_OPEN instance.
|
|
|
|
MRxContext - the context to be passed back to the mini rdr during callbacks for
|
|
processing the oplock break.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Notes:
|
|
|
|
This is an instance where in the buffering state change indications from the server
|
|
use the key generated by the client. ( the SRV_OPEN address in itself is the best
|
|
key that can be used ). This implies that no further lookup is required.
|
|
|
|
However if this routine is called at DPC level, the indication is processed as if
|
|
the lookup needs to be done.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (KeGetCurrentIrql() <= APC_LEVEL) {
|
|
|
|
PSRV_OPEN SrvOpen = (PSRV_OPEN)MRxSrvOpen;
|
|
|
|
//
|
|
// If the resource for the FCB has already been accquired by this thread
|
|
// the buffering state change indication can be processed immediately
|
|
// without further delay.
|
|
//
|
|
|
|
if (ExIsResourceAcquiredExclusiveLite( SrvOpen->Fcb->Header.Resource )) {
|
|
|
|
RxChangeBufferingState( SrvOpen, MRxContext, TRUE );
|
|
|
|
} else {
|
|
|
|
RxRegisterChangeBufferingStateRequest( (PSRV_CALL)SrvCall,
|
|
SrvOpen,
|
|
SrvOpen->Key,
|
|
MRxContext );
|
|
}
|
|
} else {
|
|
|
|
RxRegisterChangeBufferingStateRequest( (PSRV_CALL)SrvCall,
|
|
NULL,
|
|
SrvOpenKey,
|
|
MRxContext );
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RxRegisterChangeBufferingStateRequest (
|
|
PSRV_CALL SrvCall,
|
|
PSRV_OPEN SrvOpen OPTIONAL,
|
|
PVOID SrvOpenKey,
|
|
PVOID MRxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers a change buffering state requests. If necessary the worker thread
|
|
routines for further processing are activated.
|
|
|
|
Arguments:
|
|
|
|
SrvCall -
|
|
|
|
SrvOpen -
|
|
|
|
SrvOpenKey -
|
|
|
|
MRxContext -
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful.
|
|
|
|
Notes:
|
|
|
|
This routine registers the change buffering state request by either inserting it in the
|
|
registration list (DPC Level processing ) or the appropriate(dispatcher/handler list).
|
|
|
|
This is the common routine for processing both kinds of callbacks, i.e, the ones in
|
|
which the SRV_OPEN instance has been located and the ones in which only the SRV_OPEN
|
|
key is available.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
KIRQL SavedIrql;
|
|
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request;
|
|
PRX_BUFFERING_MANAGER BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
//
|
|
// Ensure that either the SRV_OPEN instance for this request has not been
|
|
// passed in or the call is not at DPC level.
|
|
//
|
|
|
|
ASSERT( (SrvOpen == NULL) || (KeGetCurrentIrql() <= APC_LEVEL) );
|
|
|
|
Request = RxAllocatePoolWithTag( NonPagedPool, sizeof( CHANGE_BUFFERING_STATE_REQUEST ), RX_BUFFERING_MANAGER_POOLTAG );
|
|
|
|
if (Request != NULL) {
|
|
|
|
BOOLEAN ActivateHandler = FALSE;
|
|
BOOLEAN ActivateDispatcher = FALSE;
|
|
|
|
Request->Flags = 0;
|
|
|
|
Request->SrvOpen = SrvOpen;
|
|
Request->SrvOpenKey = SrvOpenKey;
|
|
Request->MRxContext = MRxContext;
|
|
|
|
//
|
|
// If the SRV_OPEN instance for the request is known apriori the request can
|
|
// be directly inserted into the buffering manager's HandlerList as opposed
|
|
// to the DispatcherList for those instances in which only the SRV_OPEN key
|
|
// is available. The insertion into the HandlerList ust be accompanied by an
|
|
// additional reference to prevent finalization of the instance while a request
|
|
// is still active.
|
|
//
|
|
|
|
if (SrvOpen != NULL) {
|
|
RxReferenceSrvOpen( (PSRV_OPEN)SrvOpen );
|
|
}
|
|
|
|
KeAcquireSpinLock( &SrvCall->BufferingManager.SpinLock, &SavedIrql );
|
|
|
|
if (Request->SrvOpen != NULL) {
|
|
|
|
InsertTailList( &BufferingManager->HandlerList, &Request->ListEntry );
|
|
|
|
if (!BufferingManager->HandlerInactive) {
|
|
|
|
BufferingManager->HandlerInactive = TRUE;
|
|
ActivateHandler = TRUE;
|
|
}
|
|
|
|
RxLog(( "Req %lx SrvOpenKey %lx in Handler List\n", Request, Request->SrvOpen ));
|
|
RxWmiLog( LOG,
|
|
RxRegisterChangeBufferingStateRequest_1,
|
|
LOGPTR( Request )
|
|
LOGPTR( Request->SrvOpen ) );
|
|
} else {
|
|
|
|
InsertTailList( &BufferingManager->DispatcherList, &Request->ListEntry );
|
|
|
|
if (!BufferingManager->DispatcherActive) {
|
|
BufferingManager->DispatcherActive = TRUE;
|
|
ActivateDispatcher = TRUE;
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("Request %lx SrvOpenKey %lx in Registartion List\n", Request, Request->SrvOpenKey) );
|
|
RxLog(( "Req %lx SrvOpenKey %lx in Reg. List\n", Request, Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxRegisterChangeBufferingStateRequest_2,
|
|
LOGPTR( Request )
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
}
|
|
|
|
KeReleaseSpinLock( &SrvCall->BufferingManager.SpinLock, SavedIrql );
|
|
|
|
InterlockedIncrement( &SrvCall->BufferingManager.CumulativeNumberOfBufferingChangeRequests );
|
|
|
|
if (ActivateHandler) {
|
|
|
|
//
|
|
// Reference the SRV_CALL instance to ensure that it will not be
|
|
// finalized while the worker thread request is in the scheduler
|
|
//
|
|
|
|
RxReferenceSrvCallAtDpc( SrvCall );
|
|
|
|
RxPostToWorkerThread( RxFileSystemDeviceObject,
|
|
HyperCriticalWorkQueue,
|
|
&BufferingManager->HandlerWorkItem,
|
|
RxProcessChangeBufferingStateRequests,
|
|
SrvCall );
|
|
}
|
|
|
|
if (ActivateDispatcher) {
|
|
|
|
//
|
|
// Reference the SRV_CALL instance to ensure that it will not be
|
|
// finalized while the worker thread request is in the scheduler
|
|
//
|
|
|
|
RxReferenceSrvCallAtDpc( SrvCall );
|
|
|
|
RxPostToWorkerThread( RxFileSystemDeviceObject,
|
|
HyperCriticalWorkQueue,
|
|
&BufferingManager->DispatcherWorkItem,
|
|
RxDispatchChangeBufferingStateRequests,
|
|
SrvCall );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
RxLog(( "!!CBSReq. %lx %lx %lx %lx %lx\n", SrvCall, SrvOpen, SrvOpenKey, MRxContext, Status ));
|
|
RxWmiLogError( Status,
|
|
LOG,
|
|
RxRegisterChangeBufferingStateRequest_3,
|
|
LOGPTR( SrvCall )
|
|
LOGPTR( SrvOpen )
|
|
LOGPTR( SrvOpenKey )
|
|
LOGPTR( MRxContext )
|
|
LOGULONG( Status ) );
|
|
RxDbgTrace( 0, Dbg, ("Change Buffering State Request Ignored %lx %lx %lx\n", SrvCall,SrvOpen,SrvOpenKey,MRxContext,Status) );
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("Register SrvCall(%lx) SrvOpen (%lx) Key(%lx) Status(%lx)\n", SrvCall, SrvOpen, SrvOpenKey, Status) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxPrepareRequestForHandling (
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine preprocesses the request before initiating buffering state change
|
|
processing. In addition to obtaining the references on the FCB abd the associated
|
|
SRV_OPEN, an event is allocated as part of the FCB. This helps establish a priority
|
|
mechanism for servicing buffering state change requests.
|
|
|
|
The FCB accquisition is a two step process, i.e, wait for this event to be set followed
|
|
by a wait for the resource.
|
|
|
|
Arguments:
|
|
|
|
Request - the buffering state change request
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
|
|
Notes:
|
|
|
|
Not all the FCB's have the space for the buffering state change event allocated when the
|
|
FCB instance is created. The upside is that space is conserved and the downside is that
|
|
a separate allocation needs to be made when it is required.
|
|
|
|
This event associated with the FCB provides a two step mechanism for accelerating the
|
|
processing of buffering state change requests. Ordinary operations get delayed in favour
|
|
of the buffering state change requests. The details are in resrcsup.c
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PKEVENT Event;
|
|
PSRV_OPEN SrvOpen = Request->SrvOpen;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!FlagOn( Request->Flags,RX_REQUEST_PREPARED_FOR_HANDLING )) {
|
|
|
|
SetFlag( Request->Flags, RX_REQUEST_PREPARED_FOR_HANDLING );
|
|
|
|
RxAcquireSerializationMutex();
|
|
|
|
Event = SrvOpen->Fcb->pBufferingStateChangeCompletedEvent;
|
|
|
|
if (Event == NULL) {
|
|
|
|
Event = RxAllocatePoolWithTag( NonPagedPool, sizeof( KEVENT ), RX_BUFFERING_MANAGER_POOLTAG );
|
|
|
|
if (Event != NULL) {
|
|
|
|
SrvOpen->Fcb->pBufferingStateChangeCompletedEvent = Event;
|
|
KeInitializeEvent( Event, NotificationEvent, FALSE );
|
|
}
|
|
} else {
|
|
KeResetEvent( Event );
|
|
}
|
|
|
|
if (Event != NULL) {
|
|
|
|
SetFlag( SrvOpen->Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING );
|
|
SetFlag( SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING | SRVOPEN_FLAG_COLLAPSING_DISABLED );
|
|
|
|
RxDbgTrace( 0,Dbg,("3333 Request %lx SrvOpenKey %lx in Handler List\n",Request,Request->SrvOpenKey) );
|
|
RxLog(( "3333 Req %lx SrvOpenKey %lx in Hndlr List\n",Request,Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxPrepareRequestForHandling_1,
|
|
LOGPTR( Request )
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
} else {
|
|
|
|
RxDbgTrace( 0, Dbg, ("4444 Ignoring Request %lx SrvOpenKey %lx \n", Request, Request->SrvOpenKey) );
|
|
RxLog(( "Chg. Buf. State Ignored %lx %lx %lx\n", Request->SrvOpenKey, Request->MRxContext, STATUS_INSUFFICIENT_RESOURCES ));
|
|
RxWmiLog( LOG,
|
|
RxPrepareRequestForHandling_2,
|
|
LOGPTR( Request->SrvOpenKey )
|
|
LOGPTR( Request->MRxContext ) );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RxReleaseSerializationMutex();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
RxPrepareRequestForReuse (
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine postprocesses the request before destroying it. This involves
|
|
dereferencing and setting the appropriate state flags.
|
|
|
|
Arguments:
|
|
|
|
Request - the buffering state change request
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (FlagOn( Request->Flags, RX_REQUEST_PREPARED_FOR_HANDLING )) {
|
|
|
|
PFCB Fcb = Request->SrvOpen->Fcb;
|
|
|
|
//
|
|
// We should never clear the SrvOpen flag unless we are also clearing the FCB flag
|
|
// and setting the event!
|
|
// ClearFlag(Request->pSrvOpen->Flags,SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING);
|
|
//
|
|
|
|
if (RxIsFcbAcquiredExclusive( Fcb )) {
|
|
RxDereferenceSrvOpen( Request->SrvOpen, LHS_ExclusiveLockHeld);
|
|
} else {
|
|
RxDereferenceSrvOpen( Request->SrvOpen, LHS_LockNotHeld );
|
|
}
|
|
} else if (Request->SrvOpen != NULL) {
|
|
RxDereferenceSrvOpen( Request->SrvOpen, LHS_LockNotHeld );
|
|
}
|
|
|
|
Request->SrvOpen = NULL;
|
|
}
|
|
|
|
VOID
|
|
RxpDiscardChangeBufferingStateRequests (
|
|
PLIST_ENTRY DiscardedRequests
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine discards a list of change buffering state requests one at a time
|
|
|
|
Arguments:
|
|
|
|
DiscardedRequests - the requests to be discarded
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Process the discarded requests,i.e, free the memory
|
|
//
|
|
|
|
while (!IsListEmpty( DiscardedRequests )) {
|
|
|
|
PLIST_ENTRY Entry;
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request;
|
|
|
|
Entry = RemoveHeadList( DiscardedRequests );
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
|
|
RxDbgTrace( 0, Dbg, ("**** (2)Discarding Request(%lx) SrvOpenKey(%lx) \n", Request, Request->SrvOpenKey) );
|
|
RxLog(( "**** (2)Disc Req(%lx) SOKey(%lx) \n", Request, Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxpDiscardChangeBufferingStateRequests,
|
|
LOGPTR( Request)
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
|
|
RxPrepareRequestForReuse( Request );
|
|
RxFreePool( Request );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxpDispatchChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall,
|
|
PSRV_OPEN SrvOpen OPTIONAL,
|
|
PLIST_ENTRY DiscardedRequests
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dispatches the request before destroying it. This involves looking up
|
|
the SRV_OPEN instance associated with a given SrvOpenKey.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the associated SRV_CALL instance
|
|
|
|
SrvOpen - the associated SRV_OPEN instance.
|
|
|
|
DiscardedRequests - Returns back all the requests for which no srvopen could be found
|
|
This will only be used if SrvOpen is null
|
|
|
|
Notes:
|
|
|
|
There are two flavours of this routine. When SrvOpen is NULL this routine walks
|
|
through the list of outstanding requests and establishes the mapping between the
|
|
SrvOpenKey and the SRV_OPEN instance. On the other hand when SrvOpen is a valid
|
|
SRV_OPEN instance it merely traverses the list to gather together the requests
|
|
corresponding to the given SRV_OPEN and transfer them enmasse to to the handler
|
|
list.
|
|
|
|
The buffering manager mutex must have been accquired on entry to this routine
|
|
and the mutex ownership will remain invariant on exit.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
KIRQL SavedIrql;
|
|
|
|
PLIST_ENTRY Entry;
|
|
|
|
LIST_ENTRY DispatcherList;
|
|
LIST_ENTRY HandlerList;
|
|
|
|
BOOLEAN ActivateDispatcher;
|
|
|
|
PRX_BUFFERING_MANAGER BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request;
|
|
|
|
InitializeListHead( DiscardedRequests );
|
|
InitializeListHead( &HandlerList );
|
|
|
|
ActivateDispatcher = FALSE;
|
|
|
|
//
|
|
// Since the buffering manager lists are subject to modifications while
|
|
// the requests on the list are being processed, the requests are transferred
|
|
// enmasse onto a temporary list. This prevents multiple acquisition/release of
|
|
// the spinlock for each individual request.
|
|
//
|
|
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql );
|
|
|
|
RxTransferList( &DispatcherList, &BufferingManager->DispatcherList );
|
|
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock, SavedIrql );
|
|
|
|
//
|
|
// Process the list of requests.
|
|
//
|
|
|
|
Entry = DispatcherList.Flink;
|
|
while (Entry != &DispatcherList) {
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
|
|
Entry = Entry->Flink;
|
|
|
|
if (SrvOpen == NULL) {
|
|
|
|
Status = RxpLookupSrvOpenForRequestLite( SrvCall, Request );
|
|
|
|
} else {
|
|
|
|
if (Request->SrvOpenKey == SrvOpen->Key) {
|
|
|
|
Request->SrvOpen = SrvOpen;
|
|
RxReferenceSrvOpen( SrvOpen );
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The result of a lookup for a SRV_OPEN instance can yield
|
|
// either STATUS_PENDING, STATUS_SUCCESS or STATUS_NOT_FOUND.
|
|
//
|
|
|
|
switch (Status) {
|
|
case STATUS_SUCCESS:
|
|
|
|
RemoveEntryList( &Request->ListEntry );
|
|
InsertTailList( &HandlerList, &Request->ListEntry);
|
|
break;
|
|
|
|
default:
|
|
ASSERT( !"Valid Status Code from RxpLookuSrvOpenForRequestLite" );
|
|
|
|
case STATUS_NOT_FOUND:
|
|
|
|
RemoveEntryList( &Request->ListEntry );
|
|
InsertTailList( DiscardedRequests, &Request->ListEntry );
|
|
break;
|
|
|
|
case STATUS_PENDING:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Splice back the list of requests that cannot be dispatched onto the
|
|
// buffering manager's list and prepare for posting to another thread
|
|
// to resume processing later.
|
|
//
|
|
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql );
|
|
|
|
if (!IsListEmpty( &DispatcherList )) {
|
|
|
|
DispatcherList.Flink->Blink = BufferingManager->DispatcherList.Blink;
|
|
BufferingManager->DispatcherList.Blink->Flink = DispatcherList.Flink;
|
|
|
|
DispatcherList.Blink->Flink = &BufferingManager->DispatcherList;
|
|
BufferingManager->DispatcherList.Blink = DispatcherList.Blink;
|
|
|
|
if (ActivateDispatcher = !BufferingManager->DispatcherActive) {
|
|
BufferingManager->DispatcherActive = ActivateDispatcher;
|
|
}
|
|
}
|
|
|
|
if (!IsListEmpty( &HandlerList )) {
|
|
|
|
HandlerList.Flink->Blink = BufferingManager->HandlerList.Blink;
|
|
BufferingManager->HandlerList.Blink->Flink = HandlerList.Flink;
|
|
|
|
HandlerList.Blink->Flink = &BufferingManager->HandlerList;
|
|
BufferingManager->HandlerList.Blink = HandlerList.Blink;
|
|
}
|
|
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock, SavedIrql );
|
|
|
|
//
|
|
// if resumption at a later time is desired because of unprocessed requests
|
|
// post to a worker thread.
|
|
//
|
|
|
|
if (ActivateDispatcher) {
|
|
|
|
//
|
|
// Reference the SRV_CALL to ensure that finalization will not occur
|
|
// while the worker thread request is in the scheduler.
|
|
//
|
|
|
|
RxReferenceSrvCall( SrvCall );
|
|
|
|
RxLog(( "***** Activating Dispatcher\n" ));
|
|
RxWmiLog( LOG,
|
|
RxpDispatchChangeBufferingStateRequests,
|
|
LOGPTR( SrvCall ) );
|
|
|
|
RxPostToWorkerThread( RxFileSystemDeviceObject,
|
|
HyperCriticalWorkQueue,
|
|
&BufferingManager->DispatcherWorkItem,
|
|
RxDispatchChangeBufferingStateRequests,
|
|
SrvCall );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxDispatchChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dispatches the request. This involves looking up
|
|
the SRV_OPEN instance associated with a given SrvOpenKey.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the associated SRV_CALL instance
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
|
|
BOOLEAN ActivateHandler = FALSE;
|
|
|
|
LIST_ENTRY DiscardedRequests;
|
|
|
|
PRX_BUFFERING_MANAGER BufferingManager;
|
|
|
|
RxUndoScavengerFinalizationMarking( SrvCall );
|
|
|
|
BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql );
|
|
BufferingManager->DispatcherActive = FALSE;
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock,SavedIrql );
|
|
|
|
|
|
RxAcquireBufferingManagerMutex( BufferingManager );
|
|
|
|
RxpDispatchChangeBufferingStateRequests( SrvCall, NULL, &DiscardedRequests );
|
|
|
|
RxReleaseBufferingManagerMutex( BufferingManager );
|
|
|
|
//
|
|
// If requests have been transferred from the dispatcher list to the handler
|
|
// list ensure that the handler is activated.
|
|
//
|
|
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql );
|
|
|
|
if (!IsListEmpty( &BufferingManager->HandlerList ) &&
|
|
(ActivateHandler = !BufferingManager->HandlerInactive)) {
|
|
BufferingManager->HandlerInactive = ActivateHandler;
|
|
}
|
|
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock,SavedIrql );
|
|
|
|
//
|
|
// Note that in this case we have a continuation of processing, from the
|
|
// dispatcher to the handler. The reference that was taken to protect the
|
|
// dispatcher is transferred to the handling routine. If continuation
|
|
// is not required the SRV_CALL instance is dereferenced.
|
|
//
|
|
|
|
if (ActivateHandler) {
|
|
RxProcessChangeBufferingStateRequests( SrvCall );
|
|
} else {
|
|
RxDereferenceSrvCall( SrvCall, LHS_LockNotHeld );
|
|
}
|
|
|
|
//
|
|
// Discard the requests for which the SRV_OPEN instance cannot be located.
|
|
// This will cover all the instances for which a buffering change request
|
|
// and a close crossed on the wire.
|
|
//
|
|
|
|
RxpDiscardChangeBufferingStateRequests( &DiscardedRequests );
|
|
}
|
|
|
|
VOID
|
|
RxpProcessChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall,
|
|
BOOLEAN UpdateHandlerState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initiates the actual processing of change buffering state requests.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Notes:
|
|
|
|
The change buffering requests are received for different FCB's. If the attempt
|
|
is made to handle these requests in the order they are received teh average
|
|
response time for completing a change buffering state request can be arbitratily
|
|
high. This is because the FCB needs to be acquired exclusively to complete
|
|
processing the request. In order to avoid this case the buffering manager
|
|
adopts a two pronged strategy -- a first attempt is made to acquire the FCB
|
|
exclusively without waiting. If this attempt fails the requests are transferred
|
|
to a last chance handler list. This combined with the processing of change
|
|
buffering state requests on FCB acquisition/release ensures that most requests
|
|
are processed with a very short turn around time.
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY Entry;
|
|
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request = NULL;
|
|
PRX_BUFFERING_MANAGER BufferingManager;
|
|
|
|
PSRV_OPEN SrvOpen;
|
|
|
|
BOOLEAN ActivateHandler;
|
|
|
|
RxLog(( "RPCBSR Entry SrvCall(%lx) \n", SrvCall ));
|
|
RxWmiLog( LOG,
|
|
RxpProcessChangeBufferingStateRequests_1,
|
|
LOGPTR( SrvCall ) );
|
|
|
|
BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
ListEntry = Entry = NULL;
|
|
|
|
for (;;) {
|
|
|
|
Entry = NULL;
|
|
ActivateHandler = FALSE;
|
|
|
|
RxAcquireBufferingManagerMutex( BufferingManager );
|
|
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql );
|
|
|
|
//
|
|
// Pick a request from the handler list for change buffering state
|
|
// processing.
|
|
//
|
|
|
|
if (!IsListEmpty( &BufferingManager->HandlerList )) {
|
|
Entry = RemoveHeadList( &BufferingManager->HandlerList );
|
|
}
|
|
|
|
//
|
|
// If the FCB for the previously picked request could not be acquired
|
|
// exclusively without waiting it needs to be transferred to the last
|
|
// chance handler list and the last chance handler activated if
|
|
// required.
|
|
//
|
|
|
|
if (ListEntry != NULL) {
|
|
|
|
//
|
|
// Insert the entry into the last chance handler list.
|
|
//
|
|
|
|
InsertTailList( &BufferingManager->LastChanceHandlerList, ListEntry );
|
|
|
|
//
|
|
// reinitialize for the next pass.
|
|
//
|
|
|
|
ListEntry = NULL;
|
|
|
|
//
|
|
// prepare for spinning up the last chance handler.
|
|
//
|
|
|
|
if (!BufferingManager->LastChanceHandlerActive &&
|
|
!IsListEmpty( &BufferingManager->LastChanceHandlerList )) {
|
|
|
|
BufferingManager->LastChanceHandlerActive = TRUE;
|
|
ActivateHandler = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No more requests to be handled. Prepare for wind down.
|
|
//
|
|
|
|
if ((Entry == NULL) && UpdateHandlerState) {
|
|
BufferingManager->HandlerInactive = FALSE;
|
|
}
|
|
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock,SavedIrql );
|
|
|
|
RxReleaseBufferingManagerMutex( BufferingManager );
|
|
|
|
//
|
|
// spin up the last chance handler for processing the requests if required.
|
|
//
|
|
|
|
if (ActivateHandler) {
|
|
|
|
//
|
|
// Reference the SRV_CALL instance to ensure that it will not be
|
|
// finalized while the worker thread request is in the scheduler
|
|
//
|
|
|
|
RxReferenceSrvCall( SrvCall );
|
|
RxPostToWorkerThread( RxFileSystemDeviceObject,
|
|
DelayedWorkQueue,
|
|
&BufferingManager->LastChanceHandlerWorkItem,
|
|
RxLastChanceHandlerForChangeBufferingStateRequests,
|
|
SrvCall );
|
|
|
|
ActivateHandler = FALSE;
|
|
}
|
|
|
|
if (Entry == NULL) {
|
|
break;
|
|
}
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
|
|
RxLog(( "Proc. Req. SrvOpen (%lx) \n", Request->SrvOpen ));
|
|
RxWmiLog( LOG,
|
|
RxpProcessChangeBufferingStateRequests_2,
|
|
LOGPTR( Request->SrvOpen ) );
|
|
|
|
if (RxPrepareRequestForHandling( Request ) == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Try to acquire the FCB without waiting. If the FCB is currently unavailable
|
|
// then it is guaranteed that this request will be processed when the FCB
|
|
// resource is released.
|
|
//
|
|
|
|
ASSERT( Request->SrvOpen != NULL );
|
|
|
|
if (RxAcquireExclusiveFcb( CHANGE_BUFFERING_STATE_CONTEXT, Request->SrvOpen->Fcb ) == STATUS_SUCCESS) {
|
|
|
|
BOOLEAN FcbFinalized;
|
|
PFCB Fcb;
|
|
|
|
RxLog(( "Proc. Req. SrvOpen FCB (%lx) \n",Request->SrvOpen->Fcb ));
|
|
RxWmiLog( LOG,
|
|
RxpProcessChangeBufferingStateRequests_3,
|
|
LOGPTR( Request->SrvOpen->Fcb ) );
|
|
|
|
SrvOpen = Request->SrvOpen;
|
|
Fcb = SrvOpen->Fcb;
|
|
|
|
RxReferenceNetFcb( Fcb );
|
|
|
|
if (!FlagOn( SrvOpen->Flags, SRVOPEN_FLAG_CLOSED )) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("SrvOpenKey(%lx) being processed(Last Resort)\n", Request->SrvOpenKey) );
|
|
|
|
RxLog(( "SOKey(%lx) processed(Last Resort)\n", Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxpProcessChangeBufferingStateRequests_4,
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
|
|
RxChangeBufferingState( SrvOpen, Request->MRxContext, TRUE );
|
|
}
|
|
|
|
RxAcquireSerializationMutex();
|
|
|
|
ClearFlag( SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING );
|
|
ClearFlag( SrvOpen->Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING );
|
|
KeSetEvent( SrvOpen->Fcb->pBufferingStateChangeCompletedEvent, IO_NETWORK_INCREMENT, FALSE );
|
|
|
|
RxReleaseSerializationMutex();
|
|
|
|
RxPrepareRequestForReuse( Request );
|
|
|
|
FcbFinalized = RxDereferenceAndFinalizeNetFcb( Fcb,
|
|
CHANGE_BUFFERING_STATE_CONTEXT_WAIT,
|
|
FALSE,
|
|
FALSE );
|
|
|
|
if (!FcbFinalized) {
|
|
RxReleaseFcb( CHANGE_BUFFERING_STATE_CONTEXT, Fcb );
|
|
}
|
|
|
|
RxFreePool( Request );
|
|
} else {
|
|
|
|
//
|
|
// The FCB has been currently accquired. Transfer the change buffering state
|
|
// request to the last chance handler list. This will ensure that the
|
|
// change buffering state request is processed in all cases, i.e.,
|
|
// accquisition of the resource in shared mode as well as the acquistion
|
|
// of the FCB resource by other components ( cache manager/memory manager )
|
|
// without going through the wrapper.
|
|
//
|
|
|
|
ListEntry = &Request->ListEntry;
|
|
}
|
|
} else {
|
|
RxPrepareRequestForReuse( Request );
|
|
RxFreePool( Request );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Dereference the SRV_CALL instance.
|
|
//
|
|
|
|
RxDereferenceSrvCall( SrvCall, LHS_LockNotHeld );
|
|
|
|
RxLog(( "RPCBSR Exit SrvCall(%lx)\n", SrvCall ));
|
|
RxWmiLog( LOG,
|
|
RxpProcessChangeBufferingStateRequests_5,
|
|
LOGPTR( SrvCall ) );
|
|
}
|
|
|
|
VOID
|
|
RxProcessChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the last chance handler for processing change buffering state
|
|
requests
|
|
|
|
Arguments:
|
|
|
|
SrvCall -- the SrvCall instance
|
|
|
|
Notes:
|
|
|
|
Since the reference for the srv call instance was accquired at DPC undo
|
|
the scavenger marking if required.
|
|
|
|
--*/
|
|
{
|
|
RxUndoScavengerFinalizationMarking( SrvCall );
|
|
|
|
RxpProcessChangeBufferingStateRequests( SrvCall, TRUE );
|
|
}
|
|
|
|
VOID
|
|
RxLastChanceHandlerForChangeBufferingStateRequests (
|
|
PSRV_CALL SrvCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the last chance handler for processing change buffering state
|
|
requests
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Notes:
|
|
|
|
This routine exists because Mm/Cache manager manipulate the header resource
|
|
associated with the FCB directly in some cases. In such cases it is not possible
|
|
to determine whether the release is done through the wrapper. In such cases it
|
|
is important to have a thread actually wait on the FCB resource to be released
|
|
and subsequently process the buffering state request as a last resort mechanism.
|
|
|
|
This also handles the case when the FCB is accquired shared. In such cases the
|
|
change buffering state has to be completed in the context of a thread which can
|
|
accquire it exclusively.
|
|
|
|
The filtering of the requests must be further optimized by marking the FCB state
|
|
during resource accquisition by the wrapper so that requests do not get downgraded
|
|
easily. ( TO BE IMPLEMENTED )
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
|
|
PLIST_ENTRY Entry;
|
|
|
|
LIST_ENTRY FinalizationList;
|
|
|
|
PRX_BUFFERING_MANAGER BufferingManager;
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request = NULL;
|
|
|
|
PSRV_OPEN SrvOpen;
|
|
BOOLEAN FcbFinalized,FcbAcquired;
|
|
PFCB Fcb;
|
|
|
|
RxLog(( "RLCHCBSR Entry SrvCall(%lx)\n", SrvCall ));
|
|
RxWmiLog( LOG,
|
|
RxLastChanceHandlerForChangeBufferingStateRequests_1,
|
|
LOGPTR( SrvCall ) );
|
|
|
|
InitializeListHead( &FinalizationList );
|
|
|
|
BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
for (;;) {
|
|
|
|
RxAcquireBufferingManagerMutex( BufferingManager );
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql );
|
|
|
|
if (!IsListEmpty( &BufferingManager->LastChanceHandlerList )) {
|
|
Entry = RemoveHeadList( &BufferingManager->LastChanceHandlerList );
|
|
} else {
|
|
Entry = NULL;
|
|
BufferingManager->LastChanceHandlerActive = FALSE;
|
|
}
|
|
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock, SavedIrql );
|
|
RxReleaseBufferingManagerMutex( BufferingManager );
|
|
|
|
if (Entry == NULL) {
|
|
break;
|
|
}
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
|
|
SrvOpen = Request->SrvOpen;
|
|
Fcb = SrvOpen->Fcb;
|
|
|
|
RxReferenceNetFcb( Fcb );
|
|
|
|
FcbAcquired = (RxAcquireExclusiveFcb( CHANGE_BUFFERING_STATE_CONTEXT_WAIT, Request->SrvOpen->Fcb) == STATUS_SUCCESS);
|
|
|
|
if (FcbAcquired && !FlagOn( SrvOpen->Flags, SRVOPEN_FLAG_CLOSED )) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("SrvOpenKey(%lx) being processed(Last Resort)\n", Request->SrvOpenKey) );
|
|
|
|
RxLog(( "SOKey(%lx) processed(Last Resort)\n", Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxLastChanceHandlerForChangeBufferingStateRequests_2,
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
|
|
RxChangeBufferingState( SrvOpen, Request->MRxContext, TRUE );
|
|
}
|
|
|
|
RxAcquireSerializationMutex();
|
|
ClearFlag( SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING );
|
|
ClearFlag( SrvOpen->Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING );
|
|
KeSetEvent( SrvOpen->Fcb->pBufferingStateChangeCompletedEvent,IO_NETWORK_INCREMENT, FALSE );
|
|
RxReleaseSerializationMutex();
|
|
|
|
InsertTailList( &FinalizationList, Entry );
|
|
|
|
if (FcbAcquired) {
|
|
RxReleaseFcb( CHANGE_BUFFERING_STATE_CONTEXT,Fcb );
|
|
}
|
|
}
|
|
|
|
while (!IsListEmpty( &FinalizationList )) {
|
|
|
|
Entry = RemoveHeadList( &FinalizationList );
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
|
|
SrvOpen = Request->SrvOpen;
|
|
Fcb = SrvOpen->Fcb;
|
|
|
|
FcbAcquired = (RxAcquireExclusiveFcb( CHANGE_BUFFERING_STATE_CONTEXT_WAIT, Request->SrvOpen->Fcb) == STATUS_SUCCESS);
|
|
|
|
ASSERT(FcbAcquired == TRUE);
|
|
|
|
RxPrepareRequestForReuse( Request );
|
|
|
|
FcbFinalized = RxDereferenceAndFinalizeNetFcb( Fcb,
|
|
CHANGE_BUFFERING_STATE_CONTEXT_WAIT,
|
|
FALSE,
|
|
FALSE );
|
|
|
|
if (!FcbFinalized && FcbAcquired) {
|
|
RxReleaseFcb( CHANGE_BUFFERING_STATE_CONTEXT, Fcb );
|
|
}
|
|
|
|
RxFreePool( Request );
|
|
}
|
|
|
|
RxLog(( "RLCHCBSR Exit SrvCall(%lx)\n", SrvCall ));
|
|
RxWmiLog( LOG,
|
|
RxLastChanceHandlerForChangeBufferingStateRequests_3,
|
|
LOGPTR( SrvCall ) );
|
|
|
|
//
|
|
// Dereference the SRV_CALL instance.
|
|
//
|
|
|
|
RxDereferenceSrvCall( SrvCall, LHS_LockNotHeld );
|
|
}
|
|
|
|
|
|
VOID
|
|
RxProcessFcbChangeBufferingStateRequest (
|
|
PFCB Fcb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes all the outstanding change buffering state request for a
|
|
FCB.
|
|
|
|
Arguments:
|
|
|
|
Fcb - the FCB instance
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Notes:
|
|
|
|
The FCB instance must be acquired exclusively on entry to this routine and
|
|
its ownership will remain invariant on exit.
|
|
|
|
--*/
|
|
{
|
|
PSRV_CALL SrvCall;
|
|
|
|
LIST_ENTRY FcbRequestList;
|
|
PLIST_ENTRY Entry;
|
|
|
|
PRX_BUFFERING_MANAGER BufferingManager;
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxLog(( "RPFcbCBSR Entry FCB(%lx)\n", Fcb ));
|
|
RxWmiLog( LOG,
|
|
RxProcessFcbChangeBufferingStateRequest_1,
|
|
LOGPTR( Fcb ) );
|
|
|
|
SrvCall = (PSRV_CALL)Fcb->VNetRoot->NetRoot->SrvCall;
|
|
BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
InitializeListHead( &FcbRequestList );
|
|
|
|
//
|
|
// Walk through the list of SRV_OPENS associated with this FCB and pick up
|
|
// the requests that can be dispatched.
|
|
//
|
|
|
|
RxAcquireBufferingManagerMutex( BufferingManager );
|
|
|
|
Entry = Fcb->SrvOpenList.Flink;
|
|
while (Entry != &Fcb->SrvOpenList) {
|
|
|
|
PSRV_OPEN SrvOpen;
|
|
|
|
SrvOpen = (PSRV_OPEN) (CONTAINING_RECORD( Entry, SRV_OPEN, SrvOpenQLinks ));
|
|
Entry = Entry->Flink;
|
|
|
|
RxGatherRequestsForSrvOpen( SrvCall, SrvOpen, &FcbRequestList );
|
|
}
|
|
|
|
RxReleaseBufferingManagerMutex( BufferingManager );
|
|
|
|
if (!IsListEmpty( &FcbRequestList )) {
|
|
|
|
//
|
|
// Initiate buffering state change processing.
|
|
//
|
|
|
|
Entry = FcbRequestList.Flink;
|
|
while (Entry != &FcbRequestList) {
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
|
|
Entry = Entry->Flink;
|
|
|
|
if (RxPrepareRequestForHandling( Request ) == STATUS_SUCCESS) {
|
|
|
|
if (!FlagOn( Request->SrvOpen->Flags, SRVOPEN_FLAG_CLOSED )) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("****** SrvOpenKey(%lx) being processed\n", Request->SrvOpenKey) );
|
|
RxLog(( "****** SOKey(%lx) being processed\n", Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxProcessFcbChangeBufferingStateRequest_2,
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
RxChangeBufferingState( Request->SrvOpen, Request->MRxContext, TRUE );
|
|
} else {
|
|
|
|
RxDbgTrace( 0, Dbg, ("****** 123 SrvOpenKey(%lx) being ignored\n", Request->SrvOpenKey) );
|
|
RxLog(( "****** 123 SOKey(%lx) ignored\n", Request->SrvOpenKey ));
|
|
RxWmiLog( LOG,
|
|
RxProcessFcbChangeBufferingStateRequest_3,
|
|
LOGPTR( Request->SrvOpenKey ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Discard the requests.
|
|
//
|
|
|
|
RxpDiscardChangeBufferingStateRequests( &FcbRequestList );
|
|
}
|
|
|
|
RxLog(( "RPFcbCBSR Exit FCB(%lx)\n", Fcb ));
|
|
RxWmiLog( LOG,
|
|
RxProcessFcbChangeBufferingStateRequest_4,
|
|
LOGPTR( Fcb ) );
|
|
|
|
//
|
|
// All buffering state change requests have been processed, clear the flag
|
|
// and signal the event as necessary.
|
|
//
|
|
|
|
RxAcquireSerializationMutex();
|
|
|
|
//
|
|
// update the FCB state.
|
|
//
|
|
|
|
ClearFlag( Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING );
|
|
if (Fcb->pBufferingStateChangeCompletedEvent) {
|
|
KeSetEvent( Fcb->pBufferingStateChangeCompletedEvent, IO_NETWORK_INCREMENT, FALSE );
|
|
}
|
|
|
|
RxReleaseSerializationMutex();
|
|
}
|
|
|
|
VOID
|
|
RxGatherRequestsForSrvOpen (
|
|
IN OUT PSRV_CALL SrvCall,
|
|
IN PSRV_OPEN SrvOpen,
|
|
IN OUT PLIST_ENTRY RequestsListHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gathers all the change buffering state requests associated with a SRV_OPEN.
|
|
This routine provides the mechanism for gathering all the requests for a SRV_OPEN which
|
|
is then used bu routines which process them
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
SrvOpen - the SRV_OPEN instance
|
|
|
|
RequestsListHead - the list of requests which is constructed by this routine
|
|
|
|
Notes:
|
|
|
|
On Entry to thir routine the buffering manager Mutex must have been acquired
|
|
and the ownership remains invariant on exit
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
LIST_ENTRY DiscardedRequests;
|
|
|
|
PCHANGE_BUFFERING_STATE_REQUEST Request;
|
|
PRX_BUFFERING_MANAGER BufferingManager;
|
|
|
|
PVOID SrvOpenKey;
|
|
|
|
KIRQL SavedIrql;
|
|
|
|
BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
SrvOpenKey = SrvOpen->Key;
|
|
|
|
//
|
|
// gather all the requests from the dispatcher list
|
|
//
|
|
|
|
RxpDispatchChangeBufferingStateRequests( SrvCall, SrvOpen, &DiscardedRequests );
|
|
|
|
//
|
|
// Since srvopen is non null in above call - we will not get back any discarded
|
|
// requests
|
|
//
|
|
|
|
ASSERTMSG( "Since srvopen is non null we shouldn't discard anything", IsListEmpty( &DiscardedRequests ) );
|
|
|
|
KeAcquireSpinLock( &SrvCall->BufferingManager.SpinLock, &SavedIrql );
|
|
|
|
//
|
|
// gather all the requests with the given SrvOpenKey in the handler list
|
|
//
|
|
|
|
Entry = BufferingManager->HandlerList.Flink;
|
|
|
|
while (Entry != &BufferingManager->HandlerList) {
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
Entry = Entry->Flink;
|
|
|
|
if ( (Request->SrvOpenKey == SrvOpenKey) && (Request->SrvOpen == SrvOpen) ) {
|
|
RemoveEntryList( &Request->ListEntry );
|
|
InsertHeadList( RequestsListHead, &Request->ListEntry);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock( &SrvCall->BufferingManager.SpinLock, SavedIrql );
|
|
|
|
//
|
|
// gather all the requests from the last chance handler list
|
|
//
|
|
|
|
Entry = BufferingManager->LastChanceHandlerList.Flink;
|
|
while (Entry != &BufferingManager->LastChanceHandlerList) {
|
|
|
|
Request = (PCHANGE_BUFFERING_STATE_REQUEST) CONTAINING_RECORD( Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry );
|
|
Entry = Entry->Flink;
|
|
|
|
if ( (Request->SrvOpenKey == SrvOpen->Key) && (Request->SrvOpen == SrvOpen) ) {
|
|
RemoveEntryList( &Request->ListEntry );
|
|
InsertHeadList( RequestsListHead, &Request->ListEntry );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxPurgeChangeBufferingStateRequestsForSrvOpen (
|
|
IN PSRV_OPEN SrvOpen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine purges all the requests associated with a given SRV_OPEN. This will ensure
|
|
that all buffering state change requests received while the SRV_OPEN was being closed
|
|
will be flushed out.
|
|
|
|
Arguments:
|
|
|
|
SrvOpen - the SRV_OPEN instance
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PSRV_CALL SrvCall = (PSRV_CALL)SrvOpen->Fcb->VNetRoot->NetRoot->SrvCall;
|
|
PRX_BUFFERING_MANAGER BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
LIST_ENTRY DiscardedRequests;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( RxIsFcbAcquiredExclusive( SrvOpen->Fcb ) );
|
|
|
|
InitializeListHead( &DiscardedRequests );
|
|
|
|
RxAcquireBufferingManagerMutex( BufferingManager );
|
|
|
|
RemoveEntryList( &SrvOpen->SrvOpenKeyList );
|
|
|
|
InitializeListHead( &SrvOpen->SrvOpenKeyList );
|
|
SetFlag( SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_REQUESTS_PURGED );
|
|
|
|
RxGatherRequestsForSrvOpen( SrvCall, SrvOpen, &DiscardedRequests );
|
|
|
|
RxReleaseBufferingManagerMutex( BufferingManager );
|
|
|
|
if (!IsListEmpty( &DiscardedRequests )) {
|
|
|
|
if (BooleanFlagOn( SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING )) {
|
|
|
|
RxAcquireSerializationMutex();
|
|
|
|
ClearFlag( SrvOpen->Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING );
|
|
|
|
if (SrvOpen->Fcb->pBufferingStateChangeCompletedEvent != NULL) {
|
|
|
|
KeSetEvent( SrvOpen->Fcb->pBufferingStateChangeCompletedEvent, IO_NETWORK_INCREMENT, FALSE );
|
|
}
|
|
|
|
RxReleaseSerializationMutex();
|
|
}
|
|
|
|
RxpDiscardChangeBufferingStateRequests( &DiscardedRequests );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxProcessChangeBufferingStateRequestsForSrvOpen (
|
|
PSRV_OPEN SrvOpen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine processes all the requests associated with a given SRV_OPEN.
|
|
Since this routine is called from a fastio path it tries to defer lock accquistion
|
|
till it is required
|
|
|
|
Arguments:
|
|
|
|
SrvOpen - the SRV_OPEN instance
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LONG OldBufferingToken;
|
|
PSRV_CALL SrvCall;
|
|
PFCB Fcb;
|
|
|
|
SrvCall = SrvOpen->VNetRoot->NetRoot->SrvCall;
|
|
Fcb = SrvOpen->Fcb;
|
|
|
|
//
|
|
// If change buffering state requests have been received for this srvcall
|
|
// since the last time the request was processed ensure that we process
|
|
// all these requests now.
|
|
//
|
|
|
|
OldBufferingToken = SrvOpen->BufferingToken;
|
|
|
|
if (InterlockedCompareExchange( &SrvOpen->BufferingToken, SrvCall->BufferingManager.CumulativeNumberOfBufferingChangeRequests, SrvCall->BufferingManager.CumulativeNumberOfBufferingChangeRequests) != OldBufferingToken) {
|
|
|
|
if (RxAcquireExclusiveFcb( NULL, Fcb ) == STATUS_SUCCESS) {
|
|
|
|
RxProcessFcbChangeBufferingStateRequest( Fcb );
|
|
RxReleaseFcb( NULL, Fcb );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxInitiateSrvOpenKeyAssociation (
|
|
IN OUT PSRV_OPEN SrvOpen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prepares a SRV_OPEN instance for SrvOpenKey association.
|
|
|
|
Arguments:
|
|
|
|
SrvOpen - the SRV_OPEN instance
|
|
|
|
Notes:
|
|
|
|
The process of key association is a two phase protocol. In the initialization process
|
|
a sequence number is stowed away in the SRV_OPEN. When the
|
|
RxCompleteSrvOpenKeyAssociation routine is called the sequence number is used to
|
|
update the data structures associated with the SRV_CALL instance. This is required
|
|
because of the asynchronous nature of receiving buffering state change indications
|
|
(oplock breaks in SMB terminology ) before the open is completed.
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
|
|
PSRV_CALL SrvCall = SrvOpen->Fcb->VNetRoot->NetRoot->SrvCall;
|
|
PRX_BUFFERING_MANAGER BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
PAGED_CODE();
|
|
|
|
SrvOpen->Key = NULL;
|
|
|
|
InterlockedIncrement( &BufferingManager->NumberOfOutstandingOpens );
|
|
InitializeListHead( &SrvOpen->SrvOpenKeyList );
|
|
}
|
|
|
|
VOID
|
|
RxCompleteSrvOpenKeyAssociation (
|
|
IN OUT PSRV_OPEN SrvOpen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine associates the given key with the SRV_OPEN instance
|
|
|
|
Arguments:
|
|
|
|
MRxSrvOpen - the SRV_OPEN instance
|
|
|
|
SrvOpenKey - the key to be associated with the instance
|
|
|
|
Notes:
|
|
|
|
This routine in addition to establishing the mapping also ensures that any pending
|
|
buffering state change requests are handled correctly. This ensures that change
|
|
buffering state requests received during the duration of SRV_OPEN construction
|
|
will be handled immediately.
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
|
|
BOOLEAN ActivateHandler = FALSE;
|
|
|
|
ULONG Index = 0;
|
|
|
|
PSRV_CALL SrvCall = (PSRV_CALL)SrvOpen->Fcb->VNetRoot->NetRoot->SrvCall;
|
|
PRX_BUFFERING_MANAGER BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
LIST_ENTRY DiscardedRequests;
|
|
|
|
//
|
|
// Associate the SrvOpenKey with the SRV_OPEN instance and also dispatch the
|
|
// associated change buffering state request if any.
|
|
//
|
|
|
|
if (SrvOpen->Condition == Condition_Good) {
|
|
|
|
InitializeListHead( &DiscardedRequests );
|
|
|
|
RxAcquireBufferingManagerMutex( BufferingManager );
|
|
|
|
InsertTailList( &BufferingManager->SrvOpenLists[Index], &SrvOpen->SrvOpenKeyList);
|
|
|
|
InterlockedDecrement( &BufferingManager->NumberOfOutstandingOpens );
|
|
|
|
RxpDispatchChangeBufferingStateRequests( SrvCall,
|
|
SrvOpen,
|
|
&DiscardedRequests);
|
|
|
|
RxReleaseBufferingManagerMutex( BufferingManager );
|
|
|
|
KeAcquireSpinLock( &BufferingManager->SpinLock, &SavedIrql);
|
|
|
|
if (!IsListEmpty( &BufferingManager->HandlerList ) &&
|
|
(ActivateHandler = !BufferingManager->HandlerInactive)) {
|
|
BufferingManager->HandlerInactive = ActivateHandler;
|
|
}
|
|
|
|
KeReleaseSpinLock( &BufferingManager->SpinLock, SavedIrql );
|
|
|
|
if (ActivateHandler) {
|
|
|
|
//
|
|
// Reference the SRV_CALL instance to ensure that it will not be
|
|
// finalized while the worker thread request is in the scheduler
|
|
//
|
|
|
|
RxReferenceSrvCall( SrvCall );
|
|
|
|
RxPostToWorkerThread( RxFileSystemDeviceObject,
|
|
HyperCriticalWorkQueue,
|
|
&BufferingManager->HandlerWorkItem,
|
|
RxProcessChangeBufferingStateRequests,
|
|
SrvCall);
|
|
}
|
|
|
|
RxpDiscardChangeBufferingStateRequests( &DiscardedRequests );
|
|
|
|
} else {
|
|
|
|
InterlockedDecrement( &BufferingManager->NumberOfOutstandingOpens );
|
|
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RxpLookupSrvOpenForRequestLite (
|
|
IN PSRV_CALL SrvCall,
|
|
IN PCHANGE_BUFFERING_STATE_REQUEST Request
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine looks up the SRV_OPEN instance associated with a buffering state change
|
|
request.
|
|
|
|
Arguments:
|
|
|
|
SrvCall - the SRV_CALL instance
|
|
|
|
Request - the buffering state change request
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - the SRV_OPEN instance was found
|
|
|
|
STATUS_PENDING - the SRV_OPEN instance was not found but there are open requests
|
|
outstanding
|
|
|
|
STATUS_NOT_FOUND - the SRV_OPEN instance was not found.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PRX_BUFFERING_MANAGER BufferingManager = &SrvCall->BufferingManager;
|
|
|
|
ULONG Index = 0;
|
|
|
|
PSRV_OPEN SrvOpen = NULL;
|
|
PLIST_ENTRY ListHead,Entry;
|
|
|
|
PAGED_CODE();
|
|
|
|
ListHead = &BufferingManager->SrvOpenLists[Index];
|
|
|
|
Entry = ListHead->Flink;
|
|
while (Entry != ListHead) {
|
|
|
|
SrvOpen = (PSRV_OPEN) CONTAINING_RECORD( Entry, SRV_OPEN, SrvOpenKeyList );
|
|
|
|
if ((SrvOpen->Key == Request->SrvOpenKey) &&
|
|
(!FlagOn( SrvOpen->Fcb->FcbState, FCB_STATE_ORPHANED ))) {
|
|
|
|
RxReferenceSrvOpen( SrvOpen );
|
|
break;
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
|
|
if (Entry == ListHead) {
|
|
|
|
SrvOpen = NULL;
|
|
|
|
if (BufferingManager->NumberOfOutstandingOpens == 0) {
|
|
Status = STATUS_NOT_FOUND;
|
|
} else {
|
|
Status = STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
Request->SrvOpen = SrvOpen;
|
|
|
|
return Status;
|
|
}
|
|
|
|
#define RxIsFcbOpenedExclusively(FCB) ( ((FCB)->ShareAccess.SharedRead \
|
|
+ (FCB)->ShareAccess.SharedWrite \
|
|
+ (FCB)->ShareAccess.SharedDelete) == 0 )
|
|
|
|
|
|
#define LOSING_CAPABILITY(a) ((NewBufferingState&(a))<(OldBufferingState&(a)))
|
|
|
|
NTSTATUS
|
|
RxChangeBufferingState (
|
|
PSRV_OPEN SrvOpen,
|
|
PVOID Context,
|
|
BOOLEAN ComputeNewState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to process a buffering state change request.
|
|
|
|
Arguments:
|
|
|
|
SrvOpen - the SrvOpen to be changed;
|
|
|
|
Context - the context parameter for mini rdr callback.
|
|
|
|
ComputeNewState - determines if the new state is to be computed.
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
On entry to this routine the FCB must have been accquired exclusive.
|
|
|
|
On exit there is no change in resource ownership
|
|
|
|
--*/
|
|
{
|
|
ULONG NewBufferingState, OldBufferingState;
|
|
PFCB Fcb = SrvOpen->Fcb;
|
|
NTSTATUS FlushStatus = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace( +1, Dbg, ("RxChangeBufferingState SrvOpen=%08lx, Context=%08lx\n", SrvOpen, Context) );
|
|
RxLog(( "ChangeBufferState %lx %lx\n", SrvOpen, Context ));
|
|
RxWmiLog( LOG,
|
|
RxChangeBufferingState_1,
|
|
LOGPTR( SrvOpen )
|
|
LOGPTR( Context ) );
|
|
ASSERT( NodeTypeIsFcb( Fcb ) );
|
|
|
|
//
|
|
// this is informational for error recovery
|
|
//
|
|
|
|
SetFlag( Fcb->FcbState, FCB_STATE_BUFFERSTATE_CHANGING );
|
|
|
|
try {
|
|
|
|
if (ComputeNewState) {
|
|
|
|
NTSTATUS Status;
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxChangeBufferingState FCB(%lx) Compute New State\n", Fcb ));
|
|
|
|
//
|
|
// Compute the new buffering state with the help of the mini redirector
|
|
//
|
|
|
|
MINIRDR_CALL_THROUGH( Status,
|
|
Fcb->MRxDispatch,
|
|
MRxComputeNewBufferingState,
|
|
((PMRX_SRV_OPEN)SrvOpen,
|
|
Context,
|
|
&NewBufferingState ));
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
NewBufferingState = 0;
|
|
}
|
|
} else {
|
|
NewBufferingState = SrvOpen->BufferingFlags;
|
|
}
|
|
|
|
if (RxIsFcbOpenedExclusively( Fcb ) && !ComputeNewState) {
|
|
|
|
SetFlag( NewBufferingState, (FCB_STATE_WRITECACHING_ENABLED |
|
|
FCB_STATE_FILESIZECACHEING_ENABLED |
|
|
FCB_STATE_FILETIMECACHEING_ENABLED |
|
|
FCB_STATE_WRITEBUFFERING_ENABLED |
|
|
FCB_STATE_LOCK_BUFFERING_ENABLED |
|
|
FCB_STATE_READBUFFERING_ENABLED |
|
|
FCB_STATE_READCACHING_ENABLED) );
|
|
}
|
|
|
|
if (Fcb->OutstandingLockOperationsCount != 0) {
|
|
ClearFlag( NewBufferingState, FCB_STATE_LOCK_BUFFERING_ENABLED );
|
|
}
|
|
|
|
//
|
|
// support for disabling local buffering independent of open mode/oplock/....
|
|
//
|
|
|
|
if (FlagOn( Fcb->FcbState, FCB_STATE_DISABLE_LOCAL_BUFFERING )) {
|
|
NewBufferingState = 0;
|
|
}
|
|
|
|
OldBufferingState = FlagOn( Fcb->FcbState, FCB_STATE_BUFFERING_STATE_MASK );
|
|
|
|
RxDbgTrace( 0, Dbg, ("--> OldBS=%08lx, NewBS=%08lx, SrvOBS = %08lx\n",
|
|
OldBufferingState, NewBufferingState, SrvOpen->BufferingFlags ));
|
|
RxLog( ("CBS-2 %lx %lx %lx\n", OldBufferingState, NewBufferingState, SrvOpen->BufferingFlags) );
|
|
RxWmiLog( LOG,
|
|
RxChangeBufferingState_2,
|
|
LOGULONG( OldBufferingState )
|
|
LOGULONG( NewBufferingState )
|
|
LOGULONG( SrvOpen->BufferingFlags ) );
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxChangeBufferingState FCB(%lx) Old (%lx) New (%lx)\n", Fcb, OldBufferingState, NewBufferingState) );
|
|
|
|
if (LOSING_CAPABILITY( FCB_STATE_WRITECACHING_ENABLED )) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("-->flush\n", 0 ) );
|
|
RxLog(( "CBS-Flush" ));
|
|
RxWmiLog( LOG,
|
|
RxChangeBufferingState_3,
|
|
LOGPTR( Fcb ) );
|
|
|
|
FlushStatus = RxFlushFcbInSystemCache( Fcb, TRUE );
|
|
}
|
|
|
|
//
|
|
// If there are no handles to this file or it the read caching capability
|
|
// is lost the file needs to be purged. This will force the memory
|
|
// manager to relinquish the additional reference on the file.
|
|
//
|
|
|
|
if ((Fcb->UncleanCount == 0) ||
|
|
LOSING_CAPABILITY( FCB_STATE_READCACHING_ENABLED ) ||
|
|
FlagOn( NewBufferingState, MINIRDR_BUFSTATE_COMMAND_FORCEPURGE )) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("-->purge\n", 0 ));
|
|
RxLog(( "CBS-purge\n" ));
|
|
RxWmiLog( LOG,
|
|
RxChangeBufferingState_4,
|
|
LOGPTR( Fcb ));
|
|
|
|
if (!NT_SUCCESS( FlushStatus )) {
|
|
|
|
RxCcLogError( (PDEVICE_OBJECT)Fcb->RxDeviceObject,
|
|
&Fcb->PrivateAlreadyPrefixedName,
|
|
IO_LOST_DELAYED_WRITE,
|
|
FlushStatus,
|
|
IRP_MJ_WRITE,
|
|
Fcb );
|
|
}
|
|
|
|
CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers,
|
|
NULL,
|
|
0,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// the wrapper does not use these flags yet
|
|
//
|
|
|
|
if (LOSING_CAPABILITY( FCB_STATE_WRITEBUFFERING_ENABLED )) NOTHING;
|
|
if (LOSING_CAPABILITY( FCB_STATE_READBUFFERING_ENABLED )) NOTHING;
|
|
if (LOSING_CAPABILITY( FCB_STATE_OPENSHARING_ENABLED )) NOTHING;
|
|
if (LOSING_CAPABILITY( FCB_STATE_COLLAPSING_ENABLED )) NOTHING;
|
|
if (LOSING_CAPABILITY( FCB_STATE_FILESIZECACHEING_ENABLED )) NOTHING;
|
|
if (LOSING_CAPABILITY( FCB_STATE_FILETIMECACHEING_ENABLED )) NOTHING;
|
|
|
|
if (ComputeNewState &&
|
|
FlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING ) &&
|
|
!IsListEmpty( &SrvOpen->FobxList )) {
|
|
|
|
NTSTATUS Status;
|
|
PRX_CONTEXT RxContext = NULL;
|
|
|
|
RxContext = RxCreateRxContext( NULL,
|
|
SrvOpen->Fcb->RxDeviceObject,
|
|
RX_CONTEXT_FLAG_WAIT|RX_CONTEXT_FLAG_MUST_SUCCEED_NONBLOCKING );
|
|
|
|
if (RxContext != NULL) {
|
|
|
|
RxContext->pFcb = (PMRX_FCB)Fcb;
|
|
RxContext->pFobx = (PMRX_FOBX) (CONTAINING_RECORD( SrvOpen->FobxList.Flink, FOBX, FobxQLinks ));
|
|
RxContext->pRelevantSrvOpen = RxContext->pFobx->pSrvOpen;
|
|
|
|
if (FlagOn( SrvOpen->Flags, SRVOPEN_FLAG_CLOSE_DELAYED )) {
|
|
|
|
RxLog(( " ##### Oplock brk close %lx\n", RxContext->pFobx ));
|
|
RxWmiLog( LOG,
|
|
RxChangeBufferingState_4,
|
|
LOGPTR( RxContext->pFobx ) );
|
|
Status = RxCloseAssociatedSrvOpen( RxContext,
|
|
(PFOBX)RxContext->pFobx );
|
|
|
|
} else {
|
|
|
|
MINIRDR_CALL_THROUGH( Status,
|
|
Fcb->MRxDispatch,
|
|
MRxCompleteBufferingStateChangeRequest,
|
|
(RxContext,(PMRX_SRV_OPEN)SrvOpen,Context) );
|
|
}
|
|
|
|
|
|
RxDereferenceAndDeleteRxContext( RxContext );
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxChangeBuffering State FCB(%lx) Completeing buffering state change\n", Fcb) );
|
|
}
|
|
|
|
ClearFlag( Fcb->FcbState, FCB_STATE_BUFFERING_STATE_MASK );
|
|
SetFlag( Fcb->FcbState, FlagOn( NewBufferingState, FCB_STATE_BUFFERING_STATE_MASK ) );
|
|
|
|
} finally {
|
|
|
|
//
|
|
// this is informational for error recovery
|
|
//
|
|
|
|
ClearFlag( Fcb->FcbState, FCB_STATE_BUFFERSTATE_CHANGING );
|
|
ClearFlag( Fcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET );
|
|
}
|
|
|
|
RxDbgTrace( -1, Dbg, ("-->exit\n") );
|
|
RxLog(( "Exit-CBS\n" ));
|
|
RxWmiLog( LOG,
|
|
RxChangeBufferingState_5,
|
|
LOGPTR( Fcb ) );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RxFlushFcbInSystemCache (
|
|
IN PFCB Fcb,
|
|
IN BOOLEAN SynchronizeWithLazyWriter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine simply flushes the data section on a file.
|
|
Then, it does an acquire-release on the pagingIO resource in order to
|
|
synchronize behind any other outstanding writes if such synchronization is
|
|
desired by the caller
|
|
|
|
Arguments:
|
|
|
|
Fcb - Supplies the file being flushed
|
|
|
|
SynchronizeWithLazyWriter -- set to TRUE if the flush needs to be
|
|
synchronous
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Status from the flush.
|
|
|
|
--*/
|
|
{
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure that this thread owns the FCB.
|
|
// This assert is not valid because the flushing of the cache can be called from a routine
|
|
// that was posted to a worker thread. Thus the FCB is acquired exclusively, but not by the
|
|
// current thread and this will fail.
|
|
// ASSERT ( RxIsFcbAcquiredExclusive ( Fcb ) );
|
|
//
|
|
|
|
CcFlushCache( &Fcb->NonPaged->SectionObjectPointers,
|
|
NULL,
|
|
0,
|
|
&Iosb ); // ok4flush
|
|
|
|
if (SynchronizeWithLazyWriter &&
|
|
NT_SUCCESS( Iosb.Status )) {
|
|
|
|
RxAcquirePagingIoResource( NULL, Fcb );
|
|
RxReleasePagingIoResource( NULL, Fcb );
|
|
}
|
|
|
|
RxLog(( "Flushing %lx Status %lx\n", Fcb, Iosb.Status ));
|
|
RxWmiLogError( Iosb.Status,
|
|
LOG,
|
|
RxFlushFcbInSystemCache,
|
|
LOGPTR( Fcb )
|
|
LOGULONG( Iosb.Status ) );
|
|
|
|
return Iosb.Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxPurgeFcbInSystemCache(
|
|
IN PFCB Fcb,
|
|
IN PLARGE_INTEGER FileOffset OPTIONAL,
|
|
IN ULONG Length,
|
|
IN BOOLEAN UninitializeCacheMaps,
|
|
IN BOOLEAN FlushFile )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine purges the data section on a file. Before purging it flushes
|
|
the file and ensures that there are no outstanding writes by
|
|
Then, it does an acquire-release on the pagingIO resource in order to
|
|
synchronize behind any other outstanding writes if such synchronization is
|
|
desired by the caller
|
|
|
|
Arguments:
|
|
|
|
Fcb - Supplies the file being flushed
|
|
|
|
SynchronizeWithLazyWriter -- set to TRUE if the flush needs to be
|
|
synchronous
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Status from the flush.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN Result;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK Iosb;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure that this thread owns the FCB.
|
|
//
|
|
|
|
ASSERT( RxIsFcbAcquiredExclusive ( Fcb ) );
|
|
|
|
//
|
|
// Flush if we need to
|
|
//
|
|
|
|
if (FlushFile) {
|
|
|
|
Status = RxFlushFcbInSystemCache( Fcb, TRUE );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
PVOID p1, p2;
|
|
RtlGetCallersAddress( &p1, &p2 );
|
|
RxLogRetail(( "Flush failed %x %x, Purging anyway\n", Fcb, Status ));
|
|
RxLogRetail(( "Purge Caller = %x %x\n", p1, p2 ));
|
|
|
|
RxCcLogError( (PDEVICE_OBJECT)Fcb->RxDeviceObject,
|
|
&Fcb->PrivateAlreadyPrefixedName,
|
|
IO_LOST_DELAYED_WRITE,
|
|
Status,
|
|
IRP_MJ_WRITE,
|
|
Fcb );
|
|
}
|
|
}
|
|
|
|
Result = CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers,
|
|
FileOffset,
|
|
Length,
|
|
UninitializeCacheMaps );
|
|
|
|
if (!Result) {
|
|
|
|
MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, MmFlushForWrite );
|
|
RxReleaseFcb( NULL, Fcb );
|
|
|
|
Result = MmForceSectionClosed( &Fcb->NonPaged->SectionObjectPointers, TRUE );
|
|
|
|
RxAcquireExclusiveFcb( NULL, Fcb );
|
|
}
|
|
|
|
if (Result) {
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
RxLog(( "Purging %lx Status %lx\n", Fcb, Status ));
|
|
RxWmiLogError( Status,
|
|
LOG,
|
|
RxPurgeFcbInSystemCache,
|
|
LOGPTR( Fcb )
|
|
LOGULONG( Status ) );
|
|
|
|
return Status;
|
|
}
|
|
|