ReactOS  0.4.14-dev-337-gf981a68
lazywrite.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT: See COPYING in the top level directory
3  * PROJECT: ReactOS kernel
4  * FILE: ntoskrnl/cc/lazywrite.c
5  * PURPOSE: Cache manager
6  *
7  * PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org)
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* Counters:
17  * - Amount of pages flushed by lazy writer
18  * - Number of times lazy writer ran
19  */
22 
23 /* Internal vars (MS):
24  * - Lazy writer status structure
25  * - Lookaside list where to allocate work items
26  * - Queue for high priority work items (read ahead)
27  * - Queue for regular work items
28  * - Available worker threads
29  * - Queue for stuff to be queued after lazy writer is done
30  * - Marker for throttling queues
31  * - Number of ongoing workers
32  * - Three seconds delay for lazy writer
33  * - One second delay for lazy writer
34  * - Zero delay for lazy writer
35  * - Number of worker threads
36  */
49 
50 /* FUNCTIONS *****************************************************************/
51 
52 VOID
54  IN PWORK_QUEUE_ENTRY WorkItem,
56 {
57  KIRQL OldIrql;
58  PWORK_QUEUE_ITEM ThreadToSpawn;
59 
60  /* First of all, insert the item in the queue */
62  InsertTailList(WorkQueue, &WorkItem->WorkQueueLinks);
63 
64  /* Now, define whether we have to spawn a new work thread
65  * We will spawn a new one if:
66  * - There's no throttle in action
67  * - There's still at least one idle thread
68  */
69  ThreadToSpawn = NULL;
71  {
72  PLIST_ENTRY ListEntry;
73 
74  /* Get the idle thread */
76  ThreadToSpawn = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List);
77 
78  /* We're going to have one more! */
80  }
81 
83 
84  /* If we have a thread to spawn, do it! */
85  if (ThreadToSpawn != NULL)
86  {
87  /* We NULLify it to be consistent with initialization */
88  ThreadToSpawn->List.Flink = NULL;
89  ExQueueWorkItem(ThreadToSpawn, CriticalWorkQueue);
90  }
91 }
92 
93 VOID
94 NTAPI
96  IN PKDPC Dpc,
100 {
101  PWORK_QUEUE_ENTRY WorkItem;
102 
103  /* Allocate a work item */
104  WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
105  if (WorkItem == NULL)
106  {
108  return;
109  }
110 
111  /* And post it, it will be for lazy write */
112  WorkItem->Function = LazyScan;
114 }
115 
116 VOID
118 {
119  ULONG Target, Count;
120 
122  if (Target != 0)
123  {
124  /* Flush! */
125  DPRINT("Lazy writer starting (%d)\n", Target);
127 
128  /* And update stats */
130  ++CcLazyWriteIos;
131  DPRINT("Lazy writer done (%d)\n", Count);
132  }
133 }
134 
135 VOID
137 {
138  ULONG Target;
139  KIRQL OldIrql;
140  PLIST_ENTRY ListEntry;
141  LIST_ENTRY ToPost;
142  PWORK_QUEUE_ENTRY WorkItem;
143 
144  /* Do we have entries to queue after we're done? */
145  InitializeListHead(&ToPost);
147  if (LazyWriter.OtherWork)
148  {
150  {
151  ListEntry = RemoveHeadList(&CcPostTickWorkQueue);
152  WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ENTRY, WorkQueueLinks);
153  InsertTailList(&ToPost, &WorkItem->WorkQueueLinks);
154  }
156  }
158 
159  /* Our target is one-eighth of the dirty pages */
161  if (Target != 0)
162  {
163  /* There is stuff to flush, schedule a write-behind operation */
164 
165  /* Allocate a work item */
166  WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
167  if (WorkItem != NULL)
168  {
169  WorkItem->Function = WriteBehind;
171  }
172  }
173 
174  /* Post items that were due for end of run */
175  while (!IsListEmpty(&ToPost))
176  {
177  ListEntry = RemoveHeadList(&ToPost);
178  WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ENTRY, WorkQueueLinks);
180  }
181 
182  /* If we have deferred writes, try them now! */
184  {
186  /* Reschedule immediately a lazy writer run
187  * Keep us active to have short idle delay
188  */
190  }
191  else
192  {
193  /* We're no longer active */
197  }
198 }
199 
201  IN BOOLEAN NoDelay)
202 {
203  /* If no delay, immediately start lazy writer,
204  * no matter it was already started
205  */
206  if (NoDelay)
207  {
210  }
211  /* Otherwise, if it's not running, just wait three seconds to start it */
212  else if (!LazyWriter.ScanActive)
213  {
216  }
217  /* Finally, already running, so queue for the next second */
218  else
219  {
221  }
222 }
223 
224 VOID
225 NTAPI
228 {
229  KIRQL OldIrql;
230  BOOLEAN DropThrottle, WritePerformed;
231  PWORK_QUEUE_ITEM Item;
232 #if DBG
233  PIRP TopLevel;
234 #endif
235 
236  /* Get back our thread item */
237  Item = Parameter;
238  /* And by default, don't touch throttle */
239  DropThrottle = FALSE;
240  /* No write performed */
241  WritePerformed = FALSE;
242 
243 #if DBG
244  /* Top level IRP should be clean when started
245  * Save it to catch buggy drivers (or bugs!)
246  */
248  if (TopLevel != NULL)
249  {
250  DPRINT1("(%p) TopLevel IRP for this thread: %p\n", PsGetCurrentThread(), TopLevel);
251  }
252 #endif
253 
254  /* Loop till we have jobs */
255  while (TRUE)
256  {
257  PWORK_QUEUE_ENTRY WorkItem;
258 
259  /* Lock queues */
261 
262  /* If we have to touch throttle, reset it now! */
263  if (DropThrottle)
264  {
266  DropThrottle = FALSE;
267  }
268 
269  /* Check first if we have read ahead to do */
271  {
272  /* If not, check regular queue */
274  {
275  break;
276  }
277  else
278  {
279  WorkItem = CONTAINING_RECORD(CcRegularWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
280  }
281  }
282  else
283  {
284  WorkItem = CONTAINING_RECORD(CcExpressWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
285  }
286 
287  /* Get our work item, if someone is waiting for us to finish
288  * and we're not the only thread in queue
289  * then, quit running to let the others do
290  * and throttle so that noone starts till current activity is over
291  */
292  if (WorkItem->Function == SetDone && CcNumberActiveWorkerThreads > 1)
293  {
295  break;
296  }
297 
298  /* Otherwise, remove current entry */
299  RemoveEntryList(&WorkItem->WorkQueueLinks);
301 
302  /* And handle it */
303  switch (WorkItem->Function)
304  {
305  case ReadAhead:
306  CcPerformReadAhead(WorkItem->Parameters.Read.FileObject);
307  break;
308 
309  case WriteBehind:
310  PsGetCurrentThread()->MemoryMaker = 1;
311  CcWriteBehind();
312  PsGetCurrentThread()->MemoryMaker = 0;
313  WritePerformed = TRUE;
314  break;
315 
316  case LazyScan:
317  CcLazyWriteScan();
318  break;
319 
320  case SetDone:
321  KeSetEvent(WorkItem->Parameters.Event.Event, IO_NO_INCREMENT, FALSE);
322  DropThrottle = TRUE;
323  break;
324 
325  default:
326  DPRINT1("Ignored item: %p (%d)\n", WorkItem, WorkItem->Function);
327  break;
328  }
329 
330  /* And release the item */
331  ExFreeToNPagedLookasideList(&CcTwilightLookasideList, WorkItem);
332  }
333 
334  /* Our thread is available again */
336  /* One less worker */
339 
340  /* If there are pending write openations and we have at least 20 dirty pages */
342  {
343  /* And if we performed a write operation previously, then
344  * stress the system a bit and reschedule a scan to find
345  * stuff to write
346  */
347  if (WritePerformed)
348  {
349  CcLazyWriteScan();
350  }
351  }
352 
353 #if DBG
354  /* Top level shouldn't have changed */
355  if (TopLevel != IoGetTopLevelIrp())
356  {
357  DPRINT1("(%p) Mismatching TopLevel: %p, %p\n", PsGetCurrentThread(), TopLevel, IoGetTopLevelIrp());
358  }
359 #endif
360 }
361 
362 /*
363  * @implemented
364  */
365 NTSTATUS
366 NTAPI
368  VOID)
369 {
370  KIRQL OldIrql;
371  KEVENT WaitEvent;
372  PWORK_QUEUE_ENTRY WorkItem;
373 
374  /* Allocate a work item */
375  WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
376  if (WorkItem == NULL)
377  {
379  }
380 
381  /* We want lazy writer to set our event */
382  WorkItem->Function = SetDone;
384  WorkItem->Parameters.Event.Event = &WaitEvent;
385 
386  /* Use the post tick queue */
389 
390  /* Inform the lazy writer it will have to handle the post tick queue */
392  /* And if it's not running, queue a lazy writer run
393  * And start it NOW, we want the response now
394  */
395  if (!LazyWriter.ScanActive)
396  {
398  }
399 
401 
402  /* And now, wait until lazy writer replies */
403  return KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, NULL);
404 }
VOID CcPostWorkQueue(IN PWORK_QUEUE_ENTRY WorkItem, IN PLIST_ENTRY WorkQueue)
Definition: lazywrite.c:53
ULONG CcLazyWriteIos
Definition: lazywrite.c:21
#define IN
Definition: typedefs.h:38
KEVENT * Event
Definition: cc.h:265
BOOLEAN OtherWork
Definition: cc.h:246
#define TRUE
Definition: types.h:120
VOID NTAPI ExQueueWorkItem(IN PWORK_QUEUE_ITEM WorkItem, IN WORK_QUEUE_TYPE QueueType)
Definition: work.c:717
BOOLEAN NTAPI KeSetTimer(IN OUT PKTIMER Timer, IN LARGE_INTEGER DueTime, IN PKDPC Dpc OPTIONAL)
Definition: timerobj.c:281
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
struct LOOKASIDE_ALIGN _NPAGED_LOOKASIDE_LIST NPAGED_LOOKASIDE_LIST
LIST_ENTRY List
Definition: extypes.h:203
ULONG CcNumberWorkerThreads
Definition: lazywrite.c:48
#define PsGetCurrentThread()
Definition: env_spec_w32.h:81
LONG NTSTATUS
Definition: precomp.h:26
NTSTATUS NTAPI CcWaitForCurrentLazyWriterActivity(VOID)
Definition: lazywrite.c:367
ULONG CcLazyWritePages
Definition: lazywrite.c:20
_Inout_ __drv_aliasesMem PSLIST_ENTRY _Inout_ PSLIST_ENTRY _In_ ULONG Count
Definition: exfuncs.h:1015
NTSTATUS NTAPI CcRosFlushDirtyPages(ULONG Target, PULONG Count, BOOLEAN Wait, BOOLEAN CalledFromLazy)
Definition: view.c:183
LARGE_INTEGER CcNoDelay
Definition: lazywrite.c:47
_In_ PVOID Parameter
Definition: ldrtypes.h:241
#define InsertTailList(ListHead, Entry)
LONG NTAPI KeSetEvent(IN PKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait)
Definition: eventobj.c:159
NTSTATUS NTAPI KeWaitForSingleObject(IN PVOID Object, IN KWAIT_REASON WaitReason, IN KPROCESSOR_MODE WaitMode, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL)
Definition: wait.c:416
_Must_inspect_result_ FORCEINLINE BOOLEAN IsListEmpty(_In_ const LIST_ENTRY *ListHead)
Definition: rtlfuncs.h:57
VOID CcPostDeferredWrites(VOID)
Definition: copy.c:412
IN PFCB IN PCCB IN TYPE_OF_OPEN IN BOOLEAN IN BOOLEAN TopLevel
Definition: fatprocs.h:2401
VOID CcPerformReadAhead(IN PFILE_OBJECT FileObject)
Definition: copy.c:486
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
Definition: rtlfuncs.h:105
UCHAR KIRQL
Definition: env_spec_w32.h:591
LIST_ENTRY CcIdleWorkerThreadList
Definition: lazywrite.c:41
#define RTL_CONSTANT_LARGE_INTEGER(quad_part)
Definition: rtltypes.h:414
LAZY_WRITER LazyWriter
Definition: lazywrite.c:37
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
unsigned char Function
Definition: cc.h:272
Definition: cc.h:279
VOID CcLazyWriteScan(VOID)
Definition: lazywrite.c:136
union _WORK_QUEUE_ENTRY::@1694 Parameters
KDPC ScanDpc
Definition: cc.h:243
LIST_ENTRY WorkQueue
Definition: workqueue.c:20
LIST_ENTRY CcRegularWorkQueue
Definition: lazywrite.c:40
ULONG CcNumberActiveWorkerThreads
Definition: lazywrite.c:44
unsigned char BOOLEAN
smooth NULL
Definition: ftsmooth.c:416
KTIMER ScanTimer
Definition: cc.h:244
FORCEINLINE PLIST_ENTRY RemoveHeadList(_Inout_ PLIST_ENTRY ListHead)
Definition: rtlfuncs.h:128
Definition: cc.h:280
void DPRINT(...)
Definition: polytest.cpp:61
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
_In_opt_ PVOID _In_opt_ PVOID SystemArgument1
Definition: ketypes.h:675
int64_t LONGLONG
Definition: typedefs.h:66
struct _LIST_ENTRY * Flink
Definition: typedefs.h:119
_In_ LARGE_INTEGER _In_opt_ PKDPC Dpc
Definition: kefuncs.h:524
LIST_ENTRY List
Definition: psmgr.c:57
struct _WORK_QUEUE_ENTRY::@1694::@1695 Read
LIST_ENTRY CcDeferredWrites
Definition: view.c:61
BOOLEAN ScanActive
Definition: cc.h:245
VOID FASTCALL KeReleaseQueuedSpinLock(IN KSPIN_LOCK_QUEUE_NUMBER LockNumber, IN KIRQL OldIrql)
Definition: spinlock.c:154
PIRP NTAPI IoGetTopLevelIrp(VOID)
Definition: irp.c:1843
Definition: cc.h:250
_Must_inspect_result_ typedef _In_ ULONG _In_ BOOLEAN Target
Definition: iotypes.h:1068
VOID NTAPI CcWorkerThread(IN PVOID Parameter)
Definition: lazywrite.c:226
ULONG CcTotalDirtyPages
Definition: view.c:60
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:803
Definition: ketypes.h:687
Definition: typedefs.h:117
KIRQL FASTCALL KeAcquireQueuedSpinLock(IN KSPIN_LOCK_QUEUE_NUMBER LockNumber)
Definition: spinlock.c:108
LIST_ENTRY CcExpressWorkQueue
Definition: lazywrite.c:39
VOID CcScheduleLazyWriteScan(IN BOOLEAN NoDelay)
Definition: lazywrite.c:200
_In_opt_ PVOID _In_opt_ PVOID _In_opt_ PVOID SystemArgument2
Definition: ketypes.h:675
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
#define DPRINT1
Definition: precomp.h:8
LARGE_INTEGER CcIdleDelay
Definition: lazywrite.c:46
VOID CcWriteBehind(VOID)
Definition: lazywrite.c:117
unsigned int ULONG
Definition: retypes.h:1
#define IO_NO_INCREMENT
Definition: iotypes.h:566
Definition: cc.h:277
VOID NTAPI CcScanDpc(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
Definition: lazywrite.c:95
BOOLEAN CcQueueThrottle
Definition: lazywrite.c:43
LIST_ENTRY CcPostTickWorkQueue
Definition: lazywrite.c:42
LIST_ENTRY WorkQueueLinks
Definition: cc.h:252
LARGE_INTEGER CcFirstDelay
Definition: lazywrite.c:45
NPAGED_LOOKASIDE_LIST CcTwilightLookasideList
Definition: lazywrite.c:38
_In_opt_ PVOID DeferredContext
Definition: ketypes.h:675