/*++ Copyright (c) 1987-1993  Microsoft Corporation

Module Name:

    MRxProxyAsyncEng.c

Abstract:

    This module defines the types and functions related to the SMB protocol
    selection engine: the component that translates minirdr calldowns into
    SMBs.

Author:

    Joe Linn        [JoeLi] -- Implemented Ordinary Exchange

Notes:


--*/

#include "precomp.h"
#pragma hdrstop
#include <dfsfsctl.h>

RXDT_DefineCategory(MRXPROXY_ASYNCENG);
#define Dbg                              (DEBUG_TRACE_MRXPROXY_ASYNCENG)

#ifdef RX_PRIVATE_BUILD
#undef IoGetTopLevelIrp
#undef IoSetTopLevelIrp
#endif //ifdef RX_PRIVATE_BUILD

typedef struct _MRXPROXY_ASYNCENG_MUST_SUCCEEED_CONTEXT {
    struct {
        LIST_ENTRY ExchangeListEntry;
        union {
            MRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext;
        };
    };
//    MRXPROXY_ASYNCENG_VESTIGIAL_SMBBUF SmbBuf;
//    struct {
//        union {
//            MDL;
//            MDL Mdl;
//        };
//        ULONG Pages[2];
//    } DataPartialMdl;
} MRXPROXY_ASYNCENG_MUST_SUCCEEED_CONTEXT, *PMRXPROXY_ASYNCENG_MUST_SUCCEEED_CONTEXT;

BOOLEAN MRxProxyEntryPointIsMustSucceedable[MRXPROXY_ASYNCENG_CTX_FROM_MAXIMUM];

typedef enum {
    MRxProxyAsyncEngMustSucceed = 0,
    MRxProxyAsyncEngMustSucceedMaximum
};
RX_MUSTSUCCEED_DESCRIPTOR MRxProxyAsyncEngMustSucceedDescriptor[MRxProxyAsyncEngMustSucceedMaximum];
MRXPROXY_ASYNCENG_MUST_SUCCEEED_CONTEXT MRxProxyAsyncEngMustSucceedAsyncEngineContext[MRxProxyAsyncEngMustSucceedMaximum];

#define MIN(x,y) ((x) < (y) ? (x) : (y))


#if DBG
#define P__ASSERT(exp) {             \
    if (!(exp)) {                    \
        DbgPrint("NOT %s\n",#exp);   \
        errors++;                    \
    }}
VOID
__MRxProxyAsyncEngOEAssertConsistentLinkage(
    PSZ MsgPrefix,
    PSZ File,
    unsigned Line,
    PRX_CONTEXT RxContext,
    PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext,
    ULONG Flags
    )
/*++

Routine Description:

   This routine performs a variety of checks to ensure that the linkage between
   the RxContext and the AsyncEngineContext is correct and that various fields
   have correct values. if anything is bad....print stuff out and brkpoint;

Arguments:

     MsgPrefix          an identifying msg
     RxContext           duh
     AsyncEngineContext    .

Return Value:

    none

Notes:

--*/
{
    ULONG errors = 0;

    PMRXPROXY_RX_CONTEXT pMRxProxyContext;

    pMRxProxyContext = MRxProxyGetMinirdrContext(RxContext);

    P__ASSERT( AsyncEngineContext->SerialNumber == RxContext->SerialNumber );
    P__ASSERT( NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT );
    P__ASSERT( NodeType(AsyncEngineContext)==PROXY_NTC_ASYNCENGINE_CONTEXT );
    P__ASSERT( AsyncEngineContext->RxContext == RxContext );
    P__ASSERT( pMRxProxyContext->AsyncEngineContext == AsyncEngineContext );
    if (!FlagOn(Flags,OECHKLINKAGE_FLAG_NO_REQPCKT_CHECK)) {
        P__ASSERT( AsyncEngineContext->RxContextCapturedRequestPacket == RxContext->CurrentIrp);
    }
    if (errors==0) {
        return;
    }
    DbgPrint("%s INCONSISTENT OE STATE: %d errors at %s line %d\n",
                 MsgPrefix,errors,File,Line);
    DbgBreakPoint();
    return;
}

