be8e239b80bd71f36be0d18582451381d63bc7fc
[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 #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