#define NDEBUG
#include <debug.h>
-typedef enum _WORK_QUEUE_FUNCTIONS
-{
- ReadAhead = 1,
- WriteBehind = 2,
- LazyWrite = 3,
- SetDone = 4,
-} WORK_QUEUE_FUNCTIONS, *PWORK_QUEUE_FUNCTIONS;
-
/* Counters:
* - Amount of pages flushed by lazy writer
* - Number of times lazy writer ran
/* Internal vars (MS):
* - Lazy writer status structure
* - Lookaside list where to allocate work items
+ * - Queue for high priority work items (read ahead)
* - Queue for regular work items
* - Available worker threads
+ * - Queue for stuff to be queued after lazy writer is done
* - Marker for throttling queues
* - Number of ongoing workers
* - Three seconds delay for lazy writer
*/
LAZY_WRITER LazyWriter;
NPAGED_LOOKASIDE_LIST CcTwilightLookasideList;
+LIST_ENTRY CcExpressWorkQueue;
LIST_ENTRY CcRegularWorkQueue;
LIST_ENTRY CcIdleWorkerThreadList;
+LIST_ENTRY CcPostTickWorkQueue;
BOOLEAN CcQueueThrottle = FALSE;
ULONG CcNumberActiveWorkerThreads = 0;
LARGE_INTEGER CcFirstDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*3000*1000*10);
LARGE_INTEGER CcNoDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0);
ULONG CcNumberWorkerThreads;
-/* Internal vars (ROS):
- */
-KEVENT iLazyWriterNotify;
-
/* FUNCTIONS *****************************************************************/
VOID
CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
}
-/* FIXME: handle master lock */
VOID
CcLazyWriteScan(VOID)
{
ULONG Target;
ULONG Count;
+ KIRQL OldIrql;
PLIST_ENTRY ListEntry;
+ LIST_ENTRY ToPost;
+ PWORK_QUEUE_ENTRY WorkItem;
- /* We're not sleeping anymore */
- KeClearEvent(&iLazyWriterNotify);
+ /* Do we have entries to queue after we're done? */
+ InitializeListHead(&ToPost);
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+ if (LazyWriter.OtherWork)
+ {
+ while (!IsListEmpty(&CcPostTickWorkQueue))
+ {
+ ListEntry = RemoveHeadList(&CcPostTickWorkQueue);
+ WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ENTRY, WorkQueueLinks);
+ InsertTailList(&ToPost, &WorkItem->WorkQueueLinks);
+ }
+ LazyWriter.OtherWork = FALSE;
+ }
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
/* Our target is one-eighth of the dirty pages */
Target = CcTotalDirtyPages / 8;
if (Target != 0)
{
/* Flush! */
- DPRINT1("Lazy writer starting (%d)\n", Target);
+ DPRINT("Lazy writer starting (%d)\n", Target);
CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
/* And update stats */
CcLazyWritePages += Count;
++CcLazyWriteIos;
- DPRINT1("Lazy writer done (%d)\n", Count);
+ DPRINT("Lazy writer done (%d)\n", Count);
}
- /* Inform people waiting on us that we're done */
- KeSetEvent(&iLazyWriterNotify, IO_DISK_INCREMENT, FALSE);
-
- /* Likely not optimal, but let's handle one deferred write now! */
- ListEntry = ExInterlockedRemoveHeadList(&CcDeferredWrites, &CcDeferredWriteSpinLock);
- if (ListEntry != NULL)
+ /* If we have deferred writes, try them now! */
+ if (!IsListEmpty(&CcDeferredWrites))
{
- PDEFERRED_WRITE Context;
-
- /* Extract the context */
- Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks);
- ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE);
+ CcPostDeferredWrites();
+ }
- /* Can we write now? */
- if (CcCanIWrite(Context->FileObject, Context->BytesToWrite, FALSE, TRUE))
- {
- /* Yes! Do it, and destroy the associated context */
- Context->PostRoutine(Context->Context1, Context->Context2);
- ExFreePoolWithTag(Context, 'CcDw');
- }
- else
- {
- /* Otherwise, requeue it, but in tail, so that it doesn't block others
- * This is clearly to improve, but given the poor algorithm used now
- * It's better than nothing!
- */
- ExInterlockedInsertTailList(&CcDeferredWrites,
- &Context->DeferredWriteLinks,
- &CcDeferredWriteSpinLock);
- }
+ /* Post items that were due for end of run */
+ while (!IsListEmpty(&ToPost))
+ {
+ ListEntry = RemoveHeadList(&ToPost);
+ WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ENTRY, WorkQueueLinks);
+ CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
}
/* We're no longer active */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
LazyWriter.ScanActive = FALSE;
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
}
VOID CcScheduleLazyWriteScan(
DropThrottle = FALSE;
}
- /* If no work to do, we're done */
- if (IsListEmpty(&CcRegularWorkQueue))
+ /* Check first if we have read ahead to do */
+ if (IsListEmpty(&CcExpressWorkQueue))
{
- break;
+ /* If not, check regular queue */
+ if (IsListEmpty(&CcRegularWorkQueue))
+ {
+ break;
+ }
+ else
+ {
+ WorkItem = CONTAINING_RECORD(CcRegularWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
+ }
+ }
+ else
+ {
+ WorkItem = CONTAINING_RECORD(CcExpressWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
}
/* Get our work item, if someone is waiting for us to finish
* then, quit running to let the others do
* and throttle so that noone starts till current activity is over
*/
- WorkItem = CONTAINING_RECORD(CcRegularWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
if (WorkItem->Function == SetDone && CcNumberActiveWorkerThreads > 1)
{
CcQueueThrottle = TRUE;
/* And handle it */
switch (WorkItem->Function)
{
- /* We only support lazy write now */
+ case ReadAhead:
+ CcPerformReadAhead(WorkItem->Parameters.Read.FileObject);
+ break;
+
case LazyWrite:
CcLazyWriteScan();
break;
KeSetEvent(WorkItem->Parameters.Event.Event, IO_NO_INCREMENT, FALSE);
DropThrottle = TRUE;
break;
+
+ default:
+ DPRINT1("Ignored item: %p (%d)\n", WorkItem, WorkItem->Function);
+ break;
}
/* And release the item */
--CcNumberActiveWorkerThreads;
KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql);
}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+CcWaitForCurrentLazyWriterActivity (
+ VOID)
+{
+ KIRQL OldIrql;
+ KEVENT WaitEvent;
+ PWORK_QUEUE_ENTRY WorkItem;
+
+ /* Allocate a work item */
+ WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
+ if (WorkItem == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* We want lazy writer to set our event */
+ WorkItem->Function = SetDone;
+ KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE);
+ WorkItem->Parameters.Event.Event = &WaitEvent;
+
+ /* Use the post tick queue */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+ InsertTailList(&CcPostTickWorkQueue, &WorkItem->WorkQueueLinks);
+
+ /* Inform the lazy writer it will have to handle the post tick queue */
+ LazyWriter.OtherWork = TRUE;
+ /* And if it's not running, queue a lazy writer run
+ * And start it NOW, we want the response now
+ */
+ if (!LazyWriter.ScanActive)
+ {
+ CcScheduleLazyWriteScan(TRUE);
+ }
+
+ KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+
+ /* And now, wait until lazy writer replies */
+ return KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, NULL);
+}