/*++ Copyright (c) 1989 Microsoft Corporation Module Name: AgntEvnt.c Abstract: This module implements the interface by which the csc driver reports stuff to the agent. Author: Joe Linn [JoeLinn] 5-may-1997 Revision History: Notes: Additional synchronization surrounds net_start and net_stop. for netstop, we have to wait for the agent to signal that he is finished before we can proceed. we probably we should implement a mechanism by which he can refuse the netstop. anyway this is done by recording the netstop context that is waiting before signaling the event. the same sort of thing is true for netstart. --*/ #include "precomp.h" #pragma hdrstop #pragma code_seg("PAGE") extern DEBUG_TRACE_CONTROLPOINT RX_DEBUG_TRACE_MRXSMBCSC; #define Dbg (DEBUG_TRACE_MRXSMBCSC) // A global variable which indicates the current status of the net // TRUE implies available. This is used in controlling the // transitioning indication to the agent LONG CscNetPresent = FALSE; LONG CscAgentNotifiedOfNetStatusChange = CSC_AGENT_NOT_NOTIFIED; LONG CscAgentNotifiedOfFullCache = CSC_AGENT_NOT_NOTIFIED; PKEVENT MRxSmbAgentSynchronizationEvent = NULL; ULONG MRxSmbAgentSynchronizationEventIdx = 0; PKEVENT MRxSmbAgentFillEvent = NULL; PRX_CONTEXT MRxSmbContextAwaitingAgent = NULL; PRX_CONTEXT MRxSmbContextAwaitingFillAgent = NULL; LONG vcntTransportsForCSC=0; extern ULONG CscSessionIdCausingTransition; //CODE.IMPROVEMENT.NTIFS had to just know to do this...... extern POBJECT_TYPE *ExEventObjectType; //CODE.IMPROVEMENT.NTIFS just stole this from oak\in\zwapi.h NTSYSAPI NTSTATUS NTAPI ZwOpenEvent ( OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); typedef struct _MRXSMBCSC_OPENEVENT_POSTCONTEXT { KEVENT PostEvent; RX_WORK_QUEUE_ITEM WorkQueueItem; } MRXSMBCSC_OPENEVENT_POSTCONTEXT, *PMRXSMBCSC_OPENEVENT_POSTCONTEXT; VOID MRxSmbCscOpenAgentEvent ( BOOLEAN PostedCall); VOID MRxSmbCscOpenAgentFillEvent ( BOOLEAN PostedCall); NTSTATUS MRxSmbCscOpenAgentEventPostWrapper( IN OUT PMRXSMBCSC_OPENEVENT_POSTCONTEXT OpenEventPostContext ) { RxDbgTrace( 0, Dbg, ("MRxSmbCscOpenAgentEventPostWrapper entry\n")); MRxSmbCscOpenAgentEvent(TRUE); RxDbgTrace( 0, Dbg, ("MRxSmbCscOpenAgentEventPostWrapper exit\n")); KeSetEvent( &OpenEventPostContext->PostEvent, 0, FALSE ); return(STATUS_SUCCESS); } NTSTATUS MRxSmbCscOpenAgentFillEventPostWrapper( IN OUT PMRXSMBCSC_OPENEVENT_POSTCONTEXT OpenFillEventPostContext ) { RxDbgTrace( 0, Dbg, ("MRxSmbCscOpenAgentFillEventPostWrapper entry\n")); MRxSmbCscOpenAgentFillEvent(TRUE); RxDbgTrace( 0, Dbg, ("MRxSmbCscOpenAgentFillEventPostWrapper exit\n")); KeSetEvent( &OpenFillEventPostContext->PostEvent, 0, FALSE ); return(STATUS_SUCCESS); } VOID MRxSmbCscOpenAgentEvent ( BOOLEAN PostedCall ) /*++ Routine Description: This routine gets a pointer to the agent's event. Arguments: Return Value: Notes: The shadowcrit serialization mutex must have already been acquired before the call. --*/ { NTSTATUS Status; HANDLE EventHandle; UNICODE_STRING EventName; OBJECT_ATTRIBUTES ObjectAttributes; WCHAR SessEventName[100]; WCHAR IdBuffer[16]; UNICODE_STRING IdString; ASSERT (MRxSmbAgentSynchronizationEvent == NULL); // DbgPrint("MRxSmbCscOpenAgentEvent(%d) Caused by sess 0x%x\n", // PostedCall, // CscSessionIdCausingTransition); if (PsGetCurrentProcess()!= RxGetRDBSSProcess()) { //CODE.IMPROVEMENT we should capture the rdbss process // and avoid this call (RxGetRDBSSProcess) NTSTATUS PostStatus; MRXSMBCSC_OPENEVENT_POSTCONTEXT PostContext; ASSERT(!PostedCall); KeInitializeEvent(&PostContext.PostEvent, NotificationEvent, FALSE ); IF_DEBUG { //fill the workqueue structure with deadbeef....all the better to diagnose //a failed post ULONG i; for (i=0;i+sizeof(ULONG)-1not a posted call if (MRxSmbAgentSynchronizationEvent == NULL) { //still NULL...no agent......... RxDbgTrace(0, Dbg, ("MRxSmbCscSignalAgent no event %08lx %08lx\n", RxContext,Status)); Status = STATUS_SUCCESS; goto FINALLY; } } if (RxContext != NULL) { MRxSmbContextAwaitingAgent = RxContext; } if (!PreventLeaveCrit) { LeaveShadowCrit(); ShadowCritEntered = FALSE; } else { ASSERT(RxContext==NULL); //cant wait with critsect held } // reduce the window of MRxSmbAgentSynchronizationEvent getting nulled out // by explictly checking before pulsing if (MRxSmbAgentSynchronizationEvent) { KeSetEvent(MRxSmbAgentSynchronizationEvent,0,FALSE); } if (RxContext != NULL) { RxDbgTrace(0, Dbg, ("MRxSmbCscSignalAgent waiting %08lx\n", RxContext)); RxWaitSync(RxContext); RxDbgTrace(0, Dbg, ("MRxSmbCscSignalAgent end of wait %08lx\n", RxContext)); } RxDbgTrace(0, Dbg, ("MRxSmbCscSignalAgent %08lx\n", RxContext)); FINALLY: if (ShadowCritEntered && !PreventLeaveCrit) { LeaveShadowCrit(); } RxDbgTrace(-1, Dbg, ("MRxSmbCscSignalAgent... exit %08lx %08lx\n", RxContext, Status)); return(Status); } VOID MRxSmbCscSignalNetStatus( BOOLEAN NetPresent, BOOLEAN fFirstLast ) { if(!MRxSmbIsCscEnabled) { return; } if (NetPresent) { InterlockedIncrement(&vcntTransportsForCSC); } else { if(vcntTransportsForCSC == 0) { DbgPrint("CSC:Mismatched transport departure messages from mini redir \n"); return; } InterlockedDecrement(&vcntTransportsForCSC); } if (!fFirstLast) { return; } InterlockedExchange( &CscNetPresent, NetPresent); InterlockedExchange( &CscAgentNotifiedOfNetStatusChange, CSC_AGENT_NOT_NOTIFIED); CscNotifyAgentOfNetStatusChangeIfRequired(FALSE); } VOID CscNotifyAgentOfNetStatusChangeIfRequired( BOOLEAN fInvokeAutoDial ) { LONG AgentNotificationState; AgentNotificationState = InterlockedExchange( &CscAgentNotifiedOfNetStatusChange, CSC_AGENT_NOTIFIED); if (AgentNotificationState == CSC_AGENT_NOT_NOTIFIED) { EnterShadowCrit(); //this is dropped in the signalagent routine.... if (CscNetPresent) { SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_GOT_NET); } else { SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_NO_NET); if (fInvokeAutoDial) { SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_INVOKE_AUTODIAL); } } MRxSmbCscSignalAgent( NULL, SIGNALAGENTFLAG_CONTINUE_FOR_NO_AGENT); } } VOID CscNotifyAgentOfFullCacheIfRequired( VOID) { // DbgPrint("CscNotifyAgentOfFullCacheIfRequired()\n"); if (MRxSmbAgentSynchronizationEvent == NULL) { MRxSmbCscOpenAgentEvent(FALSE); //FALSE==>not a posted call if (MRxSmbAgentSynchronizationEvent == NULL) { RxDbgTrace(0, Dbg, ("MRxSmbCscSignalAgent no event %08lx %08lx\n")); // DbgPrint("CscNotifyAgentOfFullCacheIfRequired exit no event\n"); return; } } SetFlag(sGS.uFlagsEvents, FLAG_GLOBALSTATUS_INVOKE_FREESPACE); if (MRxSmbAgentSynchronizationEvent) KeSetEvent(MRxSmbAgentSynchronizationEvent,0,FALSE); // DbgPrint("CscNotifyAgentOfFullCacheIfRequired exit\n"); } //CODE.IMPROVEMENT.ASHAMED...the next two routines are virtually identical; also there's // a lot of very similar codein the third VOID MRxSmbCscAgentSynchronizationOnStart ( IN OUT PRX_CONTEXT RxContext ) /*++ Routine Description: This routine signals the csc usermode agent that a start is occurring. By passing the context to the signal routine, we indicate that we want to wait for an external guy (in this case ioctl_register_start) to signal us tro proceed Arguments: RxContext - the RDBSS context. Return Value: Notes: --*/ { #if 0 NTSTATUS Status; if(!MRxSmbIsCscEnabled) { return; } RxDbgTrace(+1, Dbg, ("MRxSmbCscAgentSynchronizationOnStart entry...%08lx\n",RxContext)); EnterShadowCrit(); //this is dropped in the signalagent routine.... // check if an agent is already registered // if he is then we don't need to do any of this stuff if (!hthreadReint) { SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_START); Status = MRxSmbCscSignalAgent(RxContext, SIGNALAGENTFLAG_CONTINUE_FOR_NO_AGENT); } else { LeaveShadowCrit(); } RxDbgTrace(-1, Dbg, ("MRxSmbCscAgentSynchronizationOnStart...%08lx %08lx\n", RxContext, Status)); #endif return; } VOID MRxSmbCscAgentSynchronizationOnStop ( IN OUT PRX_CONTEXT RxContext ) /*++ Routine Description: This routine signals the csc usermode agent that a stop is occurring. By passing the context to the signal routine, we indicate that we want to wait for an external guy (in this case ioctl_register_stop) to signal us tro proceed Arguments: RxContext - the RDBSS context. Return Value: Notes: --*/ { #if 0 NTSTATUS Status; if(!MRxSmbIsCscEnabled) { return; } RxDbgTrace(+1, Dbg, ("MRxSmbCscAgentSynchronizationOnStop entry...%08lx\n",RxContext)); EnterShadowCrit(); //this is dropped in the signalagent routine.... if (hthreadReint) { SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_STOP); Status = MRxSmbCscSignalAgent(RxContext,0); //0 means no special operations } else { LeaveShadowCrit(); } RxDbgTrace(-1, Dbg, ("MRxSmbCscAgentSynchronizationOnStop...%08lx %08lx\n", RxContext, Status)); #endif return; } VOID MRxSmbCscReleaseRxContextFromAgentWait ( void ) /*++ Routine Description: This routine checks to see if there is a context waiting for the agent. If so, it signals the context's syncevent. It also clears the specified flags. Arguments: RxContext - the RDBSS context. Return Value: Notes: --*/ { PRX_CONTEXT WaitingContext; RxDbgTrace(+1, Dbg, ("MRxSmbCscReleaseRxContextFromAgentWait entry...%08lx\n")); ASSERT(MRxSmbIsCscEnabled); EnterShadowCrit(); WaitingContext = MRxSmbContextAwaitingAgent; MRxSmbContextAwaitingAgent = NULL; LeaveShadowCrit(); if (WaitingContext != NULL) { RxSignalSynchronousWaiter(WaitingContext); } RxDbgTrace(-1, Dbg, ("MRxSmbCscReleaseRxContextFromAgentWait...%08lx\n")); return; } //CODE.IMPROVEMENT get this in an include file extern ULONG MRxSmbCscNumberOfShadowOpens; extern ULONG MRxSmbCscActivityThreshold; VOID MRxSmbCscReportFileOpens ( void ) /*++ Routine Description: This routine checks to see if there has been enough activity to signal the agent to recompute reference priorities. Arguments: Return Value: Notes: --*/ { NTSTATUS Status; RxDbgTrace(+1, Dbg, ("MRxSmbCscReportFileOpens entry...%08lx %08lx\n", MRxSmbCscNumberOfShadowOpens,(ULONG)(sGS.cntFileOpen) )); EnterShadowCrit(); //this is dropped in the signalagent routine.... MRxSmbCscNumberOfShadowOpens++; if ((MRxSmbCscNumberOfShadowOpens > (ULONG)(sGS.cntFileOpen) ) // to guard against rollover && ((MRxSmbCscNumberOfShadowOpens - (ULONG)(sGS.cntFileOpen)) < MRxSmbCscActivityThreshold)) { RxDbgTrace(-1, Dbg, ("MRxSmbCscReportFileOpens inactive...\n")); LeaveShadowCrit(); return; } //SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_START); sGS.cntFileOpen = MRxSmbCscNumberOfShadowOpens; Status = MRxSmbCscSignalAgent(NULL,0); //this means don't wait for a repsonse RxDbgTrace(-1, Dbg, ("MRxSmbCscReportFileOpens...activeexit\n")); return; } NTSTATUS MRxSmbCscSignalFillAgent( PRX_CONTEXT RxContext OPTIONAL, ULONG Controls) { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN ShadowCritEntered = TRUE; //must be in critsect on BOOLEAN PreventLeaveCrit = BooleanFlagOn(Controls,SIGNALAGENTFLAG_DONT_LEAVE_CRIT_SECT); RxDbgTrace(+1, Dbg, ("MRxSmbCscSignalFillAgent entry...%08lx\n",RxContext)); // DbgPrint("MRxSmbCscSignalFillAgent entry...%08lx\n",RxContext); ASSERT(MRxSmbIsCscEnabled); if (!FlagOn(Controls,SIGNALAGENTFLAG_CONTINUE_FOR_NO_AGENT)) { if (hthreadReint==0) { //no agent and no force...just get out RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent no agent/noforce %08lx\n", RxContext)); goto FINALLY; } } if (MRxSmbAgentFillEvent == NULL) { // DbgPrint("MRxSmbCscSignalFillAgent: gotta open the event...\n"); MRxSmbCscOpenAgentFillEvent(FALSE); //FALSE==>not a posted call if (MRxSmbAgentFillEvent == NULL) { //still NULL...no agent......... RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent no event %08lx %08lx\n", RxContext,Status)); // DbgPrint("MRxSmbCscSignalFillAgent no event %08lx %08lx\n"); Status = STATUS_SUCCESS; goto FINALLY; } } if (RxContext != NULL) { MRxSmbContextAwaitingFillAgent = RxContext; } // if (!PreventLeaveCrit) { // LeaveShadowCrit(); // ShadowCritEntered = FALSE; // } else { // ASSERT(RxContext==NULL); //cant wait with critsect held // } // reduce the window of MRxSmbAgentFillEvent getting nulled out // by explictly checking before pulsing if (MRxSmbAgentFillEvent) { KeSetEvent(MRxSmbAgentFillEvent,0,FALSE); } if (RxContext != NULL) { RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent waiting %08lx\n", RxContext)); RxWaitSync(RxContext); RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent end of wait %08lx\n", RxContext)); } RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent %08lx\n", RxContext)); FINALLY: // if (ShadowCritEntered && !PreventLeaveCrit) { // LeaveShadowCrit(); // } RxDbgTrace(-1, Dbg, ("MRxSmbCscSignalFillAgent... exit %08lx %08lx\n", RxContext, Status)); // DbgPrint("MRxSmbCscSignalFillAgent... exit %08lx %08lx\n", Status); return(Status); } VOID MRxSmbCscOpenAgentFillEvent ( BOOLEAN PostedCall) { NTSTATUS Status; HANDLE EventHandle; UNICODE_STRING EventName; OBJECT_ATTRIBUTES ObjectAttributes; if (PsGetCurrentProcess()!= RxGetRDBSSProcess()) { //CODE.IMPROVEMENT we should capture the rdbss process // and avoid this call (RxGetRDBSSProcess) NTSTATUS PostStatus; MRXSMBCSC_OPENEVENT_POSTCONTEXT PostContext; ASSERT(!PostedCall); // DbgPrint("MRxSmbCscOpenAgentFillEvent: posting...\n"); KeInitializeEvent(&PostContext.PostEvent, NotificationEvent, FALSE ); PostStatus = RxPostToWorkerThread( MRxSmbDeviceObject, CriticalWorkQueue, &PostContext.WorkQueueItem, MRxSmbCscOpenAgentFillEventPostWrapper, &PostContext); ASSERT(PostStatus == STATUS_SUCCESS); KeWaitForSingleObject( &PostContext.PostEvent, Executive, KernelMode, FALSE, NULL ); // DbgPrint("MRxSmbCscOpenAgentFillEvent: posting done...\n"); return; } RtlInitUnicodeString(&EventName,SHARED_FILL_EVENT_NAME_NT); InitializeObjectAttributes( &ObjectAttributes, &EventName, 0, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); Status = ZwOpenEvent( &EventHandle, EVENT_ALL_ACCESS, &ObjectAttributes); if (Status!=STATUS_SUCCESS) { RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent no event %08lx\n",Status)); DbgPrint("MRxSmbCscSignalFillAgent no event %08lx\n",Status); return; } Status = ObReferenceObjectByHandle( EventHandle, 0, *ExEventObjectType, KernelMode, (PVOID *) &MRxSmbAgentFillEvent, NULL ); ZwClose(EventHandle); if (Status!=STATUS_SUCCESS) { RxDbgTrace(0, Dbg, ("MRxSmbCscSignalFillAgent couldn't reference %08lx\n", Status)); DbgPrint("MRxSmbCscSignalFillAgent couldn't reference %08lx\n", Status); MRxSmbAgentFillEvent = NULL; return; } return; }