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