ULONG MRxProxyAsyncEngShortStatus(ULONG Status)
{
    ULONG ShortStatus;

    ShortStatus = Status & 0xc0003fff;
    ShortStatus = ShortStatus | (ShortStatus >>16);
    return(ShortStatus);
}

VOID MRxProxyAsyncEngUpdateOEHistory(
    PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext,
    ULONG Tag1,
    ULONG Tag2
    )
{
    ULONG MyIndex,Long0,Long1;

    MyIndex = InterlockedIncrement(&AsyncEngineContext->History.Next);
    MyIndex = (MyIndex-1) & (MRXPROXY_ASYNCENG_OE_HISTORY_SIZE-1);
    Long0 = (Tag1<<16) | (Tag2 & 0xffff);
    Long1 = (MRxProxyAsyncEngShortStatus(AsyncEngineContext->Status)<<16) | AsyncEngineContext->Flags;
    AsyncEngineContext->History.Markers[MyIndex].Longs[0] = Long0;
    AsyncEngineContext->History.Markers[MyIndex].Longs[1] = Long1;
}

#else
#endif

#define UPDATE_OE_HISTORY_WITH_STATUS(a) UPDATE_OE_HISTORY_2SHORTS(a,MRxProxyAsyncEngShortStatus(AsyncEngineContext->Status))

NTSTATUS
MRxProxyResumeAsyncEngineContext(
    IN OUT PRX_CONTEXT RxContext
    )
/*++

Routine Description:

   This routine resumes processing on an exchange. This is called when work is
   required to finish processing a request that cannot be completed at DPC
   level.  This happens either because the parse routine needs access to
   structures that are not locks OR because the operation if asynchronous and
   there maybe more work to be done.

   The two cases are regularized by delaying the parse if we know that we're
   going to post: this is indicated by the presense of a resume routine.

Arguments:

    RxContext  - the context of the operation. .

Return Value:

    RXSTATUS - The return status for the operation

Notes:

--*/
{
    NTSTATUS Status;
    PMRXPROXY_RX_CONTEXT pMRxProxyContext = MRxProxyGetMinirdrContext(RxContext);

    PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext =
                        (PMRXPROXY_ASYNCENGINE_CONTEXT)(pMRxProxyContext->AsyncEngineContext);
    RxCaptureFobx;
    BOOLEAN PostedResume;
    //PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;

    //PMDL SubmitMdl, HeaderFullMdl;

    RxDbgTrace(+1, Dbg, ("MRxProxyResumeAsyncEngineContext entering........OE=%08lx\n",AsyncEngineContext));
    ClearFlag(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_AWAITING_DISPATCH);
    PostedResume = BooleanFlagOn(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_POSTED_RESUME);
    ClearFlag(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_POSTED_RESUME);

    MRxProxyAsyncEngOEAssertConsistentLinkageFromOE("MRxProxyAsyncEngContinueAsyncEngineContext:");

    Status = AsyncEngineContext->Status;
    UPDATE_OE_HISTORY_WITH_STATUS('0c');

#if 0
  __RETRY_FINISH_ROUTINE__:
    if ( AsyncEngineContext->FinishRoutine != NULL ) {
        if ( Status == (STATUS_MORE_PROCESSING_REQUIRED) ){
            AsyncEngineContext->Status = (STATUS_SUCCESS);
        }
        Status = AsyncEngineContext->FinishRoutine( AsyncEngineContext );
        UPDATE_OE_HISTORY_WITH_STATUS('1c');
        AsyncEngineContext->Status = Status;
        AsyncEngineContext->FinishRoutine = NULL;

    } else if ( Status == (STATUS_MORE_PROCESSING_REQUIRED) ) {

        NOTHING;   //it used to call the receive routine here

        // after calling the receive routine again, we may NOW have a finish routine!
        if ( AsyncEngineContext->FinishRoutine != NULL ) {
            goto __RETRY_FINISH_ROUTINE__;
        }
    } else {
        NOTHING;
    }
#endif //0

    if (PostedResume) {

        Status = AsyncEngineContext->Continuation( MRXPROXY_ASYNCENGINE_ARGUMENTS );
        UPDATE_OE_HISTORY_WITH_STATUS('3c');

    }

    //remove my references, if i'm the last guy then do the putaway...
    UPDATE_OE_HISTORY_WITH_STATUS('4c');
    MRxProxyFinalizeAsyncEngineContext(AsyncEngineContext);

    RxDbgTrace(-1, Dbg, ("MRxProxyResumeAsyncEngineContext returning %08lx.\n", Status));
    return(Status);

} // MRxProxyAsyncEngContinueAsyncEngineContext



