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