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