NTSTATUS
MRxProxySubmitAsyncEngRequest(
    MRXPROXY_ASYNCENGINE_ARGUMENT_SIGNATURE,
    IN     MRXPROXY_ASYNCENGINE_CONTEXT_TYPE AECTXType
    )
/*++

Routine Description:

   This routine implements an ordinary exchange as viewed by the protocol
   selection routines.

Arguments:

    AsyncEngineContext  - the exchange to be conducted.
    AECTXType            - async engine context submit Type

Return Value:

    RXSTATUS - The return status for the operation

Notes:

--*/
{
    NTSTATUS Status;
    RxCaptureFcb;RxCaptureFobx;

    PMRXPROXY_RX_CONTEXT pMRxProxyAsyncEngineContext = MRxProxyGetMinirdrContext(RxContext);
    //PMRXPROXY_ASYNCENG_CONTINUE_ROUTINE Continuation;
    BOOLEAN AsyncOperation = FlagOn(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_ASYNC_OPERATION);

    PIRP TopIrp;

    PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
    PMRX_PROXY_SRV_OPEN proxySrvOpen = MRxProxyGetSrvOpenExtension(SrvOpen);

    RxDbgTrace(+1, Dbg, ("MRxProxyAsyncEngSubmitRequest entering.......OE=%08lx\n",AsyncEngineContext));

    MRxProxyAsyncEngOEAssertConsistentLinkageFromOE("MRxProxyAsyncEngSubmitRequest:");

    AsyncEngineContext->AECTXType = AECTXType;
    KeInitializeEvent( &RxContext->SyncEvent,
                       NotificationEvent,
                       FALSE );

    MRxProxyReferenceAsyncEngineContext( AsyncEngineContext );  //this one is taken away in Continue
    MRxProxyReferenceAsyncEngineContext( AsyncEngineContext );  //this one is taken away below...
                                                                //i must NOT finalize before InnerIo returns
    IF_DEBUG {
        //if ( ((AsyncEngineContext->AECTXType == MRXPROXY_ASYNCENG_AECTXTYPE_WRITE)
        //                          || (AsyncEngineContext->AECTXType == MRXPROXY_ASYNCENG_AECTXTYPE_READ)
        //                          || (AsyncEngineContext->AECTXType == MRXPROXY_ASYNCENG_AECTXTYPE_LOCKS))
        //     && BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)
        //   ) {
        //    ASSERT(AsyncOperation);
        //}
    }

    MRxProxyAsyncEngOEAssertConsistentLinkage("just before transceive: ");

    UPDATE_OE_HISTORY_2SHORTS('eo',AsyncOperation?'!!':0);
    DbgDoit( InterlockedIncrement(&AsyncEngineContext->History.Submits); )

    IF_DEBUG {
        PIO_STACK_LOCATION IrpSp;
        IrpSp = IoGetNextIrpStackLocation( AsyncEngineContext->CalldownIrp );  //ok4ioget
        RxLog(("SAsyIrpX %lx %lx %lx %lx %lx %lx %lx",
                            RxContext,
                            IrpSp->MajorFunction,
                            IrpSp->Flags,
                            IrpSp->Parameters.Others.Argument1,
                            IrpSp->Parameters.Others.Argument2,
                            IrpSp->Parameters.Others.Argument3,
                            IrpSp->Parameters.Others.Argument4));
    }

    try {
        TopIrp = IoGetTopLevelIrp();
        IoSetTopLevelIrp(NULL); //tell the underlying guy he's all clear
        Status = IoCallDriver(
                     proxySrvOpen->UnderlyingDeviceObject,
                     AsyncEngineContext->CalldownIrp
                     );
    } finally {
        IoSetTopLevelIrp(TopIrp); //restore my context for unwind
    }

    RxDbgTrace (0, Dbg, ("  -->Status after transceive %08lx(%08lx)\n",Status,RxContext));

    if (Status != (STATUS_PENDING)) {
        ASSERT(Status == AsyncEngineContext->CalldownIrp->IoStatus.Status);
        Status = STATUS_PENDING;
    }


    MRxProxyFinalizeAsyncEngineContext(AsyncEngineContext);  //okay to finalize now that we're back

    if ( Status == (STATUS_PENDING) ) {
        if ( AsyncOperation ) {
            goto FINALLY;
        }

        UPDATE_OE_HISTORY_WITH_STATUS('1o');
        RxWaitSync( RxContext );
    } else {
        RxDbgTrace (0, Dbg, ("  -->Status after transceive %08lx\n",Status));
        MRxProxyAsyncEngOEAssertConsistentLinkage("nonpending return from transceive: ");
        // if it's an error, remove the references that i placed and get out
        if (NT_ERROR(Status)) {
            MRxProxyFinalizeAsyncEngineContext(AsyncEngineContext);
            goto FINALLY;
        }
    }

    //at last, call the continuation........

    MRxProxyAsyncEngOEAssertConsistentLinkage("just before continueOE: ");
    Status = MRxProxyResumeAsyncEngineContext( RxContext );
    UPDATE_OE_HISTORY_WITH_STATUS('9o');

FINALLY:
    RxDbgTrace(-1, Dbg, ("MRxProxyAsyncEngSubmitRequest returning %08lx.\n", Status));
    return(Status);

} // MRxProxyAsyncEngSubmitRequest



