2003-02-18 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.58 2003/02/18 22:06:53 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 PLIST_ENTRY current_entry;
458 NTSTATUS Status;
459 KIRQL oldIrql;
460 #ifdef CACHE_BITMAP
461 ULONG StartingOffset;
462 #endif
463
464 assert(Bcb);
465
466 DPRINT("CcRosCreateCacheSegment()\n");
467
468 current = ExAllocateFromNPagedLookasideList(&CacheSegLookasideList);
469 current->Valid = FALSE;
470 current->Dirty = FALSE;
471 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
472 current->Bcb = Bcb;
473 current->MappedCount = 0;
474 current->DirtySegmentListEntry.Flink = NULL;
475 current->DirtySegmentListEntry.Blink = NULL;
476 current->ReferenceCount = 1;
477 ExInitializeFastMutex(&current->Lock);
478 ExAcquireFastMutex(&current->Lock);
479 ExAcquireFastMutex(&ViewLock);
480
481 *CacheSeg = current;
482 /* There is window between the call to CcRosLookupCacheSegment
483 * and CcRosCreateCacheSegment. We must check if a segment on
484 * the fileoffset exist. If there exist a segment, we release
485 * our new created segment and return the existing one.
486 */
487 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
488 current_entry = Bcb->BcbSegmentListHead.Flink;
489 while (current_entry != &Bcb->BcbSegmentListHead)
490 {
491 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
492 BcbSegmentListEntry);
493 if (current->FileOffset <= FileOffset &&
494 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
495 {
496 current->ReferenceCount++;
497 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
498 ExReleaseFastMutex(&(*CacheSeg)->Lock);
499 ExReleaseFastMutex(&ViewLock);
500 ExFreeToNPagedLookasideList(&CacheSegLookasideList, *CacheSeg);
501 *CacheSeg = current;
502 if (Lock)
503 {
504 ExAcquireFastMutex(&current->Lock);
505 }
506 return STATUS_SUCCESS;
507 }
508 current_entry = current_entry->Flink;
509 }
510 /* There was no existing segment. */
511 current = *CacheSeg;
512 InsertTailList(&Bcb->BcbSegmentListHead, &current->BcbSegmentListEntry);
513 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
514 InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
515 InsertTailList(&CacheSegmentLRUListHead, &current->CacheSegmentLRUListEntry);
516 ExReleaseFastMutex(&ViewLock);
517 #ifdef CACHE_BITMAP
518 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
519
520 StartingOffset = RtlFindClearBitsAndSet(&CiCacheSegMappingRegionAllocMap, Bcb->CacheSegmentSize / PAGE_SIZE, CiCacheSegMappingRegionHint);
521
522 if (StartingOffset == 0xffffffff)
523 {
524 DPRINT1("Out of CacheSeg mapping space\n");
525 KeBugCheck(0);
526 }
527
528 current->BaseAddress = CiCacheSegMappingRegionBase + StartingOffset * PAGE_SIZE;
529
530 if (CiCacheSegMappingRegionHint == StartingOffset)
531 {
532 CiCacheSegMappingRegionHint += Bcb->CacheSegmentSize / PAGE_SIZE;
533 }
534
535 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
536 #else
537 MmLockAddressSpace(MmGetKernelAddressSpace());
538 current->BaseAddress = NULL;
539 Status = MmCreateMemoryArea(NULL,
540 MmGetKernelAddressSpace(),
541 MEMORY_AREA_CACHE_SEGMENT,
542 &current->BaseAddress,
543 Bcb->CacheSegmentSize,
544 PAGE_READWRITE,
545 (PMEMORY_AREA*)&current->MemoryArea,
546 FALSE);
547 MmUnlockAddressSpace(MmGetKernelAddressSpace());
548 if (!NT_SUCCESS(Status))
549 {
550 KeBugCheck(0);
551 }
552 #endif
553 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
554 {
555 PHYSICAL_ADDRESS Page;
556
557 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
558 if (!NT_SUCCESS(Status))
559 {
560 KeBugCheck(0);
561 }
562
563 Status = MmCreateVirtualMapping(NULL,
564 current->BaseAddress + (i * PAGE_SIZE),
565 PAGE_READWRITE,
566 Page,
567 TRUE);
568 if (!NT_SUCCESS(Status))
569 {
570 KeBugCheck(0);
571 }
572 }
573 if (!Lock)
574 {
575 ExReleaseFastMutex(&current->Lock);
576 }
577
578 return(STATUS_SUCCESS);
579 }
580
581 NTSTATUS
582 CcRosGetCacheSegmentChain(PBCB Bcb,
583 ULONG FileOffset,
584 ULONG Length,
585 PCACHE_SEGMENT* CacheSeg)
586 {
587 PCACHE_SEGMENT current;
588 ULONG i;
589 PCACHE_SEGMENT* CacheSegList;
590 PCACHE_SEGMENT Previous = NULL;
591
592 assert(Bcb);
593
594 DPRINT("CcRosGetCacheSegmentChain()\n");
595
596 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
597
598 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
599 (Length / Bcb->CacheSegmentSize));
600
601 /*
602 * Look for a cache segment already mapping the same data.
603 */
604 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
605 {
606 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
607 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
608 if (current != NULL)
609 {
610 CacheSegList[i] = current;
611 }
612 else
613 {
614 CcRosCreateCacheSegment(Bcb, CurrentOffset, &current, FALSE);
615 CacheSegList[i] = current;
616 }
617 }
618
619 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
620 {
621 ExAcquireFastMutex(&CacheSegList[i]->Lock);
622 if (i == 0)
623 {
624 *CacheSeg = CacheSegList[i];
625 Previous = CacheSegList[i];
626 }
627 else
628 {
629 Previous->NextInChain = CacheSegList[i];
630 Previous = CacheSegList[i];
631 }
632 }
633 Previous->NextInChain = NULL;
634
635 return(STATUS_SUCCESS);
636 }
637
638 NTSTATUS
639 CcRosGetCacheSegment(PBCB Bcb,
640 ULONG FileOffset,
641 PULONG BaseOffset,
642 PVOID* BaseAddress,
643 PBOOLEAN UptoDate,
644 PCACHE_SEGMENT* CacheSeg)
645 {
646 PCACHE_SEGMENT current;
647 NTSTATUS Status;
648
649 assert(Bcb);
650
651 DPRINT("CcRosGetCacheSegment()\n");
652
653 /*
654 * Look for a cache segment already mapping the same data.
655 */
656 current = CcRosLookupCacheSegment(Bcb, FileOffset);
657 if (current != NULL)
658 {
659 ExAcquireFastMutex(&current->Lock);
660 }
661 else
662 {
663 /*
664 * Otherwise create a new segment.
665 */
666 Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current, TRUE);
667 }
668 /*
669 * Return information about the segment to the caller.
670 */
671 *UptoDate = current->Valid;
672 *BaseAddress = current->BaseAddress;
673 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
674 *CacheSeg = current;
675 *BaseOffset = current->FileOffset;
676 return(STATUS_SUCCESS);
677 }
678
679 NTSTATUS STDCALL
680 CcRosRequestCacheSegment(PBCB Bcb,
681 ULONG FileOffset,
682 PVOID* BaseAddress,
683 PBOOLEAN UptoDate,
684 PCACHE_SEGMENT* CacheSeg)
685 /*
686 * FUNCTION: Request a page mapping for a BCB
687 */
688 {
689 ULONG BaseOffset;
690
691 assert(Bcb);
692
693 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
694 {
695 CPRINT("Bad fileoffset %x should be multiple of %x",
696 FileOffset, Bcb->CacheSegmentSize);
697 KeBugCheck(0);
698 }
699
700 return(CcRosGetCacheSegment(Bcb,
701 FileOffset,
702 &BaseOffset,
703 BaseAddress,
704 UptoDate,
705 CacheSeg));
706 }
707 #ifdef CACHE_BITMAP
708 #else
709 STATIC VOID
710 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
711 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
712 {
713 assert(SwapEntry == 0);
714 if (PhysAddr.QuadPart != 0)
715 {
716 MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
717 }
718 }
719 #endif
720 NTSTATUS
721 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
722 /*
723 * FUNCTION: Releases a cache segment associated with a BCB
724 */
725 {
726 #ifdef CACHE_BITMAP
727 ULONG i;
728 ULONG RegionSize;
729 ULONG Base;
730 PHYSICAL_ADDRESS PhysicalAddr;
731 KIRQL oldIrql;
732 #endif
733 DPRINT("Freeing cache segment %x\n", CacheSeg);
734 #ifdef CACHE_BITMAP
735 RegionSize = CacheSeg->Bcb->CacheSegmentSize / PAGE_SIZE;
736
737 /* Unmap all the pages. */
738 for (i = 0; i < RegionSize; i++)
739 {
740 MmDeleteVirtualMapping(NULL,
741 CacheSeg->BaseAddress + (i * PAGE_SIZE),
742 FALSE,
743 NULL,
744 &PhysicalAddr);
745 MmReleasePageMemoryConsumer(MC_CACHE, PhysicalAddr);
746 }
747
748 KeAcquireSpinLock(&CiCacheSegMappingRegionLock, &oldIrql);
749 /* Deallocate all the pages used. */
750 Base = (ULONG)(CacheSeg->BaseAddress - CiCacheSegMappingRegionBase) / PAGE_SIZE;
751
752 RtlClearBits(&CiCacheSegMappingRegionAllocMap, Base, RegionSize);
753
754 CiCacheSegMappingRegionHint = min (CiCacheSegMappingRegionHint, Base);
755
756 KeReleaseSpinLock(&CiCacheSegMappingRegionLock, oldIrql);
757 #else
758 MmLockAddressSpace(MmGetKernelAddressSpace());
759 MmFreeMemoryArea(MmGetKernelAddressSpace(),
760 CacheSeg->BaseAddress,
761 CacheSeg->Bcb->CacheSegmentSize,
762 CcFreeCachePage,
763 NULL);
764 MmUnlockAddressSpace(MmGetKernelAddressSpace());
765 #endif
766 ExFreeToNPagedLookasideList(&CacheSegLookasideList, CacheSeg);
767 return(STATUS_SUCCESS);
768 }
769
770 NTSTATUS
771 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
772 {
773 NTSTATUS Status;
774 KIRQL oldIrql;
775
776 assert(Bcb);
777
778 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
779 Bcb, CacheSeg);
780
781 ExAcquireFastMutex(&ViewLock);
782 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
783 RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
784 RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
785 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
786 if (CacheSeg->Dirty)
787 {
788 RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
789 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
790
791 }
792 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
793 ExReleaseFastMutex(&ViewLock);
794
795 Status = CcRosInternalFreeCacheSegment(CacheSeg);
796 return(Status);
797 }
798
799 VOID STDCALL
800 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
801 IN PLARGE_INTEGER FileOffset OPTIONAL,
802 IN ULONG Length,
803 OUT PIO_STATUS_BLOCK IoStatus)
804 {
805 PBCB Bcb;
806 LARGE_INTEGER Offset;
807 PCACHE_SEGMENT current;
808 NTSTATUS Status;
809 KIRQL oldIrql;
810
811 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
812 SectionObjectPointers, FileOffset, Length, IoStatus);
813
814 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
815 {
816 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
817 assert(Bcb);
818 if (FileOffset)
819 {
820 Offset = *FileOffset;
821 }
822 else
823 {
824 Offset.QuadPart = 0LL;
825 Length = Bcb->FileSize.u.LowPart;
826 }
827
828 if (IoStatus)
829 {
830 IoStatus->Status = STATUS_SUCCESS;
831 IoStatus->Information = 0;
832 }
833
834 while (Length > 0)
835 {
836 current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
837 if (current != NULL)
838 {
839 ExAcquireFastMutex(&current->Lock);
840 if (current->Dirty)
841 {
842 Status = CcRosFlushCacheSegment(current);
843 if (!NT_SUCCESS(Status) && IoStatus != NULL)
844 {
845 IoStatus->Status = Status;
846 }
847 }
848 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
849 ExReleaseFastMutex(&current->Lock);
850 current->ReferenceCount--;
851 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
852 }
853
854 Offset.QuadPart += Bcb->CacheSegmentSize;
855 if (Length > Bcb->CacheSegmentSize)
856 {
857 Length -= Bcb->CacheSegmentSize;
858 }
859 else
860 {
861 Length = 0;
862 }
863 }
864 }
865 else
866 {
867 if (IoStatus)
868 {
869 IoStatus->Status = STATUS_INVALID_PARAMETER;
870 }
871 }
872 }
873
874 NTSTATUS
875 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
876 /*
877 * FUNCTION: Releases the BCB associated with a file object
878 */
879 {
880 PLIST_ENTRY current_entry;
881 PCACHE_SEGMENT current;
882 NTSTATUS Status;
883 LIST_ENTRY FreeList;
884 KIRQL oldIrql;
885
886 assert(Bcb);
887
888 Bcb->RefCount++;
889 ExReleaseFastMutex(&ViewLock);
890
891 CcFlushCache(FileObject->SectionObjectPointers, NULL, 0, NULL);
892
893 ExAcquireFastMutex(&ViewLock);
894 Bcb->RefCount--;
895 if (Bcb->RefCount == 0)
896 {
897 if (Bcb->BcbRemoveListEntry.Flink != NULL)
898 {
899 RemoveEntryList(&Bcb->BcbRemoveListEntry);
900 Bcb->BcbRemoveListEntry.Flink = NULL;
901 }
902
903 FileObject->SectionObjectPointers->SharedCacheMap = NULL;
904
905 /*
906 * Release all cache segments.
907 */
908 InitializeListHead(&FreeList);
909 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
910 current_entry = Bcb->BcbSegmentListHead.Flink;
911 while (!IsListEmpty(&Bcb->BcbSegmentListHead))
912 {
913 current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
914 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
915 RemoveEntryList(&current->CacheSegmentListEntry);
916 RemoveEntryList(&current->CacheSegmentLRUListEntry);
917 if (current->Dirty)
918 {
919 RemoveEntryList(&current->DirtySegmentListEntry);
920 DirtyPageCount -= Bcb->CacheSegmentSize / PAGE_SIZE;
921 DPRINT1("Freeing dirty segment\n");
922 }
923 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
924 }
925 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
926
927 ExReleaseFastMutex(&ViewLock);
928 ObDereferenceObject (Bcb->FileObject);
929
930 while (!IsListEmpty(&FreeList))
931 {
932 current_entry = RemoveTailList(&FreeList);
933 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
934 Status = CcRosInternalFreeCacheSegment(current);
935 }
936 ExFreeToNPagedLookasideList(&BcbLookasideList, Bcb);
937 ExAcquireFastMutex(&ViewLock);
938 }
939 return(STATUS_SUCCESS);
940 }
941
942 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
943 {
944 PBCB Bcb;
945 ExAcquireFastMutex(&ViewLock);
946 Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
947 assert(Bcb);
948 Bcb->RefCount++;
949 ExReleaseFastMutex(&ViewLock);
950 }
951
952 VOID CcRosSetRemoveOnClose(PSECTION_OBJECT_POINTERS SectionObjectPointer)
953 {
954 PBCB Bcb;
955 // DPRINT1("CcRosSetRemoveOnClose()\n");
956 ExAcquireFastMutex(&ViewLock);
957 Bcb = (PBCB)SectionObjectPointer->SharedCacheMap;
958 if (Bcb)
959 {
960 Bcb->RemoveOnClose = TRUE;
961 if (Bcb->RefCount == 0)
962 {
963 CcRosDeleteFileCache(Bcb->FileObject, Bcb);
964 }
965 }
966 ExReleaseFastMutex(&ViewLock);
967 }
968
969
970 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
971 {
972 PBCB Bcb;
973 ExAcquireFastMutex(&ViewLock);
974 Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
975 assert(Bcb);
976 if (Bcb->RefCount > 0)
977 {
978 Bcb->RefCount--;
979 if (Bcb->RefCount == 0)
980 {
981 MmFreeSectionSegments(Bcb->FileObject);
982 if (Bcb->RemoveOnClose)
983 {
984 CcRosDeleteFileCache(FileObject, Bcb);
985 }
986 else
987 {
988 Bcb->TimeStamp = CcTimeStamp;
989 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
990 }
991 }
992 }
993 ExReleaseFastMutex(&ViewLock);
994 }
995
996 NTSTATUS STDCALL
997 CcRosReleaseFileCache(PFILE_OBJECT FileObject)
998 /*
999 * FUNCTION: Called by the file system when a handle to a file object
1000 * has been closed.
1001 */
1002 {
1003 PBCB Bcb;
1004
1005 ExAcquireFastMutex(&ViewLock);
1006
1007 if (FileObject->SectionObjectPointers->SharedCacheMap != NULL)
1008 {
1009 Bcb = FileObject->SectionObjectPointers->SharedCacheMap;
1010 if (FileObject->PrivateCacheMap != NULL)
1011 {
1012 FileObject->PrivateCacheMap = NULL;
1013 if (Bcb->RefCount > 0)
1014 {
1015 Bcb->RefCount--;
1016 if (Bcb->RefCount == 0)
1017 {
1018 if (Bcb->RemoveOnClose)
1019 {
1020 CcRosDeleteFileCache(FileObject, Bcb);
1021 }
1022 else
1023 {
1024 Bcb->TimeStamp = CcTimeStamp;
1025 InsertHeadList(&ClosedListHead, &Bcb->BcbRemoveListEntry);
1026 }
1027 }
1028 }
1029 }
1030 }
1031 ExReleaseFastMutex(&ViewLock);
1032 return(STATUS_SUCCESS);
1033 }
1034
1035 NTSTATUS STDCALL
1036 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
1037 ULONG CacheSegmentSize)
1038 /*
1039 * FUNCTION: Initializes a BCB for a file object
1040 */
1041 {
1042 PBCB Bcb;
1043 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
1044 FileObject, Bcb, CacheSegmentSize);
1045
1046 ExAcquireFastMutex(&ViewLock);
1047
1048 Bcb = FileObject->SectionObjectPointers->SharedCacheMap;
1049 if (Bcb == NULL)
1050 {
1051 Bcb = ExAllocateFromNPagedLookasideList(&BcbLookasideList);
1052 if (Bcb == NULL)
1053 {
1054 ExReleaseFastMutex(&ViewLock);
1055 return(STATUS_UNSUCCESSFUL);
1056 }
1057 memset(Bcb, 0, sizeof(BCB));
1058 ObReferenceObjectByPointer(FileObject,
1059 FILE_ALL_ACCESS,
1060 NULL,
1061 KernelMode);
1062 Bcb->FileObject = FileObject;
1063 Bcb->CacheSegmentSize = CacheSegmentSize;
1064 if (FileObject->FsContext)
1065 {
1066 Bcb->AllocationSize =
1067 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->AllocationSize;
1068 Bcb->FileSize =
1069 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->FileSize;
1070 }
1071 KeInitializeSpinLock(&Bcb->BcbLock);
1072 InitializeListHead(&Bcb->BcbSegmentListHead);
1073 FileObject->SectionObjectPointers->SharedCacheMap = Bcb;
1074 }
1075 if (FileObject->PrivateCacheMap == NULL)
1076 {
1077 FileObject->PrivateCacheMap = Bcb;
1078 Bcb->RefCount++;
1079 }
1080 if (Bcb->BcbRemoveListEntry.Flink != NULL)
1081 {
1082 RemoveEntryList(&Bcb->BcbRemoveListEntry);
1083 Bcb->BcbRemoveListEntry.Flink = NULL;
1084 }
1085 ExReleaseFastMutex(&ViewLock);
1086
1087 return(STATUS_SUCCESS);
1088 }
1089
1090 PFILE_OBJECT STDCALL
1091 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
1092 {
1093 PBCB Bcb;
1094 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
1095 {
1096 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
1097 assert(Bcb);
1098 return Bcb->FileObject;
1099 }
1100 return NULL;
1101 }
1102
1103 VOID STDCALL
1104 CmLazyCloseThreadMain(PVOID Ignored)
1105 {
1106 LARGE_INTEGER Timeout;
1107 PLIST_ENTRY current_entry;
1108 PBCB current;
1109 ULONG RemoveTimeStamp;
1110 NTSTATUS Status;
1111
1112 KeQuerySystemTime (&Timeout);
1113
1114 while (1)
1115 {
1116 Timeout.QuadPart += 100000000LL; // 10sec
1117 Status = KeWaitForSingleObject(&LazyCloseThreadEvent,
1118 0,
1119 KernelMode,
1120 FALSE,
1121 &Timeout);
1122
1123 DPRINT("LazyCloseThreadMain %d\n", CcTimeStamp);
1124
1125 if (!NT_SUCCESS(Status))
1126 {
1127 DbgPrint("LazyCloseThread: Wait failed\n");
1128 KeBugCheck(0);
1129 break;
1130 }
1131 if (LazyCloseThreadShouldTerminate)
1132 {
1133 DbgPrint("LazyCloseThread: Terminating\n");
1134 break;
1135 }
1136
1137 ExAcquireFastMutex(&ViewLock);
1138 CcTimeStamp++;
1139 if (CcTimeStamp >= 30)
1140 {
1141 RemoveTimeStamp = CcTimeStamp - 30; /* 5min = 10sec * 30 */
1142 while (!IsListEmpty(&ClosedListHead))
1143 {
1144 current_entry = ClosedListHead.Blink;
1145 current = CONTAINING_RECORD(current_entry, BCB, BcbRemoveListEntry);
1146 if (current->TimeStamp >= RemoveTimeStamp)
1147 {
1148 break;
1149 }
1150 CcRosDeleteFileCache(current->FileObject, current);
1151 }
1152 }
1153 ExReleaseFastMutex(&ViewLock);
1154 }
1155 }
1156
1157 VOID
1158 CcInitView(VOID)
1159 {
1160 #ifdef CACHE_BITMAP
1161 PMEMORY_AREA marea;
1162 PVOID Buffer;
1163 #endif
1164 NTSTATUS Status;
1165 KPRIORITY Priority;
1166
1167 DPRINT("CcInitView()\n");
1168 #ifdef CACHE_BITMAP
1169 CiCacheSegMappingRegionHint = 0;
1170 CiCacheSegMappingRegionBase = NULL;
1171
1172 MmLockAddressSpace(MmGetKernelAddressSpace());
1173
1174 Status = MmCreateMemoryArea(NULL,
1175 MmGetKernelAddressSpace(),
1176 MEMORY_AREA_CACHE_SEGMENT,
1177 &CiCacheSegMappingRegionBase,
1178 CI_CACHESEG_MAPPING_REGION_SIZE,
1179 0,
1180 &marea,
1181 FALSE);
1182 MmUnlockAddressSpace(MmGetKernelAddressSpace());
1183 if (!NT_SUCCESS(Status))
1184 {
1185 KeBugCheck(0);
1186 }
1187
1188 Buffer = ExAllocatePool(NonPagedPool, CI_CACHESEG_MAPPING_REGION_SIZE / (PAGE_SIZE * 8));
1189
1190 RtlInitializeBitMap(&CiCacheSegMappingRegionAllocMap, Buffer, CI_CACHESEG_MAPPING_REGION_SIZE / PAGE_SIZE);
1191 RtlClearAllBits(&CiCacheSegMappingRegionAllocMap);
1192
1193 KeInitializeSpinLock(&CiCacheSegMappingRegionLock);
1194 #endif
1195 InitializeListHead(&CacheSegmentListHead);
1196 InitializeListHead(&DirtySegmentListHead);
1197 InitializeListHead(&CacheSegmentLRUListHead);
1198 InitializeListHead(&ClosedListHead);
1199 ExInitializeFastMutex(&ViewLock);
1200 ExInitializeNPagedLookasideList (&iBcbLookasideList,
1201 NULL,
1202 NULL,
1203 0,
1204 sizeof(INTERNAL_BCB),
1205 TAG_IBCB,
1206 20);
1207 ExInitializeNPagedLookasideList (&BcbLookasideList,
1208 NULL,
1209 NULL,
1210 0,
1211 sizeof(BCB),
1212 TAG_BCB,
1213 20);
1214 ExInitializeNPagedLookasideList (&CacheSegLookasideList,
1215 NULL,
1216 NULL,
1217 0,
1218 sizeof(CACHE_SEGMENT),
1219 TAG_CSEG,
1220 20);
1221
1222 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
1223
1224 CcInitCacheZeroPage();
1225
1226 CcTimeStamp = 0;
1227 LazyCloseThreadShouldTerminate = FALSE;
1228 KeInitializeEvent (&LazyCloseThreadEvent, SynchronizationEvent, FALSE);
1229 Status = PsCreateSystemThread(&LazyCloseThreadHandle,
1230 THREAD_ALL_ACCESS,
1231 NULL,
1232 NULL,
1233 &LazyCloseThreadId,
1234 (PKSTART_ROUTINE)CmLazyCloseThreadMain,
1235 NULL);
1236 if (NT_SUCCESS(Status))
1237 {
1238 Priority = LOW_REALTIME_PRIORITY;
1239 NtSetInformationThread(LazyCloseThreadHandle,
1240 ThreadPriority,
1241 &Priority,
1242 sizeof(Priority));
1243 }
1244
1245 }
1246
1247 /* EOF */
1248
1249
1250
1251
1252
1253
1254