434 lines
9.5 KiB
C
434 lines
9.5 KiB
C
/*++
|
||
|
||
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;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// 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);
|
||
|
||
Status = PsCreateSystemThread( &Thread,
|
||
THREAD_ALL_ACCESS,
|
||
&ObjectAttributes,
|
||
0L,
|
||
NULL,
|
||
FsRtlWorkerThread,
|
||
ULongToPtr( i ));
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
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 Status;
|
||
}
|
||
|
||
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);
|
||
}
|