|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
umrx.h
Abstract:
This header defines the UMRxEngine object and associated functions. The UMRxEngine provides a set of services for dispatch function writers so they can reflect requests to user-mode components.
Notes:
Code / Ideas have been adopted from Joe Linn's user-mode reflector
Author:
Rohan Phillips [Rohanp] 18-Jan-2001
--*/
#ifndef _UMRX_H_
#define _UMRX_H_
//
// defines and tags
//
#define UMRX_ENGINE_TAG (ULONG) 'xrmU'
#define UMRX_CONTEXT_TAG (ULONG) 'xtcU'
#define REASSIGN_MID 1
#define DONT_REASSIGN_MID 0
#define TICKS_PER_SECOND (10 * 1000 * 1000)
typedef USHORT NODE_TYPE_CODE; typedef CSHORT NODE_BYTE_SIZE;
typedef struct _MRX_NORMAL_NODE_HEADER { NODE_TYPE_CODE NodeTypeCode; NODE_BYTE_SIZE NodeByteSize; ULONG NodeReferenceCount; } MRX_NORMAL_NODE_HEADER;
enum { UMRX_ENGINE_STATE_STOPPED = 0, UMRX_ENGINE_STATE_STARTING, UMRX_ENGINE_STATE_STARTED, UMRX_ENGINE_STATE_STOPPING };
//
// Define the UMRxEngine object. There is one such object for
// every NET_ROOT. This object contains all the data str needed
// for routing kernel-mode requests to user-mode.
//
typedef struct _UMRX_ENGINE { // MID to UMRxContext mapping table
struct { PRX_MID_ATLAS MidAtlas; FAST_MUTEX MidManagementMutex; LIST_ENTRY WaitingForMidListhead; }; struct { KQUEUE Queue; LARGE_INTEGER TimeOut; LIST_ENTRY PoisonEntry; ULONG NumberOfWorkerThreads; ULONG NumberOfWorkItems; ERESOURCE Lock; ULONG State; ULONG ThreadAborted; } Q; ULONG NextSerialNumber; ULONG cUserModeReflectionsInProgress; LIST_ENTRY ActiveLinkHead; } UMRX_ENGINE, *PUMRX_ENGINE;
void UMRxAbortPendingRequests(IN PUMRX_ENGINE pUMRxEngine);
//
// Forwards
//
struct _UMRX_CONTEXT; typedef struct _UMRX_CONTEXT *PUMRX_CONTEXT;
//
// Signatures for function pointers
//
//
// Continue routine is called by InitiateRequest -
// This turns around and submits the request to the
// UMR engine with callbacks for FORMAT and COMPLETION.
//
typedef NTSTATUS (*PUMRX_CONTINUE_ROUTINE) ( PUMRX_CONTEXT pUMRxContext, PRX_CONTEXT pRxContext );
//
// Format routine - called before user-mode worker thread completes.
// Each dispatch routine will interpret the WORK_ITEM union based on opcode.
// eg: for Create, WorkItem is a CREATE_REQUEST.
//
typedef NTSTATUS (*PUMRX_USERMODE_FORMAT_ROUTINE) ( PUMRX_CONTEXT pUMRxContext, PRX_CONTEXT pRxContext, PUMRX_USERMODE_WORKITEM WorkItem, ULONG WorkItemLength, PULONG ReturnedLength );
//
// Completion routine - called when user-mode response is received.
// Each dispatch routine will interpret the WORK_ITEM union based on opcode.
// eg: for Create, WorkItem is a CREATE_RESPONSE.
//
typedef VOID (*PUMRX_USERMODE_COMPLETION_ROUTINE) ( PUMRX_CONTEXT pUMRxContext, PRX_CONTEXT pRxContext, PUMRX_USERMODE_WORKITEM WorkItem, ULONG WorkItemLength );
//
// Type of operation reflected to user-mode
//
typedef enum _UMRX_CONTEXT_TYPE { UMRX_CTXTYPE_IFSDFSLINK = 0, UMRX_CTXTYPE_GETDFSREPLICAS, UMRX_CTXTYPE_MAXIMUM } UMRX_CONTEXT_TYPE;
//
// Define the UMRxContext. This context is sent as part of
// the REQUEST to user-mode. The user-mode handler will
// send the context back in a RESPONSE. The context will be
// used to do the rendezvous with blocked requests.
//
#define UMRX_NTC_CONTEXT ((USHORT)0xedd0)
typedef struct _UMRX_CONTEXT{ MRX_NORMAL_NODE_HEADER; PUMRX_ENGINE pUMRxEngine; // owning engine object
PRX_CONTEXT RxContext; PVOID SavedMinirdrContextPtr; union { IO_STATUS_BLOCK; IO_STATUS_BLOCK IoStatusBlock; }; UMRX_CONTEXT_TYPE CTXType; PUMRX_CONTINUE_ROUTINE Continuation; struct { LIST_ENTRY WorkQueueLinks; PUMRX_USERMODE_FORMAT_ROUTINE FormatRoutine; PUMRX_USERMODE_COMPLETION_ROUTINE CompletionRoutine; KEVENT WaitForMidEvent; ULONG CallUpSerialNumber; USHORT CallUpMid; } UserMode; LIST_ENTRY ActiveLink; } UMRX_CONTEXT, *PUMRX_CONTEXT;
#define UMRxReferenceContext(pUMRxContext) {\
ULONG result = InterlockedIncrement(&(pUMRxContext)->NodeReferenceCount); \ RxDbgTrace(0, (DEBUG_TRACE_UMRX), \ ("ReferenceContext result=%08lx\n", result )); \ } typedef struct _UMRX_WORKITEM_HEADER_PRIVATE { PUMRX_CONTEXT pUMRxContext; ULONG SerialNumber; USHORT Mid; } UMRX_WORKITEM_HEADER_PRIVATE, *PUMRX_WORKITEM_HEADER_PRIVATE;
//
// Create a UMRX_ENGINE object
//
PUMRX_ENGINE CreateUMRxEngine();
//
// Close a UMRX_ENGINE object -
// Owner of object ensures that all usage of this object
// is within the Create/Finalize span.
//
VOID FinalizeUMRxEngine( IN PUMRX_ENGINE pUMRxEngine );
//
// Complete queued requests and optional cleanup when the store has exited
//
NTSTATUS UMRxEngineCompleteQueuedRequests( IN PUMRX_ENGINE pUMRxEngine, IN NTSTATUS CompletionStatus, IN BOOLEAN fCleanup ); //
// Used to allow an engine to be used again after it's been shutdown.
//
//
NTSTATUS UMRxEngineRestart( IN PUMRX_ENGINE pUMRxEngine );
//
// Initiate a request to the UMR engine -
// This creates a UMRxContext that is used for response rendezvous.
// All IFS dispatch routines will start a user-mode reflection by
// calling this routine. Steps in routine:
//
// 1. Allocate a UMRxContext and set RxContext
// (NOTE: need to have ASSERTs that validate this linkage)
// 2. Set Continue routine ptr and call Continue routine
// 3. If Continue routine is done ie not PENDING, Finalize UMRxContext
//
NTSTATUS UMRxEngineInitiateRequest ( IN PUMRX_ENGINE pUMRxEngine, IN PRX_CONTEXT RxContext, IN UMRX_CONTEXT_TYPE RequestType, IN PUMRX_CONTINUE_ROUTINE Continuation );
//
// Create/Finalize UMRX_CONTEXTs
// These are pool allocs/frees
//
PUMRX_CONTEXT UMRxCreateAndReferenceContext ( IN PRX_CONTEXT RxContext, IN UMRX_CONTEXT_TYPE RequestType );
BOOLEAN UMRxDereferenceAndFinalizeContext ( IN OUT PUMRX_CONTEXT pUMRxContext );
//
// Submit a request to the UMR engine -
// This adds the request to the engine KQUEUE for processing by
// a user-mode thread. Steps:
//
// 1. set the FORMAT and COMPLETION callbacks in the UMRxContext
// 2. initialize the RxContext sync event
// 3. insert the UMRxContext into the engine KQUEUE
// 4. block on RxContext sync event (for SYNC operations)
// 5. after unblock (ie umode response is back), call Resume routine
//
NTSTATUS UMRxEngineSubmitRequest( IN PUMRX_CONTEXT pUMRxContext, IN PRX_CONTEXT pRxContext, IN UMRX_CONTEXT_TYPE RequestType, IN PUMRX_USERMODE_FORMAT_ROUTINE FormatRoutine, IN PUMRX_USERMODE_COMPLETION_ROUTINE CompletionRoutine );
//
// Resume is called after I/O thread is unblocked by umode RESPONSE.
// This routine calls any Finish callbacks and then Finalizes the
// UMRxContext.
//
NTSTATUS UMRxResumeEngineContext( IN OUT PRX_CONTEXT RxContext );
//
// The following functions run in the context of user-mode
// worker threads that issue WORK IOCTLs. The IOCTL calls the
// following functions in order:
// 1. UMRxCompleteUserModeRequest() - process a response if needed
// 2. UMRxEngineProcessRequest() - process a request if one is
// available on the UMRxEngine KQUEUE. Since these IOCTLs are
// made on a NET_ROOT, the corresponding UMRxEngine is readily
// available in the NET_ROOT extension.
//
//
// Every IOCTL pended is potentially a Response. If so, process it.
// The first IOCTL pended is usually a NULL Response or 'listen'.
// Steps:
// 1. Get MID from response buffer. Map MID to UMRxContext.
// 2. Call UMRxContext COMPLETION routine.
// 3. Unblock the I/O thread waiting in UMRxEngineSubmitRequest()
//
NTSTATUS UMRxCompleteUserModeRequest( IN PUMRX_ENGINE pUMRxEngine, IN OUT PUMRX_USERMODE_WORKITEM WorkItem, IN ULONG WorkItemLength, IN BOOLEAN fReleaseUmrRef, OUT PIO_STATUS_BLOCK IoStatus, OUT BOOLEAN * pfReturnImmediately );
//
// NOTE: if no requests are available, the user-mode thread will
// block till a request is available (It is trivial to make this
// a more async model).
//
// If a request is available, get the corresponding UMRxContext and
// call ProcessRequest.
// Steps:
// 1. Call KeRemoveQueue() to remove a request from the UMRxEngine KQUEUE.
// 2. Get a MID for this UMRxContext and fill it in the WORK_ITEM header.
// 3. Call the UMRxContext FORMAT routine - this fills in the Request params.
// 4. return STATUS_SUCCESS - this causes the IOCTL to complete which
// triggers the user-mode completion and processing of the REQUEST.
//
NTSTATUS UMRxEngineProcessRequest( IN PUMRX_ENGINE pUMRxEngine, OUT PUMRX_USERMODE_WORKITEM WorkItem, IN ULONG WorkItemLength, OUT PULONG FormattedWorkItemLength );
//
// This is called in response to a WORK_CLEANUP IOCTL.
// This routine will insert a dummy item in the engine KQUEUE.
// Each such dummy item inserted will release one thread.
//
NTSTATUS UMRxEngineReleaseThreads( IN PUMRX_ENGINE pUMRxEngine );
//
// Cancel I/O infrastructure
//
typedef NTSTATUS (NTAPI *PUMRX_CANCEL_ROUTINE) ( PRX_CONTEXT pRxContext);
// The RX_CONTEXT instance has four fields ( ULONG's ) provided by the wrapper
// which can be used by the mini rdr to store its context. This is used by
// the reflector to identify the parameters for request cancellation
typedef struct _UMRX_RX_CONTEXT { PUMRX_CANCEL_ROUTINE pCancelRoutine; PVOID pCancelContext; union { struct { PUMRX_CONTEXT pUMRxContext; ULONG RxSyncTimeout; }; IO_STATUS_BLOCK SyncCallDownIoStatus; }; } UMRX_RX_CONTEXT, *PUMRX_RX_CONTEXT;
#define UMRxGetMinirdrContext(pRxContext) \
((PUMRX_RX_CONTEXT)(&(pRxContext)->UMRScratchSpace[0]))
#endif // _UMRX_H_
|