diff --git a/reactos/ntoskrnl/io/iowork.c b/reactos/ntoskrnl/io/iowork.c new file mode 100644 index 00000000000..7eb34596374 --- /dev/null +++ b/reactos/ntoskrnl/io/iowork.c @@ -0,0 +1,263 @@ +/* $Id: iowork.c,v 1.1 2002/10/03 19:17:26 robd Exp $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: reactos/ntoskrnk/io/iowork.c + * PURPOSE: Manage IO system work queues + * PROGRAMMER: David Welch (welch@mcmail.com) + * Robert Dickenson (odin@pnc.com.au) + * REVISION HISTORY: + * 28/09/2002: (RDD) Created from copy of ex/work.c + */ + +/* INCLUDES ******************************************************************/ + +#include +#include + +#define NDEBUG +#include + + +/* DEFINES *******************************************************************/ + +#define NUMBER_OF_WORKER_THREADS (5) + + +/* TYPES *********************************************************************/ + +typedef struct _WORK_QUEUE { + LIST_ENTRY Head; // Head of the list of waiting work items + KSPIN_LOCK Lock; // Sychronize access to the work queue + KSEMAPHORE Sem; // Worker threads with nothing to do wait on this event + HANDLE Thread[NUMBER_OF_WORKER_THREADS]; // Thread associated with work queue +} WORK_QUEUE, *PWORK_QUEUE; + + +struct _IO_WORKITEM { + LIST_ENTRY IoWorkItemsList; + PDEVICE_OBJECT DeviceObject; + PDRIVER_OBJECT DriverObject; + int size; + PIO_WORKITEM_ROUTINE WorkerRoutine; + WORK_QUEUE_TYPE QueueType; + PVOID Context; + int reserved[50]; +}; + +//typedef VOID (*PIO_WORKITEM_ROUTINE)(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context); + + +/* GLOBALS *******************************************************************/ + +#define TAG_IOWI TAG('I', 'O', 'W', 'I') + +// Queue of items waiting to be processed at normal priority +WORK_QUEUE IoNormalWorkQueue; +WORK_QUEUE IoCriticalWorkQueue; +WORK_QUEUE IoHyperCriticalWorkQueue; + + +/* LOCALS *******************************************************************/ + +static BOOL IoWorkQueueItemsListInitialised = FALSE; +static LIST_ENTRY IoWorkQueueItemsListHead; +static KSPIN_LOCK IoWorkQueueItemsListLock; + + +/* FUNCTIONS ****************************************************************/ + +static +NTSTATUS +STDCALL +IoWorkerThreadEntryPoint(PVOID context) +/* + * FUNCTION: Entry point for a worker thread + * ARGUMENTS: + * context = Parameters + * RETURNS: Status + * NOTE: To kill a worker thread you must queue an item whose callback + * calls PsTerminateSystemThread + */ +{ + PWORK_QUEUE queue = (PWORK_QUEUE)context; + PIO_WORKITEM pWorkItem; + PLIST_ENTRY pListEntry; + + for (;;) { + pListEntry = ExInterlockedRemoveHeadList(&queue->Head, &queue->Lock); + if (pListEntry != NULL) { + pWorkItem = CONTAINING_RECORD(pListEntry, struct _IO_WORKITEM, IoWorkItemsList); + (pWorkItem->WorkerRoutine)(pWorkItem->DeviceObject, pWorkItem->Context); + } else { + KeWaitForSingleObject((PVOID)&queue->Sem, + Executive, + KernelMode, + FALSE, + NULL); + DPRINT("Woke from wait\n"); + } + } +} +/* + +VOID +Scan_IoQueueWorkItems(PDEVICE_OBJECT DeviceObject, WORK_QUEUE_TYPE QueueType) +{ + PIO_WORKITEM pWorkItem; + PLIST_ENTRY pListEntry; + KIRQL oldlvl; + + pListEntry = IoWorkQueueItemsListHead.Flink; + while (pListEntry != &IoWorkQueueItemsListHead) { + pWorkItem = CONTAINING_RECORD(pListEntry, struct _IO_WORKITEM, IoWorkItemsList); + if (pWorkItem->QueueType == QueueType) { + KeAcquireSpinLock(&IoWorkQueueItemsListLock,&oldlvl); + RemoveEntryList(pListEntry); + KeReleaseSpinLock(&IoWorkQueueItemsListLock,oldlvl); + pListEntry = pListEntry->Flink; + (pWorkItem->WorkerRoutine)(pWorkItem->DeviceObject, pWorkItem->Context); + } else { + pListEntry = pListEntry->Flink; + } + } +} + */ + +static +VOID +IoInitializeWorkQueue(PWORK_QUEUE WorkQueue, KPRIORITY Priority) +{ + ULONG i; + PETHREAD Thread; + + InitializeListHead(&WorkQueue->Head); + KeInitializeSpinLock(&WorkQueue->Lock); + KeInitializeSemaphore(&WorkQueue->Sem, 0, 256); + + for (i = 0; i < NUMBER_OF_WORKER_THREADS; i++) { + PsCreateSystemThread(&WorkQueue->Thread[i], + THREAD_ALL_ACCESS, + NULL, + NULL, + NULL, + IoWorkerThreadEntryPoint, + WorkQueue); + ObReferenceObjectByHandle(WorkQueue->Thread[i], + THREAD_ALL_ACCESS, + PsThreadType, + KernelMode, + (PVOID*)&Thread, + NULL); + KeSetPriorityThread(&Thread->Tcb, Priority); + ObDereferenceObject(Thread); + } +} + +VOID +IoInitializeWorkerThreads(VOID) +{ + IoInitializeWorkQueue(&IoNormalWorkQueue, LOW_PRIORITY); + IoInitializeWorkQueue(&IoCriticalWorkQueue, LOW_REALTIME_PRIORITY); + IoInitializeWorkQueue(&IoHyperCriticalWorkQueue, HIGH_PRIORITY); +} + +/* NOTES: +IoQueueWorkItem inserts the specified work item into a queue from which a + system worker thread removes the item and gives control to the specified + callback routine. +Highest-level drivers can call IoQueueWorkItem. +Callers of IoQueueWorkItem must be running at IRQL <= DISPATCH_LEVEL. +The callback is run within a system thread context at IRQL PASSIVE_LEVEL. +This caller-supplied routine is responsible for calling IoFreeWorkItem to + reclaim the storage allocated for the work item. +IoQueueWorkItem should be used instead of ExQueueWorkItem because + IoQueueWorkItem will ensure that the device object associated with the + specified work item is available for the processing of the work item. + */ +VOID +STDCALL +IoQueueWorkItem(IN PIO_WORKITEM pIoWorkItem, IN PIO_WORKITEM_ROUTINE pWorkerRoutine, + IN WORK_QUEUE_TYPE QueueType, IN PVOID pContext) +/* + * FUNCTION: Inserts a work item in a queue for one of the system worker + * threads to process + * ARGUMENTS: + * pIoWorkItem = Item to insert + * QueueType = Queue to insert it in + */ +{ + DPRINT("IoQueueWorkItem(%p, %p, %p, %p)\n", pIoWorkItem, pWorkerRoutine, QueueType, pContext); + + assert(pIoWorkItem!=NULL); + + if (!IoWorkQueueItemsListInitialised) { + IoWorkQueueItemsListInitialised = TRUE; + InitializeListHead(&IoWorkQueueItemsListHead); + KeInitializeSpinLock(&IoWorkQueueItemsListLock); + } + pIoWorkItem->WorkerRoutine = pWorkerRoutine; + pIoWorkItem->QueueType = QueueType; + pIoWorkItem->Context = pContext; + //ExInterlockedInsertHeadList(&IoWorkQueueItemsListHead, &pIoWorkItem->IoWorkItemsList, &IoWorkQueueItemsListLock); + + switch(QueueType) { + case DelayedWorkQueue: + ExInterlockedInsertTailList(&IoNormalWorkQueue.Head, + &pIoWorkItem->IoWorkItemsList, + &IoNormalWorkQueue.Lock); + KeReleaseSemaphore(&IoNormalWorkQueue.Sem, + IO_NO_INCREMENT, 1, FALSE); + break; + case CriticalWorkQueue: + ExInterlockedInsertTailList(&IoCriticalWorkQueue.Head, + &pIoWorkItem->IoWorkItemsList, + &IoCriticalWorkQueue.Lock); + KeReleaseSemaphore(&IoCriticalWorkQueue.Sem, + IO_NO_INCREMENT, 1, FALSE); + break; + case HyperCriticalWorkQueue: + ExInterlockedInsertTailList(&IoHyperCriticalWorkQueue.Head, + &pIoWorkItem->IoWorkItemsList, + &IoHyperCriticalWorkQueue.Lock); + KeReleaseSemaphore(&IoHyperCriticalWorkQueue.Sem, + IO_NO_INCREMENT, 1, FALSE); + break; + } +} + +/* + */ +VOID STDCALL +IoFreeWorkItem(PIO_WORKITEM pIoWorkItem) +{ + if (pIoWorkItem != NULL) { + ExFreePool(pIoWorkItem); + } else { + DPRINT("IoFreeWorkItem() passed NULL pointer ???\n"); + } +} + +/* NOTES: +Callers of IoAllocateWorkItem must be running at IRQL <= DISPATCH_LEVEL + */ +PIO_WORKITEM STDCALL +IoAllocateWorkItem(PDEVICE_OBJECT DeviceObject) +{ + PIO_WORKITEM pIoWorkItem = NULL; + + assert(DeviceObject!=NULL); + //ASSERT_IRQL(DISPATCH_LEVEL); + + pIoWorkItem = ExAllocatePoolWithTag(NonPagedPool, sizeof(struct _IO_WORKITEM), TAG_IOWI); + if (pIoWorkItem != NULL) { + RtlZeroMemory(pIoWorkItem, sizeof(struct _IO_WORKITEM)); + pIoWorkItem->size = sizeof(struct _IO_WORKITEM); + pIoWorkItem->DeviceObject = DeviceObject; + } else { + DPRINT("IoAllocateWorkItem() FAILED to allocated %d bytes memory\n", sizeof(struct _IO_WORKITEM)); + } + return pIoWorkItem; +} + +/* EOF */