/*++ 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_