//#define MRXSMB_TEST_MUST_SUCCEED
#ifdef MRXSMB_TEST_MUST_SUCCEED
ULONG MRxProxyAllocatedMustSucceedExchange;
ULONG MRxProxyAllocatedMustSucceedSmbBuf;
#define MSFAILPAT ((0x3f)<<2)
#define FAIL_XXX_ALLOCATE() (FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED) \
                                    &&((RxContext->SerialNumber&MSFAILPAT)==MSFAILPAT) \
                                    &&((MRxProxyEntryPointIsMustSucceedable[EntryPoint])) )
#define FAIL_EXCHANGE_ALLOCATE() ( FAIL_XXX_ALLOCATE() && (RxContext->SerialNumber&2) )
#define FAIL_SMBBUF_ALLOCATE()   ( FAIL_XXX_ALLOCATE() && (RxContext->SerialNumber&1) )
#define COUNT_MUST_SUCCEED_EXCHANGE_ALLOCATED() {MRxProxyAllocatedMustSucceedExchange++;}
#define COUNT_MUST_SUCCEED_SMBBUF_ALLOCATED() {MRxProxyAllocatedMustSucceedSmbBuf++;}
#define MUST_SUCCEED_ASSERT(x) {ASSERT(x);}
#else
#define FAIL_EXCHANGE_ALLOCATE() (FALSE)
#define FAIL_SMBBUF_ALLOCATE()   (FALSE)
#define COUNT_MUST_SUCCEED_EXCHANGE_ALLOCATED() {NOTHING;}
#define COUNT_MUST_SUCCEED_SMBBUF_ALLOCATED() {NOTHING;}
#define MUST_SUCCEED_ASSERT(x) {NOTHING;}
#endif

PMRXPROXY_ASYNCENGINE_CONTEXT
MRxProxyCreateAsyncEngineContext (
    IN PRX_CONTEXT RxContext,
    IN MRXPROXY_ASYNCENGINE_CONTEXT_ENTRYPOINTS EntryPoint
    )
