/*++ Copyright (c) 1989 Microsoft Corporation Module Name: StackOvf.c Abstract: The file lock package provides a worker thread to handle stack overflow conditions in the file systems. When the file system detects that it is near the end of its stack during a paging I/O read request it will post the request to this extra thread. Author: Gary Kimura [GaryKi] 24-Nov-1992 Revision History: --*/ #include "FsRtlP.h" // // Queue object that is used to hold work queue entries and synchronize // worker thread activity. // KQUEUE FsRtlWorkerQueues[2]; // // Define a tag for general pool allocations from this module // #undef MODULE_POOL_TAG #define MODULE_POOL_TAG ('srSF') // // Local Support Routine // VOID FsRtlStackOverflowRead ( IN PVOID Context ); VOID FsRtlpPostStackOverflow ( IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine, IN BOOLEAN PagingFile ); // // Procedure prototype for the worker thread. // VOID FsRtlWorkerThread( IN PVOID StartContext ); // // The following type is used to store an enqueue work item // typedef struct _STACK_OVERFLOW_ITEM { WORK_QUEUE_ITEM Item; // // This is the call back routine // PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine; // // Here are the parameters for the call back routine // PVOID Context; PKEVENT Event; } STACK_OVERFLOW_ITEM; typedef STACK_OVERFLOW_ITEM *PSTACK_OVERFLOW_ITEM; KEVENT StackOverflowFallbackSerialEvent; STACK_OVERFLOW_ITEM StackOverflowFallback; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, FsRtlInitializeWorkerThread) #endif NTSTATUS FsRtlInitializeWorkerThread ( VOID ) { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Thread; ULONG i; // // Create worker threads to handle normal and paging overflow reads. // InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); for (i=0; i < 2; i++) { // // Initialize the FsRtl stack overflow work Queue objects. // KeInitializeQueue(&FsRtlWorkerQueues[i], 0); if (!NT_SUCCESS(PsCreateSystemThread(&Thread, THREAD_ALL_ACCESS, &ObjectAttributes, 0L, NULL, FsRtlWorkerThread, ULongToPtr( i )))) { return FALSE; } ZwClose( Thread ); } // // Initialize the serial workitem so we can guarantee to post items // for paging files to the worker threads. // KeInitializeEvent( &StackOverflowFallbackSerialEvent, SynchronizationEvent, TRUE ); return TRUE; } VOID FsRtlPostStackOverflow ( IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine ) /*++ Routine Description: This routines posts a stack overflow item to the stack overflow thread and returns. Arguments: Context - Supplies the context to pass to the stack overflow call back routine. If the low order bit is set, then this overflow was a read to a paging file. Event - Supplies a pointer to an event to pass to the stack overflow call back routine. StackOverflowRoutine - Supplies the call back to use when processing the request in the overflow thread. Return Value: None. --*/ { FsRtlpPostStackOverflow( Context, Event, StackOverflowRoutine, FALSE ); return; } VOID FsRtlPostPagingFileStackOverflow ( IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine ) /*++ Routine Description: This routines posts a stack overflow item to the stack overflow thread and returns. Arguments: Context - Supplies the context to pass to the stack overflow call back routine. If the low order bit is set, then this overflow was a read to a paging file. Event - Supplies a pointer to an event to pass to the stack overflow call back routine. StackOverflowRoutine - Supplies the call back to use when processing the request in the overflow thread. Return Value: None. --*/ { FsRtlpPostStackOverflow( Context, Event, StackOverflowRoutine, TRUE ); return; } VOID FsRtlpPostStackOverflow ( IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine, IN BOOLEAN PagingFile ) /*++ Routine Description: This routines posts a stack overflow item to the stack overflow thread and returns. Arguments: Context - Supplies the context to pass to the stack overflow call back routine. If the low order bit is set, then this overflow was a read to a paging file. Event - Supplies a pointer to an event to pass to the stack overflow call back routine. StackOverflowRoutine - Supplies the call back to use when processing the request in the overflow thread. PagingFile - Indicates if the read is destined to a paging file. Return Value: None. --*/ { PSTACK_OVERFLOW_ITEM StackOverflowItem; // // Allocate a stack overflow work item it will later be deallocated by // the stack overflow thread. Conserve stack by raising here. // StackOverflowItem = ExAllocatePoolWithTag( NonPagedPool, sizeof(STACK_OVERFLOW_ITEM), MODULE_POOL_TAG ); // // If this fails, go to the fallback item for the paging file overflows. // We can't have a single fallback item for non-pagingfile IO since this // could lead to deadlocks if it waits on a thread that itself needs // the fallback item. // if (StackOverflowItem == NULL) { if (!PagingFile) { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); } KeWaitForSingleObject( &StackOverflowFallbackSerialEvent, Executive, KernelMode, FALSE, NULL ); StackOverflowItem = &StackOverflowFallback; } // // Fill in the fields in the new item // StackOverflowItem->Context = Context; StackOverflowItem->Event = Event; StackOverflowItem->StackOverflowRoutine = StackOverflowRoutine; ExInitializeWorkItem( &StackOverflowItem->Item, &FsRtlStackOverflowRead, StackOverflowItem ); // // Safely add it to the overflow queue // KeInsertQueue( &FsRtlWorkerQueues[PagingFile], &StackOverflowItem->Item.List ); // // And return to our caller // return; } // // Local Support Routine // VOID FsRtlStackOverflowRead ( IN PVOID Context ) /*++ Routine Description: This routine processes all of the stack overflow request posted by the various file systems Arguments: Return Value: None. --*/ { PSTACK_OVERFLOW_ITEM StackOverflowItem; // // Since stack overflow reads are always recursive, set the // TopLevelIrp field appropriately so that recurive reads // from this point will not think they are top level. // PsGetCurrentThread()->TopLevelIrp = FSRTL_FSP_TOP_LEVEL_IRP; // // Get a pointer to the stack overflow item and then call // the callback routine to do the work // StackOverflowItem = (PSTACK_OVERFLOW_ITEM)Context; (StackOverflowItem->StackOverflowRoutine)(StackOverflowItem->Context, StackOverflowItem->Event); // // Deallocate the work item, or simply return the serial item. // if (StackOverflowItem == &StackOverflowFallback) { KeSetEvent( &StackOverflowFallbackSerialEvent, 0, FALSE ); } else { ExFreePool( StackOverflowItem ); } PsGetCurrentThread()->TopLevelIrp = (ULONG_PTR)NULL; } VOID FsRtlWorkerThread( IN PVOID StartContext ) { PLIST_ENTRY Entry; PWORK_QUEUE_ITEM WorkItem; ULONG PagingFile = (ULONG)(ULONG_PTR)StartContext; // // Set our priority to low realtime, or +1 for PagingFile. // (VOID)KeSetPriorityThread( &PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + PagingFile ); // // Loop forever waiting for a work queue item, calling the processing // routine, and then waiting for another work queue item. // do { // // Wait until something is put in the queue. // // By specifying a wait mode of KernelMode, the thread's kernel stack is // NOT swappable // Entry = KeRemoveQueue(&FsRtlWorkerQueues[PagingFile], KernelMode, NULL); WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List); // // Execute the specified routine. // (WorkItem->WorkerRoutine)(WorkItem->Parameter); if (KeGetCurrentIrql() != 0) { KeBugCheckEx( IRQL_NOT_LESS_OR_EQUAL, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)KeGetCurrentIrql(), (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)WorkItem ); } } while(TRUE); }