3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
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.
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.
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.
19 /* $Id: view.c,v 1.32 2001/12/31 01:53:44 dwelch Exp $
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: ntoskrnl/cc/view.c
24 * PURPOSE: Cache manager
25 * PROGRAMMER: David Welch (welch@mcmail.com)
26 * PORTABILITY: Checked
31 /* NOTES **********************************************************************
33 * This is not the NT implementation of a file cache nor anything much like
36 * The general procedure for a filesystem to implement a read or write
37 * dispatch routine is as follows
39 * (1) If caching for the FCB hasn't been initiated then so do by calling
40 * CcInitializeFileCache.
42 * (2) For each 4k region which is being read or written obtain a cache page
43 * by calling CcRequestCachePage.
45 * (3) If either the page is being read or not completely written, and it is
46 * not up to date then read its data from the underlying medium. If the read
47 * fails then call CcReleaseCachePage with VALID as FALSE and return a error.
49 * (4) Copy the data into or out of the page as necessary.
51 * (5) Release the cache page
53 /* INCLUDES ******************************************************************/
55 #include <ddk/ntddk.h>
56 #include <ddk/ntifs.h>
57 #include <internal/mm.h>
58 #include <internal/cc.h>
59 #include <internal/pool.h>
60 #include <ntos/minmax.h>
63 #include <internal/debug.h>
65 /* GLOBALS *******************************************************************/
67 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
68 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
70 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
71 #define TAG_BCB TAG('B', 'C', 'B', ' ')
73 static LIST_ENTRY DirtySegmentListHead
;
74 static LIST_ENTRY CacheSegmentListHead
;
75 static LIST_ENTRY CacheSegmentLRUListHead
;
77 static FAST_MUTEX ViewLock
;
80 CcRosInternalFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
);
82 /* FUNCTIONS *****************************************************************/
85 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
87 PLIST_ENTRY current_entry
;
88 PCACHE_SEGMENT current
;
89 ULONG PagesPerSegment
;
92 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
96 ExAcquireFastMutex(&ViewLock
);
97 current_entry
= CacheSegmentLRUListHead
.Flink
;
98 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
100 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, CacheSegmentLRUListEntry
);
101 current_entry
= current_entry
->Flink
;
102 ExAcquireFastMutex(¤t
->Lock
);
103 if (current
->MappedCount
> 0 || current
->Dirty
|| current
->ReferenceCount
> 0)
105 ExReleaseFastMutex(¤t
->Lock
);
108 ExReleaseFastMutex(¤t
->Lock
);
109 DPRINT("current->Bcb->CacheSegmentSize %d\n", current
->Bcb
->CacheSegmentSize
);
110 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGESIZE
;
111 CcRosInternalFreeCacheSegment(current
->Bcb
, current
);
112 DPRINT("CcRosTrimCache(): Freed %d\n", PagesPerSegment
);
113 PagesFreed
= min(PagesPerSegment
, Target
);
114 Target
= Target
- PagesFreed
;
115 (*NrFreed
) = (*NrFreed
) + PagesFreed
;
117 ExReleaseFastMutex(&ViewLock
);
118 DPRINT("CcRosTrimCache() finished\n");
119 return(STATUS_SUCCESS
);
123 CcRosReleaseCacheSegment(PBCB Bcb
,
124 PCACHE_SEGMENT CacheSeg
,
129 DPRINT("CcReleaseCachePage(Bcb %x, CacheSeg %x, Valid %d)\n",
130 Bcb
, CacheSeg
, Valid
);
132 CacheSeg
->Valid
= Valid
;
133 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
136 CacheSeg
->MappedCount
++;
138 ExReleaseFastMutex(&CacheSeg
->Lock
);
139 ExAcquireFastMutex(&ViewLock
);
140 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
141 InsertTailList(&CacheSegmentLRUListHead
, &CacheSeg
->CacheSegmentLRUListEntry
);
142 ExReleaseFastMutex(&ViewLock
);
143 InterlockedDecrement(&CacheSeg
->ReferenceCount
);
145 DPRINT("CcReleaseCachePage() finished\n");
147 return(STATUS_SUCCESS
);
150 PCACHE_SEGMENT
CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
152 PLIST_ENTRY current_entry
;
153 PCACHE_SEGMENT current
;
156 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
157 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
158 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
160 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
161 BcbSegmentListEntry
);
162 if (current
->FileOffset
<= FileOffset
&&
163 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
165 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
168 current_entry
= current_entry
->Flink
;
170 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
175 CcRosSuggestFreeCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
177 PCACHE_SEGMENT CacheSeg
;
179 ExAcquireFastMutex(&ViewLock
);
180 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
181 if (CacheSeg
== NULL
)
185 ExAcquireFastMutex(&CacheSeg
->Lock
);
186 if (CacheSeg
->MappedCount
> 0)
190 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
191 if (CacheSeg
->Dirty
|| CacheSeg
->ReferenceCount
> 0)
193 ExReleaseFastMutex(&CacheSeg
->Lock
);
194 ExReleaseFastMutex(&ViewLock
);
195 return(STATUS_UNSUCCESSFUL
);
197 ExReleaseFastMutex(&CacheSeg
->Lock
);
198 CcRosInternalFreeCacheSegment(CacheSeg
->Bcb
, CacheSeg
);
199 ExReleaseFastMutex(&ViewLock
);
200 return(STATUS_SUCCESS
);
204 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
206 PCACHE_SEGMENT CacheSeg
;
208 ExAcquireFastMutex(&ViewLock
);
209 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
210 if (CacheSeg
== NULL
)
212 ExReleaseFastMutex(&ViewLock
);
213 return(STATUS_UNSUCCESSFUL
);
215 CacheSeg
->ReferenceCount
++;
216 ExReleaseFastMutex(&ViewLock
);
217 ExAcquireFastMutex(&CacheSeg
->Lock
);
218 CacheSeg
->MappedCount
--;
219 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
220 ExReleaseFastMutex(&CacheSeg
->Lock
);
221 return(STATUS_SUCCESS
);
225 CcRosGetCacheSegment(PBCB Bcb
,
230 PCACHE_SEGMENT
* CacheSeg
)
232 PCACHE_SEGMENT current
;
238 * Acquire the global lock.
240 ExAcquireFastMutex(&ViewLock
);
243 * Look for a cache segment already mapping the same data.
245 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
249 * Make sure the cache segment can't go away outside of our control.
251 current
->ReferenceCount
++;
253 * Release the global lock and lock the cache segment.
255 ExReleaseFastMutex(&ViewLock
);
256 ExAcquireFastMutex(¤t
->Lock
);
258 * Return information about the segment to the caller.
260 *UptoDate
= current
->Valid
;
261 *BaseAddress
= current
->BaseAddress
;
263 *BaseOffset
= current
->FileOffset
;
264 return(STATUS_SUCCESS
);
268 * Otherwise create a new segment.
270 current
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CACHE_SEGMENT
),
273 MmLockAddressSpace(MmGetKernelAddressSpace());
274 current
->BaseAddress
= NULL
;
275 Status
= MmCreateMemoryArea(KernelMode
,
276 MmGetKernelAddressSpace(),
277 MEMORY_AREA_CACHE_SEGMENT
,
278 ¤t
->BaseAddress
,
279 Bcb
->CacheSegmentSize
,
281 (PMEMORY_AREA
*)¤t
->MemoryArea
,
283 if (!NT_SUCCESS(Status
))
285 MmUnlockAddressSpace(MmGetKernelAddressSpace());
288 MmUnlockAddressSpace(MmGetKernelAddressSpace());
289 current
->Valid
= FALSE
;
290 current
->Dirty
= FALSE
;
291 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
293 current
->MappedCount
= 0;
294 ExInitializeFastMutex(¤t
->Lock
);
295 current
->ReferenceCount
= 1;
296 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
297 InsertTailList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
298 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
299 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
300 InsertTailList(&CacheSegmentLRUListHead
, ¤t
->CacheSegmentLRUListEntry
);
301 current
->DirtySegmentListEntry
.Flink
= current
->DirtySegmentListEntry
.Blink
= NULL
;
302 ExAcquireFastMutex(¤t
->Lock
);
303 ExReleaseFastMutex(&ViewLock
);
304 *UptoDate
= current
->Valid
;
305 *BaseAddress
= current
->BaseAddress
;
307 *BaseOffset
= current
->FileOffset
;
308 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGESIZE
); i
++)
312 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Page
);
313 if (!NT_SUCCESS(Status
))
318 Status
= MmCreateVirtualMapping(NULL
,
319 current
->BaseAddress
+ (i
* PAGESIZE
),
323 if (!NT_SUCCESS(Status
))
328 return(STATUS_SUCCESS
);
332 CcRosRequestCacheSegment(PBCB Bcb
,
336 PCACHE_SEGMENT
* CacheSeg
)
338 * FUNCTION: Request a page mapping for a BCB
343 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
345 CPRINT("Bad fileoffset %x should be multiple of %x",
346 FileOffset
, Bcb
->CacheSegmentSize
);
350 return(CcRosGetCacheSegment(Bcb
,
359 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
, ULONG PhysAddr
,
364 MmReleasePageMemoryConsumer(MC_CACHE
, (PVOID
)PhysAddr
);
369 CcRosInternalFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
371 * FUNCTION: Releases a cache segment associated with a BCB
374 DPRINT("Freeing cache segment %x\n", CacheSeg
);
375 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
376 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
377 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
378 MmLockAddressSpace(MmGetKernelAddressSpace());
379 MmFreeMemoryArea(MmGetKernelAddressSpace(),
380 CacheSeg
->BaseAddress
,
381 Bcb
->CacheSegmentSize
,
384 MmUnlockAddressSpace(MmGetKernelAddressSpace());
385 ExFreePool(CacheSeg
);
386 return(STATUS_SUCCESS
);
390 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
393 ExAcquireFastMutex(&ViewLock
);
394 Status
= CcRosInternalFreeCacheSegment(Bcb
, CacheSeg
);
395 ExReleaseFastMutex(&ViewLock
);
400 CcRosReleaseFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
402 * FUNCTION: Releases the BCB associated with a file object
405 PLIST_ENTRY current_entry
;
406 PCACHE_SEGMENT current
;
408 DPRINT("CcRosReleaseFileCache(FileObject %x, Bcb %x)\n", Bcb
->FileObject
, Bcb
);
410 MmFreeSectionSegments(Bcb
->FileObject
);
413 * Release all cache segments.
415 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
416 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
419 CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
420 current_entry
= current_entry
->Flink
;
421 CcRosFreeCacheSegment(Bcb
, current
);
424 ObDereferenceObject (Bcb
->FileObject
);
427 return(STATUS_SUCCESS
);
431 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
433 ULONG CacheSegmentSize
)
435 * FUNCTION: Initializes a BCB for a file object
438 (*Bcb
) = ExAllocatePoolWithTag(NonPagedPool
, sizeof(BCB
), TAG_BCB
);
441 return(STATUS_UNSUCCESSFUL
);
444 ObReferenceObjectByPointer(FileObject
,
448 (*Bcb
)->FileObject
= FileObject
;
449 (*Bcb
)->CacheSegmentSize
= CacheSegmentSize
;
450 if (FileObject
->FsContext
)
452 (*Bcb
)->AllocationSize
=
453 ((REACTOS_COMMON_FCB_HEADER
*)FileObject
->FsContext
)->AllocationSize
;
454 (*Bcb
)->FileSize
= ((REACTOS_COMMON_FCB_HEADER
*)FileObject
->FsContext
)->FileSize
;
456 KeInitializeSpinLock(&(*Bcb
)->BcbLock
);
457 InitializeListHead(&(*Bcb
)->BcbSegmentListHead
);
459 return(STATUS_SUCCESS
);
465 DPRINT1("CcInitView()\n");
466 InitializeListHead(&CacheSegmentListHead
);
467 InitializeListHead(&DirtySegmentListHead
);
468 InitializeListHead(&CacheSegmentLRUListHead
);
469 ExInitializeFastMutex(&ViewLock
);
470 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);