/*++

Routine Description:

   This routine allocates and initializes an SMB header buffer. Currently,
   we just allocate them from pool except when must_succeed is specified.

Arguments:

    RxContext       - the RDBSS context
    VNetRoot        -
    DispatchVector  -

Return Value:

    A buffer ready to go, OR NULL.

Notes:



--*/
{
    PMRXPROXY_RX_CONTEXT pMRxProxyAsyncEngineContext = MRxProxyGetMinirdrContext(RxContext);
    PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext = NULL;
    PMRXPROXY_ASYNCENG_MUST_SUCCEEED_CONTEXT MustSucceedAsyncEngineContext = NULL;
    //NTSTATUS Status;
    RxCaptureFobx;


    RxDbgTrace( +1, Dbg, ("MRxProxyCreateAsyncEngineContext\n") );

    //DbgBreakPoint();

    if (!FAIL_EXCHANGE_ALLOCATE()) {
        AsyncEngineContext = (PMRXPROXY_ASYNCENGINE_CONTEXT)RxAllocatePoolWithTag(
                                   NonPagedPool,
                                   sizeof(MRXPROXY_ASYNCENGINE_CONTEXT),
                                   MRXPROXY_ASYNCENGINECONTEXT_POOLTAG );
    }

    if ( AsyncEngineContext == NULL ) {
        //ASSERT(!"must-succeed");
        if (TRUE
               || !FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED)
               || !MRxProxyEntryPointIsMustSucceedable[EntryPoint]) {
            RxDbgTrace( 0, Dbg, ("  --> Couldn't get the asyncengcontext!\n") );
            return NULL;
        }
    //    MustSucceedAsyncEngineContext = RxAcquireMustSucceedStructure(&MRxProxyAsyncEngMustSucceedDescriptor[MRxProxyAsyncEngMustSucceed]);
    //    COUNT_MUST_SUCCEED_EXCHANGE_ALLOCATED();
    //    AsyncEngineContext = (PMRXPROXY_ASYNCENGINE_CONTEXT)SmbMmAllocateExchange(ORDINARY_EXCHANGE,
    //                                                                         &MustSucceedAsyncEngineContext->ExchangeListEntry);
    //    SetFlag( AsyncEngineContext->Flags, MRXPROXY_ASYNCENG_CTX_FLAG_MUST_SUCCEED_ALLOCATED_OE );
    }

    ZeroAndInitializeNodeType( AsyncEngineContext,
                               PROXY_NTC_ASYNCENGINE_CONTEXT,
                               sizeof(MRXPROXY_ASYNCENGINE_CONTEXT));
    InterlockedIncrement( &AsyncEngineContext->NodeReferenceCount );

    DbgDoit(AsyncEngineContext->SerialNumber = RxContext->SerialNumber);

    //place a reference on the rxcontext until we are finished
    InterlockedIncrement( &RxContext->ReferenceCount );

    AsyncEngineContext->RxContext = RxContext;
    AsyncEngineContext->EntryPoint = EntryPoint;

    DbgDoit(AsyncEngineContext->RxContextCapturedRequestPacket = RxContext->CurrentIrp;);

    pMRxProxyAsyncEngineContext->AsyncEngineContext     = AsyncEngineContext;


    RxDbgTrace( -1, Dbg, ("  --> exiting w!\n") );
    return(AsyncEngineContext);


//UNWIND:
//    //RxDbgTraceUnIndent(-1, Dbg);
//    RxDbgTrace( -1, Dbg, ("  --> exiting w/o!\n") );
//    MUST_SUCCEED_ASSERT(!"Finalizing on the way out");
//    MRxProxyAsyncEngFinalizeAsyncEngineContext( AsyncEngineContext );
//    return(NULL);

}



#if DBG
ULONG MRxProxyFinalizeAECtxTraceLevel = 1200;
#define FINALIZESS_LEVEL MRxProxyFinalizeAECtxTraceLevel
#define FINALIZE_TRACKING_SETUP() \
    struct {                    \
        ULONG marker1;          \
        ULONG finalstate;       \
        ULONG marker2;          \
    } Tracking = {'ereh',0,'ereh'};
#define FINALIZE_TRACKING(x) {\
    Tracking.finalstate |= x; \
    }

#define FINALIZE_TRACE(x) MRxProxyAsyncEngFinalizeAECtxTrace(x,Tracking.finalstate)
VOID
MRxProxyAsyncEngFinalizeAECtxTrace(PSZ text,ULONG finalstate)
{
    RxDbgTraceLV(0, Dbg, FINALIZESS_LEVEL,
                   ("MRxProxyFinalizeAsyncEngineContext  --> %s(%08lx)\n",text,finalstate));
}
#else
#define FINALIZE_TRACKING_SETUP()
#define FINALIZE_TRACKING(x)
#define FINALIZE_TRACE(x)
#endif

BOOLEAN
MRxProxyFinalizeAsyncEngineContext (
    IN OUT PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext
    )
