Example #1
0
VOID
FsRtlWorkerThread(
    IN PVOID StartContext
)

{
    PLIST_ENTRY Entry;
    PWORK_QUEUE_ITEM WorkItem;
    ULONG PagingFile = (ULONG)StartContext;

    //
    //  Set our priority to low realtime, or +1 for PagingFile.
    //

    (PVOID)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)WorkItem->WorkerRoutine,
                (ULONG)KeGetCurrentIrql(),
                (ULONG)WorkItem->WorkerRoutine,
                (ULONG)WorkItem
            );
        }

    } while(TRUE);
}
Example #2
0
File: work.c Project: killvxk/NT_OS
/*++
 * @name ExpWorkerThreadEntryPoint
 *
 *     The ExpWorkerThreadEntryPoint routine is the entrypoint for any new
 *     worker thread created by teh system.
 *
 * @param Context
 *        Contains the work queue type masked with a flag specifing whether the
 *        thread is dynamic or not.
 *
 * @return None.
 *
 * @remarks A dynamic thread can timeout after 10 minutes of waiting on a queue
 *          while a static thread will never timeout.
 *
 *          Worker threads must return at IRQL == PASSIVE_LEVEL, must not have
 *          active impersonation info, and must not have disabled APCs.
 *
 *          NB: We will re-enable APCs for broken threads but all other cases
 *              will generate a bugcheck.
 *
 *--*/
VOID
NTAPI
ExpWorkerThreadEntryPoint(IN PVOID Context)
{
    PWORK_QUEUE_ITEM WorkItem;
    PLIST_ENTRY QueueEntry;
    WORK_QUEUE_TYPE WorkQueueType;
    PEX_WORK_QUEUE WorkQueue;
    LARGE_INTEGER Timeout;
    PLARGE_INTEGER TimeoutPointer = NULL;
    PETHREAD Thread = PsGetCurrentThread();
    KPROCESSOR_MODE WaitMode;
    EX_QUEUE_WORKER_INFO OldValue, NewValue;

    /* Check if this is a dyamic thread */
    if ((ULONG_PTR)Context & EX_DYNAMIC_WORK_THREAD)
    {
        /* It is, which means we will eventually time out after 10 minutes */
        Timeout.QuadPart = Int32x32To64(10, -10000000 * 60);
        TimeoutPointer = &Timeout;
    }

    /* Get Queue Type and Worker Queue */
    WorkQueueType = (WORK_QUEUE_TYPE)((ULONG_PTR)Context &
                                      ~EX_DYNAMIC_WORK_THREAD);
    WorkQueue = &ExWorkerQueue[WorkQueueType];

    /* Select the wait mode */
    WaitMode = (UCHAR)WorkQueue->Info.WaitMode;

    /* Nobody should have initialized this yet, do it now */
    ASSERT(Thread->ExWorkerCanWaitUser == 0);
    if (WaitMode == UserMode) Thread->ExWorkerCanWaitUser = TRUE;

    /* If we shouldn't swap, disable that feature */
    if (!ExpWorkersCanSwap) KeSetKernelStackSwapEnable(FALSE);

    /* Set the worker flags */
    do
    {
        /* Check if the queue is being disabled */
        if (WorkQueue->Info.QueueDisabled)
        {
            /* Re-enable stack swapping and kill us */
            KeSetKernelStackSwapEnable(TRUE);
            PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);
        }

        /* Increase the worker count */
        OldValue = WorkQueue->Info;
        NewValue = OldValue;
        NewValue.WorkerCount++;
    }
    while (InterlockedCompareExchange((PLONG)&WorkQueue->Info,
                                      *(PLONG)&NewValue,
                                      *(PLONG)&OldValue) != *(PLONG)&OldValue);

    /* Success, you are now officially a worker thread! */
    Thread->ActiveExWorker = TRUE;

    /* Loop forever */
