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.38 2002/05/05 14:57:42 chorns 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 extern void * alloca(size_t);
67 /* GLOBALS *******************************************************************/
69 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
70 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
72 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
73 #define TAG_BCB TAG('B', 'C', 'B', ' ')
75 static LIST_ENTRY DirtySegmentListHead
;
76 static LIST_ENTRY CacheSegmentListHead
;
77 static LIST_ENTRY CacheSegmentLRUListHead
;
79 static FAST_MUTEX ViewLock
;
82 CcRosInternalFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
);
84 /* FUNCTIONS *****************************************************************/
87 CcRosTrimCache(ULONG Target
, ULONG Priority
, PULONG NrFreed
)
89 PLIST_ENTRY current_entry
;
90 PCACHE_SEGMENT current
;
91 ULONG PagesPerSegment
;
95 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
99 ExAcquireFastMutex(&ViewLock
);
100 current_entry
= CacheSegmentLRUListHead
.Flink
;
101 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
103 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
104 CacheSegmentLRUListEntry
);
105 current_entry
= current_entry
->Flink
;
106 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
111 if (current
->MappedCount
> 0 || current
->Dirty
||
112 current
->ReferenceCount
> 0)
114 ExReleaseFastMutex(¤t
->Lock
);
117 ExReleaseFastMutex(¤t
->Lock
);
118 DPRINT("current->Bcb->CacheSegmentSize %d\n",
119 current
->Bcb
->CacheSegmentSize
);
120 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGESIZE
;
121 CcRosInternalFreeCacheSegment(current
->Bcb
, current
);
122 DPRINT("CcRosTrimCache(): Freed %d\n", PagesPerSegment
);
123 PagesFreed
= min(PagesPerSegment
, Target
);
124 Target
= Target
- PagesFreed
;
125 (*NrFreed
) = (*NrFreed
) + PagesFreed
;
127 ExReleaseFastMutex(&ViewLock
);
128 DPRINT("CcRosTrimCache() finished\n");
129 return(STATUS_SUCCESS
);
133 CcRosReleaseCacheSegment(PBCB Bcb
,
134 PCACHE_SEGMENT CacheSeg
,
139 DPRINT("CcReleaseCachePage(Bcb %x, CacheSeg %x, Valid %d)\n",
140 Bcb
, CacheSeg
, Valid
);
142 CacheSeg
->Valid
= Valid
;
143 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
146 CacheSeg
->MappedCount
++;
148 ExReleaseFastMutex(&CacheSeg
->Lock
);
149 ExAcquireFastMutex(&ViewLock
);
150 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
151 InsertTailList(&CacheSegmentLRUListHead
,
152 &CacheSeg
->CacheSegmentLRUListEntry
);
153 ExReleaseFastMutex(&ViewLock
);
154 InterlockedDecrement(&CacheSeg
->ReferenceCount
);
156 DPRINT("CcReleaseCachePage() finished\n");
158 return(STATUS_SUCCESS
);
161 PCACHE_SEGMENT
CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
163 PLIST_ENTRY current_entry
;
164 PCACHE_SEGMENT current
;
167 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
168 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
169 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
171 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
172 BcbSegmentListEntry
);
173 if (current
->FileOffset
<= FileOffset
&&
174 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
176 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
179 current_entry
= current_entry
->Flink
;
181 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
186 CcRosSuggestFreeCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
188 PCACHE_SEGMENT CacheSeg
;
190 ExAcquireFastMutex(&ViewLock
);
191 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
192 if (CacheSeg
== NULL
)
196 ExAcquireFastMutex(&CacheSeg
->Lock
);
197 if (CacheSeg
->MappedCount
> 0)
201 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
202 if (CacheSeg
->Dirty
|| CacheSeg
->ReferenceCount
> 0)
204 ExReleaseFastMutex(&CacheSeg
->Lock
);
205 ExReleaseFastMutex(&ViewLock
);
206 return(STATUS_UNSUCCESSFUL
);
208 ExReleaseFastMutex(&CacheSeg
->Lock
);
209 CcRosInternalFreeCacheSegment(CacheSeg
->Bcb
, CacheSeg
);
210 ExReleaseFastMutex(&ViewLock
);
211 return(STATUS_SUCCESS
);
215 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
217 PCACHE_SEGMENT CacheSeg
;
219 ExAcquireFastMutex(&ViewLock
);
220 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
221 if (CacheSeg
== NULL
)
223 ExReleaseFastMutex(&ViewLock
);
224 return(STATUS_UNSUCCESSFUL
);
226 CacheSeg
->ReferenceCount
++;
227 ExReleaseFastMutex(&ViewLock
);
228 ExAcquireFastMutex(&CacheSeg
->Lock
);
229 CacheSeg
->MappedCount
--;
230 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
231 ExReleaseFastMutex(&CacheSeg
->Lock
);
232 return(STATUS_SUCCESS
);
236 CcRosCreateCacheSegment(PBCB Bcb
,
238 PCACHE_SEGMENT
* CacheSeg
,
242 PCACHE_SEGMENT current
;
246 current
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CACHE_SEGMENT
),
249 MmLockAddressSpace(MmGetKernelAddressSpace());
250 current
->BaseAddress
= NULL
;
251 Status
= MmCreateMemoryArea(KernelMode
,
252 MmGetKernelAddressSpace(),
253 MEMORY_AREA_CACHE_SEGMENT
,
254 ¤t
->BaseAddress
,
255 Bcb
->CacheSegmentSize
,
257 (PMEMORY_AREA
*)¤t
->MemoryArea
,
259 if (!NT_SUCCESS(Status
))
261 MmUnlockAddressSpace(MmGetKernelAddressSpace());
264 MmUnlockAddressSpace(MmGetKernelAddressSpace());
265 current
->Valid
= FALSE
;
266 current
->Dirty
= FALSE
;
267 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
269 current
->MappedCount
= 0;
270 ExInitializeFastMutex(¤t
->Lock
);
271 current
->ReferenceCount
= 1;
272 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
273 InsertTailList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
274 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
275 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
276 InsertTailList(&CacheSegmentLRUListHead
,
277 ¤t
->CacheSegmentLRUListEntry
);
278 current
->DirtySegmentListEntry
.Flink
=
279 current
->DirtySegmentListEntry
.Blink
= NULL
;
282 ExAcquireFastMutex(¤t
->Lock
);
284 ExReleaseFastMutex(&ViewLock
);
285 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGESIZE
); i
++)
289 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Page
);
290 if (!NT_SUCCESS(Status
))
295 Status
= MmCreateVirtualMapping(NULL
,
296 current
->BaseAddress
+ (i
* PAGESIZE
),
300 if (!NT_SUCCESS(Status
))
306 return(STATUS_SUCCESS
);
310 CcRosGetCacheSegmentChain(PBCB Bcb
,
313 PCACHE_SEGMENT
* CacheSeg
)
315 PCACHE_SEGMENT current
;
317 PCACHE_SEGMENT
* CacheSegList
;
318 PCACHE_SEGMENT Previous
;
320 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
322 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
323 (Length
/ Bcb
->CacheSegmentSize
));
326 * Acquire the global lock.
328 ExAcquireFastMutex(&ViewLock
);
331 * Look for a cache segment already mapping the same data.
333 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
335 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
336 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
340 * Make sure the cache segment can't go away outside of our control.
342 current
->ReferenceCount
++;
343 CacheSegList
[i
] = current
;
347 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
, FALSE
);
348 CacheSegList
[i
] = current
;
349 ExAcquireFastMutex(&ViewLock
);
352 ExReleaseFastMutex(&ViewLock
);
354 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
356 ExAcquireFastMutex(&CacheSegList
[i
]->Lock
);
359 *CacheSeg
= CacheSegList
[i
];
360 Previous
= CacheSegList
[i
];
364 Previous
->NextInChain
= CacheSegList
[i
];
365 Previous
= CacheSegList
[i
];
368 Previous
->NextInChain
= NULL
;
370 return(STATUS_SUCCESS
);
374 CcRosGetCacheSegment(PBCB Bcb
,
379 PCACHE_SEGMENT
* CacheSeg
)
381 PCACHE_SEGMENT current
;
385 * Acquire the global lock.
387 ExAcquireFastMutex(&ViewLock
);
390 * Look for a cache segment already mapping the same data.
392 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
396 * Make sure the cache segment can't go away outside of our control.
398 current
->ReferenceCount
++;
400 * Release the global lock and lock the cache segment.
402 ExReleaseFastMutex(&ViewLock
);
403 ExAcquireFastMutex(¤t
->Lock
);
405 * Return information about the segment to the caller.
407 *UptoDate
= current
->Valid
;
408 *BaseAddress
= current
->BaseAddress
;
409 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
411 *BaseOffset
= current
->FileOffset
;
412 return(STATUS_SUCCESS
);
416 * Otherwise create a new segment.
418 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
, TRUE
);
419 *UptoDate
= current
->Valid
;
420 *BaseAddress
= current
->BaseAddress
;
421 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
423 *BaseOffset
= current
->FileOffset
;
425 return(STATUS_SUCCESS
);
429 CcRosRequestCacheSegment(PBCB Bcb
,
433 PCACHE_SEGMENT
* CacheSeg
)
435 * FUNCTION: Request a page mapping for a BCB
440 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
442 CPRINT("Bad fileoffset %x should be multiple of %x",
443 FileOffset
, Bcb
->CacheSegmentSize
);
447 return(CcRosGetCacheSegment(Bcb
,
456 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
457 ULONG PhysAddr
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
459 assert(SwapEntry
== 0);
462 MmReleasePageMemoryConsumer(MC_CACHE
, (PVOID
)PhysAddr
);
467 CcRosInternalFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
469 * FUNCTION: Releases a cache segment associated with a BCB
472 DPRINT("Freeing cache segment %x\n", CacheSeg
);
473 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
474 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
475 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
476 MmLockAddressSpace(MmGetKernelAddressSpace());
477 MmFreeMemoryArea(MmGetKernelAddressSpace(),
478 CacheSeg
->BaseAddress
,
479 Bcb
->CacheSegmentSize
,
482 MmUnlockAddressSpace(MmGetKernelAddressSpace());
483 ExFreePool(CacheSeg
);
484 return(STATUS_SUCCESS
);
488 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
491 ExAcquireFastMutex(&ViewLock
);
492 Status
= CcRosInternalFreeCacheSegment(Bcb
, CacheSeg
);
493 ExReleaseFastMutex(&ViewLock
);
498 CcRosReleaseFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
500 * FUNCTION: Releases the BCB associated with a file object
503 PLIST_ENTRY current_entry
;
504 PCACHE_SEGMENT current
;
506 DPRINT("CcRosReleaseFileCache(FileObject %x, Bcb %x)\n", Bcb
->FileObject
,
509 MmFreeSectionSegments(Bcb
->FileObject
);
512 * Release all cache segments.
514 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
515 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
518 CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
519 current_entry
= current_entry
->Flink
;
520 CcRosFreeCacheSegment(Bcb
, current
);
523 ObDereferenceObject (Bcb
->FileObject
);
526 return(STATUS_SUCCESS
);
530 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
532 ULONG CacheSegmentSize
)
534 * FUNCTION: Initializes a BCB for a file object
537 (*Bcb
) = ExAllocatePoolWithTag(NonPagedPool
, sizeof(BCB
), TAG_BCB
);
540 return(STATUS_UNSUCCESSFUL
);
543 ObReferenceObjectByPointer(FileObject
,
547 (*Bcb
)->FileObject
= FileObject
;
548 (*Bcb
)->CacheSegmentSize
= CacheSegmentSize
;
549 if (FileObject
->FsContext
)
551 (*Bcb
)->AllocationSize
=
552 ((REACTOS_COMMON_FCB_HEADER
*)FileObject
->FsContext
)->AllocationSize
;
554 ((REACTOS_COMMON_FCB_HEADER
*)FileObject
->FsContext
)->FileSize
;
556 KeInitializeSpinLock(&(*Bcb
)->BcbLock
);
557 InitializeListHead(&(*Bcb
)->BcbSegmentListHead
);
559 return(STATUS_SUCCESS
);
565 DPRINT("CcInitView()\n");
566 InitializeListHead(&CacheSegmentListHead
);
567 InitializeListHead(&DirtySegmentListHead
);
568 InitializeListHead(&CacheSegmentLRUListHead
);
569 ExInitializeFastMutex(&ViewLock
);
570 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);