d6c57ab45125092445035e8ac6c3f742137d861f
[reactos.git] / reactos / ntoskrnl / cc / view.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/cc/view.c
6 * PURPOSE: Cache manager
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 */
10
11 /* NOTES **********************************************************************
12 *
13 * This is not the NT implementation of a file cache nor anything much like
14 * it.
15 *
16 * The general procedure for a filesystem to implement a read or write
17 * dispatch routine is as follows
18 *
19 * (1) If caching for the FCB hasn't been initiated then so do by calling
20 * CcInitializeFileCache.
21 *
22 * (2) For each 4k region which is being read or written obtain a cache page
23 * by calling CcRequestCachePage.
24 *
25 * (3) If either the page is being read or not completely written, and it is
26 * not up to date then read its data from the underlying medium. If the read
27 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
28 *
29 * (4) Copy the data into or out of the page as necessary.
30 *
31 * (5) Release the cache page
32 */
33 /* INCLUDES ******************************************************************/
34
35 #include <ntoskrnl.h>
36 #define NDEBUG
37 #include <internal/debug.h>
38
39 /* GLOBALS *******************************************************************/
40
41 /*
42 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
43 * within the kernel address space and allocate/deallocate space from this block
44 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
45 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
46 */
47 //#define CACHE_BITMAP
48
49 static LIST_ENTRY DirtySegmentListHead;
50 static LIST_ENTRY CacheSegmentListHead;
51 static LIST_ENTRY CacheSegmentLRUListHead;
52 static LIST_ENTRY ClosedListHead;
53 ULONG DirtyPageCount=0;
54
55 FAST_MUTEX ViewLock;
56
57 #ifdef CACHE_BITMAP
58 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
59
60 static PVOID CiCacheSegMappingRegionBase = NULL;
61 static RTL_BITMAP CiCacheSegMappingRegionAllocMap;
62 static ULONG CiCacheSegMappingRegionHint;
63 static KSPIN_LOCK CiCacheSegMappingRegionLock;
64 #endif
65
66 NPAGED_LOOKASIDE_LIST iBcbLookasideList;
67 static NPAGED_LOOKASIDE_LIST BcbLookasideList;
68 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList;
69
70 static ULONG CcTimeStamp;
71 static KEVENT LazyCloseThreadEvent;
72 static HANDLE LazyCloseThreadHandle;
73 static CLIENT_ID LazyCloseThreadId;
74 static volatile BOOLEAN LazyCloseThreadShouldTerminate;
75
76 #if defined(__GNUC__)
77 /* void * alloca(size_t size); */
78 #elif defined(_MSC_VER)
79 void* _alloca(size_t size);
80 #else
81 #error Unknown compiler for alloca intrinsic stack allocation "function"
82 #endif
83
84
85 NTSTATUS
86 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg);
87
88 /* FUNCTIONS *****************************************************************/
89
90 NTSTATUS
91 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
92 {
93 NTSTATUS Status;
94 KIRQL oldIrql;
95 Status = WriteCacheSegment(CacheSegment);
96 if (NT_SUCCESS(Status))
97 {
98 ExAcquireFastMutex(&ViewLock);
99 KeAcquireSpinLock(&CacheSegment->Bcb->BcbLock, &oldIrql);
100 CacheSegment->Dirty = FALSE;
101 RemoveEntryList(&CacheSegment->DirtySegmentListEntry);
102 DirtyPageCount -= CacheSegment->Bcb->CacheSegmentSize / PAGE_SIZE;
103 CacheSegment->ReferenceCount--;
104 KeReleaseSpinLock(&CacheSegment->Bcb->BcbLock, oldIrql);
105 ExReleaseFastMutex(&ViewLock);
106 }
107 return(Status);
108 }
109
110 NTSTATUS
111 CcRosFlushDirtyPages(ULONG Target, PULONG Count)
112 {
113 PLIST_ENTRY current_entry;
114 PCACHE_SEGMENT current;
115 ULONG PagesPerSegment;
116 BOOLEAN Locked;
117 NTSTATUS Status;
118 static ULONG WriteCount[4] = {0, 0, 0, 0};
119 ULONG NewTarget;
120
121 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target);
122
123 (*Count) = 0;
124
125 ExAcquireFastMutex(&ViewLock);
126
127 WriteCount[0] = WriteCount[1];
128 WriteCount[1] = WriteCount[2];
129 WriteCount[2] = WriteCount[3];
130 WriteCount[3] = 0;
131
132 NewTarget = WriteCount[0] + WriteCount[1] + WriteCount[2];
133
134 if (NewTarget < DirtyPageCount)
135 {
136 NewTarget = (DirtyPageCount - NewTarget + 3) / 4;
137 WriteCount[0] += NewTarget;
138 WriteCount[1] += NewTarget;
139 WriteCount[2] += NewTarget;
140 WriteCount[3] += NewTarget;
141 }
142
143 NewTarget = WriteCount[0];
144
145 Target = max(NewTarget, Target);
146
147 current_entry = DirtySegmentListHead.Flink;
148 if (current_entry == &DirtySegmentListHead)
149 {
150 DPRINT("No Dirty pages\n");
151 }
152 while (current_entry != &DirtySegmentListHead && Target > 0)
153 {
154 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
155 DirtySegmentListEntry);
156 current_entry = current_entry->Flink;
157 Locked = ExTryToAcquireFastMutex(&current->Lock);
158 if (!Locked)
159 {
160 continue;
161 }
162 ASSERT(current->Dirty);
163 if (current->ReferenceCount > 1)
164 {
165 ExReleaseFastMutex(&current->Lock);
166 continue;
167 }
168 ExReleaseFastMutex(&ViewLock);
169 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
170 Status = CcRosFlushCacheSegment(current);
171 ExReleaseFastMutex(&current->Lock);
172 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
173 {
174 DPRINT1("CC: Failed to flush cache segment.\n");
175 }
176 else
177 {
178 (*Count) += PagesPerSegment;
179 Target -= PagesPerSegment;
180 }
181 ExAcquireFastMutex(&ViewLock);
182 current_entry = DirtySegmentListHead.Flink;
183 }
184 if (*Count < NewTarget)
185 {
186 WriteCount[1] += (NewTarget - *Count);
187 }
188 ExReleaseFastMutex(&ViewLock);
189 DPRINT("CcRosFlushDirtyPages() finished\n");
190
191 return(STATUS_SUCCESS);
192 }
193
194 NTSTATUS
195 CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
196 /*
197 * FUNCTION: Try to free some memory from the file cache.
198 * ARGUMENTS:
199 * Target - The number of pages to be freed.
200 * Priority - The priority of free (currently unused).
201 * NrFreed - Points to a variable where the number of pages
202 * actually freed is returned.
203 */
204 {
205 PLIST_ENTRY current_entry;
206 PCACHE_SEGMENT current, last = NULL;
207 ULONG PagesPerSegment;
208 ULONG PagesFreed;
209 KIRQL oldIrql;
210 LIST_ENTRY FreeList;
211
212 DPRINT("CcRosTrimCache(Target %d)\n", Target);
213
214 *NrFreed = 0;
215
216 InitializeListHead(&FreeList);
217
218 ExAcquireFastMutex(&ViewLock);
219 current_entry = CacheSegmentLRUListHead.Flink;
220 while (current_entry != &CacheSegmentLRUListHead && Target > 0)
221 {
222 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
223 CacheSegmentLRUListEntry);
224 current_entry = current_entry->Flink;
225
226 KeAcquireSpinLock(&current->Bcb->BcbLock, &oldIrql);
227 if (current->ReferenceCount == 0)
228 {
229 RemoveEntryList(&current->BcbSegmentListEntry);
230 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
231 RemoveEntryList(&current->CacheSegmentListEntry);
232 RemoveEntryList(&current->CacheSegmentLRUListEntry);
233 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
234 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
235 PagesFreed = min(PagesPerSegment, Target);
236 Target -= PagesFreed;
237 (*NrFreed) += PagesFreed;
238 }
239 else
240 {
241 if (last != current && current->MappedCount > 0 && !current->Dirty && !current->PageOut)
242 {
243 ULONG i;
244 NTSTATUS Status;
245
246 current->ReferenceCount++;
247 last = current;
248 current->PageOut = TRUE;
249 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
250 ExReleaseFastMutex(&ViewLock);
251 for (i = 0; i < current->Bcb->CacheSegmentSize / PAGE_SIZE; i++)
252 {
253 PFN_TYPE Page;
254 Page = MmGetPhysicalAddress((char*)current->BaseAddress + i * PAGE_SIZE).QuadPart >> PAGE_SHIFT;
255 Status = MmPageOutPhysicalAddress(Page);
256 if (!NT_SUCCESS(Status))
257 {
258 break;
259 }
260 }
261 ExAcquireFastMutex(&ViewLock);
262 KeAcquireSpinLock(&current->Bcb->BcbLock, &oldIrql);
263 current->ReferenceCount--;
264 current->PageOut = FALSE;
265 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
266 current_entry = &current->CacheSegmentLRUListEntry;
267 continue;
268 }
269 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
270 }
271 }
272 ExReleaseFastMutex(&ViewLock);
273
274 while (!IsListEmpty(&FreeList))
275 {
276 current_entry = RemoveHeadList(&FreeList);
277 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
278 BcbSegmentListEntry);
279 CcRosInternalFreeCacheSegment(current);
280 }
281
282 DPRINT("CcRosTrimCache() finished\n");
283 return(STATUS_SUCCESS);
284 }
285
286 NTSTATUS
287 CcRosReleaseCacheSegment(PBCB Bcb,
288 PCACHE_SEGMENT CacheSeg,
289 BOOLEAN Valid,
290 BOOLEAN Dirty,
291 BOOLEAN Mapped)
292 {
293 BOOLEAN WasDirty = CacheSeg->Dirty;
294 KIRQL oldIrql;
295
296 ASSERT(Bcb);
297
298 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
299 Bcb, CacheSeg, Valid);
300
301 CacheSeg->Valid = Valid;
302 CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
303
304 ExAcquireFastMutex(&ViewLock);
305 if (!WasDirty && CacheSeg->Dirty)
306 {
307 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
308 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
309 }
310 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
311 InsertTailList(&CacheSegmentLRUListHead, &CacheSeg->CacheSegmentLRUListEntry);
312
313 if (Mapped)
314 {
315 CacheSeg->MappedCount++;
316 }
317 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
318 CacheSeg->ReferenceCount--;
319 if (Mapped && CacheSeg->MappedCount == 1)
320 {
321 CacheSeg->ReferenceCount++;
322 }
323 if (!WasDirty && CacheSeg->Dirty)
324 {
325 CacheSeg->ReferenceCount++;
326 }
327 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
328 ExReleaseFastMutex(&ViewLock);
329 ExReleaseFastMutex(&CacheSeg->Lock);
330
331 return(STATUS_SUCCESS);
332 }
333
334 PCACHE_SEGMENT
335 CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
336 {
337 PLIST_ENTRY current_entry;
338 PCACHE_SEGMENT current;
339 KIRQL oldIrql;
340
341 ASSERT(Bcb);
342
343 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
344
345 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
346 current_entry = Bcb->BcbSegmentListHead.Flink;
347 while (current_entry != &Bcb->BcbSegmentListHead)
348 {
349 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
350 BcbSegmentListEntry);
351 if (current->FileOffset <= FileOffset &&
352 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
353 {
354 current->ReferenceCount++;
355 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
356 ExAcquireFastMutex(&current->Lock);
357 return(current);
358 }
359 current_entry = current_entry->Flink;
360 }
361 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
362 return(NULL);
363 }
364
365 NTSTATUS
366 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
367 {
368 PCACHE_SEGMENT CacheSeg;
369 KIRQL oldIrql;
370
371 ASSERT(Bcb);
372
373 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
374
375 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
376 if (CacheSeg == NULL)
377 {
378 KEBUGCHECKCC;
379 }
380 if (!CacheSeg->Dirty)
381 {
382 ExAcquireFastMutex(&ViewLock);
383 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
384 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
385 ExReleaseFastMutex(&ViewLock);
386 }
387 else
388 {
389 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
390 CacheSeg->ReferenceCount--;
391 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
392 }
393
394
395 CacheSeg->Dirty = TRUE;
396 ExReleaseFastMutex(&CacheSeg->Lock);
397
398 return(STATUS_SUCCESS);
399 }
400
401 NTSTATUS
402 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
403 {
404 PCACHE_SEGMENT CacheSeg;
405 BOOLEAN WasDirty;
406 KIRQL oldIrql;
407
408 ASSERT(Bcb);
409
410 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
411 Bcb, FileOffset, NowDirty);
412
413 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
414 if (CacheSeg == NULL)
415 {
416 return(STATUS_UNSUCCESSFUL);
417 }
418
419 WasDirty = CacheSeg->Dirty;
420 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
421
422 CacheSeg->MappedCount--;
423
424 if (!WasDirty && NowDirty)
425 {
426 ExAcquireFastMutex(&ViewLock);
427 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
428 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
429 ExReleaseFastMutex(&ViewLock);
430 }
431
432 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
433 CacheSeg->ReferenceCount--;
434 if (!WasDirty && NowDirty)
435 {
436 CacheSeg->ReferenceCount++;
437 }
438 if (CacheSeg->MappedCount == 0)
439 {
440 CacheSeg->ReferenceCount--;
441 }
442 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
443
444 ExReleaseFastMutex(&CacheSeg->Lock);
445 return(STATUS_SUCCESS);
446 }
447
448 NTSTATUS STATIC
449 CcRosCreateCacheSegment(PBCB Bcb,
450 ULONG FileOffset,
451 PCACHE_SEGMENT* CacheSeg)
452 {
453 ULONG i;
454 PCACHE_SEGMENT current;
455 PCACHE_SEGMENT previous;
456 PLIST_ENTRY current_entry;
457 NTSTATUS Status;
458 KIRQL oldIrql;
459 PPFN_TYPE Pfn;
460 #ifdef CACHE_BITMAP
461 ULONG StartingOffset;
462 #else
463 #endif
464 PHYSICAL_ADDRESS BoundaryAddressMultiple;
465
466 ASSERT(Bcb);
467
468 DPRINT("CcRosCreateCacheSegment()\n");
469
470 BoundaryAddressMultiple.QuadPart = 0;
471 if (FileOffset >= Bcb->FileSize.u.LowPart)
472 {
473 CacheSeg = NULL;
474 return STATUS_INVALID_PARAMETER;
475 }
476
477 current = ExAllocateFromNPagedLookasideList(&CacheSegLookasideList);
478 current->Valid = FALSE;
479 current->Dirty = FALSE;
480 current->PageOut = FALSE;
481 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
482 current->Bcb = Bcb;
483 current->MappedCount = 0;
484 current->DirtySegmentListEntry.Flink = NULL;
485 current->DirtySegmentListEntry.Blink = NULL;
486 current->ReferenceCount = 1;
487 ExInitializeFastMutex(&current->Lock);
488 ExAcquireFastMutex(&current->Lock);
489 ExAcquireFastMutex(&ViewLock);
490
491 *CacheSeg = current;
492 /* There is window between the call to CcRosLookupCacheSegment
493 * and CcRosCreateCacheSegment. We must check if a segment on
494 * the fileoffset exist. If there exist a segment, we release
495 * our new created segment and return the existing one.
496 */
497 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
498 current_entry = Bcb->BcbSegmentListHead.Flink;
499 previous = NULL;
500 while (current_entry != &Bcb->BcbSegmentListHead)
501 {
502 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
503 BcbSegmentListEntry);
504 if (current->FileOffset <= FileOffset &&
505 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
506 {
507 current->ReferenceCount++;
508 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
509 ExReleaseFastMutex(&(*CacheSeg)->Lock);
510 ExReleaseFastMutex(&ViewLock);
511 ExFreeToNPagedLookasideList(&CacheSegLookasideList, *CacheSeg);
512 *CacheSeg = current;
513 ExAcquireFastMutex(&current->Lock);
514 return STATUS_SUCCESS;
515 }
516 if (current->FileOffset < FileOffset)
517 {
518 if (previous == NULL)
519 {
520 previous = current;
521 }
522 else
523 {
524 if (previous->FileOffset < current->FileOffset)
525 {
526 previous = current;
527 }
528 }
529 }
530 current_entry = current_entry->Flink;
531 }
532 /* There was no existing segment. */
533 current = *CacheSeg;
534 if (previous)
535 {
536 InsertHeadList(&previous->BcbSegmentListEntry, &current->BcbSegmentListEntry);
537 }
538 else
539 {
540 InsertHeadList(&Bcb->BcbSegmentListHead, &current->BcbSegmentListEntry);
541 }
542 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
543 InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
544 InsertTailList(&CacheSegmentLRUListHead, &current->CacheSegmentLRUListEntry);
545 ExReleaseFastMutex(&ViewLock);
546 #ifdef CACHE_BITMAP
547 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
548
549 StartingOffset = RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap, Bcb->CacheSegmentSize / PAGE_SIZE, CiCacheSegMappingRegionHint);
550
551 if (StartingOffset == 0xffffffff)
552 {
553 DPRINT1("Out of CacheSeg mapping space\n");
554 KEBUGCHECKCC;
555 }
556
557 current->BaseAddress = CiCacheSegMappingRegionBase + StartingOffset * PAGE_SIZE;
558
559 if (CiCacheSegMappingRegionHint == StartingOffset)
560 {
561 CiCacheSegMappingRegionHint += Bcb->CacheSegmentSize / PAGE_SIZE;
562 }
563
564 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
565 #else
566 MmLockAddressSpace(MmGetKernelAddressSpace());
567 current->BaseAddress = NULL;
568 Status = MmCreateMemoryArea(NULL,
569 MmGetKernelAddressSpace(),
570 MEMORY_AREA_CACHE_SEGMENT,
571 &current->BaseAddress,
572 Bcb->CacheSegmentSize,
573 PAGE_READWRITE,
574 (PMEMORY_AREA*)&current->MemoryArea,
575 FALSE,
576 FALSE,
577 BoundaryAddressMultiple);
578 MmUnlockAddressSpace(MmGetKernelAddressSpace());
579 if (!NT_SUCCESS(Status))
580 {
581 KEBUGCHECKCC;
582 }
583 #endif
584 Pfn = alloca(sizeof(PFN_TYPE) * (Bcb->CacheSegmentSize / PAGE_SIZE));
585 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
586 {
587 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Pfn[i]);
588 if (!NT_SUCCESS(Status))
589 {
590 KEBUGCHECKCC;
591 }
592 }
593 Status = MmCreateVirtualMapping(NULL,
594 current->BaseAddress,
595 PAGE_READWRITE,
596 Pfn,
597 Bcb->CacheSegmentSize / PAGE_SIZE);
598 if (!NT_SUCCESS(Status))
599 {
600 KEBUGCHECKCC;
601 }
602 return(STATUS_SUCCESS);
603 }
604
605 NTSTATUS
606 CcRosGetCacheSegmentChain(PBCB Bcb,
607 ULONG FileOffset,
608 ULONG Length,
609 PCACHE_SEGMENT* CacheSeg)
610 {
611 PCACHE_SEGMENT current;
612 ULONG i;
613 PCACHE_SEGMENT* CacheSegList;
614 PCACHE_SEGMENT Previous = NULL;
615
616 ASSERT(Bcb);
617
618 DPRINT("CcRosGetCacheSegmentChain()\n");
619
620 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
621
622 #if defined(__GNUC__)
623 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
624 (Length / Bcb->CacheSegmentSize));
625 #elif defined(_MSC_VER)
626 CacheSegList = _alloca(sizeof(PCACHE_SEGMENT) *
627 (Length / Bcb->CacheSegmentSize));
628 #else
629 #error Unknown compiler for alloca intrinsic stack allocation "function"
630 #endif
631
632 /*
633 * Look for a cache segment already mapping the same data.
634 */
635 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
636 {
637 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
638 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
639 if (current != NULL)
640 {
641 CacheSegList[i] = current;
642 }
643 else
644 {
645 CcRosCreateCacheSegment(Bcb, CurrentOffset, &current);
646 CacheSegList[i] = current;
647 }
648 }
649
650 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
651 {
652 if (i == 0)
653 {
654 *CacheSeg = CacheSegList[i];
655 Previous = CacheSegList[i];
656 }
657 else
658 {
659 Previous->NextInChain = CacheSegList[i];
660 Previous = CacheSegList[i];
661 }
662 }
663 Previous->NextInChain = NULL;
664
665 return(STATUS_SUCCESS);
666 }
667
668 NTSTATUS
669 CcRosGetCacheSegment(PBCB Bcb,
670 ULONG FileOffset,
671 PULONG BaseOffset,
672 PVOID* BaseAddress,
673 PBOOLEAN UptoDate,
674 PCACHE_SEGMENT* CacheSeg)
675 {
676 PCACHE_SEGMENT current;
677 NTSTATUS Status;
678
679 ASSERT(Bcb);
680
681 DPRINT("CcRosGetCacheSegment()\n");
682
683 /*
684 * Look for a cache segment already mapping the same data.
685 */
686 current = CcRosLookupCacheSegment(Bcb, FileOffset);
687 if (current == NULL)
688 {
689 /*
690 * Otherwise create a new segment.
691 */
692 Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current);
693 if (!NT_SUCCESS(Status))
694 {
695 return Status;
696 }
697 }
698 /*
699 * Return information about the segment to the caller.
700 */
701 *UptoDate = current->Valid;
702 *BaseAddress = current->BaseAddress;
703 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
704 *CacheSeg = current;
705 *BaseOffset = current->FileOffset;
706 return(STATUS_SUCCESS);
707 }
708
709 NTSTATUS STDCALL
710 CcRosRequestCacheSegment(PBCB Bcb,
711 ULONG FileOffset,
712 PVOID* BaseAddress,
713 PBOOLEAN UptoDate,
714 PCACHE_SEGMENT* CacheSeg)
715 /*
716 * FUNCTION: Request a page mapping for a BCB
717 */
718 {
719 ULONG BaseOffset;
720
721 ASSERT(Bcb);
722
723 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
724 {
725 CPRINT("Bad fileoffset %x should be multiple of %x",
726 FileOffset, Bcb->CacheSegmentSize);
727 KEBUGCHECKCC;
728 }
729
730 return(CcRosGetCacheSegment(Bcb,
731 FileOffset,
732 &BaseOffset,
733 BaseAddress,
734 UptoDate,
735 CacheSeg));
736 }
737 #ifdef CACHE_BITMAP
738 #else
739 STATIC VOID
740 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
741 PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
742 {
743 ASSERT(SwapEntry == 0);
744 if (Page != 0)
745 {
746 MmReleasePageMemoryConsumer(MC_CACHE, Page);
747 }
748 }
749 #endif
750 NTSTATUS
751 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
752 /*
753 * FUNCTION: Releases a cache segment associated with a BCB
754 */
755 {
756 #ifdef CACHE_BITMAP
757 ULONG i;
758 ULONG RegionSize;
759 ULONG Base;
760 PFN_TYPE Page;
761 KIRQL oldIrql;
762 #endif
763 DPRINT("Freeing cache segment %x\n", CacheSeg);
764 #ifdef CACHE_BITMAP
765 RegionSize = CacheSeg->Bcb->CacheSegmentSize / PAGE_SIZE;
766
767 /* Unmap all the pages. */
768 for (i = 0; i < RegionSize; i++)
769 {
770 MmDeleteVirtualMapping(NULL,
771 CacheSeg->BaseAddress + (i * PAGE_SIZE),
772 FALSE,
773 NULL,
774 &Page);
775 MmReleasePageMemoryConsumer(MC_CACHE, Page);
776 }
777
778 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
779 /* Deallocate all the pages used. */
780 Base = (ULONG)(CacheSeg->BaseAddress - CiCacheSegMappingRegionBase) / PAGE_SIZE;
781
782 RtlClearBits(&CiCacheSegMappingRegionAllocMap, Base, RegionSize);
783
784 CiCacheSegMappingRegionHint = min (CiCacheSegMappingRegionHint, Base);
785
786 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
787 #else
788 MmLockAddressSpace(MmGetKernelAddressSpace());
789 MmFreeMemoryArea(MmGetKernelAddressSpace(),
790 CacheSeg->MemoryArea,
791 CcFreeCachePage,
792 NULL);
793 MmUnlockAddressSpace(MmGetKernelAddressSpace());
794 #endif
795 ExFreeToNPagedLookasideList(&CacheSegLookasideList, CacheSeg);
796 return(STATUS_SUCCESS);
797 }
798
799 NTSTATUS
800 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
801 {
802 NTSTATUS Status;
803 KIRQL oldIrql;
804
805 ASSERT(Bcb);
806
807 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
808 Bcb, CacheSeg);
809
810 ExAcquireFastMutex(&ViewLock);
811 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
812 RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
813 RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
814 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
815 if (CacheSeg->Dirty)
816 {
817 RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
818 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
819
820 }
821 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
822 ExReleaseFastMutex(&ViewLock);
823
824 Status = CcRosInternalFreeCacheSegment(CacheSeg);
825 return(Status);
826 }
827
828 /*
829 * @implemented
830 */
831 VOID STDCALL
832 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
833 IN PLARGE_INTEGER FileOffset OPTIONAL,
834 IN ULONG Length,
835 OUT PIO_STATUS_BLOCK IoStatus)
836 {
837 PBCB Bcb;
838 LARGE_INTEGER Offset;
839 PCACHE_SEGMENT current;
840 NTSTATUS Status;
841 KIRQL oldIrql;
842
843 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
844 SectionObjectPointers, FileOffset, Length, IoStatus);
845
846 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
847 {
848 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
849 ASSERT(Bcb);
850 if (FileOffset)
851 {
852 Offset = *FileOffset;
853 }
854 else
855 {
856 Offset.QuadPart = (LONGLONG)0;
857 Length = Bcb->FileSize.u.LowPart;
858 }
859
860 if (IoStatus)
861 {
862 IoStatus->Status = STATUS_SUCCESS;
863 IoStatus->Information = 0;
864 }
865
866 while (Length > 0)
867 {
868 current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
869 if (current != NULL)
870 {
871 if (current->Dirty)
872 {
873 Status = CcRosFlushCacheSegment(current);
874 if (!NT_SUCCESS(Status) && IoStatus != NULL)
875 {
876 IoStatus->Status = Status;
877 }
878 }
879 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
880 ExReleaseFastMutex(&current->Lock);
881 current->ReferenceCount--;
882 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
883 }
884
885 Offset.QuadPart += Bcb->CacheSegmentSize;
886 if (Length > Bcb->CacheSegmentSize)
887 {
888 Length -= Bcb->CacheSegmentSize;
889 }
890 else
891 {
892 Length = 0;
893 }
894 }
895 }
896 else
897 {
898 if (IoStatus)
899 {
900 IoStatus->Status = STATUS_INVALID_PARAMETER;
901 }
902 }
903 }
904
905 NTSTATUS
906 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
907 /*
908 * FUNCTION: Releases the BCB associated with a file object
909 */
910 {
911 PLIST_ENTRY current_entry;
912 PCACHE_SEGMENT current;
913 NTSTATUS Status;
914 LIST_ENTRY FreeList;
915 KIRQL oldIrql;
916
917 ASSERT(Bcb);
918
919 Bcb->RefCount++;
920 ExReleaseFastMutex(&ViewLock);
921
922 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
923
924 ExAcquireFastMutex(&ViewLock);
925 Bcb->RefCount--;
926 if (Bcb->RefCount == 0)
927 {
928 if (Bcb->BcbRemoveListEntry.Flink != NULL)
929 {
930 RemoveEntryList(&Bcb->BcbRemoveListEntry);
931 Bcb->BcbRemoveListEntry.Flink = NULL;
932 }
933
934 FileObject->SectionObjectPointer->SharedCacheMap = NULL;
935
936 /*
937 * Release all cache segments.
938 */
939 InitializeListHead(&FreeList);
940 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
941 current_entry = Bcb->BcbSegmentListHead.Flink;
942 while (!IsListEmpty(&Bcb->BcbSegmentListHead))
943 {
944 current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
945 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
946 RemoveEntryList(&current->CacheSegmentListEntry);
947 RemoveEntryList(&current->CacheSegmentLRUListEntry);
948 if (current->Dirty)
949 {
950 RemoveEntryList(&current->DirtySegmentListEntry);
951 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
952 DPRINT1("Freeing dirty segment\n");
953 }
954 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
955 }
956 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
957
958 ExReleaseFastMutex(&ViewLock);
959 ObDereferenceObject (Bcb->FileObject);
960
961 while (!IsListEmpty(&FreeList))
962 {
963 current_entry = RemoveTailList(&FreeList);
964 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
965 Status = CcRosInternalFreeCacheSegment(current);
966 }
967 ExFreeToNPagedLookasideList(&BcbLookasideList, Bcb);
968 ExAcquireFastMutex(&ViewLock);
969 }
970 return(STATUS_SUCCESS);
971 }
972
973 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
974 {
975 PBCB Bcb;
976 ExAcquireFastMutex(&ViewLock);
977 Bcb = (PBCB)FileObject->SectionObjectPointer->SharedCacheMap;
978 ASSERT(Bcb);
979 if (Bcb->RefCount == 0)
980 {
981 ASSERT(Bcb->BcbRemoveListEntry.Flink != NULL);
982 RemoveEntryList(&Bcb->BcbRemoveListEntry);
983 Bcb->BcbRemoveListEntry.Flink = NULL;
984
985 }
986 else
987 {
988 ASSERT(Bcb->BcbRemoveListEntry.Flink == NULL);
989 }
990 Bcb->RefCount++;
991 ExReleaseFastMutex(&ViewLock);
992 }
993
994 VOID CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer)
995 {
996 PBCB Bcb;
997 DPRINT("CcRosSetRemoveOnClose()\n");
998 ExAcquireFastMutex(&ViewLock);
999 Bcb = (PBCB)SectionObjectPointer->SharedCacheMap;
1000 if (Bcb)
1001 {
1002 Bcb->RemoveOnClose = TRUE;
1003 if (Bcb->RefCount == 0)
1004 {
1005 CcRosDeleteFileCache(Bcb->FileObject, Bcb);
1006 }
1007 }
1008 ExReleaseFastMutex(&ViewLock);
1009 }
1010
1011
1012 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
1013 {
1014 PBCB Bcb;
1015 ExAcquireFastMutex(&ViewLock);
1016 Bcb = (PBCB)FileObject->SectionObjectPointer->SharedCacheMap;
1017 ASSERT(Bcb);
1018 if (Bcb->RefCount > 0)
1019 {
1020 Bcb->RefCount--;
1021 if (Bcb->RefCount == 0)
1022 {
1023 MmFreeSectionSegments(Bcb->FileObject);
1024 if (Bcb->RemoveOnClose)
1025 {
1026 CcRosDeleteFileCache(FileObject, Bcb);
1027 }
1028 else
1029 {
1030 Bcb->TimeStamp = CcTimeStamp;
1031 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1032 }
1033 }
1034 }
1035 ExReleaseFastMutex(&ViewLock);
1036 }
1037
1038 NTSTATUS STDCALL
1039 CcRosReleaseFileCache(PFILE_OBJECT FileObject)
1040 /*
1041 * FUNCTION: Called by the file system when a handle to a file object
1042 * has been closed.
1043 */
1044 {
1045 PBCB Bcb;
1046
1047 ExAcquireFastMutex(&ViewLock);
1048
1049 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL)
1050 {
1051 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1052 if (FileObject->PrivateCacheMap != NULL)
1053 {
1054 FileObject->PrivateCacheMap = NULL;
1055 if (Bcb->RefCount > 0)
1056 {
1057 Bcb->RefCount--;
1058 if (Bcb->RefCount == 0)
1059 {
1060 MmFreeSectionSegments(Bcb->FileObject);
1061 if (Bcb->RemoveOnClose)
1062 {
1063 CcRosDeleteFileCache(FileObject, Bcb);
1064 }
1065 else
1066 {
1067 Bcb->TimeStamp = CcTimeStamp;
1068 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1069 }
1070 }
1071 }
1072 }
1073 }
1074 ExReleaseFastMutex(&ViewLock);
1075 return(STATUS_SUCCESS);
1076 }
1077
1078 NTSTATUS
1079 CcTryToInitializeFileCache(PFILE_OBJECT FileObject)
1080 {
1081 PBCB Bcb;
1082 NTSTATUS Status;
1083
1084 ExAcquireFastMutex(&ViewLock);
1085
1086 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1087 if (Bcb == NULL)
1088 {
1089 Status = STATUS_UNSUCCESSFUL;
1090 }
1091 else
1092 {
1093 if (FileObject->PrivateCacheMap == NULL)
1094 {
1095 FileObject->PrivateCacheMap = Bcb;
1096 Bcb->RefCount++;
1097 }
1098 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1099 {
1100 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1101 Bcb->BcbRemoveListEntry.Flink = NULL;
1102 }
1103 Status = STATUS_SUCCESS;
1104 }
1105 ExReleaseFastMutex(&ViewLock);
1106
1107 return Status;
1108 }
1109
1110
1111 NTSTATUS STDCALL
1112 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
1113 ULONG CacheSegmentSize)
1114 /*
1115 * FUNCTION: Initializes a BCB for a file object
1116 */
1117 {
1118 PBCB Bcb;
1119
1120 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1121 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1122 FileObject, Bcb, CacheSegmentSize);
1123
1124 ExAcquireFastMutex(&ViewLock);
1125 if (Bcb == NULL)
1126 {
1127 Bcb = ExAllocateFromNPagedLookasideList(&BcbLookasideList);
1128 if (Bcb == NULL)
1129 {
1130 ExReleaseFastMutex(&ViewLock);
1131 return(STATUS_UNSUCCESSFUL);
1132 }
1133 memset(Bcb, 0, sizeof(BCB));
1134 ObReferenceObjectByPointer(FileObject,
1135 FILE_ALL_ACCESS,
1136 NULL,
1137 KernelMode);
1138 Bcb->FileObject = FileObject;
1139 Bcb->CacheSegmentSize = CacheSegmentSize;
1140 if (FileObject->FsContext)
1141 {
1142 Bcb->AllocationSize =
1143 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->AllocationSize;
1144 Bcb->FileSize =
1145 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize;
1146 }
1147 KeInitializeSpinLock(&Bcb->BcbLock);
1148 InitializeListHead(&Bcb->BcbSegmentListHead);
1149 FileObject->SectionObjectPointer->SharedCacheMap = Bcb;
1150 }
1151 if (FileObject->PrivateCacheMap == NULL)
1152 {
1153 FileObject->PrivateCacheMap = Bcb;
1154 Bcb->RefCount++;
1155 }
1156 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1157 {
1158 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1159 Bcb->BcbRemoveListEntry.Flink = NULL;
1160 }
1161 ExReleaseFastMutex(&ViewLock);
1162
1163 return(STATUS_SUCCESS);
1164 }
1165
1166 /*
1167 * @implemented
1168 */
1169 PFILE_OBJECT STDCALL
1170 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
1171 {
1172 PBCB Bcb;
1173 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
1174 {
1175 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
1176 ASSERT(Bcb);
1177 return Bcb->FileObject;
1178 }
1179 return NULL;
1180 }
1181
1182 VOID STDCALL
1183 CmLazyCloseThreadMain(PVOID Ignored)
1184 {
1185 LARGE_INTEGER Timeout;
1186 PLIST_ENTRY current_entry;
1187 PBCB current;
1188 ULONG RemoveTimeStamp;
1189 NTSTATUS Status;
1190
1191 KeQuerySystemTime (&Timeout);
1192
1193 while (1)
1194 {
1195 Timeout.QuadPart += (LONGLONG)100000000; // 10sec
1196 Status = KeWaitForSingleObject(&LazyCloseThreadEvent,
1197 0,
1198 KernelMode,
1199 FALSE,
1200 &Timeout);
1201
1202 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp);
1203
1204 if (!NT_SUCCESS(Status))
1205 {
1206 DbgPrint("LazyCloseThread: Wait failed\n");
1207 KEBUGCHECKCC;
1208 break;
1209 }
1210 if (LazyCloseThreadShouldTerminate)
1211 {
1212 DbgPrint("LazyCloseThread: Terminating\n");
1213 break;
1214 }
1215
1216 ExAcquireFastMutex(&ViewLock);
1217 CcTimeStamp++;
1218 if (CcTimeStamp >= 30)
1219 {
1220 RemoveTimeStamp = CcTimeStamp - 30; /* 5min = 10sec * 30 */
1221 while (!IsListEmpty(&ClosedListHead))
1222 {
1223 current_entry = ClosedListHead.Blink;
1224 current = CONTAINING_RECORD(current_entry, BCB, BcbRemoveListEntry);
1225 if (current->TimeStamp >= RemoveTimeStamp)
1226 {
1227 break;
1228 }
1229 CcRosDeleteFileCache(current->FileObject, current);
1230 }
1231 }
1232 ExReleaseFastMutex(&ViewLock);
1233 }
1234 }
1235
1236 VOID INIT_FUNCTION
1237 CcInitView(VOID)
1238 {
1239 #ifdef CACHE_BITMAP
1240 PMEMORY_AREA marea;
1241 PVOID Buffer;
1242 PHYSICAL_ADDRESS BoundaryAddressMultiple;
1243 #endif
1244 NTSTATUS Status;
1245 KPRIORITY Priority;
1246
1247 DPRINT("CcInitView()\n");
1248 #ifdef CACHE_BITMAP
1249 BoundaryAddressMultiple.QuadPart = 0;
1250 CiCacheSegMappingRegionHint = 0;
1251 CiCacheSegMappingRegionBase = NULL;
1252
1253 MmLockAddressSpace(MmGetKernelAddressSpace());
1254
1255 Status = MmCreateMemoryArea(NULL,
1256 MmGetKernelAddressSpace(),
1257 MEMORY_AREA_CACHE_SEGMENT,
1258 &CiCacheSegMappingRegionBase,
1259 CI_CACHESEG_MAPPING_REGION_SIZE,
1260 0,
1261 &marea,
1262 FALSE,
1263 FALSE,
1264 BoundaryAddressMultiple);
1265 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1266 if (!NT_SUCCESS(Status))
1267 {
1268 KEBUGCHECKCC;
1269 }
1270
1271 Buffer = ExAllocatePool(NonPagedPool, CI_CACHESEG_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
1272
1273 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap, Buffer, CI_CACHESEG_MAPPING_REGION_SIZE / PAGE_SIZE);
1274 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap);
1275
1276 KeInitializeSpinLock(&CiCacheSegMappingRegionLock);
1277 #endif
1278 InitializeListHead(&CacheSegmentListHead);
1279 InitializeListHead(&DirtySegmentListHead);
1280 InitializeListHead(&CacheSegmentLRUListHead);
1281 InitializeListHead(&ClosedListHead);
1282 ExInitializeFastMutex(&ViewLock);
1283 ExInitializeNPagedLookasideList (&iBcbLookasideList,
1284 NULL,
1285 NULL,
1286 0,
1287 sizeof(INTERNAL_BCB),
1288 TAG_IBCB,
1289 20);
1290 ExInitializeNPagedLookasideList (&BcbLookasideList,
1291 NULL,
1292 NULL,
1293 0,
1294 sizeof(BCB),
1295 TAG_BCB,
1296 20);
1297 ExInitializeNPagedLookasideList (&CacheSegLookasideList,
1298 NULL,
1299 NULL,
1300 0,
1301 sizeof(CACHE_SEGMENT),
1302 TAG_CSEG,
1303 20);
1304
1305 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
1306
1307 CcInitCacheZeroPage();
1308
1309 CcTimeStamp = 0;
1310 LazyCloseThreadShouldTerminate = FALSE;
1311 KeInitializeEvent (&LazyCloseThreadEvent, SynchronizationEvent, FALSE);
1312 Status = PsCreateSystemThread(&LazyCloseThreadHandle,
1313 THREAD_ALL_ACCESS,
1314 NULL,
1315 NULL,
1316 &LazyCloseThreadId,
1317 (PKSTART_ROUTINE)CmLazyCloseThreadMain,
1318 NULL);
1319 if (NT_SUCCESS(Status))
1320 {
1321 Priority = LOW_REALTIME_PRIORITY;
1322 NtSetInformationThread(LazyCloseThreadHandle,
1323 ThreadPriority,
1324 &Priority,
1325 sizeof(Priority));
1326 }
1327
1328 }
1329
1330 /* EOF */
1331
1332
1333
1334
1335
1336
1337