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.43 2002/06/10 21:11:56 hbirr 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 * FUNCTION: Try to free some memory from the file cache.
91 * Target - The number of pages to be freed.
92 * Priority - The priority of free (currently unused).
93 * NrFreed - Points to a variable where the number of pages
94 * actually freed is returned.
97 PLIST_ENTRY current_entry
;
98 PCACHE_SEGMENT current
;
99 ULONG PagesPerSegment
;
103 DPRINT("CcRosTrimCache(Target %d)\n", Target
);
107 ExAcquireFastMutex(&ViewLock
);
108 current_entry
= CacheSegmentLRUListHead
.Flink
;
109 while (current_entry
!= &CacheSegmentLRUListHead
&& Target
> 0)
111 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
112 CacheSegmentLRUListEntry
);
113 current_entry
= current_entry
->Flink
;
114 Locked
= ExTryToAcquireFastMutex(¤t
->Lock
);
119 if (current
->MappedCount
> 0 || current
->Dirty
||
120 current
->ReferenceCount
> 0)
122 ExReleaseFastMutex(¤t
->Lock
);
125 ExReleaseFastMutex(¤t
->Lock
);
126 DPRINT("current->Bcb->CacheSegmentSize %d\n",
127 current
->Bcb
->CacheSegmentSize
);
128 PagesPerSegment
= current
->Bcb
->CacheSegmentSize
/ PAGESIZE
;
129 CcRosInternalFreeCacheSegment(current
->Bcb
, current
);
130 DPRINT("CcRosTrimCache(): Freed %d\n", PagesPerSegment
);
131 PagesFreed
= min(PagesPerSegment
, Target
);
132 Target
= Target
- PagesFreed
;
133 (*NrFreed
) = (*NrFreed
) + PagesFreed
;
135 ExReleaseFastMutex(&ViewLock
);
136 DPRINT("CcRosTrimCache() finished\n");
137 return(STATUS_SUCCESS
);
141 CcRosReleaseCacheSegment(PBCB Bcb
,
142 PCACHE_SEGMENT CacheSeg
,
147 DPRINT("CcReleaseCachePage(Bcb %x, CacheSeg %x, Valid %d)\n",
148 Bcb
, CacheSeg
, Valid
);
150 CacheSeg
->Valid
= Valid
;
151 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| Dirty
;
154 CacheSeg
->MappedCount
++;
156 ExReleaseFastMutex(&CacheSeg
->Lock
);
157 ExAcquireFastMutex(&ViewLock
);
158 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
159 InsertTailList(&CacheSegmentLRUListHead
,
160 &CacheSeg
->CacheSegmentLRUListEntry
);
161 ExReleaseFastMutex(&ViewLock
);
162 InterlockedDecrement(&CacheSeg
->ReferenceCount
);
164 DPRINT("CcReleaseCachePage() finished\n");
166 return(STATUS_SUCCESS
);
169 PCACHE_SEGMENT
CcRosLookupCacheSegment(PBCB Bcb
, ULONG FileOffset
)
171 PLIST_ENTRY current_entry
;
172 PCACHE_SEGMENT current
;
175 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
176 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
177 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
179 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
180 BcbSegmentListEntry
);
181 if (current
->FileOffset
<= FileOffset
&&
182 (current
->FileOffset
+ Bcb
->CacheSegmentSize
) > FileOffset
)
184 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
187 current_entry
= current_entry
->Flink
;
189 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
194 CcRosSuggestFreeCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
196 PCACHE_SEGMENT CacheSeg
;
198 ExAcquireFastMutex(&ViewLock
);
199 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
200 if (CacheSeg
== NULL
)
204 ExAcquireFastMutex(&CacheSeg
->Lock
);
205 if (CacheSeg
->MappedCount
> 0)
209 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
210 if (CacheSeg
->Dirty
|| CacheSeg
->ReferenceCount
> 0)
212 ExReleaseFastMutex(&CacheSeg
->Lock
);
213 ExReleaseFastMutex(&ViewLock
);
214 return(STATUS_UNSUCCESSFUL
);
216 ExReleaseFastMutex(&CacheSeg
->Lock
);
217 CcRosInternalFreeCacheSegment(CacheSeg
->Bcb
, CacheSeg
);
218 ExReleaseFastMutex(&ViewLock
);
219 return(STATUS_SUCCESS
);
223 CcRosUnmapCacheSegment(PBCB Bcb
, ULONG FileOffset
, BOOLEAN NowDirty
)
225 PCACHE_SEGMENT CacheSeg
;
227 ExAcquireFastMutex(&ViewLock
);
228 CacheSeg
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
229 if (CacheSeg
== NULL
)
231 ExReleaseFastMutex(&ViewLock
);
232 return(STATUS_UNSUCCESSFUL
);
234 CacheSeg
->ReferenceCount
++;
235 ExReleaseFastMutex(&ViewLock
);
236 ExAcquireFastMutex(&CacheSeg
->Lock
);
237 CacheSeg
->MappedCount
--;
238 CacheSeg
->Dirty
= CacheSeg
->Dirty
|| NowDirty
;
239 ExReleaseFastMutex(&CacheSeg
->Lock
);
240 return(STATUS_SUCCESS
);
244 CcRosCreateCacheSegment(PBCB Bcb
,
246 PCACHE_SEGMENT
* CacheSeg
,
250 PCACHE_SEGMENT current
;
254 current
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CACHE_SEGMENT
),
257 MmLockAddressSpace(MmGetKernelAddressSpace());
258 current
->BaseAddress
= NULL
;
259 Status
= MmCreateMemoryArea(KernelMode
,
260 MmGetKernelAddressSpace(),
261 MEMORY_AREA_CACHE_SEGMENT
,
262 ¤t
->BaseAddress
,
263 Bcb
->CacheSegmentSize
,
265 (PMEMORY_AREA
*)¤t
->MemoryArea
,
267 if (!NT_SUCCESS(Status
))
269 MmUnlockAddressSpace(MmGetKernelAddressSpace());
272 MmUnlockAddressSpace(MmGetKernelAddressSpace());
273 current
->Valid
= FALSE
;
274 current
->Dirty
= FALSE
;
275 current
->FileOffset
= ROUND_DOWN(FileOffset
, Bcb
->CacheSegmentSize
);
277 current
->MappedCount
= 0;
278 ExInitializeFastMutex(¤t
->Lock
);
279 current
->ReferenceCount
= 1;
280 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldIrql
);
281 InsertTailList(&Bcb
->BcbSegmentListHead
, ¤t
->BcbSegmentListEntry
);
282 KeReleaseSpinLock(&Bcb
->BcbLock
, oldIrql
);
283 InsertTailList(&CacheSegmentListHead
, ¤t
->CacheSegmentListEntry
);
284 InsertTailList(&CacheSegmentLRUListHead
,
285 ¤t
->CacheSegmentLRUListEntry
);
286 current
->DirtySegmentListEntry
.Flink
=
287 current
->DirtySegmentListEntry
.Blink
= NULL
;
290 ExAcquireFastMutex(¤t
->Lock
);
292 ExReleaseFastMutex(&ViewLock
);
293 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGESIZE
); i
++)
295 PHYSICAL_ADDRESS Page
;
297 Status
= MmRequestPageMemoryConsumer(MC_CACHE
, TRUE
, &Page
);
298 if (!NT_SUCCESS(Status
))
303 Status
= MmCreateVirtualMapping(NULL
,
304 current
->BaseAddress
+ (i
* PAGESIZE
),
308 if (!NT_SUCCESS(Status
))
314 return(STATUS_SUCCESS
);
318 CcRosGetCacheSegmentChain(PBCB Bcb
,
321 PCACHE_SEGMENT
* CacheSeg
)
323 PCACHE_SEGMENT current
;
325 PCACHE_SEGMENT
* CacheSegList
;
326 PCACHE_SEGMENT Previous
;
328 Length
= ROUND_UP(Length
, Bcb
->CacheSegmentSize
);
330 CacheSegList
= alloca(sizeof(PCACHE_SEGMENT
) *
331 (Length
/ Bcb
->CacheSegmentSize
));
334 * Acquire the global lock.
336 ExAcquireFastMutex(&ViewLock
);
339 * Look for a cache segment already mapping the same data.
341 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
343 ULONG CurrentOffset
= FileOffset
+ (i
* Bcb
->CacheSegmentSize
);
344 current
= CcRosLookupCacheSegment(Bcb
, CurrentOffset
);
348 * Make sure the cache segment can't go away outside of our control.
350 current
->ReferenceCount
++;
351 CacheSegList
[i
] = current
;
355 CcRosCreateCacheSegment(Bcb
, CurrentOffset
, ¤t
, FALSE
);
356 CacheSegList
[i
] = current
;
357 ExAcquireFastMutex(&ViewLock
);
360 ExReleaseFastMutex(&ViewLock
);
362 for (i
= 0; i
< (Length
/ Bcb
->CacheSegmentSize
); i
++)
364 ExAcquireFastMutex(&CacheSegList
[i
]->Lock
);
367 *CacheSeg
= CacheSegList
[i
];
368 Previous
= CacheSegList
[i
];
372 Previous
->NextInChain
= CacheSegList
[i
];
373 Previous
= CacheSegList
[i
];
376 Previous
->NextInChain
= NULL
;
378 return(STATUS_SUCCESS
);
382 CcRosGetCacheSegment(PBCB Bcb
,
387 PCACHE_SEGMENT
* CacheSeg
)
389 PCACHE_SEGMENT current
;
393 * Acquire the global lock.
395 ExAcquireFastMutex(&ViewLock
);
398 * Look for a cache segment already mapping the same data.
400 current
= CcRosLookupCacheSegment(Bcb
, FileOffset
);
404 * Make sure the cache segment can't go away outside of our control.
406 current
->ReferenceCount
++;
408 * Release the global lock and lock the cache segment.
410 ExReleaseFastMutex(&ViewLock
);
411 ExAcquireFastMutex(¤t
->Lock
);
413 * Return information about the segment to the caller.
415 *UptoDate
= current
->Valid
;
416 *BaseAddress
= current
->BaseAddress
;
417 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
419 *BaseOffset
= current
->FileOffset
;
420 return(STATUS_SUCCESS
);
424 * Otherwise create a new segment.
426 Status
= CcRosCreateCacheSegment(Bcb
, FileOffset
, ¤t
, TRUE
);
427 *UptoDate
= current
->Valid
;
428 *BaseAddress
= current
->BaseAddress
;
429 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress
);
431 *BaseOffset
= current
->FileOffset
;
433 return(STATUS_SUCCESS
);
437 CcRosRequestCacheSegment(PBCB Bcb
,
441 PCACHE_SEGMENT
* CacheSeg
)
443 * FUNCTION: Request a page mapping for a BCB
448 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
450 CPRINT("Bad fileoffset %x should be multiple of %x",
451 FileOffset
, Bcb
->CacheSegmentSize
);
455 return(CcRosGetCacheSegment(Bcb
,
464 CcFreeCachePage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
465 PHYSICAL_ADDRESS PhysAddr
, SWAPENTRY SwapEntry
, BOOLEAN Dirty
)
467 assert(SwapEntry
== 0);
468 if (PhysAddr
.QuadPart
!= 0)
470 MmReleasePageMemoryConsumer(MC_CACHE
, PhysAddr
);
475 CcRosInternalFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
477 * FUNCTION: Releases a cache segment associated with a BCB
480 DPRINT("Freeing cache segment %x\n", CacheSeg
);
481 RemoveEntryList(&CacheSeg
->CacheSegmentListEntry
);
482 RemoveEntryList(&CacheSeg
->CacheSegmentLRUListEntry
);
483 RemoveEntryList(&CacheSeg
->BcbSegmentListEntry
);
484 MmLockAddressSpace(MmGetKernelAddressSpace());
485 MmFreeMemoryArea(MmGetKernelAddressSpace(),
486 CacheSeg
->BaseAddress
,
487 Bcb
->CacheSegmentSize
,
490 MmUnlockAddressSpace(MmGetKernelAddressSpace());
491 ExFreePool(CacheSeg
);
492 return(STATUS_SUCCESS
);
496 CcRosFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
499 ExAcquireFastMutex(&ViewLock
);
500 Status
= CcRosInternalFreeCacheSegment(Bcb
, CacheSeg
);
501 ExReleaseFastMutex(&ViewLock
);
506 CcRosReleaseFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
508 * FUNCTION: Releases the BCB associated with a file object
511 PLIST_ENTRY current_entry
;
512 PCACHE_SEGMENT current
;
514 DPRINT("CcRosReleaseFileCache(FileObject %x, Bcb %x)\n", Bcb
->FileObject
,
517 MmFreeSectionSegments(Bcb
->FileObject
);
520 * Release all cache segments.
522 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
523 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
526 CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, BcbSegmentListEntry
);
527 current_entry
= current_entry
->Flink
;
528 CcRosFreeCacheSegment(Bcb
, current
);
531 ObDereferenceObject (Bcb
->FileObject
);
534 return(STATUS_SUCCESS
);
538 CcRosInitializeFileCache(PFILE_OBJECT FileObject
,
540 ULONG CacheSegmentSize
)
542 * FUNCTION: Initializes a BCB for a file object
545 (*Bcb
) = ExAllocatePoolWithTag(NonPagedPool
, sizeof(BCB
), TAG_BCB
);
548 return(STATUS_UNSUCCESSFUL
);
551 ObReferenceObjectByPointer(FileObject
,
555 (*Bcb
)->FileObject
= FileObject
;
556 (*Bcb
)->CacheSegmentSize
= CacheSegmentSize
;
557 if (FileObject
->FsContext
)
559 (*Bcb
)->AllocationSize
=
560 ((REACTOS_COMMON_FCB_HEADER
*)FileObject
->FsContext
)->AllocationSize
;
562 ((REACTOS_COMMON_FCB_HEADER
*)FileObject
->FsContext
)->FileSize
;
564 KeInitializeSpinLock(&(*Bcb
)->BcbLock
);
565 InitializeListHead(&(*Bcb
)->BcbSegmentListHead
);
567 return(STATUS_SUCCESS
);
573 DPRINT("CcInitView()\n");
574 InitializeListHead(&CacheSegmentListHead
);
575 InitializeListHead(&DirtySegmentListHead
);
576 InitializeListHead(&CacheSegmentLRUListHead
);
577 ExInitializeFastMutex(&ViewLock
);
578 MmInitializeMemoryConsumer(MC_CACHE
, CcRosTrimCache
);