/*++

Routine Description:

    This finalizes an OE.

Arguments:

    AsyncEngineContext - pointer to the OE to be dismantled.

Return Value:

    TRUE if finalization occurs otherwise FALSE.

Notes:



--*/
{
    LONG result;
    PIRP irp;
    ULONG AsyncEngineContextFlags = AsyncEngineContext->Flags;
    ULONG ThisIsMustSucceedAllocated =
              AsyncEngineContextFlags & (MRXPROXY_ASYNCENG_CTX_FLAG_MUST_SUCCEED_ALLOCATED);
    FINALIZE_TRACKING_SETUP()

    MRxProxyAsyncEngOEAssertConsistentLinkageFromOEwithFlags("MRxProxyAsyncEngFinalizeAsyncEngineContext:",OECHKLINKAGE_FLAG_NO_REQPCKT_CHECK);

    RxDbgTraceLV(+1, Dbg, 1000, ("MRxProxyFinalizeAsyncEngineContext\n"));

    result =  InterlockedDecrement(&AsyncEngineContext->NodeReferenceCount);
    if ( result != 0 ) {
        RxDbgTraceLV(-1, Dbg, 1000, ("MRxProxyFinalizeAsyncEngineContext -- returning w/o finalizing (%d)\n",result));
        return FALSE;
    }

    RxLog((">>>OE %lx %lx",
            AsyncEngineContext,
            AsyncEngineContext->Flags));

    FINALIZE_TRACKING( 0x1 );

    if ( (irp =AsyncEngineContext->CalldownIrp) != NULL ) {
        if (irp->MdlAddress) {
            IoFreeMdl(irp->MdlAddress);
        }
        IoFreeIrp( irp );
        FINALIZE_TRACKING( 0x20 );
    }

    if ( AsyncEngineContext->RxContext != NULL ) {
        PMRXPROXY_RX_CONTEXT pMRxProxyAsyncEngineContext = MRxProxyGetMinirdrContext(AsyncEngineContext->RxContext);
        ASSERT( pMRxProxyAsyncEngineContext->AsyncEngineContext == AsyncEngineContext );

        //get rid of the reference on the RxContext....if i'm the last guy this will finalize
        RxDereferenceAndDeleteRxContext( AsyncEngineContext->RxContext );
        FINALIZE_TRACKING( 0x600 );
    } else {
        FINALIZE_TRACKING( 0xf00 );
    }

    FINALIZE_TRACE("ready to discard exchange");
    RxFreePool(AsyncEngineContext);
    FINALIZE_TRACKING( 0x3000 );

    if (ThisIsMustSucceedAllocated) {
        //RxReleaseMustSucceedStructure(&MRxProxyAsyncEngMustSucceedDescriptor[MRxProxyAsyncEngMustSucceed]);
    }


    FINALIZE_TRACKING( 0x40000 );
    RxDbgTraceLV(-1, Dbg, 1000, ("MRxProxyFinalizeAsyncEngineContext  --> exit finalstate=%x\n",Tracking.finalstate));
    return(TRUE);

} // MRxProxyFinalizeAsyncEngineContext


NTSTATUS
MRxProxyAsyncEngineCalldownIrpCompletion (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP CalldownIrp,
    IN PVOID Context
    )
