:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[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 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
67 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
68
69 #define TAG_CSEG  TAG('C', 'S', 'E', 'G')
70 #define TAG_BCB   TAG('B', 'C', 'B', ' ')
71
72 static LIST_ENTRY DirtySegmentListHead;
73 static LIST_ENTRY CacheSegmentListHead;
74 static LIST_ENTRY CacheSegmentLRUListHead;
75
76 static FAST_MUTEX ViewLock;
77
78 void * alloca(size_t size);
79
80 NTSTATUS STDCALL
81 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg);
82
83 /* FUNCTIONS *****************************************************************/
84
85 NTSTATUS STATIC
86 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
87 {
88   NTSTATUS Status;
89   KIRQL oldIrql;
90   Status = WriteCacheSegment(CacheSegment);
91   if (NT_SUCCESS(Status))
92     {
93       ExAcquireFastMutex(&ViewLock);
94       KeAcquireSpinLock(&CacheSegment->Bcb->BcbLock, &oldIrql);
95       CacheSegment->Dirty = FALSE;
96       RemoveEntryList(&CacheSegment->DirtySegmentListEntry);
97       CacheSegment->ReferenceCount--;
98       KeReleaseSpinLock(&CacheSegment->Bcb->BcbLock, oldIrql);
99       ExReleaseFastMutex(&ViewLock);
100     }
101   return(Status);
102 }
103
104 NTSTATUS
105 CcRosFlushDirtyPages(ULONG Target, PULONG Count)
106 {
107   PLIST_ENTRY current_entry;
108   PCACHE_SEGMENT current;
109   ULONG PagesPerSegment;
110   BOOLEAN Locked;
111   NTSTATUS Status;
112
113   DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target);
114
115   (*Count) = 0;
116
117   ExAcquireFastMutex(&ViewLock);
118   current_entry = DirtySegmentListHead.Flink;
119   while (current_entry != &DirtySegmentListHead && Target > 0)
120     {
121       current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, 
122                                   DirtySegmentListEntry);
123       current_entry = current_entry->Flink;
124       Locked = ExTryToAcquireFastMutex(&current->Lock);
125       if (!Locked)
126         {
127           continue;
128         }
129       assert(current->Dirty);
130       if (current->ReferenceCount > 1)
131         {
132           ExReleaseFastMutex(&current->Lock);
133           continue;
134         }
135       ExReleaseFastMutex(&ViewLock);
136       PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
137       Status = CcRosFlushCacheSegment(current);      
138       ExReleaseFastMutex(&current->Lock);
139       if (!NT_SUCCESS(Status))
140       {
141          DPRINT1("CC: Failed to flush cache segment.\n");
142       }
143       else
144       {
145          (*Count) += PagesPerSegment;
146          Target -= PagesPerSegment;     
147       }
148       ExAcquireFastMutex(&ViewLock);
149       current_entry = DirtySegmentListHead.Flink;
150     }
151   ExReleaseFastMutex(&ViewLock);
152   DPRINT("CcRosTrimCache() finished\n");
153   return(STATUS_SUCCESS);
154 }
155
156 NTSTATUS
157 CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
158 /*
159  * FUNCTION: Try to free some memory from the file cache.
160  * ARGUMENTS:
161  *       Target - The number of pages to be freed.
162  *       Priority - The priority of free (currently unused).
163  *       NrFreed - Points to a variable where the number of pages 
164  *                 actually freed is returned.
165  */
166 {
167   PLIST_ENTRY current_entry;
168   PCACHE_SEGMENT current;
169   ULONG PagesPerSegment;
170   ULONG PagesFreed;
171   KIRQL oldIrql;
172   LIST_ENTRY FreeList;
173
174   DPRINT("CcRosTrimCache(Target %d)\n", Target);
175
176   *NrFreed = 0;
177   
178   InitializeListHead(&FreeList);
179   
180   ExAcquireFastMutex(&ViewLock);
181   current_entry = CacheSegmentLRUListHead.Flink;
182   while (current_entry != &CacheSegmentLRUListHead && Target > 0)
183     {
184       current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, 
185                                   CacheSegmentLRUListEntry);
186       current_entry = current_entry->Flink;
187       
188       KeAcquireSpinLock(&current->Bcb->BcbLock, &oldIrql);
189       if (current->ReferenceCount == 0)
190       {
191          RemoveEntryList(&current->BcbSegmentListEntry);
192          KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
193          RemoveEntryList(&current->CacheSegmentListEntry);
194          RemoveEntryList(&current->CacheSegmentLRUListEntry);
195          InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
196       }
197       else
198       {
199          KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
200       }
201   }
202   ExReleaseFastMutex(&ViewLock);
203
204   while (!IsListEmpty(&FreeList))
205   {
206      current_entry = RemoveHeadList(&FreeList);
207      current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, 
208                                  BcbSegmentListEntry);
209      PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
210      PagesFreed = min(PagesPerSegment, Target);
211      Target -= PagesFreed;
212      (*NrFreed) += PagesFreed;
213      CcRosInternalFreeCacheSegment(current);
214   }
215
216   DPRINT("CcRosTrimCache() finished\n");
217   return(STATUS_SUCCESS);
218 }
219
220 NTSTATUS STDCALL 
221 CcRosReleaseCacheSegment(PBCB Bcb,
222                          PCACHE_SEGMENT CacheSeg,
223                          BOOLEAN Valid,
224                          BOOLEAN Dirty,
225                          BOOLEAN Mapped)
226 {
227   BOOLEAN WasDirty = CacheSeg->Dirty;
228   KIRQL oldIrql;
229
230   DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
231          Bcb, CacheSeg, Valid);
232
233   CacheSeg->Valid = Valid;
234   CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
235
236   ExAcquireFastMutex(&ViewLock);
237   if (!WasDirty && CacheSeg->Dirty)
238     {
239       InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
240     }
241   RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
242   InsertTailList(&CacheSegmentLRUListHead, &CacheSeg->CacheSegmentLRUListEntry);
243
244   if (Mapped)
245   {
246      CacheSeg->MappedCount++;
247   }
248   KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
249   CacheSeg->ReferenceCount--;
250   if (Mapped && CacheSeg->MappedCount == 1)
251   {
252       CacheSeg->ReferenceCount++;
253   }
254   if (!WasDirty && CacheSeg->Dirty)
255   {
256       CacheSeg->ReferenceCount++;
257   }
258   KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
259   ExReleaseFastMutex(&ViewLock);
260   ExReleaseFastMutex(&CacheSeg->Lock);
261   
262   return(STATUS_SUCCESS);
263 }
264
265 PCACHE_SEGMENT CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
266 {
267   PLIST_ENTRY current_entry;
268   PCACHE_SEGMENT current;
269   KIRQL oldIrql;
270
271   DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
272
273   KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
274   current_entry = Bcb->BcbSegmentListHead.Flink;
275   while (current_entry != &Bcb->BcbSegmentListHead)
276     {
277       current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, 
278                                   BcbSegmentListEntry);
279       if (current->FileOffset <= FileOffset &&
280           (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
281         {
282           current->ReferenceCount++;
283           KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
284           return(current);
285         }
286       current_entry = current_entry->Flink;
287     }
288   KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
289   return(NULL);
290 }
291
292 NTSTATUS
293 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
294 {
295   PCACHE_SEGMENT CacheSeg;
296   KIRQL oldIrql;
297
298   DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
299
300   CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
301   if (CacheSeg == NULL)
302     {
303       KeBugCheck(0);
304     }
305   ExAcquireFastMutex(&CacheSeg->Lock);
306   if (!CacheSeg->Dirty)
307     {
308       ExAcquireFastMutex(&ViewLock);
309       InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
310       ExReleaseFastMutex(&ViewLock);
311     }
312   else
313   {
314      KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
315      CacheSeg->ReferenceCount--;
316      KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
317   }
318
319
320   CacheSeg->Dirty = TRUE;
321   ExReleaseFastMutex(&CacheSeg->Lock);
322
323   return(STATUS_SUCCESS);
324 }
325
326 NTSTATUS
327 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
328 {
329   PCACHE_SEGMENT CacheSeg;
330   BOOLEAN WasDirty;
331   KIRQL oldIrql;
332
333   DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
334           Bcb, FileOffset, NowDirty);
335
336   CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
337   if (CacheSeg == NULL)
338     {
339       return(STATUS_UNSUCCESSFUL);
340     }
341   ExAcquireFastMutex(&CacheSeg->Lock);
342
343   WasDirty = CacheSeg->Dirty;
344   CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
345
346   CacheSeg->MappedCount--;
347
348   if (!WasDirty && NowDirty)
349   {
350      ExAcquireFastMutex(&ViewLock);
351      InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
352      ExReleaseFastMutex(&ViewLock);
353   }
354
355   KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
356   CacheSeg->ReferenceCount--;
357   if (!WasDirty && NowDirty)
358   {
359      CacheSeg->ReferenceCount++;
360   }
361   if (CacheSeg->MappedCount == 0)
362   {
363      CacheSeg->ReferenceCount--;
364   }
365   KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
366
367   ExReleaseFastMutex(&CacheSeg->Lock);
368   return(STATUS_SUCCESS);
369 }
370
371 NTSTATUS STATIC
372 CcRosCreateCacheSegment(PBCB Bcb,
373                         ULONG FileOffset,
374                         PCACHE_SEGMENT* CacheSeg,
375                         BOOLEAN Lock)
376 {
377   ULONG i;
378   PCACHE_SEGMENT current;
379   PLIST_ENTRY current_entry;
380   NTSTATUS Status;
381   KIRQL oldIrql;
382
383   DPRINT("CcRosCreateCacheSegment()\n");
384
385   current = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_SEGMENT), 
386                                   TAG_CSEG);
387   current->Valid = FALSE;
388   current->Dirty = FALSE;
389   current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
390   current->Bcb = Bcb;
391   current->MappedCount = 0;
392   current->DirtySegmentListEntry.Flink = NULL;
393   current->DirtySegmentListEntry.Blink = NULL;
394   current->ReferenceCount = 1;
395   ExInitializeFastMutex(&current->Lock);
396   ExAcquireFastMutex(&current->Lock);
397   ExAcquireFastMutex(&ViewLock);
398
399   *CacheSeg = current;
400   /* There is window between the call to CcRosLookupCacheSegment
401    * and CcRosCreateCacheSegment. We must check if a segment on
402    * the fileoffset exist. If there exist a segment, we release
403    * our new created segment and return the existing one. 
404    */
405   KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
406   current_entry = Bcb->BcbSegmentListHead.Flink;
407   while (current_entry != &Bcb->BcbSegmentListHead)
408   {
409      current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, 
410                                  BcbSegmentListEntry);
411      if (current->FileOffset <= FileOffset &&
412         (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
413      {
414         current->ReferenceCount++;
415         KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
416         ExReleaseFastMutex(&(*CacheSeg)->Lock);
417         ExReleaseFastMutex(&ViewLock);
418         ExFreePool(*CacheSeg);
419         *CacheSeg = current;
420         if (Lock)
421         {
422           ExAcquireFastMutex(&current->Lock);
423         }
424         return STATUS_SUCCESS;
425      }
426      current_entry = current_entry->Flink;
427   }
428   /* There was no existing segment. */
429   current = *CacheSeg;
430   InsertTailList(&Bcb->BcbSegmentListHead, &current->BcbSegmentListEntry);
431   KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
432   InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
433   InsertTailList(&CacheSegmentLRUListHead, &current->CacheSegmentLRUListEntry);
434   ExReleaseFastMutex(&ViewLock);
435
436   MmLockAddressSpace(MmGetKernelAddressSpace());
437   current->BaseAddress = NULL;
438   Status = MmCreateMemoryArea(NULL,
439                               MmGetKernelAddressSpace(),
440                               MEMORY_AREA_CACHE_SEGMENT,
441                               &current->BaseAddress,
442                               Bcb->CacheSegmentSize,
443                               PAGE_READWRITE,
444                               (PMEMORY_AREA*)&current->MemoryArea,
445                               FALSE);
446   MmUnlockAddressSpace(MmGetKernelAddressSpace());
447   if (!NT_SUCCESS(Status))
448   {
449      KeBugCheck(0);
450   }
451   for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
452   {
453      PHYSICAL_ADDRESS Page;
454       
455      Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
456      if (!NT_SUCCESS(Status))
457      {
458         KeBugCheck(0);
459      }
460       
461      Status = MmCreateVirtualMapping(NULL,
462                                      current->BaseAddress + (i * PAGE_SIZE),
463                                      PAGE_READWRITE,
464                                      Page,
465                                      TRUE);
466      if (!NT_SUCCESS(Status))
467      {
468         KeBugCheck(0);
469      }
470   }
471   if (!Lock)
472   {
473      ExReleaseFastMutex(&current->Lock);
474   }
475
476   return(STATUS_SUCCESS);
477 }
478
479 NTSTATUS
480 CcRosGetCacheSegmentChain(PBCB Bcb,
481                           ULONG FileOffset,
482                           ULONG Length,
483                           PCACHE_SEGMENT* CacheSeg)
484 {
485   PCACHE_SEGMENT current;
486   ULONG i;
487   PCACHE_SEGMENT* CacheSegList;
488   PCACHE_SEGMENT Previous = NULL;
489
490   DPRINT("CcRosGetCacheSegmentChain()\n");
491
492   Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
493
494   CacheSegList = alloca(sizeof(PCACHE_SEGMENT) * 
495                         (Length / Bcb->CacheSegmentSize));
496
497   /*
498    * Look for a cache segment already mapping the same data.
499    */
500   for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
501     {
502       ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
503       current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
504       if (current != NULL)
505         {
506           CacheSegList[i] = current;
507         }
508       else
509         {
510           CcRosCreateCacheSegment(Bcb, CurrentOffset, &current, FALSE);
511           CacheSegList[i] = current;
512         }
513     }
514
515   for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
516     {
517       ExAcquireFastMutex(&CacheSegList[i]->Lock);
518       if (i == 0)
519         {
520           *CacheSeg = CacheSegList[i];
521           Previous = CacheSegList[i];
522         }
523       else
524         {
525           Previous->NextInChain = CacheSegList[i];
526           Previous = CacheSegList[i];
527         }
528     }
529   Previous->NextInChain = NULL;
530   
531   return(STATUS_SUCCESS);
532 }
533
534 NTSTATUS
535 CcRosGetCacheSegment(PBCB Bcb,
536                      ULONG FileOffset,
537                      PULONG BaseOffset,
538                      PVOID* BaseAddress,
539                      PBOOLEAN UptoDate,
540                      PCACHE_SEGMENT* CacheSeg)
541 {
542    PCACHE_SEGMENT current;
543    NTSTATUS Status;
544
545    DPRINT("CcRosGetCacheSegment()\n");
546
547    /*
548     * Look for a cache segment already mapping the same data.
549     */
550    current = CcRosLookupCacheSegment(Bcb, FileOffset);
551    if (current != NULL)
552    {
553       ExAcquireFastMutex(&current->Lock);
554    }
555    else
556    {
557      /*
558       * Otherwise create a new segment.
559       */
560       Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current, TRUE);
561    }
562    /*
563     * Return information about the segment to the caller.
564     */
565    *UptoDate = current->Valid;
566    *BaseAddress = current->BaseAddress;
567    DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
568    *CacheSeg = current;
569    *BaseOffset = current->FileOffset;
570    return(STATUS_SUCCESS);
571 }
572
573 NTSTATUS STDCALL 
574 CcRosRequestCacheSegment(PBCB Bcb,
575                       ULONG FileOffset,
576                       PVOID* BaseAddress,
577                       PBOOLEAN UptoDate,
578                       PCACHE_SEGMENT* CacheSeg)
579 /*
580  * FUNCTION: Request a page mapping for a BCB
581  */
582 {
583   ULONG BaseOffset;
584
585   if ((FileOffset % Bcb->CacheSegmentSize) != 0)
586     {
587       CPRINT("Bad fileoffset %x should be multiple of %x",
588         FileOffset, Bcb->CacheSegmentSize);
589       KeBugCheck(0);
590     }
591
592   return(CcRosGetCacheSegment(Bcb,
593                            FileOffset,
594                            &BaseOffset,
595                            BaseAddress,
596                            UptoDate,
597                            CacheSeg));
598 }
599
600 STATIC VOID 
601 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, 
602                 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
603 {
604   assert(SwapEntry == 0);
605   if (PhysAddr.QuadPart != 0)
606     {
607       MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
608     }
609 }
610
611 NTSTATUS STDCALL 
612 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
613 /*
614  * FUNCTION: Releases a cache segment associated with a BCB
615  */
616 {
617
618   DPRINT("Freeing cache segment %x\n", CacheSeg);
619
620   MmLockAddressSpace(MmGetKernelAddressSpace());
621   MmFreeMemoryArea(MmGetKernelAddressSpace(),
622                    CacheSeg->BaseAddress,
623                    CacheSeg->Bcb->CacheSegmentSize,
624                    CcFreeCachePage,
625                    NULL);
626   MmUnlockAddressSpace(MmGetKernelAddressSpace());
627   ExFreePool(CacheSeg);
628   return(STATUS_SUCCESS);
629 }
630
631 NTSTATUS STDCALL 
632 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
633 {
634   NTSTATUS Status;
635   KIRQL oldIrql;
636
637   DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
638          Bcb, CacheSeg);
639
640   ExAcquireFastMutex(&ViewLock);
641   KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
642   RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
643   RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
644   RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
645   if (CacheSeg->Dirty)
646   {
647      RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
648   }
649   KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
650   ExReleaseFastMutex(&ViewLock);
651
652   Status = CcRosInternalFreeCacheSegment(CacheSeg);
653   return(Status);
654 }
655
656 VOID STDCALL
657 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
658              IN PLARGE_INTEGER FileOffset OPTIONAL,
659              IN ULONG Length,
660              OUT PIO_STATUS_BLOCK IoStatus)
661 {
662    PBCB Bcb;
663    LARGE_INTEGER Offset;
664    PCACHE_SEGMENT current;
665    NTSTATUS Status;
666    KIRQL oldIrql;
667
668    DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
669            SectionObjectPointers, FileOffset, Length, IoStatus);
670
671    if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
672    {
673       Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
674       if (FileOffset)
675       {
676          Offset = *FileOffset;
677       }
678       else 
679       {
680          Offset.QuadPart = 0LL;
681          Length = Bcb->FileSize.u.LowPart;
682       }
683    
684       if (IoStatus)
685       {
686          IoStatus->Status = STATUS_SUCCESS;
687          IoStatus->Information = 0;
688       }
689
690       while (Length > 0)
691       {
692          current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
693          if (current != NULL)
694          {
695             ExAcquireFastMutex(&current->Lock);
696             if (current->Dirty)
697             {
698                Status = CcRosFlushCacheSegment(current);
699                if (!NT_SUCCESS(Status) && IoStatus != NULL)
700                {
701                    IoStatus->Status = Status;
702                }
703             }
704             ExReleaseFastMutex(&current->Lock);
705             KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
706             current->ReferenceCount--;
707             KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
708          }
709
710          Offset.QuadPart += Bcb->CacheSegmentSize;
711          if (Length > Bcb->CacheSegmentSize)
712          {
713             Length -= Bcb->CacheSegmentSize;
714          }
715          else
716          {
717             Length = 0;
718          }
719       }
720    }
721    else
722    {
723       if (IoStatus)
724       {
725          IoStatus->Status = STATUS_INVALID_PARAMETER;
726       }
727    }
728 }
729
730 NTSTATUS STDCALL 
731 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
732 /*
733  * FUNCTION: Releases the BCB associated with a file object
734  */
735 {
736    PLIST_ENTRY current_entry;
737    PCACHE_SEGMENT current;
738    NTSTATUS Status;
739    LIST_ENTRY FreeList;
740    KIRQL oldIrql;
741    
742    DPRINT("CcRosDeleteFileCache(FileObject %x, Bcb %x)\n", 
743           Bcb->FileObject, Bcb);
744
745    ExReleaseFastMutex(&ViewLock);
746
747    CcFlushCache(FileObject->SectionObjectPointers, NULL, 0, NULL);
748
749    ExAcquireFastMutex(&ViewLock);
750
751    if (Bcb->RefCount == 0)
752    {
753       MmFreeSectionSegments(Bcb->FileObject);
754
755       /*
756        * Release all cache segments.
757        */
758       InitializeListHead(&FreeList);
759
760       FileObject->SectionObjectPointers->SharedCacheMap = NULL;  
761       KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
762       current_entry = Bcb->BcbSegmentListHead.Flink;
763       while (!IsListEmpty(&Bcb->BcbSegmentListHead))
764       {
765          current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
766          current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
767          RemoveEntryList(&current->CacheSegmentListEntry);
768          RemoveEntryList(&current->CacheSegmentLRUListEntry);
769          if (current->Dirty)
770          {
771             RemoveEntryList(&current->DirtySegmentListEntry);
772          }
773          InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
774
775       }
776       KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);        
777       while (!IsListEmpty(&FreeList))
778       {
779          current_entry = RemoveTailList(&FreeList);
780          current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
781          Status = CcRosInternalFreeCacheSegment(current);
782       }
783       
784       ObDereferenceObject (Bcb->FileObject);
785       ExFreePool(Bcb);
786    }
787    return(STATUS_SUCCESS);
788 }
789
790 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
791 {
792   PBCB Bcb;
793   ExAcquireFastMutex(&ViewLock);
794   Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
795   Bcb->RefCount++;
796   ExReleaseFastMutex(&ViewLock);
797 }
798
799 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
800 {
801   PBCB Bcb;
802   ExAcquireFastMutex(&ViewLock);
803   Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
804   Bcb->RefCount--;
805   if (Bcb->RefCount == 0)
806   {
807      CcRosDeleteFileCache(FileObject, Bcb);
808   }
809   ExReleaseFastMutex(&ViewLock);
810 }
811
812 NTSTATUS STDCALL 
813 CcRosReleaseFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
814 /*
815  * FUNCTION: Called by the file system when a handle to a file object
816  * has been closed.
817  */
818 {
819   ExAcquireFastMutex(&ViewLock);
820
821   if (FileObject->SectionObjectPointers->SharedCacheMap != NULL)
822   {
823     if (FileObject->PrivateCacheMap != NULL)
824     {
825       FileObject->PrivateCacheMap = NULL;
826       Bcb->RefCount--;
827     }
828     if (Bcb->RefCount == 0)
829     {
830       CcRosDeleteFileCache(FileObject, Bcb);
831     }
832   }
833   ExReleaseFastMutex(&ViewLock);
834   return(STATUS_SUCCESS);
835 }
836
837 NTSTATUS STDCALL 
838 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
839                          PBCB* Bcb,
840                          ULONG CacheSegmentSize)
841 /*
842  * FUNCTION: Initializes a BCB for a file object
843  */
844 {
845    DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
846            FileObject, Bcb, CacheSegmentSize);
847
848    ExAcquireFastMutex(&ViewLock);
849
850    if (*Bcb == NULL)
851    {
852       (*Bcb) = ExAllocatePoolWithTag(NonPagedPool, sizeof(BCB), TAG_BCB);
853       if ((*Bcb) == NULL)
854       {
855         return(STATUS_UNSUCCESSFUL);
856       }
857    
858       ObReferenceObjectByPointer(FileObject,
859                                  FILE_ALL_ACCESS,
860                                  NULL,
861                                  KernelMode);
862       (*Bcb)->FileObject = FileObject;
863       (*Bcb)->CacheSegmentSize = CacheSegmentSize;
864       if (FileObject->FsContext)
865       {
866          (*Bcb)->AllocationSize = 
867            ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->AllocationSize;
868          (*Bcb)->FileSize = 
869            ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->FileSize;
870       }
871       KeInitializeSpinLock(&(*Bcb)->BcbLock);
872       InitializeListHead(&(*Bcb)->BcbSegmentListHead);
873       FileObject->SectionObjectPointers->SharedCacheMap = *Bcb;
874    }
875    if (FileObject->PrivateCacheMap == NULL)
876    {
877       FileObject->PrivateCacheMap = *Bcb;
878       (*Bcb)->RefCount++;
879    }
880    ExReleaseFastMutex(&ViewLock);
881
882    return(STATUS_SUCCESS);
883 }
884
885 PFILE_OBJECT STDCALL
886 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
887 {
888    PBCB Bcb;
889    if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
890    {
891       Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
892       return Bcb->FileObject;
893    }
894    return NULL;
895 }
896
897 VOID
898 CcInitView(VOID)
899 {
900   DPRINT("CcInitView()\n");
901   InitializeListHead(&CacheSegmentListHead);
902   InitializeListHead(&DirtySegmentListHead);
903   InitializeListHead(&CacheSegmentLRUListHead);
904   ExInitializeFastMutex(&ViewLock);
905   MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
906   CcInitCacheZeroPage();
907 }
908
909 /* EOF */
910
911
912
913
914
915
916