/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    calldown.c

Abstract:

    This module implements the calldown routines for coordinating between multiple
    calldowns/callouts.

    Calldowns refer to invocation of a mini rdr routine by the wrapper while callouts
    refer to invocations made by the wrapper to other components, e.g., TDI.

Revision History:

    Balan Sethu Raman     [SethuR]    15-Feb-1995

Notes:

    There are a number of instances in which the same function needs to be invoked
    on all the mini redirectors that have been registered. The RDBSS needs to be
    synchronized with the completion of the invocation of all these calls. It
    will be beneficial to invoke these calls in parallel when more than one
    mini redirectors are registered. This module provides the framework for such
    calldowns. This is provided by the routine RxCalldownMiniRedirectors.

    An instance of coordination between multiple callouts occurs when a connect
    request is initiated across multiple instances in parallel. The data structures
    corresponding to this are defined in rxcep.h for now since the usage is restricted
    to the connection engine. It would be a suitable candidate for migration if more uses
    are found later.

--*/

#include "precomp.h"
#pragma  hdrstop

#include "mrx.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxInitializeMRxCalldownContext)
#pragma alloc_text(PAGE, RxCompleteMRxCalldownRoutine)
#pragma alloc_text(PAGE, RxCalldownMRxWorkerRoutine)
#pragma alloc_text(PAGE, RxCalldownMiniRedirectors)
#endif

VOID
RxInitializeMRxCalldownContext(
   PMRX_CALLDOWN_CONTEXT    pContext,
   PRDBSS_DEVICE_OBJECT     pMRxDeviceObject,
   PMRX_CALLDOWN_ROUTINE pRoutine,
   PVOID                    pParameter)
/*++

Routine Description:

    This routine initializes a mini redirector calldown context.

Arguments:

    pContext - the MRx calldown context

Notes:

--*/
{
   PAGED_CODE();

   pContext->pMRxDeviceObject   = pMRxDeviceObject;
   pContext->pRoutine           = pRoutine;
   pContext->pParameter         = pParameter;
   pContext->pCompletionContext = NULL;
}

VOID
RxCompleteMRxCalldownRoutine(
   PMRX_CALLDOWN_COMPLETION_CONTEXT pCompletionContext)
/*++

Routine Description:

    This routine constitutes the tail of a mini redirector calldown completion.
    It encapsulates the synchronization mechanism for the resumption of RDBSS

Arguments:

    pCompletionContext - the MRx calldown completion context

Notes:

--*/
{
   PAGED_CODE();

   if (pCompletionContext != NULL) {
      LONG WaitCount;

      WaitCount = InterlockedDecrement(&pCompletionContext->WaitCount);
      if (WaitCount == 0) {
         KeSetEvent(
            &pCompletionContext->Event,
            IO_NO_INCREMENT,
            FALSE);
      }
   }
}

VOID
RxCalldownMRxWorkerRoutine(
   PMRX_CALLDOWN_CONTEXT pContext)
/*++

Routine Description:

    This is the calldown worker routine that invokes the appropriate mini
    redirector routine and follows it up with a call to the completion routine.

Arguments:

    pContext - the MRx calldown context

Notes:

--*/
{
    PRDBSS_DEVICE_OBJECT pMRxDeviceObject = pContext->pMRxDeviceObject;

    PAGED_CODE();

    if ( pContext->pRoutine != NULL) {
        pContext->CompletionStatus = (pContext->pRoutine)(pContext->pParameter);
    }

    RxCompleteMRxCalldownRoutine(pContext->pCompletionContext);
}

NTSTATUS
RxCalldownMiniRedirectors(
   LONG                  NumberOfMiniRdrs,
   PMRX_CALLDOWN_CONTEXT pCalldownContext,
   BOOLEAN               PostCalldowns)
/*++

Routine Description:

    This routine encapsulates the multiple mini redirector calldown.

Arguments:

    NumberOfMiniRdrs  - the number of mini redirectors

    pCalldownContext  - the MRx calldown context array for the mini redirectors

    PostCalldowns     - if TRUE the calldown employs multiple threads

Notes:

    The three parameters for this routine constitute an effort to provide
    maximum flexibility. The values should be carefully specified for
    utmost efficiency.

    Since the different calldowns can choose to employ a subset of the
    mini redirectors registered at any time the calldown mechanism accepts an
    array of calldown contexts and the appropriate number.

    In most cases when there is only one mini redirector registered it is
    necessary that the context switches be minimized. The routine provides
    for this by having an explicit specification (PostCalldowns ) parameter.

--*/
{
   LONG     Index;
   PMRX_CALLDOWN_CONTEXT pContext;

   MRX_CALLDOWN_COMPLETION_CONTEXT CompletionContext;

   PAGED_CODE();

   if (NumberOfMiniRdrs == 0) {
      return STATUS_SUCCESS;
   }

   pContext = pCalldownContext;

   CompletionContext.WaitCount = NumberOfMiniRdrs;
   KeInitializeEvent(
         &CompletionContext.Event,
         NotificationEvent,
         FALSE);

   for (Index = 0,pContext = pCalldownContext;
        Index < NumberOfMiniRdrs;
        Index++,pContext++) {
      pContext->pCompletionContext = &CompletionContext;
   }

   if (PostCalldowns) {
      for (Index = 0, pContext = pCalldownContext;
           Index < NumberOfMiniRdrs;
           Index++,pContext++) {
         RxPostToWorkerThread(
               RxFileSystemDeviceObject,
               CriticalWorkQueue,
               &pContext->WorkQueueItem,
               RxCalldownMRxWorkerRoutine,
               pContext);
      }
   } else {
      for (Index = 0, pContext = pCalldownContext;
           Index < NumberOfMiniRdrs;
           Index++,pContext++) {
         RxCalldownMRxWorkerRoutine(&pCalldownContext[Index]);
      }
   }

   KeWaitForSingleObject(
      &CompletionContext.Event,
      Executive,
      KernelMode,
      FALSE,
      NULL);

   return STATUS_SUCCESS;
}