/*++

Routine Description:

    This routine is called when the calldownirp is completed.

Arguments:

    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP CalldownIrp,
    IN PVOID Context

Return Value:

    RXSTATUS - STATUS_MORE_PROCESSING_REQUIRED

--*/
{
    PRX_CONTEXT RxContext = Context;
    PMRXPROXY_RX_CONTEXT pMRxProxyContext = MRxProxyGetMinirdrContext(RxContext);
    PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext = pMRxProxyContext->AsyncEngineContext;
    BOOLEAN AsyncOperation = FlagOn(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_ASYNC_OPERATION);

    UPDATE_OE_HISTORY_WITH_STATUS('ff');
    MRxProxyAsyncEngOEAssertConsistentLinkage("MRxProxyCalldownCompletion: ");

    AsyncEngineContext->IoStatusBlock = CalldownIrp->IoStatus;

    if (AsyncOperation) {
        NTSTATUS PostStatus;
        RxDbgTraceLV(0, Dbg, 1000, ("Resume with post-to-async\n"));

        SetFlag(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_AWAITING_DISPATCH);
        SetFlag(AsyncEngineContext->Flags,MRXPROXY_ASYNCENG_CTX_FLAG_POSTED_RESUME);
        IF_DEBUG {
            //fill the workqueue structure with deadbeef....all the better to diagnose
            //a failed post
            ULONG i;
            for (i=0;i+sizeof(ULONG)-1<sizeof(AsyncEngineContext->WorkQueueItem);i+=sizeof(ULONG)) {
                //*((PULONG)(((PBYTE)&AsyncEngineContext->WorkQueueItem)+i)) = 0xdeadbeef;
                PBYTE BytePtr = ((PBYTE)&AsyncEngineContext->WorkQueueItem)+i;
                PULONG UlongPtr = (PULONG)BytePtr;
                *UlongPtr = 0xdeadbeef;
            }
        }
        PostStatus = RxPostToWorkerThread(&MRxProxyDeviceObject->RxDeviceObject,
                                          CriticalWorkQueue,
                                          &AsyncEngineContext->WorkQueueItem,
                                          MRxProxyResumeAsyncEngineContext,
                                          RxContext);
        ASSERT(PostStatus == STATUS_SUCCESS);
    } else {
        RxDbgTraceLV(0, Dbg, 1000, ("sync resume\n"));
        RxSignalSynchronousWaiter(RxContext);
    }

    return((STATUS_MORE_PROCESSING_REQUIRED));
}

#if DBG
#define DEBUG_ONLY_CODE(x) x
#else
#define DEBUG_ONLY_CODE(x)
#endif
NTSTATUS
__MRxProxyAsyncEngineOuterWrapper (
    IN PRX_CONTEXT RxContext,
    IN MRXPROXY_ASYNCENGINE_CONTEXT_ENTRYPOINTS EntryPoint,
    IN PMRXPROXY_ASYNCENG_CONTINUE_ROUTINE Continuation
#if DBG
   ,IN PSZ RoutineName,
    IN BOOLEAN LoudProcessing,
    IN BOOLEAN StopOnLoud
#endif
    )
/*++

Routine Description:

   This routine is common to guys who use the async context engine. it has the
   responsibility for getting a context, initing, starting, finalizing but the
   internal guts of the procesing is via the continuation routine that is passed in.

Arguments:

    RxContext - the RDBSS context

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    RxCaptureFcb; RxCaptureFobx;
    PMRXPROXY_ASYNCENGINE_CONTEXT AsyncEngineContext;

    PAGED_CODE();

    DEBUG_ONLY_CODE(
        RxDbgTrace(+1, Dbg, ("Wrapped%s %08lx\n", RoutineName, RxContext ))
        );

    ASSERT( NodeType(capFobx->pSrvOpen) == RDBSS_NTC_SRVOPEN );

    AsyncEngineContext = MRxProxyCreateAsyncEngineContext(
                             RxContext,
                             EntryPoint);

    if (AsyncEngineContext==NULL) {
        RxDbgTrace(-1, Dbg, ("Couldn't get the AsyncEngineContext!\n"));
        return((STATUS_INSUFFICIENT_RESOURCES));
    }

    AsyncEngineContext->Continuation = Continuation;
    Status = Continuation(MRXPROXY_ASYNCENGINE_ARGUMENTS);

    if (Status!=(STATUS_PENDING)) {
        BOOLEAN FinalizationComplete;
        DEBUG_ONLY_CODE(
            if (LoudProcessing) {
                if ((Status!=STATUS_SUCCESS) && (RxContext->LoudCompletionString)) {
                    DbgPrint("LoudFailure %08lx on %wZ\n",Status,RxContext->LoudCompletionString);
                    if (StopOnLoud) {
                        DbgBreakPoint();
                    }
                }
            }
            )
        FinalizationComplete = MRxProxyFinalizeAsyncEngineContext(AsyncEngineContext);
        ASSERT(FinalizationComplete);
    } else {
        ASSERT(BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
    }

    DEBUG_ONLY_CODE(
        RxDbgTrace(+1, Dbg, ("Wrapped%s %08lx exit with status=%08lx\n", RoutineName, RxContext, Status ))
        );
    return(Status);

} // MRxProxyQueryDir


