b1170272b7e917e747871f7de0a837198769e07b
[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 CcRosReleaseVacbLock(Vacb);
426
427 return STATUS_SUCCESS;
428 }
429
430 /* Returns with VACB Lock Held! */
431 PROS_VACB
432 NTAPI
433 CcRosLookupVacb (
434 PROS_SHARED_CACHE_MAP SharedCacheMap,
435 LONGLONG FileOffset)
436 {
437 PLIST_ENTRY current_entry;
438 PROS_VACB current;
439 KIRQL oldIrql;
440
441 ASSERT(SharedCacheMap);
442
443 DPRINT("CcRosLookupVacb(SharedCacheMap 0x%p, FileOffset %I64u)\n",
444 SharedCacheMap, FileOffset);
445
446 KeAcquireGuardedMutex(&ViewLock);
447 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
448
449 current_entry = SharedCacheMap->CacheMapVacbListHead.Flink;
450 while (current_entry != &SharedCacheMap->CacheMapVacbListHead)
451 {
452 current = CONTAINING_RECORD(current_entry,
453 ROS_VACB,
454 CacheMapVacbListEntry);
455 if (IsPointInRange(current->FileOffset.QuadPart,
456 VACB_MAPPING_GRANULARITY,
457 FileOffset))
458 {
459 CcRosVacbIncRefCount(current);
460 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
461 KeReleaseGuardedMutex(&ViewLock);
462 CcRosAcquireVacbLock(current, NULL);
463 return current;
464 }
465 if (current->FileOffset.QuadPart > FileOffset)
466 break;
467 current_entry = current_entry->Flink;
468 }
469
470 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
471 KeReleaseGuardedMutex(&ViewLock);
472
473 return NULL;
474 }
475
476 NTSTATUS
477 NTAPI
478 CcRosMarkDirtyVacb (
479 PROS_SHARED_CACHE_MAP SharedCacheMap,
480 LONGLONG FileOffset)
481 {
482 PROS_VACB Vacb;
483 KIRQL oldIrql;
484
485 ASSERT(SharedCacheMap);
486
487 DPRINT("CcRosMarkDirtyVacb(SharedCacheMap 0x%p, FileOffset %I64u)\n",
488 SharedCacheMap, FileOffset);
489
490 Vacb = CcRosLookupVacb(SharedCacheMap, FileOffset);
491 if (Vacb == NULL)
492 {
493 KeBugCheck(CACHE_MANAGER);
494 }
495
496 KeAcquireGuardedMutex(&ViewLock);
497 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
498
499 if (!Vacb->Dirty)
500 {
501 InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
502 DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
503 }
504 else
505 {
506 CcRosVacbDecRefCount(Vacb);
507 }
508
509 /* Move to the tail of the LRU list */
510 RemoveEntryList(&Vacb->VacbLruListEntry);
511 InsertTailList(&VacbLruListHead, &Vacb->VacbLruListEntry);
512
513 Vacb->Dirty = TRUE;
514
515 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
516 KeReleaseGuardedMutex(&ViewLock);
517 CcRosReleaseVacbLock(Vacb);
518
519 return STATUS_SUCCESS;
520 }
521
522 NTSTATUS
523 NTAPI
524 CcRosUnmapVacb (
525 PROS_SHARED_CACHE_MAP SharedCacheMap,
526 LONGLONG FileOffset,
527 BOOLEAN NowDirty)
528 {
529 PROS_VACB Vacb;
530 BOOLEAN WasDirty;
531 KIRQL oldIrql;
532
533 ASSERT(SharedCacheMap);
534
535 DPRINT("CcRosUnmapVacb(SharedCacheMap 0x%p, FileOffset %I64u, NowDirty %u)\n",
536 SharedCacheMap, FileOffset, NowDirty);
537
538 Vacb = CcRosLookupVacb(SharedCacheMap, FileOffset);
539 if (Vacb == NULL)
540 {
541 return STATUS_UNSUCCESSFUL;
542 }
543
544 KeAcquireGuardedMutex(&ViewLock);
545 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
546
547 WasDirty = Vacb->Dirty;
548 Vacb->Dirty = Vacb->Dirty || NowDirty;
549
550 Vacb->MappedCount--;
551
552 if (!WasDirty && NowDirty)
553 {
554 InsertTailList(&DirtyVacbListHead, &Vacb->DirtyVacbListEntry);
555 DirtyPageCount += VACB_MAPPING_GRANULARITY / PAGE_SIZE;
556 }
557
558 CcRosVacbDecRefCount(Vacb);
559 if (!WasDirty && NowDirty)
560 {
561 CcRosVacbIncRefCount(Vacb);
562 }
563 if (Vacb->MappedCount == 0)
564 {
565 CcRosVacbDecRefCount(Vacb);
566 }
567
568 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
569 KeReleaseGuardedMutex(&ViewLock);
570 CcRosReleaseVacbLock(Vacb);
571
572 return STATUS_SUCCESS;
573 }
574
575 static
576 NTSTATUS
577 CcRosMapVacb(
578 PROS_VACB Vacb)
579 {
580 ULONG i;
581 NTSTATUS Status;
582 ULONG_PTR NumberOfPages;
583
584 /* Create a memory area. */
585 MmLockAddressSpace(MmGetKernelAddressSpace());
586 Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
587 0, // nothing checks for VACB mareas, so set to 0
588 &Vacb->BaseAddress,
589 VACB_MAPPING_GRANULARITY,
590 PAGE_READWRITE,
591 (PMEMORY_AREA*)&Vacb->MemoryArea,
592 0,
593 PAGE_SIZE);
594 MmUnlockAddressSpace(MmGetKernelAddressSpace());
595 if (!NT_SUCCESS(Status))
596 {
597 DPRINT1("MmCreateMemoryArea failed with %lx for VACB %p\n", Status, Vacb);
598 return Status;
599 }
600
601 ASSERT(((ULONG_PTR)Vacb->BaseAddress % PAGE_SIZE) == 0);
602 ASSERT((ULONG_PTR)Vacb->BaseAddress > (ULONG_PTR)MmSystemRangeStart);
603
604 /* Create a virtual mapping for this memory area */
605 NumberOfPages = BYTES_TO_PAGES(VACB_MAPPING_GRANULARITY);
606 for (i = 0; i < NumberOfPages; i++)
607 {
608 PFN_NUMBER PageFrameNumber;
609
610 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &PageFrameNumber);
611 if (PageFrameNumber == 0)
612 {
613 DPRINT1("Unable to allocate page\n");
614 KeBugCheck(MEMORY_MANAGEMENT);
615 }
616
617 Status = MmCreateVirtualMapping(NULL,
618 (PVOID)((ULONG_PTR)Vacb->BaseAddress + (i * PAGE_SIZE)),
619 PAGE_READWRITE,
620 &PageFrameNumber,
621 1);
622 if (!NT_SUCCESS(Status))
623 {
624 DPRINT1("Unable to create virtual mapping\n");
625 KeBugCheck(MEMORY_MANAGEMENT);
626 }
627 }
628
629 return STATUS_SUCCESS;
630 }
631
632 static
633 NTSTATUS
634 CcRosCreateVacb (
635 PROS_SHARED_CACHE_MAP SharedCacheMap,
636 LONGLONG FileOffset,
637 PROS_VACB *Vacb)
638 {
639 PROS_VACB current;
640 PROS_VACB previous;
641 PLIST_ENTRY current_entry;
642 NTSTATUS Status;
643 KIRQL oldIrql;
644
645 ASSERT(SharedCacheMap);
646
647 DPRINT("CcRosCreateVacb()\n");
648
649 if (FileOffset >= SharedCacheMap->SectionSize.QuadPart)
650 {
651 *Vacb = NULL;
652 return STATUS_INVALID_PARAMETER;
653 }
654
655 current = ExAllocateFromNPagedLookasideList(&VacbLookasideList);
656 current->BaseAddress = NULL;
657 current->Valid = FALSE;
658 current->Dirty = FALSE;
659 current->PageOut = FALSE;
660 current->FileOffset.QuadPart = ROUND_DOWN(FileOffset, VACB_MAPPING_GRANULARITY);
661 current->SharedCacheMap = SharedCacheMap;
662 #if DBG
663 if (SharedCacheMap->Trace)
664 {
665 DPRINT1("CacheMap 0x%p: new VACB: 0x%p\n", SharedCacheMap, current);
666 }
667 #endif
668 current->MappedCount = 0;
669 current->DirtyVacbListEntry.Flink = NULL;
670 current->DirtyVacbListEntry.Blink = NULL;
671 current->ReferenceCount = 1;
672 current->PinCount = 0;
673 KeInitializeMutex(&current->Mutex, 0);
674 CcRosAcquireVacbLock(current, NULL);
675 KeAcquireGuardedMutex(&ViewLock);
676
677 *Vacb = current;
678 /* There is window between the call to CcRosLookupVacb
679 * and CcRosCreateVacb. We must check if a VACB for the
680 * file offset exist. If there is a VACB, we release
681 * our newly created VACB and return the existing one.
682 */
683 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
684 current_entry = SharedCacheMap->CacheMapVacbListHead.Flink;
685 previous = NULL;
686 while (current_entry != &SharedCacheMap->CacheMapVacbListHead)
687 {
688 current = CONTAINING_RECORD(current_entry,
689 ROS_VACB,
690 CacheMapVacbListEntry);
691 if (IsPointInRange(current->FileOffset.QuadPart,
692 VACB_MAPPING_GRANULARITY,
693 FileOffset))
694 {
695 CcRosVacbIncRefCount(current);
696 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
697 #if DBG
698 if (SharedCacheMap->Trace)
699 {
700 DPRINT1("CacheMap 0x%p: deleting newly created VACB 0x%p ( found existing one 0x%p )\n",
701 SharedCacheMap,
702 (*Vacb),
703 current);
704 }
705 #endif
706 CcRosReleaseVacbLock(*Vacb);
707 KeReleaseGuardedMutex(&ViewLock);
708 ExFreeToNPagedLookasideList(&VacbLookasideList, *Vacb);
709 *Vacb = current;
710 CcRosAcquireVacbLock(current, NULL);
711 return STATUS_SUCCESS;
712 }
713 if (current->FileOffset.QuadPart < FileOffset)
714 {
715 ASSERT(previous == NULL ||
716 previous->FileOffset.QuadPart < current->FileOffset.QuadPart);
717 previous = current;
718 }
719 if (current->FileOffset.QuadPart > FileOffset)
720 break;
721 current_entry = current_entry->Flink;
722 }
723 /* There was no existing VACB. */
724 current = *Vacb;
725 if (previous)
726 {
727 InsertHeadList(&previous->CacheMapVacbListEntry, &current->CacheMapVacbListEntry);
728 }
729 else
730 {
731 InsertHeadList(&SharedCacheMap->CacheMapVacbListHead, &current->CacheMapVacbListEntry);
732 }
733 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
734 InsertTailList(&VacbLruListHead, &current->VacbLruListEntry);
735 KeReleaseGuardedMutex(&ViewLock);
736
737 MI_SET_USAGE(MI_USAGE_CACHE);
738 #if MI_TRACE_PFNS
739 if ((SharedCacheMap->FileObject) && (SharedCacheMap->FileObject->FileName.Buffer))
740 {
741 PWCHAR pos = NULL;
742 ULONG len = 0;
743 pos = wcsrchr(SharedCacheMap->FileObject->FileName.Buffer, '\\');
744 len = wcslen(pos) * sizeof(WCHAR);
745 if (pos) snprintf(MI_PFN_CURRENT_PROCESS_NAME, min(16, len), "%S", pos);
746 }
747 #endif
748
749 Status = CcRosMapVacb(current);
750 if (!NT_SUCCESS(Status))
751 {
752 RemoveEntryList(&current->CacheMapVacbListEntry);
753 RemoveEntryList(&current->VacbLruListEntry);
754 CcRosReleaseVacbLock(current);
755 ExFreeToNPagedLookasideList(&VacbLookasideList, current);
756 }
757
758 return Status;
759 }
760
761 NTSTATUS
762 NTAPI
763 CcRosGetVacb (
764 PROS_SHARED_CACHE_MAP SharedCacheMap,
765 LONGLONG FileOffset,
766 PLONGLONG BaseOffset,
767 PVOID* BaseAddress,
768 PBOOLEAN UptoDate,
769 PROS_VACB *Vacb)
770 {
771 PROS_VACB current;
772 NTSTATUS Status;
773
774 ASSERT(SharedCacheMap);
775
776 DPRINT("CcRosGetVacb()\n");
777
778 /*
779 * Look for a VACB already mapping the same data.
780 */
781 current = CcRosLookupVacb(SharedCacheMap, FileOffset);
782 if (current == NULL)
783 {
784 /*
785 * Otherwise create a new VACB.
786 */
787 Status = CcRosCreateVacb(SharedCacheMap, FileOffset, &current);
788 if (!NT_SUCCESS(Status))
789 {
790 return Status;
791 }
792 }
793
794 KeAcquireGuardedMutex(&ViewLock);
795
796 /* Move to the tail of the LRU list */
797 RemoveEntryList(&current->VacbLruListEntry);
798 InsertTailList(&VacbLruListHead, &current->VacbLruListEntry);
799
800 KeReleaseGuardedMutex(&ViewLock);
801
802 /*
803 * Return information about the VACB to the caller.
804 */
805 *UptoDate = current->Valid;
806 *BaseAddress = current->BaseAddress;
807 DPRINT("*BaseAddress %p\n", *BaseAddress);
808 *Vacb = current;
809 *BaseOffset = current->FileOffset.QuadPart;
810 return STATUS_SUCCESS;
811 }
812
813 NTSTATUS
814 NTAPI
815 CcRosRequestVacb (
816 PROS_SHARED_CACHE_MAP SharedCacheMap,
817 LONGLONG FileOffset,
818 PVOID* BaseAddress,
819 PBOOLEAN UptoDate,
820 PROS_VACB *Vacb)
821 /*
822 * FUNCTION: Request a page mapping for a shared cache map
823 */
824 {
825 LONGLONG BaseOffset;
826
827 ASSERT(SharedCacheMap);
828
829 if (FileOffset % VACB_MAPPING_GRANULARITY != 0)
830 {
831 DPRINT1("Bad fileoffset %I64x should be multiple of %x",
832 FileOffset, VACB_MAPPING_GRANULARITY);
833 KeBugCheck(CACHE_MANAGER);
834 }
835
836 return CcRosGetVacb(SharedCacheMap,
837 FileOffset,
838 &BaseOffset,
839 BaseAddress,
840 UptoDate,
841 Vacb);
842 }
843
844 static
845 VOID
846 CcFreeCachePage (
847 PVOID Context,
848 MEMORY_AREA* MemoryArea,
849 PVOID Address,
850 PFN_NUMBER Page,
851 SWAPENTRY SwapEntry,
852 BOOLEAN Dirty)
853 {
854 ASSERT(SwapEntry == 0);
855 if (Page != 0)
856 {
857 ASSERT(MmGetReferenceCountPage(Page) == 1);
858 MmReleasePageMemoryConsumer(MC_CACHE, Page);
859 }
860 }
861
862 NTSTATUS
863 CcRosInternalFreeVacb (
864 PROS_VACB Vacb)
865 /*
866 * FUNCTION: Releases a VACB associated with a shared cache map
867 */
868 {
869 DPRINT("Freeing VACB 0x%p\n", Vacb);
870 #if DBG
871 if (Vacb->SharedCacheMap->Trace)
872 {
873 DPRINT1("CacheMap 0x%p: deleting VACB: 0x%p\n", Vacb->SharedCacheMap, Vacb);
874 }
875 #endif
876
877 MmLockAddressSpace(MmGetKernelAddressSpace());
878 MmFreeMemoryArea(MmGetKernelAddressSpace(),
879 Vacb->MemoryArea,
880 CcFreeCachePage,
881 NULL);
882 MmUnlockAddressSpace(MmGetKernelAddressSpace());
883
884 ExFreeToNPagedLookasideList(&VacbLookasideList, Vacb);
885 return STATUS_SUCCESS;
886 }
887
888 /*
889 * @implemented
890 */
891 VOID
892 NTAPI
893 CcFlushCache (
894 IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
895 IN PLARGE_INTEGER FileOffset OPTIONAL,
896 IN ULONG Length,
897 OUT PIO_STATUS_BLOCK IoStatus)
898 {
899 PROS_SHARED_CACHE_MAP SharedCacheMap;
900 LARGE_INTEGER Offset;
901 LONGLONG RemainingLength;
902 PROS_VACB current;
903 NTSTATUS Status;
904 KIRQL oldIrql;
905
906 CCTRACE(CC_API_DEBUG, "SectionObjectPointers=%p FileOffset=%p Length=%lu\n",
907 SectionObjectPointers, FileOffset, Length);
908
909 DPRINT("CcFlushCache(SectionObjectPointers 0x%p, FileOffset 0x%p, Length %lu, IoStatus 0x%p)\n",
910 SectionObjectPointers, FileOffset, Length, IoStatus);
911
912 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
913 {
914 SharedCacheMap = SectionObjectPointers->SharedCacheMap;
915 ASSERT(SharedCacheMap);
916 if (FileOffset)
917 {
918 Offset = *FileOffset;
919 RemainingLength = Length;
920 }
921 else
922 {
923 Offset.QuadPart = 0;
924 RemainingLength = SharedCacheMap->FileSize.QuadPart;
925 }
926
927 if (IoStatus)
928 {
929 IoStatus->Status = STATUS_SUCCESS;
930 IoStatus->Information = 0;
931 }
932
933 while (RemainingLength > 0)
934 {
935 current = CcRosLookupVacb(SharedCacheMap, Offset.QuadPart);
936 if (current != NULL)
937 {
938 if (current->Dirty)
939 {
940 Status = CcRosFlushVacb(current);
941 if (!NT_SUCCESS(Status) && IoStatus != NULL)
942 {
943 IoStatus->Status = Status;
944 }
945 }
946
947 CcRosReleaseVacbLock(current);
948
949 KeAcquireGuardedMutex(&ViewLock);
950 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
951 CcRosVacbDecRefCount(current);
952 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
953 KeReleaseGuardedMutex(&ViewLock);
954 }
955
956 Offset.QuadPart += VACB_MAPPING_GRANULARITY;
957 RemainingLength -= min(RemainingLength, VACB_MAPPING_GRANULARITY);
958 }
959 }
960 else
961 {
962 if (IoStatus)
963 {
964 IoStatus->Status = STATUS_INVALID_PARAMETER;
965 }
966 }
967 }
968
969 NTSTATUS
970 NTAPI
971 CcRosDeleteFileCache (
972 PFILE_OBJECT FileObject,
973 PROS_SHARED_CACHE_MAP SharedCacheMap)
974 /*
975 * FUNCTION: Releases the shared cache map associated with a file object
976 */
977 {
978 PLIST_ENTRY current_entry;
979 PROS_VACB current;
980 LIST_ENTRY FreeList;
981 KIRQL oldIrql;
982
983 ASSERT(SharedCacheMap);
984
985 SharedCacheMap->OpenCount++;
986 KeReleaseGuardedMutex(&ViewLock);
987
988 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
989
990 KeAcquireGuardedMutex(&ViewLock);
991 SharedCacheMap->OpenCount--;
992 if (SharedCacheMap->OpenCount == 0)
993 {
994 FileObject->SectionObjectPointer->SharedCacheMap = NULL;
995
996 /*
997 * Release all VACBs
998 */
999 InitializeListHead(&FreeList);
1000 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldIrql);
1001 while (!IsListEmpty(&SharedCacheMap->CacheMapVacbListHead))
1002 {
1003 current_entry = RemoveTailList(&SharedCacheMap->CacheMapVacbListHead);
1004 current = CONTAINING_RECORD(current_entry, ROS_VACB, CacheMapVacbListEntry);
1005 RemoveEntryList(&current->VacbLruListEntry);
1006 if (current->Dirty)
1007 {
1008 RemoveEntryList(&current->DirtyVacbListEntry);
1009 DirtyPageCount -= VACB_MAPPING_GRANULARITY / PAGE_SIZE;
1010 DPRINT1("Freeing dirty VACB\n");
1011 }
1012 InsertHeadList(&FreeList, &current->CacheMapVacbListEntry);
1013 }
1014 #if DBG
1015 SharedCacheMap->Trace = FALSE;
1016 #endif
1017 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
1018
1019 KeReleaseGuardedMutex(&ViewLock);
1020 ObDereferenceObject(SharedCacheMap->FileObject);
1021
1022 while (!IsListEmpty(&FreeList))
1023 {
1024 current_entry = RemoveTailList(&FreeList);
1025 current = CONTAINING_RECORD(current_entry, ROS_VACB, CacheMapVacbListEntry);
1026 CcRosInternalFreeVacb(current);
1027 }
1028 ExFreeToNPagedLookasideList(&SharedCacheMapLookasideList, SharedCacheMap);
1029 KeAcquireGuardedMutex(&ViewLock);
1030 }
1031 return STATUS_SUCCESS;
1032 }
1033
1034 VOID
1035 NTAPI
1036 CcRosReferenceCache (
1037 PFILE_OBJECT FileObject)
1038 {
1039 PROS_SHARED_CACHE_MAP SharedCacheMap;
1040 KeAcquireGuardedMutex(&ViewLock);
1041 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1042 ASSERT(SharedCacheMap);
1043 ASSERT(SharedCacheMap->OpenCount != 0);
1044 SharedCacheMap->OpenCount++;
1045 KeReleaseGuardedMutex(&ViewLock);
1046 }
1047
1048 VOID
1049 NTAPI
1050 CcRosRemoveIfClosed (
1051 PSECTION_OBJECT_POINTERS SectionObjectPointer)
1052 {
1053 PROS_SHARED_CACHE_MAP SharedCacheMap;
1054 DPRINT("CcRosRemoveIfClosed()\n");
1055 KeAcquireGuardedMutex(&ViewLock);
1056 SharedCacheMap = SectionObjectPointer->SharedCacheMap;
1057 if (SharedCacheMap && SharedCacheMap->OpenCount == 0)
1058 {
1059 CcRosDeleteFileCache(SharedCacheMap->FileObject, SharedCacheMap);
1060 }
1061 KeReleaseGuardedMutex(&ViewLock);
1062 }
1063
1064
1065 VOID
1066 NTAPI
1067 CcRosDereferenceCache (
1068 PFILE_OBJECT FileObject)
1069 {
1070 PROS_SHARED_CACHE_MAP SharedCacheMap;
1071 KeAcquireGuardedMutex(&ViewLock);
1072 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1073 ASSERT(SharedCacheMap);
1074 if (SharedCacheMap->OpenCount > 0)
1075 {
1076 SharedCacheMap->OpenCount--;
1077 if (SharedCacheMap->OpenCount == 0)
1078 {
1079 MmFreeSectionSegments(SharedCacheMap->FileObject);
1080 CcRosDeleteFileCache(FileObject, SharedCacheMap);
1081 }
1082 }
1083 KeReleaseGuardedMutex(&ViewLock);
1084 }
1085
1086 NTSTATUS
1087 NTAPI
1088 CcRosReleaseFileCache (
1089 PFILE_OBJECT FileObject)
1090 /*
1091 * FUNCTION: Called by the file system when a handle to a file object
1092 * has been closed.
1093 */
1094 {
1095 PROS_SHARED_CACHE_MAP SharedCacheMap;
1096
1097 KeAcquireGuardedMutex(&ViewLock);
1098
1099 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL)
1100 {
1101 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1102 if (FileObject->PrivateCacheMap != NULL)
1103 {
1104 FileObject->PrivateCacheMap = NULL;
1105 if (SharedCacheMap->OpenCount > 0)
1106 {
1107 SharedCacheMap->OpenCount--;
1108 if (SharedCacheMap->OpenCount == 0)
1109 {
1110 MmFreeSectionSegments(SharedCacheMap->FileObject);
1111 CcRosDeleteFileCache(FileObject, SharedCacheMap);
1112 }
1113 }
1114 }
1115 }
1116 KeReleaseGuardedMutex(&ViewLock);
1117 return STATUS_SUCCESS;
1118 }
1119
1120 NTSTATUS
1121 NTAPI
1122 CcTryToInitializeFileCache (
1123 PFILE_OBJECT FileObject)
1124 {
1125 PROS_SHARED_CACHE_MAP SharedCacheMap;
1126 NTSTATUS Status;
1127
1128 KeAcquireGuardedMutex(&ViewLock);
1129
1130 ASSERT(FileObject->SectionObjectPointer);
1131 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1132 if (SharedCacheMap == NULL)
1133 {
1134 Status = STATUS_UNSUCCESSFUL;
1135 }
1136 else
1137 {
1138 if (FileObject->PrivateCacheMap == NULL)
1139 {
1140 FileObject->PrivateCacheMap = SharedCacheMap;
1141 SharedCacheMap->OpenCount++;
1142 }
1143 Status = STATUS_SUCCESS;
1144 }
1145 KeReleaseGuardedMutex(&ViewLock);
1146
1147 return Status;
1148 }
1149
1150
1151 NTSTATUS
1152 NTAPI
1153 CcRosInitializeFileCache (
1154 PFILE_OBJECT FileObject,
1155 PCC_FILE_SIZES FileSizes,
1156 BOOLEAN PinAccess,
1157 PCACHE_MANAGER_CALLBACKS CallBacks,
1158 PVOID LazyWriterContext)
1159 /*
1160 * FUNCTION: Initializes a shared cache map for a file object
1161 */
1162 {
1163 PROS_SHARED_CACHE_MAP SharedCacheMap;
1164
1165 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1166 DPRINT("CcRosInitializeFileCache(FileObject 0x%p, SharedCacheMap 0x%p)\n",
1167 FileObject, SharedCacheMap);
1168
1169 KeAcquireGuardedMutex(&ViewLock);
1170 if (SharedCacheMap == NULL)
1171 {
1172 SharedCacheMap = ExAllocateFromNPagedLookasideList(&SharedCacheMapLookasideList);
1173 if (SharedCacheMap == NULL)
1174 {
1175 KeReleaseGuardedMutex(&ViewLock);
1176 return STATUS_INSUFFICIENT_RESOURCES;
1177 }
1178 RtlZeroMemory(SharedCacheMap, sizeof(*SharedCacheMap));
1179 ObReferenceObjectByPointer(FileObject,
1180 FILE_ALL_ACCESS,
1181 NULL,
1182 KernelMode);
1183 SharedCacheMap->FileObject = FileObject;
1184 SharedCacheMap->Callbacks = CallBacks;
1185 SharedCacheMap->LazyWriteContext = LazyWriterContext;
1186 SharedCacheMap->SectionSize = FileSizes->AllocationSize;
1187 SharedCacheMap->FileSize = FileSizes->FileSize;
1188 SharedCacheMap->PinAccess = PinAccess;
1189 KeInitializeSpinLock(&SharedCacheMap->CacheMapLock);
1190 InitializeListHead(&SharedCacheMap->CacheMapVacbListHead);
1191 FileObject->SectionObjectPointer->SharedCacheMap = SharedCacheMap;
1192 }
1193 if (FileObject->PrivateCacheMap == NULL)
1194 {
1195 FileObject->PrivateCacheMap = SharedCacheMap;
1196 SharedCacheMap->OpenCount++;
1197 }
1198 KeReleaseGuardedMutex(&ViewLock);
1199
1200 return STATUS_SUCCESS;
1201 }
1202
1203 /*
1204 * @implemented
1205 */
1206 PFILE_OBJECT
1207 NTAPI
1208 CcGetFileObjectFromSectionPtrs (
1209 IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
1210 {
1211 PROS_SHARED_CACHE_MAP SharedCacheMap;
1212
1213 CCTRACE(CC_API_DEBUG, "SectionObjectPointers=%p\n", SectionObjectPointers);
1214
1215 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
1216 {
1217 SharedCacheMap = SectionObjectPointers->SharedCacheMap;
1218 ASSERT(SharedCacheMap);
1219 return SharedCacheMap->FileObject;
1220 }
1221 return NULL;
1222 }
1223
1224 VOID
1225 INIT_FUNCTION
1226 NTAPI
1227 CcInitView (
1228 VOID)
1229 {
1230 DPRINT("CcInitView()\n");
1231
1232 InitializeListHead(&DirtyVacbListHead);
1233 InitializeListHead(&VacbLruListHead);
1234 KeInitializeGuardedMutex(&ViewLock);
1235 ExInitializeNPagedLookasideList(&iBcbLookasideList,
1236 NULL,
1237 NULL,
1238 0,
1239 sizeof(INTERNAL_BCB),
1240 TAG_BCB,
1241 20);
1242 ExInitializeNPagedLookasideList(&SharedCacheMapLookasideList,
1243 NULL,
1244 NULL,
1245 0,
1246 sizeof(ROS_SHARED_CACHE_MAP),
1247 TAG_SHARED_CACHE_MAP,
1248 20);
1249 ExInitializeNPagedLookasideList(&VacbLookasideList,
1250 NULL,
1251 NULL,
1252 0,
1253 sizeof(ROS_VACB),
1254 TAG_VACB,
1255 20);
1256
1257 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
1258
1259 CcInitCacheZeroPage();
1260 }
1261
1262 /* EOF */