diff --git a/ntoskrnl/cache/newcc.h b/ntoskrnl/cache/newcc.h index 63f4841229b..adbf1c5d926 100644 --- a/ntoskrnl/cache/newcc.h +++ b/ntoskrnl/cache/newcc.h @@ -53,7 +53,7 @@ CcMdlWriteComplete2(IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset, IN PMDL MdlChain); -BOOLEAN +VOID NTAPI CcInitView(VOID); diff --git a/ntoskrnl/cc/cacheman.c b/ntoskrnl/cc/cacheman.c index 8303dd9dd89..1718ddebca2 100644 --- a/ntoskrnl/cc/cacheman.c +++ b/ntoskrnl/cc/cacheman.c @@ -16,6 +16,9 @@ BOOLEAN CcPfEnablePrefetcher; PFSN_PREFETCHER_GLOBALS CcPfGlobals; +MM_SYSTEMSIZE CcCapturedSystemSize; + +static ULONG BugCheckFileId = 0x4 << 16; /* FUNCTIONS *****************************************************************/ @@ -42,15 +45,78 @@ NTAPI INIT_FUNCTION CcInitializeCacheManager(VOID) { - return CcInitView(); + ULONG Thread; + + CcInitView(); + + /* Initialize lazy-writer lists */ + InitializeListHead(&CcIdleWorkerThreadList); + InitializeListHead(&CcRegularWorkQueue); + + /* Define lazy writer threshold and the amount of workers, + * depending on the system type + */ + CcCapturedSystemSize = MmQuerySystemSize(); + switch (CcCapturedSystemSize) + { + case MmSmallSystem: + CcNumberWorkerThreads = ExCriticalWorkerThreads - 1; + CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8; + break; + + case MmMediumSystem: + CcNumberWorkerThreads = ExCriticalWorkerThreads - 1; + CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4; + break; + + case MmLargeSystem: + CcNumberWorkerThreads = ExCriticalWorkerThreads - 2; + CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4; + break; + + default: + CcNumberWorkerThreads = 1; + CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8; + break; + } + + /* Allocate a work item for all our threads */ + for (Thread = 0; Thread < CcNumberWorkerThreads; ++Thread) + { + PWORK_QUEUE_ITEM Item; + + Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_QUEUE_ITEM), 'qWcC'); + if (Item == NULL) + { + CcBugCheck(0, 0, 0); + } + + /* By default, it's obviously idle */ + ExInitializeWorkItem(Item, CcWorkerThread, Item); + InsertTailList(&CcIdleWorkerThreadList, &Item->List); + } + + /* Initialize our lazy writer */ + RtlZeroMemory(&LazyWriter, sizeof(LazyWriter)); + InitializeListHead(&LazyWriter.WorkQueue); + /* Delay activation of the lazy writer */ + KeInitializeDpc(&LazyWriter.ScanDpc, CcScanDpc, NULL); + KeInitializeTimer(&LazyWriter.ScanTimer); + + /* Lookaside list for our work items */ + ExInitializeNPagedLookasideList(&CcTwilightLookasideList, NULL, NULL, 0, sizeof(WORK_QUEUE_ENTRY), 'KWcC', 0); + + /* HACK: for lazy writer watching */ + KeInitializeEvent(&iLazyWriterNotify, NotificationEvent, FALSE); + + return TRUE; } VOID NTAPI CcShutdownSystem(VOID) { - /* Inform the lazy writer it has to stop activity */ - CcShutdownLazyWriter(); + /* NOTHING TO DO */ } /* diff --git a/ntoskrnl/cc/copy.c b/ntoskrnl/cc/copy.c index 3d60a1aded8..194526c559d 100644 --- a/ntoskrnl/cc/copy.c +++ b/ntoskrnl/cc/copy.c @@ -533,6 +533,12 @@ CcDeferWrite ( &Context->DeferredWriteLinks, &CcDeferredWriteSpinLock); } + + /* FIXME: lock master */ + if (!LazyWriter.ScanActive) + { + CcScheduleLazyWriteScan(FALSE); + } } /* diff --git a/ntoskrnl/cc/lazywrite.c b/ntoskrnl/cc/lazywrite.c new file mode 100644 index 00000000000..bda7ca27de3 --- /dev/null +++ b/ntoskrnl/cc/lazywrite.c @@ -0,0 +1,283 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/cc/lazywrite.c + * PURPOSE: Cache manager + * + * PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org) + */ + +/* INCLUDES *****************************************************************/ + +#include +#define NDEBUG +#include + +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 + */ +ULONG CcLazyWritePages = 0; +ULONG CcLazyWriteIos = 0; + +/* Internal vars (MS): + * - Lazy writer status structure + * - Lookaside list where to allocate work items + * - Queue for regular work items + * - Available worker threads + * - Marker for throttling queues + * - Number of ongoing workers + * - Three seconds delay for lazy writer + * - One second delay for lazy writer + * - Zero delay for lazy writer + * - Number of worker threads + */ +LAZY_WRITER LazyWriter; +NPAGED_LOOKASIDE_LIST CcTwilightLookasideList; +LIST_ENTRY CcRegularWorkQueue; +LIST_ENTRY CcIdleWorkerThreadList; +BOOLEAN CcQueueThrottle = FALSE; +ULONG CcNumberActiveWorkerThreads = 0; +LARGE_INTEGER CcFirstDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*3000*1000*10); +LARGE_INTEGER CcIdleDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*1000*1000*10); +LARGE_INTEGER CcNoDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0); +ULONG CcNumberWorkerThreads; + +/* Internal vars (ROS): + */ +KEVENT iLazyWriterNotify; + +/* FUNCTIONS *****************************************************************/ + +VOID +CcPostWorkQueue( + IN PWORK_QUEUE_ENTRY WorkItem, + IN PLIST_ENTRY WorkQueue) +{ + KIRQL OldIrql; + PWORK_QUEUE_ITEM ThreadToSpawn; + + /* First of all, insert the item in the queue */ + OldIrql = KeAcquireQueuedSpinLock(LockQueueWorkQueueLock); + InsertTailList(WorkQueue, &WorkItem->WorkQueueLinks); + + /* Now, define whether we have to spawn a new work thread + * We will spawn a new one if: + * - There's no throttle in action + * - There's still at least one idle thread + */ + ThreadToSpawn = NULL; + if (!CcQueueThrottle && !IsListEmpty(&CcIdleWorkerThreadList)) + { + PLIST_ENTRY ListEntry; + + /* Get the idle thread */ + ListEntry = RemoveHeadList(&CcIdleWorkerThreadList); + ThreadToSpawn = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List); + + /* We're going to have one more! */ + CcNumberActiveWorkerThreads += 1; + } + + KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql); + + /* If we have a thread to spawn, do it! */ + if (ThreadToSpawn != NULL) + { + /* We NULLify it to be consistent with initialization */ + ThreadToSpawn->List.Flink = NULL; + ExQueueWorkItem(ThreadToSpawn, CriticalWorkQueue); + } +} + +VOID +NTAPI +CcScanDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2) +{ + PWORK_QUEUE_ENTRY WorkItem; + + /* Allocate a work item */ + WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList); + if (WorkItem == NULL) + { + LazyWriter.ScanActive = FALSE; + return; + } + + /* And post it, it will be for lazy write */ + WorkItem->Function = LazyWrite; + CcPostWorkQueue(WorkItem, &CcRegularWorkQueue); +} + +/* FIXME: handle master lock */ +VOID +CcLazyWriteScan(VOID) +{ + ULONG Target; + ULONG Count; + PLIST_ENTRY ListEntry; + + /* We're not sleeping anymore */ + KeClearEvent(&iLazyWriterNotify); + + /* Our target is one-eighth of the dirty pages */ + Target = CcTotalDirtyPages / 8; + if (Target != 0) + { + /* Flush! */ + DPRINT1("Lazy writer starting (%d)\n", Target); + CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE); + + /* And update stats */ + CcLazyWritePages += Count; + ++CcLazyWriteIos; + DPRINT1("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) + { + PDEFERRED_WRITE Context; + + /* Extract the context */ + Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks); + ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE); + + /* 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); + } + } + + /* We're no longer active */ + LazyWriter.ScanActive = FALSE; +} + +VOID CcScheduleLazyWriteScan( + IN BOOLEAN NoDelay) +{ + /* If no delay, immediately start lazy writer, + * no matter it was already started + */ + if (NoDelay) + { + LazyWriter.ScanActive = TRUE; + KeSetTimer(&LazyWriter.ScanTimer, CcNoDelay, &LazyWriter.ScanDpc); + } + /* Otherwise, if it's not running, just wait three seconds to start it */ + else if (!LazyWriter.ScanActive) + { + LazyWriter.ScanActive = TRUE; + KeSetTimer(&LazyWriter.ScanTimer, CcFirstDelay, &LazyWriter.ScanDpc); + } + /* Finally, already running, so queue for the next second */ + else + { + KeSetTimer(&LazyWriter.ScanTimer, CcIdleDelay, &LazyWriter.ScanDpc); + } +} + +VOID +NTAPI +CcWorkerThread( + IN PVOID Parameter) +{ + KIRQL OldIrql; + BOOLEAN DropThrottle; + PWORK_QUEUE_ITEM Item; + + /* Get back our thread item */ + Item = Parameter; + /* And by default, don't touch throttle */ + DropThrottle = FALSE; + + /* Loop till we have jobs */ + while (TRUE) + { + PWORK_QUEUE_ENTRY WorkItem; + + /* Lock queues */ + OldIrql = KeAcquireQueuedSpinLock(LockQueueWorkQueueLock); + + /* If we have to touch throttle, reset it now! */ + if (DropThrottle) + { + CcQueueThrottle = FALSE; + DropThrottle = FALSE; + } + + /* If no work to do, we're done */ + if (IsListEmpty(&CcRegularWorkQueue)) + { + break; + } + + /* Get our work item, if someone is waiting for us to finish + * and we're not the only thread in queue + * 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; + break; + } + + /* Otherwise, remove current entry */ + RemoveEntryList(&WorkItem->WorkQueueLinks); + KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql); + + /* And handle it */ + switch (WorkItem->Function) + { + /* We only support lazy write now */ + case LazyWrite: + CcLazyWriteScan(); + break; + + case SetDone: + KeSetEvent(WorkItem->Parameters.Event.Event, IO_NO_INCREMENT, FALSE); + DropThrottle = TRUE; + break; + } + + /* And release the item */ + ExFreeToNPagedLookasideList(&CcTwilightLookasideList, WorkItem); + } + + /* Our thread is available again */ + InsertTailList(&CcIdleWorkerThreadList, &Item->List); + /* One less worker */ + --CcNumberActiveWorkerThreads; + KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql); +} diff --git a/ntoskrnl/cc/view.c b/ntoskrnl/cc/view.c index 99e7b1c6126..e050a33c17e 100644 --- a/ntoskrnl/cc/view.c +++ b/ntoskrnl/cc/view.c @@ -51,39 +51,22 @@ NPAGED_LOOKASIDE_LIST iBcbLookasideList; static NPAGED_LOOKASIDE_LIST SharedCacheMapLookasideList; static NPAGED_LOOKASIDE_LIST VacbLookasideList; -/* Counters: - * - Amount of pages flushed by lazy writer - * - Number of times lazy writer ran - */ -ULONG CcLazyWritePages = 0; -ULONG CcLazyWriteIos = 0; - /* Internal vars (MS): * - Threshold above which lazy writer will start action * - Amount of dirty pages * - List for deferred writes * - Spinlock when dealing with the deferred list * - List for "clean" shared cache maps - * - One second delay for lazy writer - * - System size when system started - * - Number of worker threads */ ULONG CcDirtyPageThreshold = 0; ULONG CcTotalDirtyPages = 0; LIST_ENTRY CcDeferredWrites; KSPIN_LOCK CcDeferredWriteSpinLock; LIST_ENTRY CcCleanSharedCacheMapList; -LARGE_INTEGER CcIdleDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*1000*1000*10); -MM_SYSTEMSIZE CcCapturedSystemSize; -ULONG CcNumberWorkerThreads; /* Internal vars (ROS): - * - Event to notify lazy writer to shutdown - * - Event to inform watchers lazy writer is done for this loop * - Lock for the CcCleanSharedCacheMapList list */ -KEVENT iLazyWriterShutdown; -KEVENT iLazyWriterNotify; KSPIN_LOCK iSharedCacheMapLock; #if DBG @@ -304,81 +287,6 @@ CcRosFlushDirtyPages ( return STATUS_SUCCESS; } -/* FIXME: Someday this could somewhat implement write-behind/read-ahead */ -VOID -NTAPI -CciLazyWriter(PVOID Unused) -{ - while (TRUE) - { - NTSTATUS Status; - PLIST_ENTRY ListEntry; - ULONG Target, Count = 0; - - /* One per second or until we have to stop */ - Status = KeWaitForSingleObject(&iLazyWriterShutdown, - Executive, - KernelMode, - FALSE, - &CcIdleDelay); - - /* If we succeeed, we've to stop running! */ - if (Status == STATUS_SUCCESS) - { - break; - } - - /* We're not sleeping anymore */ - KeClearEvent(&iLazyWriterNotify); - - /* Our target is one-eighth of the dirty pages */ - Target = CcTotalDirtyPages / 8; - if (Target != 0) - { - /* Flush! */ - DPRINT("Lazy writer starting (%d)\n", Target); - CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE); - - /* And update stats */ - CcLazyWritePages += Count; - ++CcLazyWriteIos; - 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) - { - PDEFERRED_WRITE Context; - - /* Extract the context */ - Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks); - ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE); - - /* 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); - } - } - } -} - NTSTATUS CcRosTrimCache ( ULONG Target, @@ -611,6 +519,12 @@ CcRosMarkDirtyVacb ( KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql); KeReleaseGuardedMutex(&ViewLock); + + /* FIXME: lock master */ + if (!LazyWriter.ScanActive) + { + CcScheduleLazyWriteScan(FALSE); + } } VOID @@ -1394,25 +1308,11 @@ CcGetFileObjectFromSectionPtrs ( } VOID -NTAPI -CcShutdownLazyWriter ( - VOID) -{ - /* Simply set the event, lazy writer will stop when it's done */ - KeSetEvent(&iLazyWriterShutdown, IO_DISK_INCREMENT, FALSE); -} - -BOOLEAN INIT_FUNCTION NTAPI CcInitView ( VOID) { - HANDLE LazyWriter; - NTSTATUS Status; - KPRIORITY Priority; - OBJECT_ATTRIBUTES ObjectAttributes; - DPRINT("CcInitView()\n"); InitializeListHead(&DirtyVacbListHead); @@ -1446,68 +1346,7 @@ CcInitView ( MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache); - /* Initialize lazy writer events */ - KeInitializeEvent(&iLazyWriterShutdown, SynchronizationEvent, FALSE); - KeInitializeEvent(&iLazyWriterNotify, NotificationEvent, FALSE); - - /* Define lazy writer threshold and the amount of workers, - * depending on the system type - */ - CcCapturedSystemSize = MmQuerySystemSize(); - switch (CcCapturedSystemSize) - { - case MmSmallSystem: - CcNumberWorkerThreads = ExCriticalWorkerThreads - 1; - CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8; - break; - - case MmMediumSystem: - CcNumberWorkerThreads = ExCriticalWorkerThreads - 1; - CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4; - break; - - case MmLargeSystem: - CcNumberWorkerThreads = ExCriticalWorkerThreads - 2; - CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4; - break; - - default: - CcNumberWorkerThreads = 1; - CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8; - break; - } - - /* Start the lazy writer thread */ - InitializeObjectAttributes(&ObjectAttributes, - NULL, - OBJ_KERNEL_HANDLE, - NULL, - NULL); - Status = PsCreateSystemThread(&LazyWriter, - THREAD_ALL_ACCESS, - &ObjectAttributes, - NULL, - NULL, - CciLazyWriter, - NULL); - if (!NT_SUCCESS(Status)) - { - return FALSE; - } - - Priority = 27; - Status = NtSetInformationThread(LazyWriter, - ThreadPriority, - &Priority, - sizeof(Priority)); - ASSERT(NT_SUCCESS(Status)); - - /* Handle is not needed */ - ObCloseHandle(LazyWriter, KernelMode); - CcInitCacheZeroPage(); - - return TRUE; } #if DBG && defined(KDBG) diff --git a/ntoskrnl/include/internal/cc.h b/ntoskrnl/include/internal/cc.h index 37875639412..18d34ef550c 100644 --- a/ntoskrnl/include/internal/cc.h +++ b/ntoskrnl/include/internal/cc.h @@ -45,6 +45,11 @@ extern ULONG CcDirtyPageThreshold; extern ULONG CcTotalDirtyPages; extern LIST_ENTRY CcDeferredWrites; extern KSPIN_LOCK CcDeferredWriteSpinLock; +extern ULONG CcNumberWorkerThreads; +extern LIST_ENTRY CcIdleWorkerThreadList; +extern LIST_ENTRY CcRegularWorkQueue; +extern NPAGED_LOOKASIDE_LIST CcTwilightLookasideList; +extern KEVENT iLazyWriterNotify; typedef struct _PF_SCENARIO_ID { @@ -211,6 +216,43 @@ typedef struct _INTERNAL_BCB CSHORT RefCount; /* (At offset 0x34 on WinNT4) */ } INTERNAL_BCB, *PINTERNAL_BCB; +typedef struct _LAZY_WRITER +{ + LIST_ENTRY WorkQueue; + KDPC ScanDpc; + KTIMER ScanTimer; + BOOLEAN ScanActive; + BOOLEAN OtherWork; + BOOLEAN PendingTeardown; +} LAZY_WRITER, *PLAZY_WRITER; + +typedef struct _WORK_QUEUE_ENTRY +{ + LIST_ENTRY WorkQueueLinks; + union + { + struct + { + FILE_OBJECT *FileObject; + } Read; + struct + { + SHARED_CACHE_MAP *SharedCacheMap; + } Write; + struct + { + KEVENT *Event; + } Event; + struct + { + unsigned long Reason; + } Notification; + } Parameters; + unsigned char Function; +} WORK_QUEUE_ENTRY, *PWORK_QUEUE_ENTRY; + +extern LAZY_WRITER LazyWriter; + #define NODE_TYPE_DEFERRED_WRITE 0x02FC VOID @@ -249,7 +291,7 @@ CcRosGetVacb( PROS_VACB *Vacb ); -BOOLEAN +VOID NTAPI CcInitView(VOID); @@ -371,6 +413,21 @@ VOID NTAPI CcShutdownSystem(VOID); +VOID +NTAPI +CcWorkerThread(PVOID Parameter); + +VOID +NTAPI +CcScanDpc( + PKDPC Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2); + +VOID +CcScheduleLazyWriteScan(BOOLEAN NoDelay); + FORCEINLINE NTSTATUS CcRosAcquireVacbLock( @@ -418,3 +475,5 @@ IsPointInRange( { return DoRangesIntersect(Offset1, Length1, Point, 1); } + +#define CcBugCheck(A, B, C) KeBugCheckEx(CACHE_MANAGER, BugCheckFileId | ((ULONG)(__LINE__)), A, B, C) diff --git a/ntoskrnl/ntos.cmake b/ntoskrnl/ntos.cmake index ceb287656af..94e0dc80552 100644 --- a/ntoskrnl/ntos.cmake +++ b/ntoskrnl/ntos.cmake @@ -33,6 +33,7 @@ else() ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/cacheman.c ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/copy.c ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/fs.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/lazywrite.c ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/mdl.c ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/pin.c ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/view.c)