/* * @implemented */ VOID NTAPI FsRtlpPostStackOverflow(IN PVOID Context, IN PKEVENT Event, IN PFSRTL_STACK_OVERFLOW_ROUTINE StackOverflowRoutine, IN BOOLEAN IsPaging) { PSTACK_OVERFLOW_WORK_ITEM WorkItem; /* Try to allocate a work item */ WorkItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(STACK_OVERFLOW_WORK_ITEM), 'FSrs'); if (WorkItem == NULL) { /* If we failed, and we are not a paging file, just raise an error */ if (!IsPaging) { RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); } /* Otherwise, wait for fallback workitem to be available and use it */ KeWaitForSingleObject(&StackOverflowFallbackSerialEvent, Executive, KernelMode, FALSE, NULL); WorkItem = &StackOverflowFallback; } /* Initialize work item */ WorkItem->Context = Context; WorkItem->Event = Event; WorkItem->Routine = StackOverflowRoutine; ExInitializeWorkItem(&WorkItem->WorkItem, FsRtlStackOverflowRead, WorkItem); /* And queue it in the appropriate queue (paging or not?) */ KeInsertQueue(&FsRtlWorkerQueues[IsPaging], &WorkItem->WorkItem.List); }
VOID ExpShutdownWorkerThreads ( VOID ) { PULONG QueueEnable; SHUTDOWN_WORK_ITEM ShutdownItem; if ((PoCleanShutdownEnabled () & PO_CLEAN_SHUTDOWN_WORKERS) == 0) { return; } ASSERT (KeGetCurrentThread()->Queue == &ExWorkerQueue[PO_SHUTDOWN_QUEUE].WorkerQueue); // // Mark the queues as terminating. // QueueEnable = (PULONG)&ExWorkerQueue[DelayedWorkQueue].Info.QueueWorkerInfo; RtlInterlockedSetBitsDiscardReturn (QueueEnable, EX_WORKER_QUEUE_DISABLED); QueueEnable = (PULONG)&ExWorkerQueue[CriticalWorkQueue].Info.QueueWorkerInfo; RtlInterlockedSetBitsDiscardReturn (QueueEnable, EX_WORKER_QUEUE_DISABLED); // // Queue the shutdown work item to the delayed work queue. After // all currently queued work items are complete, this will fire, // repeatedly taking out every worker thread in every queue until // they're all done. // ExInitializeWorkItem (&ShutdownItem.WorkItem, &ExpShutdownWorker, &ShutdownItem); ShutdownItem.QueueType = DelayedWorkQueue; ShutdownItem.PrevThread = NULL; KeInsertQueue (&ExWorkerQueue[DelayedWorkQueue].WorkerQueue, &ShutdownItem.WorkItem.List); // // Wait for all of the workers and the balancer to exit. // if (ExpWorkerThreadBalanceManagerPtr != NULL) { KeWaitForSingleObject(ExpWorkerThreadBalanceManagerPtr, Executive, KernelMode, FALSE, NULL); ASSERT(!ShutdownItem.PrevThread); ObDereferenceObject(ExpWorkerThreadBalanceManagerPtr); } }
/*++ * @name ExQueueWorkItem * @implemented NT4 * * The ExQueueWorkItem routine acquires rundown protection for * the specified descriptor. * * @param WorkItem * Pointer to an initialized Work Queue Item structure. This structure * must be located in nonpaged pool memory. * * @param QueueType * Type of the queue to use for this item. Can be one of the following: * - DelayedWorkQueue * - CriticalWorkQueue * - HyperCriticalWorkQueue * * @return None. * * @remarks This routine is obsolete. Use IoQueueWorkItem instead. * * Callers of this routine must be running at IRQL <= DISPATCH_LEVEL. * *--*/ VOID NTAPI ExQueueWorkItem(IN PWORK_QUEUE_ITEM WorkItem, IN WORK_QUEUE_TYPE QueueType) { PEX_WORK_QUEUE WorkQueue = &ExWorkerQueue[QueueType]; ASSERT(QueueType < MaximumWorkQueue); ASSERT(WorkItem->List.Flink == NULL); /* Don't try to trick us */ if ((ULONG_PTR)WorkItem->WorkerRoutine < MmUserProbeAddress) { /* Bugcheck the system */ KeBugCheckEx(WORKER_INVALID, 1, (ULONG_PTR)WorkItem, (ULONG_PTR)WorkItem->WorkerRoutine, 0); } /* Insert the Queue */ KeInsertQueue(&WorkQueue->WorkerQueue, &WorkItem->List); ASSERT(!WorkQueue->Info.QueueDisabled); /* * Check if we need a new thread. Our decision is as follows: * - This queue type must support Dynamic Threads (duh!) * - It actually has to have unprocessed items * - We have CPUs which could be handling another thread * - We haven't abused our usage of dynamic threads. */ if ((WorkQueue->Info.MakeThreadsAsNecessary) && (!IsListEmpty(&WorkQueue->WorkerQueue.EntryListHead)) && (WorkQueue->WorkerQueue.CurrentCount < WorkQueue->WorkerQueue.MaximumCount) && (WorkQueue->DynamicThreadCount < 16)) { /* Let the balance manager know about it */ DPRINT1("Requesting a new thread. CurrentCount: %d. MaxCount: %d\n", WorkQueue->WorkerQueue.CurrentCount, WorkQueue->WorkerQueue.MaximumCount); KeSetEvent(&ExpThreadSetManagerEvent, 0, FALSE); } }
LOGICAL ExpCheckQueueShutdown ( IN WORK_QUEUE_TYPE QueueType, IN PSHUTDOWN_WORK_ITEM ShutdownItem ) { ULONG CountForQueueEmpty; if (PO_SHUTDOWN_QUEUE == QueueType) { CountForQueueEmpty = 1; } else { CountForQueueEmpty = 0; } // // Note that using interlocked sequences to increment the worker count // and decrement it to CountForQueueEmpty ensures that once it // *is* equal to CountForQueueEmpty and the disabled flag is set, // we won't be incrementing it any more, so we're safe making this // check without locks. // // See ExpWorkerThread, ExpShutdownWorker, and ExpShutdownWorkerThreads. // if (ExWorkerQueue[QueueType].Info.WorkerCount > CountForQueueEmpty) { // // There're still worker threads; send one of them the axe. // ShutdownItem->QueueType = QueueType; ShutdownItem->PrevThread = PsGetCurrentThread(); ObReferenceObject (ShutdownItem->PrevThread); KeInsertQueue (&ExWorkerQueue[QueueType].WorkerQueue, &ShutdownItem->WorkItem.List); return TRUE; } return FALSE; // we did not queue a shutdown }
/* * @implemented */ NTSTATUS NTAPI IoSetIoCompletion(IN PVOID IoCompletion, IN PVOID KeyContext, IN PVOID ApcContext, IN NTSTATUS IoStatus, IN ULONG_PTR IoStatusInformation, IN BOOLEAN Quota) { PKQUEUE Queue = (PKQUEUE)IoCompletion; PNPAGED_LOOKASIDE_LIST List; PKPRCB Prcb = KeGetCurrentPrcb(); PIOP_MINI_COMPLETION_PACKET Packet; /* Get the P List */ List = (PNPAGED_LOOKASIDE_LIST)Prcb-> PPLookasideList[LookasideCompletionList].P; /* Try to allocate the Packet */ List->L.TotalAllocates++; Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead); /* Check if that failed, use the L list if it did */ if (!Packet) { /* Let the balancer know */ List->L.AllocateMisses++; /* Get L List */ List = (PNPAGED_LOOKASIDE_LIST)Prcb-> PPLookasideList[LookasideCompletionList].L; /* Try to allocate the Packet */ List->L.TotalAllocates++; Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead); } /* Still failed, use pool */ if (!Packet) { /* Let the balancer know */ List->L.AllocateMisses++; /* Allocate from Nonpaged Pool */ Packet = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Packet), IOC_TAG); } /* Make sure we have one by now... */ if (Packet) { /* Set up the Packet */ Packet->PacketType = IopCompletionPacketMini; Packet->KeyContext = KeyContext; Packet->ApcContext = ApcContext; Packet->IoStatus = IoStatus; Packet->IoStatusInformation = IoStatusInformation; /* Insert the Queue */ KeInsertQueue(Queue, &Packet->ListEntry); } else { /* Out of memory, fail */ return STATUS_INSUFFICIENT_RESOURCES; } /* Return Success */ return STATUS_SUCCESS; }
VOID ExQueueWorkItem ( __inout PWORK_QUEUE_ITEM WorkItem, __in WORK_QUEUE_TYPE QueueType ) /*++ Routine Description: This function inserts a work item into a work queue that is processed by a worker thread of the corresponding type. Arguments: WorkItem - Supplies a pointer to the work item to add the the queue. This structure must be located in NonPagedPool. The work item structure contains a doubly linked list entry, the address of a routine to call and a parameter to pass to that routine. QueueType - Specifies the type of work queue that the work item should be placed in. Return Value: None. --*/ { PEX_WORK_QUEUE Queue; ASSERT (QueueType < MaximumWorkQueue); ASSERT (WorkItem->List.Flink == NULL); // // Perform a rudimentary validation on the worker routine. // if ((ULONG64)WorkItem->WorkerRoutine <= MmUserProbeAddress) { KeBugCheckEx (WORKER_INVALID, 0x1, (ULONG_PTR)WorkItem, (ULONG_PTR)WorkItem->WorkerRoutine, 0); } Queue = &ExWorkerQueue[QueueType]; // // Insert the work item in the appropriate queue object. // KeInsertQueue (&Queue->WorkerQueue, &WorkItem->List); // // We check the queue's shutdown state after we insert the work // item to avoid the race condition when the queue's marked // between checking the queue and inserting the item. It's // possible for the queue to be marked for shutdown between the // insert and this assert (so the insert would've barely sneaked // in), but it's not worth guarding against this -- barely // sneaking in is not a good design strategy, and at this point in // the shutdown sequence, the caller simply should not be trying // to insert new queue items. // ASSERT (!Queue->Info.QueueDisabled); // // Determine whether another thread should be created, and signal the // thread set balance manager if so. // if (ExpNewThreadNecessary (Queue) != FALSE) { KeSetEvent (&ExpThreadSetManagerEvent, 0, FALSE); } 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 // StackOverflowItem = FsRtlAllocatePool( PagingFile ? NonPagedPoolMustSucceed : NonPagedPool, sizeof(STACK_OVERFLOW_ITEM) ); // // 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; }
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; }