ProcessLoop:
    for (;;)
    {
        /* Wait for Something to Happen on the Queue */
        QueueEntry = KeRemoveQueue(&WorkQueue->WorkerQueue,
                                   WaitMode,
                                   TimeoutPointer);

        /* Check if we timed out and quit this loop in that case */
        if ((NTSTATUS)(ULONG_PTR)QueueEntry == STATUS_TIMEOUT) break;

        /* Increment Processed Work Items */
        InterlockedIncrement((PLONG)&WorkQueue->WorkItemsProcessed);

        /* Get the Work Item */
        WorkItem = CONTAINING_RECORD(QueueEntry, WORK_QUEUE_ITEM, List);

        /* Make sure nobody is trying to play smart with us */
        ASSERT((ULONG_PTR)WorkItem->WorkerRoutine > MmUserProbeAddress);

        /* Call the Worker Routine */
        WorkItem->WorkerRoutine(WorkItem->Parameter);

        /* Make sure APCs are not disabled */
        if (Thread->Tcb.SpecialApcDisable)
        {
            /* We're nice and do it behind your back */
            DPRINT1("Warning: Broken Worker Thread: %p %lx %p came back "
                    "with APCs disabled!\n",
                    WorkItem->WorkerRoutine,
                    WorkItem->Parameter,
                    WorkItem);
            Thread->Tcb.SpecialApcDisable = 0;
        }

        /* Make sure it returned at right IRQL */
        if (KeGetCurrentIrql() != PASSIVE_LEVEL)
        {
            /* It didn't, bugcheck! */
            KeBugCheckEx(WORKER_THREAD_RETURNED_AT_BAD_IRQL,
                         (ULONG_PTR)WorkItem->WorkerRoutine,
                         KeGetCurrentIrql(),
                         (ULONG_PTR)WorkItem->Parameter,
                         (ULONG_PTR)WorkItem);
        }

        /* Make sure it returned with Impersionation Disabled */
        if (Thread->ActiveImpersonationInfo)
        {
            /* It didn't, bugcheck! */
            KeBugCheckEx(IMPERSONATING_WORKER_THREAD,
                         (ULONG_PTR)WorkItem->WorkerRoutine,
                         (ULONG_PTR)WorkItem->Parameter,
                         (ULONG_PTR)WorkItem,
                         0);
        }
    }

    /* This is a dynamic thread. Terminate it unless IRPs are pending */
    if (!IsListEmpty(&Thread->IrpList)) goto ProcessLoop;

    /* Don't terminate it if the queue is disabled either */
    if (WorkQueue->Info.QueueDisabled) goto ProcessLoop;

    /* Set the worker flags */
    do
    {
        /* Decrease the worker count */
        OldValue = WorkQueue->Info;
        NewValue = OldValue;
        NewValue.WorkerCount--;
    }
    while (InterlockedCompareExchange((PLONG)&WorkQueue->Info,
                                      *(PLONG)&NewValue,
                                      *(PLONG)&OldValue) != *(PLONG)&OldValue);

    /* Decrement dynamic thread count */
    InterlockedDecrement(&WorkQueue->DynamicThreadCount);

    /* We're not a worker thread anymore */
    Thread->ActiveExWorker = FALSE;

    /* Re-enable the stack swap */
    KeSetKernelStackSwapEnable(TRUE);
    return;
}
Example #3
0
VOID
ExpWorkerThread (
    IN PVOID StartContext
    )
{
    PLIST_ENTRY Entry;
    WORK_QUEUE_TYPE QueueType;
    PWORK_QUEUE_ITEM WorkItem;
    KPROCESSOR_MODE WaitMode;
    LARGE_INTEGER TimeoutValue;
    PLARGE_INTEGER Timeout;
    PETHREAD Thread;
    PEX_WORK_QUEUE WorkerQueue;
    PWORKER_THREAD_ROUTINE WorkerRoutine;
    PVOID Parameter;
    EX_QUEUE_WORKER_INFO OldWorkerInfo;
    EX_QUEUE_WORKER_INFO NewWorkerInfo;
    ULONG CountForQueueEmpty;

    //
    // Set timeout value etc according to whether we are static or dynamic.
    //

    if (((ULONG_PTR)StartContext & DYNAMIC_WORKER_THREAD) == 0) {

        //
        // We are being created as a static thread.  As such it will not
        // terminate, so there is no point in timing out waiting for a work
        // item.
        //

        Timeout = NULL;
    }
    else {

        //
        // This is a dynamic worker thread.  It has a non-infinite timeout
        // so that it can eventually terminate.
        //

        TimeoutValue.QuadPart = -DYNAMIC_THREAD_TIMEOUT;
        Timeout = &TimeoutValue;
    }

    Thread = PsGetCurrentThread ();

    //
    // If the thread is a critical worker thread, then set the thread
    // priority to the lowest realtime level. Otherwise, set the base
    // thread priority to time critical.
    //

    QueueType = (WORK_QUEUE_TYPE)
                ((ULONG_PTR)StartContext & ~DYNAMIC_WORKER_THREAD);

    WorkerQueue = &ExWorkerQueue[QueueType];

    WaitMode = (KPROCESSOR_MODE) WorkerQueue->Info.WaitMode;

    ASSERT (Thread->ExWorkerCanWaitUser == 0);

    if (WaitMode == UserMode) {
        Thread->ExWorkerCanWaitUser = 1;
    }

#if defined(REMOTE_BOOT)
    //
    // In diskless NT scenarios ensure that the kernel stack of the worker
    // threads will not be swapped out.
    //

    if (IoRemoteBootClient) {
        KeSetKernelStackSwapEnable (FALSE);
    }
#endif // defined(REMOTE_BOOT)

    //
    // Register as a worker, exiting if the queue's going down and
    // there aren't any workers in the queue to hand us the shutdown
    // work item if we enter the queue (we want to be able to enter a
    // queue even if the queue's shutting down, in case there's a
    // backlog of work items that the balance manager thread's decided
    // we should be helping to process).
    //

    if (PO_SHUTDOWN_QUEUE == QueueType) {
        CountForQueueEmpty = 1;
    }
    else {
        CountForQueueEmpty = 0;
    }

    if (ExpWorkersCanSwap == FALSE) {
        KeSetKernelStackSwapEnable (FALSE);
    }

    do {

        OldWorkerInfo.QueueWorkerInfo = ReadForWriteAccess (&WorkerQueue->Info.QueueWorkerInfo);

        if (OldWorkerInfo.QueueDisabled &&
            OldWorkerInfo.WorkerCount <= CountForQueueEmpty) {

            //
            // The queue is disabled and empty so just exit.
            //

            KeSetKernelStackSwapEnable (TRUE);
            PsTerminateSystemThread (STATUS_SYSTEM_SHUTDOWN);
        }

        NewWorkerInfo.QueueWorkerInfo = OldWorkerInfo.QueueWorkerInfo;
        NewWorkerInfo.WorkerCount += 1;

    } while (OldWorkerInfo.QueueWorkerInfo !=

        InterlockedCompareExchange (&WorkerQueue->Info.QueueWorkerInfo,
                                    NewWorkerInfo.QueueWorkerInfo,
                                    OldWorkerInfo.QueueWorkerInfo));

    //
    // As of this point, we must only exit if we decrement the worker
    // count without the queue disabled flag being set.  (Unless we
    // exit due to the shutdown work item, which also decrements the
    // worker count).
    //

    Thread->ActiveExWorker = 1;

    //
    // 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 or until we time out.
        //
        // By specifying a wait mode of UserMode, the thread's kernel
        // stack is swappable.
        //

        Entry = KeRemoveQueue (&WorkerQueue->WorkerQueue,
                               WaitMode,
                               Timeout);

        if ((ULONG_PTR)Entry != STATUS_TIMEOUT) {

            //
            // This is a real work item, process it.
            //
            // Update the total number of work items processed.
            //

            InterlockedIncrement ((PLONG)&WorkerQueue->WorkItemsProcessed);

            WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
            WorkerRoutine = WorkItem->WorkerRoutine;
            Parameter = WorkItem->Parameter;

            //
            // Catch worker routines referencing a user mode address.
            //

            ASSERT ((ULONG_PTR)WorkerRoutine > MmUserProbeAddress);

            //
            // Execute the specified routine.
            //

            ((PWORKER_THREAD_ROUTINE)WorkerRoutine) (Parameter);

#if DBG
            if (IsListEmpty (&Thread->IrpList)) {
                //
                // See if a worker just returned while holding a resource
                //
                ExCheckIfResourceOwned ();
            }
#endif
            //
            // Catch worker routines that forget to leave a critial/guarded
            // region. In the debug case execute a breakpoint. In the free
            // case zero the flag so that  APCs can continue to fire to this
            // thread.
            //

            if (Thread->Tcb.CombinedApcDisable != 0) {
                DbgPrint ((char*)ExpWorkerApcDisabledMessage,
                          WorkerRoutine,
                          Parameter,
                          WorkItem);

                ASSERT (FALSE);

                Thread->Tcb.CombinedApcDisable = 0;
            }

            if (KeGetCurrentIrql () != PASSIVE_LEVEL) {
                KeBugCheckEx (WORKER_THREAD_RETURNED_AT_BAD_IRQL,
                              (ULONG_PTR)WorkerRoutine,
                              (ULONG_PTR)KeGetCurrentIrql(),
                              (ULONG_PTR)Parameter,
                              (ULONG_PTR)WorkItem);
            }

            if (PS_IS_THREAD_IMPERSONATING (Thread)) {
                KeBugCheckEx (IMPERSONATING_WORKER_THREAD,
                              (ULONG_PTR)WorkerRoutine,
                              (ULONG_PTR)Parameter,
                              (ULONG_PTR)WorkItem,
                              0);
            }

            continue;
        }

        //
        // These things are known:
        //
        // - Static worker threads do not time out, so this is a dynamic
        //   worker thread.
        //
        // - This thread has been waiting for a long time with nothing
        //   to do.
        //

        if (IsListEmpty (&Thread->IrpList) == FALSE) {

            //
            // There is still I/O pending, can't terminate yet.
            //

            continue;
        }

        //
        // Get out of the queue, if we can
        //

        do {
            OldWorkerInfo.QueueWorkerInfo = ReadForWriteAccess (&WorkerQueue->Info.QueueWorkerInfo);

            if (OldWorkerInfo.QueueDisabled) {

                //
                // We're exiting via the queue disable work item;
                // there's no point in expiring here.
                //

                break;
            }

            NewWorkerInfo.QueueWorkerInfo = OldWorkerInfo.QueueWorkerInfo;
            NewWorkerInfo.WorkerCount -= 1;

        } while (OldWorkerInfo.QueueWorkerInfo
                 != InterlockedCompareExchange(&WorkerQueue->Info.QueueWorkerInfo,
                                               NewWorkerInfo.QueueWorkerInfo,
                                               OldWorkerInfo.QueueWorkerInfo));

        if (OldWorkerInfo.QueueDisabled) {

            //
            // We're exiting via the queue disable work item
            //

            continue;
        }

        //
        // This dynamic thread can be terminated.
        //

        break;

    } while (TRUE);

    //
    // Terminate this dynamic thread.
    //

    InterlockedDecrement ((PLONG)&WorkerQueue->DynamicThreadCount);

    //
    // Carefully clear this before marking the thread stack as swap enabled
    // so that an incoming APC won't inadvertently disable the stack swap
    // afterwards.
    //

    Thread->ActiveExWorker = 0;

    //
    // We will bugcheck if we terminate a thread with stack swapping
    // disabled.
    //

    KeSetKernelStackSwapEnable (TRUE);

    return;
}
Example #4
-1
/*
 * @implemented
 */
VOID
NTAPI
FsRtlWorkerThread(IN PVOID StartContext)
{
    KIRQL Irql;
    PLIST_ENTRY Entry;
    PWORK_QUEUE_ITEM WorkItem;
    ULONG QueueId = (ULONG)StartContext;

    /* Set our priority according to the queue we're dealing with */
    KeSetPriorityThread(&PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + QueueId);

    /* Loop for events */
    for (;;)
    {
        /* Look for next event */
        Entry = KeRemoveQueue(&FsRtlWorkerQueues[QueueId], KernelMode, NULL);
        WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);

        /* Call its routine (here: FsRtlStackOverflowRead) */
        WorkItem->WorkerRoutine(WorkItem->Parameter);

        /* Check we're still at passive level or bugcheck */
        Irql = KeGetCurrentIrql();
        if (Irql != PASSIVE_LEVEL)
        {
            KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL, (ULONG_PTR)WorkItem->WorkerRoutine,   
                         (ULONG_PTR)Irql, (ULONG_PTR)WorkItem->WorkerRoutine,
                         (ULONG_PTR)WorkItem);
        }
    }
}