2003-06-07 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / ntoskrnl / cc / view.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: view.c,v 1.62 2003/06/07 11:34:36 chorns Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/cc/view.c
23 * PURPOSE: Cache manager
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * PORTABILITY: Checked
26 * UPDATE HISTORY:
27 * Created 22/05/98
28 */
29
30 /* NOTES **********************************************************************
31 *
32 * This is not the NT implementation of a file cache nor anything much like
33 * it.
34 *
35 * The general procedure for a filesystem to implement a read or write
36 * dispatch routine is as follows
37 *
38 * (1) If caching for the FCB hasn't been initiated then so do by calling
39 * CcInitializeFileCache.
40 *
41 * (2) For each 4k region which is being read or written obtain a cache page
42 * by calling CcRequestCachePage.
43 *
44 * (3) If either the page is being read or not completely written, and it is
45 * not up to date then read its data from the underlying medium. If the read
46 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
47 *
48 * (4) Copy the data into or out of the page as necessary.
49 *
50 * (5) Release the cache page
51 */
52 /* INCLUDES ******************************************************************/
53
54 #include <ddk/ntddk.h>
55 #include <ddk/ntifs.h>
56 #include <internal/mm.h>
57 #include <internal/cc.h>
58 #include <internal/pool.h>
59 #include <ntos/minmax.h>
60
61 #define NDEBUG
62 #include <internal/debug.h>
63
64 /* GLOBALS *******************************************************************/
65
66 /*
67 * If CACHE_BITMAP is defined, the cache manager uses one large memory region
68 * within the kernel address space and allocate/deallocate space from this block
69 * over a bitmap. If CACHE_BITMAP is used, the size of the mdl mapping region
70 * must be reduced (ntoskrnl\mm\mdl.c, MI_MDLMAPPING_REGION_SIZE).
71 */
72 //#define CACHE_BITMAP
73
74 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
75 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
76
77 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
78 #define TAG_BCB TAG('B', 'C', 'B', ' ')
79 #define TAG_IBCB TAG('i', 'B', 'C', 'B')
80
81 static LIST_ENTRY DirtySegmentListHead;
82 static LIST_ENTRY CacheSegmentListHead;
83 static LIST_ENTRY CacheSegmentLRUListHead;
84 static LIST_ENTRY ClosedListHead;
85 ULONG DirtyPageCount=0;
86
87 FAST_MUTEX ViewLock;
88
89 #ifdef CACHE_BITMAP
90 #define CI_CACHESEG_MAPPING_REGION_SIZE (128*1024*1024)
91
92 static PVOID CiCacheSegMappingRegionBase = NULL;
93 static RTL_BITMAP CiCacheSegMappingRegionAllocMap;
94 static ULONG CiCacheSegMappingRegionHint;
95 static KSPIN_LOCK CiCacheSegMappingRegionLock;
96 #endif
97
98 NPAGED_LOOKASIDE_LIST iBcbLookasideList;
99 static NPAGED_LOOKASIDE_LIST BcbLookasideList;
100 static NPAGED_LOOKASIDE_LIST CacheSegLookasideList;
101
102 static ULONG CcTimeStamp;
103 static KEVENT LazyCloseThreadEvent;
104 static HANDLE LazyCloseThreadHandle;
105 static CLIENT_ID LazyCloseThreadId;
106 static volatile BOOLEAN LazyCloseThreadShouldTerminate;
107
108 void * alloca(size_t size);
109
110 NTSTATUS
111 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg);
112
113 /* FUNCTIONS *****************************************************************/
114
115 NTSTATUS STATIC
116 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
117 {
118 NTSTATUS Status;
119 KIRQL oldIrql;
120 Status = WriteCacheSegment(CacheSegment);
121 if (NT_SUCCESS(Status))
122 {
123 ExAcquireFastMutex(&ViewLock);
124 KeAcquireSpinLock(&CacheSegment->Bcb->BcbLock, &oldIrql);
125 CacheSegment->Dirty = FALSE;
126 RemoveEntryList(&CacheSegment->DirtySegmentListEntry);
127 DirtyPageCount -= CacheSegment->Bcb->CacheSegmentSize / PAGE_SIZE;
128 CacheSegment->ReferenceCount--;
129 KeReleaseSpinLock(&CacheSegment->Bcb->BcbLock, oldIrql);
130 ExReleaseFastMutex(&ViewLock);
131 }
132 return(Status);
133 }
134
135 VOID CcRosRemoveUnusedFiles(VOID);
136
137
138 NTSTATUS
139 CcRosFlushDirtyPages(ULONG Target, PULONG Count)
140 {
141 PLIST_ENTRY current_entry;
142 PCACHE_SEGMENT current;
143 ULONG PagesPerSegment;
144 BOOLEAN Locked;
145 NTSTATUS Status;
146 static ULONG WriteCount[4] = {0, 0, 0, 0};
147 ULONG NewTarget;
148
149 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target);
150
151 (*Count) = 0;
152
153 ExAcquireFastMutex(&ViewLock);
154
155 WriteCount[0] = WriteCount[1];
156 WriteCount[1] = WriteCount[2];
157 WriteCount[2] = WriteCount[3];
158 WriteCount[3] = 0;
159
160 NewTarget = WriteCount[0] + WriteCount[1] + WriteCount[2];
161
162 if (NewTarget < DirtyPageCount)
163 {
164 NewTarget = (DirtyPageCount - NewTarget + 3) / 4;
165 WriteCount[0] += NewTarget;
166 WriteCount[1] += NewTarget;
167 WriteCount[2] += NewTarget;
168 WriteCount[3] += NewTarget;
169 }
170
171 NewTarget = WriteCount[0];
172
173 Target = max(NewTarget, Target);
174
175 current_entry = DirtySegmentListHead.Flink;
176 if (current_entry == &DirtySegmentListHead)
177 {
178 DPRINT("No Dirty pages\n");
179 }
180 while (current_entry != &DirtySegmentListHead && Target > 0)
181 {
182 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
183 DirtySegmentListEntry);
184 current_entry = current_entry->Flink;
185 Locked = ExTryToAcquireFastMutex(&current->Lock);
186 if (!Locked)
187 {
188 continue;
189 }
190 assert(current->Dirty);
191 if (current->ReferenceCount > 1)
192 {
193 ExReleaseFastMutex(&current->Lock);
194 continue;
195 }
196 ExReleaseFastMutex(&ViewLock);
197 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
198 Status = CcRosFlushCacheSegment(current);
199 ExReleaseFastMutex(&current->Lock);
200 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
201 {
202 DPRINT1("CC: Failed to flush cache segment.\n");
203 }
204 else
205 {
206 (*Count) += PagesPerSegment;
207 Target -= PagesPerSegment;
208 }
209 ExAcquireFastMutex(&ViewLock);
210 current_entry = DirtySegmentListHead.Flink;
211 }
212 if (*Count < NewTarget)
213 {
214 WriteCount[1] += (NewTarget - *Count);
215 }
216 ExReleaseFastMutex(&ViewLock);
217 DPRINT("CcRosFlushDirtyPages() finished\n");
218
219 return(STATUS_SUCCESS);
220 }
221
222 NTSTATUS
223 CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
224 /*
225 * FUNCTION: Try to free some memory from the file cache.
226 * ARGUMENTS:
227 * Target - The number of pages to be freed.
228 * Priority - The priority of free (currently unused).
229 * NrFreed - Points to a variable where the number of pages
230 * actually freed is returned.
231 */
232 {
233 PLIST_ENTRY current_entry;
234 PCACHE_SEGMENT current;
235 ULONG PagesPerSegment;
236 ULONG PagesFreed;
237 KIRQL oldIrql;
238 LIST_ENTRY FreeList;
239
240 DPRINT("CcRosTrimCache(Target %d)\n", Target);
241
242 *NrFreed = 0;
243
244 InitializeListHead(&FreeList);
245
246 ExAcquireFastMutex(&ViewLock);
247 current_entry = CacheSegmentLRUListHead.Flink;
248 while (current_entry != &CacheSegmentLRUListHead && Target > 0)
249 {
250 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
251 CacheSegmentLRUListEntry);
252 current_entry = current_entry->Flink;
253
254 KeAcquireSpinLock(&current->Bcb->BcbLock, &oldIrql);
255 if (current->ReferenceCount == 0)
256 {
257 RemoveEntryList(&current->BcbSegmentListEntry);
258 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
259 RemoveEntryList(&current->CacheSegmentListEntry);
260 RemoveEntryList(&current->CacheSegmentLRUListEntry);
261 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
262 }
263 else
264 {
265 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
266 }
267 }
268 ExReleaseFastMutex(&ViewLock);
269
270 while (!IsListEmpty(&FreeList))
271 {
272 current_entry = RemoveHeadList(&FreeList);
273 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
274 BcbSegmentListEntry);
275 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
276 PagesFreed = min(PagesPerSegment, Target);
277 Target -= PagesFreed;
278 (*NrFreed) += PagesFreed;
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 return(current);
357 }
358 current_entry = current_entry->Flink;
359 }
360 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
361 return(NULL);
362 }
363
364 NTSTATUS
365 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
366 {
367 PCACHE_SEGMENT CacheSeg;
368 KIRQL oldIrql;
369
370 assert(Bcb);
371
372 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
373
374 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
375 if (CacheSeg == NULL)
376 {
377 KeBugCheck(0);
378 }
379 ExAcquireFastMutex(&CacheSeg->Lock);
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 ExAcquireFastMutex(&CacheSeg->Lock);
419
420 WasDirty = CacheSeg->Dirty;
421 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
422
423 CacheSeg->MappedCount--;
424
425 if (!WasDirty && NowDirty)
426 {
427 ExAcquireFastMutex(&ViewLock);
428 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
429 DirtyPageCount += Bcb->CacheSegmentSize / PAGE_SIZE;
430 ExReleaseFastMutex(&ViewLock);
431 }
432
433 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
434 CacheSeg->ReferenceCount--;
435 if (!WasDirty && NowDirty)
436 {
437 CacheSeg->ReferenceCount++;
438 }
439 if (CacheSeg->MappedCount == 0)
440 {
441 CacheSeg->ReferenceCount--;
442 }
443 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
444
445 ExReleaseFastMutex(&CacheSeg->Lock);
446 return(STATUS_SUCCESS);
447 }
448
449 NTSTATUS STATIC
450 CcRosCreateCacheSegment(PBCB Bcb,
451 ULONG FileOffset,
452 PCACHE_SEGMENT* CacheSeg,
453 BOOLEAN Lock)
454 {
455 ULONG i;
456 PCACHE_SEGMENT current;
457 PCACHE_SEGMENT previous;
458 PLIST_ENTRY current_entry;
459 NTSTATUS Status;
460 KIRQL oldIrql;
461 #ifdef CACHE_BITMAP
462 ULONG StartingOffset;
463 #endif
464
465 assert(Bcb);
466
467 DPRINT("CcRosCreateCacheSegment()\n");
468
469 if (FileOffset >= Bcb->FileSize.u.LowPart)
470 {
471 CacheSeg = NULL;
472 return STATUS_INVALID_PARAMETER;
473 }
474
475 current = ExAllocateFromNPagedLookasideList(&CacheSegLookasideList);
476 current->Valid = FALSE;
477 current->Dirty = FALSE;
478 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
479 current->Bcb = Bcb;
480 current->MappedCount = 0;
481 current->DirtySegmentListEntry.Flink = NULL;
482 current->DirtySegmentListEntry.Blink = NULL;
483 current->ReferenceCount = 1;
484 ExInitializeFastMutex(&current->Lock);
485 ExAcquireFastMutex(&current->Lock);
486 ExAcquireFastMutex(&ViewLock);
487
488 *CacheSeg = current;
489 /* There is window between the call to CcRosLookupCacheSegment
490 * and CcRosCreateCacheSegment. We must check if a segment on
491 * the fileoffset exist. If there exist a segment, we release
492 * our new created segment and return the existing one.
493 */
494 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
495 current_entry = Bcb->BcbSegmentListHead.Flink;
496 previous = NULL;
497 while (current_entry != &Bcb->BcbSegmentListHead)
498 {
499 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
500 BcbSegmentListEntry);
501 if (current->FileOffset <= FileOffset &&
502 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
503 {
504 current->ReferenceCount++;
505 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
506 ExReleaseFastMutex(&(*CacheSeg)->Lock);
507 ExReleaseFastMutex(&ViewLock);
508 ExFreeToNPagedLookasideList(&CacheSegLookasideList, *CacheSeg);
509 *CacheSeg = current;
510 if (Lock)
511 {
512 ExAcquireFastMutex(&current->Lock);
513 }
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 KeBugCheck(0);
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 MmUnlockAddressSpace(MmGetKernelAddressSpace());
578 if (!NT_SUCCESS(Status))
579 {
580 KeBugCheck(0);
581 }
582 #endif
583 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
584 {
585 PHYSICAL_ADDRESS Page;
586
587 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
588 if (!NT_SUCCESS(Status))
589 {
590 KeBugCheck(0);
591 }
592
593 Status = MmCreateVirtualMapping(NULL,
594 current->BaseAddress + (i * PAGE_SIZE),
595 PAGE_READWRITE,
596 Page,
597 TRUE);
598 if (!NT_SUCCESS(Status))
599 {
600 KeBugCheck(0);
601 }
602 }
603 if (!Lock)
604 {
605 ExReleaseFastMutex(&current->Lock);
606 }
607
608 return(STATUS_SUCCESS);
609 }
610
611 NTSTATUS
612 CcRosGetCacheSegmentChain(PBCB Bcb,
613 ULONG FileOffset,
614 ULONG Length,
615 PCACHE_SEGMENT* CacheSeg)
616 {
617 PCACHE_SEGMENT current;
618 ULONG i;
619 PCACHE_SEGMENT* CacheSegList;
620 PCACHE_SEGMENT Previous = NULL;
621
622 assert(Bcb);
623
624 DPRINT("CcRosGetCacheSegmentChain()\n");
625
626 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
627
628 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
629 (Length / Bcb->CacheSegmentSize));
630
631 /*
632 * Look for a cache segment already mapping the same data.
633 */
634 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
635 {
636 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
637 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
638 if (current != NULL)
639 {
640 CacheSegList[i] = current;
641 }
642 else
643 {
644 CcRosCreateCacheSegment(Bcb, CurrentOffset, &current, FALSE);
645 CacheSegList[i] = current;
646 }
647 }
648
649 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
650 {
651 ExAcquireFastMutex(&CacheSegList[i]->Lock);
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 ExAcquireFastMutex(&current->Lock);
690 }
691 else
692 {
693 /*
694 * Otherwise create a new segment.
695 */
696 Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current, TRUE);
697 if (!NT_SUCCESS(Status))
698 {
699 return Status;
700 }
701 }
702 /*
703 * Return information about the segment to the caller.
704 */
705 *UptoDate = current->Valid;
706 *BaseAddress = current->BaseAddress;
707 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
708 *CacheSeg = current;
709 *BaseOffset = current->FileOffset;
710 return(STATUS_SUCCESS);
711 }
712
713 NTSTATUS STDCALL
714 CcRosRequestCacheSegment(PBCB Bcb,
715 ULONG FileOffset,
716 PVOID* BaseAddress,
717 PBOOLEAN UptoDate,
718 PCACHE_SEGMENT* CacheSeg)
719 /*
720 * FUNCTION: Request a page mapping for a BCB
721 */
722 {
723 ULONG BaseOffset;
724
725 assert(Bcb);
726
727 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
728 {
729 CPRINT("Bad fileoffset %x should be multiple of %x",
730 FileOffset, Bcb->CacheSegmentSize);
731 KeBugCheck(0);
732 }
733
734 return(CcRosGetCacheSegment(Bcb,
735 FileOffset,
736 &BaseOffset,
737 BaseAddress,
738 UptoDate,
739 CacheSeg));
740 }
741 #ifdef CACHE_BITMAP
742 #else
743 STATIC VOID
744 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
745 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
746 {
747 assert(SwapEntry == 0);
748 if (PhysAddr.QuadPart != 0)
749 {
750 MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
751 }
752 }
753 #endif
754 NTSTATUS
755 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
756 /*
757 * FUNCTION: Releases a cache segment associated with a BCB
758 */
759 {
760 #ifdef CACHE_BITMAP
761 ULONG i;
762 ULONG RegionSize;
763 ULONG Base;
764 PHYSICAL_ADDRESS PhysicalAddr;
765 KIRQL oldIrql;
766 #endif
767 DPRINT("Freeing cache segment %x\n", CacheSeg);
768 #ifdef CACHE_BITMAP
769 RegionSize = CacheSeg->Bcb->CacheSegmentSize / PAGE_SIZE;
770
771 /* Unmap all the pages. */
772 for (i = 0; i < RegionSize; i++)
773 {
774 MmDeleteVirtualMapping(NULL,
775 CacheSeg->BaseAddress + (i * PAGE_SIZE),
776 FALSE,
777 NULL,
778 &PhysicalAddr);
779 MmReleasePageMemoryConsumer(MC_CACHE, PhysicalAddr);
780 }
781
782 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
783 /* Deallocate all the pages used. */
784 Base = (ULONG)(CacheSeg->BaseAddress - CiCacheSegMappingRegionBase) / PAGE_SIZE;
785
786 RtlClearBits(&CiCacheSegMappingRegionAllocMap, Base, RegionSize);
787
788 CiCacheSegMappingRegionHint = min (CiCacheSegMappingRegionHint, Base);
789
790 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
791 #else
792 MmLockAddressSpace(MmGetKernelAddressSpace());
793 MmFreeMemoryArea(MmGetKernelAddressSpace(),
794 CacheSeg->BaseAddress,
795 CacheSeg->Bcb->CacheSegmentSize,
796 CcFreeCachePage,
797 NULL);
798 MmUnlockAddressSpace(MmGetKernelAddressSpace());
799 #endif
800 ExFreeToNPagedLookasideList(&CacheSegLookasideList, CacheSeg);
801 return(STATUS_SUCCESS);
802 }
803
804 NTSTATUS
805 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
806 {
807 NTSTATUS Status;
808 KIRQL oldIrql;
809
810 assert(Bcb);
811
812 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
813 Bcb, CacheSeg);
814
815 ExAcquireFastMutex(&ViewLock);
816 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
817 RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
818 RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
819 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
820 if (CacheSeg->Dirty)
821 {
822 RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
823 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
824
825 }
826 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
827 ExReleaseFastMutex(&ViewLock);
828
829 Status = CcRosInternalFreeCacheSegment(CacheSeg);
830 return(Status);
831 }
832
833 VOID STDCALL
834 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
835 IN PLARGE_INTEGER FileOffset OPTIONAL,
836 IN ULONG Length,
837 OUT PIO_STATUS_BLOCK IoStatus)
838 {
839 PBCB Bcb;
840 LARGE_INTEGER Offset;
841 PCACHE_SEGMENT current;
842 NTSTATUS Status;
843 KIRQL oldIrql;
844
845 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
846 SectionObjectPointers, FileOffset, Length, IoStatus);
847
848 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
849 {
850 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
851 assert(Bcb);
852 if (FileOffset)
853 {
854 Offset = *FileOffset;
855 }
856 else
857 {
858 Offset.QuadPart = 0LL;
859 Length = Bcb->FileSize.u.LowPart;
860 }
861
862 if (IoStatus)
863 {
864 IoStatus->Status = STATUS_SUCCESS;
865 IoStatus->Information = 0;
866 }
867
868 while (Length > 0)
869 {
870 current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
871 if (current != NULL)
872 {
873 ExAcquireFastMutex(&current->Lock);
874 if (current->Dirty)
875 {
876 Status = CcRosFlushCacheSegment(current);
877 if (!NT_SUCCESS(Status) && IoStatus != NULL)
878 {
879 IoStatus->Status = Status;
880 }
881 }
882 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
883 ExReleaseFastMutex(&current->Lock);
884 current->ReferenceCount--;
885 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
886 }
887
888 Offset.QuadPart += Bcb->CacheSegmentSize;
889 if (Length > Bcb->CacheSegmentSize)
890 {
891 Length -= Bcb->CacheSegmentSize;
892 }
893 else
894 {
895 Length = 0;
896 }
897 }
898 }
899 else
900 {
901 if (IoStatus)
902 {
903 IoStatus->Status = STATUS_INVALID_PARAMETER;
904 }
905 }
906 }
907
908 NTSTATUS
909 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
910 /*
911 * FUNCTION: Releases the BCB associated with a file object
912 */
913 {
914 PLIST_ENTRY current_entry;
915 PCACHE_SEGMENT current;
916 NTSTATUS Status;
917 LIST_ENTRY FreeList;
918 KIRQL oldIrql;
919
920 assert(Bcb);
921
922 Bcb->RefCount++;
923 ExReleaseFastMutex(&ViewLock);
924
925 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
926
927 ExAcquireFastMutex(&ViewLock);
928 Bcb->RefCount--;
929 if (Bcb->RefCount == 0)
930 {
931 if (Bcb->BcbRemoveListEntry.Flink != NULL)
932 {
933 RemoveEntryList(&Bcb->BcbRemoveListEntry);
934 Bcb->BcbRemoveListEntry.Flink = NULL;
935 }
936
937 FileObject->SectionObjectPointer->SharedCacheMap = NULL;
938
939 /*
940 * Release all cache segments.
941 */
942 InitializeListHead(&FreeList);
943 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
944 current_entry = Bcb->BcbSegmentListHead.Flink;
945 while (!IsListEmpty(&Bcb->BcbSegmentListHead))
946 {
947 current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
948 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
949 RemoveEntryList(&current->CacheSegmentListEntry);
950 RemoveEntryList(&current->CacheSegmentLRUListEntry);
951 if (current->Dirty)
952 {
953 RemoveEntryList(&current->DirtySegmentListEntry);
954 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
955 DPRINT1("Freeing dirty segment\n");
956 }
957 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
958 }
959 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
960
961 ExReleaseFastMutex(&ViewLock);
962 ObDereferenceObject (Bcb->FileObject);
963
964 while (!IsListEmpty(&FreeList))
965 {
966 current_entry = RemoveTailList(&FreeList);
967 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
968 Status = CcRosInternalFreeCacheSegment(current);
969 }
970 ExFreeToNPagedLookasideList(&BcbLookasideList, Bcb);
971 ExAcquireFastMutex(&ViewLock);
972 }
973 return(STATUS_SUCCESS);
974 }
975
976 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
977 {
978 PBCB Bcb;
979 ExAcquireFastMutex(&ViewLock);
980 Bcb = (PBCB)FileObject->SectionObjectPointer->SharedCacheMap;
981 assert(Bcb);
982 if (Bcb->RefCount == 0)
983 {
984 assert(Bcb->BcbRemoveListEntry.Flink != NULL);
985 RemoveEntryList(&Bcb->BcbRemoveListEntry);
986 Bcb->BcbRemoveListEntry.Flink = NULL;
987
988 }
989 else
990 {
991 assert(Bcb->BcbRemoveListEntry.Flink == NULL);
992 }
993 Bcb->RefCount++;
994 ExReleaseFastMutex(&ViewLock);
995 }
996
997 VOID CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer)
998 {
999 PBCB Bcb;
1000 // DPRINT1("CcRosSetRemoveOnClose()\n");
1001 ExAcquireFastMutex(&ViewLock);
1002 Bcb = (PBCB)SectionObjectPointer->SharedCacheMap;
1003 if (Bcb)
1004 {
1005 Bcb->RemoveOnClose = TRUE;
1006 if (Bcb->RefCount == 0)
1007 {
1008 CcRosDeleteFileCache(Bcb->FileObject, Bcb);
1009 }
1010 }
1011 ExReleaseFastMutex(&ViewLock);
1012 }
1013
1014
1015 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
1016 {
1017 PBCB Bcb;
1018 ExAcquireFastMutex(&ViewLock);
1019 Bcb = (PBCB)FileObject->SectionObjectPointer->SharedCacheMap;
1020 assert(Bcb);
1021 if (Bcb->RefCount > 0)
1022 {
1023 Bcb->RefCount--;
1024 if (Bcb->RefCount == 0)
1025 {
1026 MmFreeSectionSegments(Bcb->FileObject);
1027 if (Bcb->RemoveOnClose)
1028 {
1029 CcRosDeleteFileCache(FileObject, Bcb);
1030 }
1031 else
1032 {
1033 Bcb->TimeStamp = CcTimeStamp;
1034 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1035 }
1036 }
1037 }
1038 ExReleaseFastMutex(&ViewLock);
1039 }
1040
1041 NTSTATUS STDCALL
1042 CcRosReleaseFileCache(PFILE_OBJECT FileObject)
1043 /*
1044 * FUNCTION: Called by the file system when a handle to a file object
1045 * has been closed.
1046 */
1047 {
1048 PBCB Bcb;
1049
1050 ExAcquireFastMutex(&ViewLock);
1051
1052 if (FileObject->SectionObjectPointer->SharedCacheMap != NULL)
1053 {
1054 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1055 if (FileObject->PrivateCacheMap != NULL)
1056 {
1057 FileObject->PrivateCacheMap = NULL;
1058 if (Bcb->RefCount > 0)
1059 {
1060 Bcb->RefCount--;
1061 if (Bcb->RefCount == 0)
1062 {
1063 if (Bcb->RemoveOnClose)
1064 {
1065 CcRosDeleteFileCache(FileObject, Bcb);
1066 }
1067 else
1068 {
1069 Bcb->TimeStamp = CcTimeStamp;
1070 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1071 }
1072 }
1073 }
1074 }
1075 }
1076 ExReleaseFastMutex(&ViewLock);
1077 return(STATUS_SUCCESS);
1078 }
1079
1080 NTSTATUS STDCALL
1081 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
1082 ULONG CacheSegmentSize)
1083 /*
1084 * FUNCTION: Initializes a BCB for a file object
1085 */
1086 {
1087 PBCB Bcb;
1088 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1089 FileObject, Bcb, CacheSegmentSize);
1090
1091 ExAcquireFastMutex(&ViewLock);
1092
1093 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1094 if (Bcb == NULL)
1095 {
1096 Bcb = ExAllocateFromNPagedLookasideList(&BcbLookasideList);
1097 if (Bcb == NULL)
1098 {
1099 ExReleaseFastMutex(&ViewLock);
1100 return(STATUS_UNSUCCESSFUL);
1101 }
1102 memset(Bcb, 0, sizeof(BCB));
1103 ObReferenceObjectByPointer(FileObject,
1104 FILE_ALL_ACCESS,
1105 NULL,
1106 KernelMode);
1107 Bcb->FileObject = FileObject;
1108 Bcb->CacheSegmentSize = CacheSegmentSize;
1109 if (FileObject->FsContext)
1110 {
1111 Bcb->AllocationSize =
1112 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->AllocationSize;
1113 Bcb->FileSize =
1114 ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->FileSize;
1115 }
1116 KeInitializeSpinLock(&Bcb->BcbLock);
1117 InitializeListHead(&Bcb->BcbSegmentListHead);
1118 FileObject->SectionObjectPointer->SharedCacheMap = Bcb;
1119 }
1120 if (FileObject->PrivateCacheMap == NULL)
1121 {
1122 FileObject->PrivateCacheMap = Bcb;
1123 Bcb->RefCount++;
1124 }
1125 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1126 {
1127 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1128 Bcb->BcbRemoveListEntry.Flink = NULL;
1129 }
1130 ExReleaseFastMutex(&ViewLock);
1131
1132 return(STATUS_SUCCESS);
1133 }
1134
1135 PFILE_OBJECT STDCALL
1136 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
1137 {
1138 PBCB Bcb;
1139 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
1140 {
1141 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
1142 assert(Bcb);
1143 return Bcb->FileObject;
1144 }
1145 return NULL;
1146 }
1147
1148 VOID STDCALL
1149 CmLazyCloseThreadMain(PVOID Ignored)
1150 {
1151 LARGE_INTEGER Timeout;
1152 PLIST_ENTRY current_entry;
1153 PBCB current;
1154 ULONG RemoveTimeStamp;
1155 NTSTATUS Status;
1156
1157 KeQuerySystemTime (&Timeout);
1158
1159 while (1)
1160 {
1161 Timeout.QuadPart += 100000000LL; // 10sec
1162 Status = KeWaitForSingleObject(&LazyCloseThreadEvent,
1163 0,
1164 KernelMode,
1165 FALSE,
1166 &Timeout);
1167
1168 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp);
1169
1170 if (!NT_SUCCESS(Status))
1171 {
1172 DbgPrint("LazyCloseThread: Wait failed\n");
1173 KeBugCheck(0);
1174 break;
1175 }
1176 if (LazyCloseThreadShouldTerminate)
1177 {
1178 DbgPrint("LazyCloseThread: Terminating\n");
1179 break;
1180 }
1181
1182 ExAcquireFastMutex(&ViewLock);
1183 CcTimeStamp++;
1184 if (CcTimeStamp >= 30)
1185 {
1186 RemoveTimeStamp = CcTimeStamp - 30; /* 5min = 10sec * 30 */
1187 while (!IsListEmpty(&ClosedListHead))
1188 {
1189 current_entry = ClosedListHead.Blink;
1190 current = CONTAINING_RECORD(current_entry, BCB, BcbRemoveListEntry);
1191 if (current->TimeStamp >= RemoveTimeStamp)
1192 {
1193 break;
1194 }
1195 CcRosDeleteFileCache(current->FileObject, current);
1196 }
1197 }
1198 ExReleaseFastMutex(&ViewLock);
1199 }
1200 }
1201
1202 VOID
1203 CcInitView(VOID)
1204 {
1205 #ifdef CACHE_BITMAP
1206 PMEMORY_AREA marea;
1207 PVOID Buffer;
1208 #endif
1209 NTSTATUS Status;
1210 KPRIORITY Priority;
1211
1212 DPRINT("CcInitView()\n");
1213 #ifdef CACHE_BITMAP
1214 CiCacheSegMappingRegionHint = 0;
1215 CiCacheSegMappingRegionBase = NULL;
1216
1217 MmLockAddressSpace(MmGetKernelAddressSpace());
1218
1219 Status = MmCreateMemoryArea(NULL,
1220 MmGetKernelAddressSpace(),
1221 MEMORY_AREA_CACHE_SEGMENT,
1222 &CiCacheSegMappingRegionBase,
1223 CI_CACHESEG_MAPPING_REGION_SIZE,
1224 0,
1225 &marea,
1226 FALSE,
1227 FALSE);
1228 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1229 if (!NT_SUCCESS(Status))
1230 {
1231 KeBugCheck(0);
1232 }
1233
1234 Buffer = ExAllocatePool(NonPagedPool, CI_CACHESEG_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
1235
1236 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap, Buffer, CI_CACHESEG_MAPPING_REGION_SIZE / PAGE_SIZE);
1237 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap);
1238
1239 KeInitializeSpinLock(&CiCacheSegMappingRegionLock);
1240 #endif
1241 InitializeListHead(&CacheSegmentListHead);
1242 InitializeListHead(&DirtySegmentListHead);
1243 InitializeListHead(&CacheSegmentLRUListHead);
1244 InitializeListHead(&ClosedListHead);
1245 ExInitializeFastMutex(&ViewLock);
1246 ExInitializeNPagedLookasideList (&iBcbLookasideList,
1247 NULL,
1248 NULL,
1249 0,
1250 sizeof(INTERNAL_BCB),
1251 TAG_IBCB,
1252 20);
1253 ExInitializeNPagedLookasideList (&BcbLookasideList,
1254 NULL,
1255 NULL,
1256 0,
1257 sizeof(BCB),
1258 TAG_BCB,
1259 20);
1260 ExInitializeNPagedLookasideList (&CacheSegLookasideList,
1261 NULL,
1262 NULL,
1263 0,
1264 sizeof(CACHE_SEGMENT),
1265 TAG_CSEG,
1266 20);
1267
1268 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
1269
1270 CcInitCacheZeroPage();
1271
1272 CcTimeStamp = 0;
1273 LazyCloseThreadShouldTerminate = FALSE;
1274 KeInitializeEvent (&LazyCloseThreadEvent, SynchronizationEvent, FALSE);
1275 Status = PsCreateSystemThread(&LazyCloseThreadHandle,
1276 THREAD_ALL_ACCESS,
1277 NULL,
1278 NULL,
1279 &LazyCloseThreadId,
1280 (PKSTART_ROUTINE)CmLazyCloseThreadMain,
1281 NULL);
1282 if (NT_SUCCESS(Status))
1283 {
1284 Priority = LOW_REALTIME_PRIORITY;
1285 NtSetInformationThread(LazyCloseThreadHandle,
1286 ThreadPriority,
1287 &Priority,
1288 sizeof(Priority));
1289 }
1290
1291 }
1292
1293 /* EOF */
1294
1295
1296
1297
1298
1299
1300