3 * Copyright (C) 1998, 1999, 2000, 2001 David Welch <welch@cwcom.net>
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.18 2001/03/09 14:40:27 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)
30 /* NOTES **********************************************************************
32 * This is not the NT implementation of a file cache nor anything much like
35 * The general procedure for a filesystem to implement a read or write
36 * dispatch routine is as follows
38 * (1) If caching for the FCB hasn't been initiated then so do by calling
39 * CcInitializeFileCache.
41 * (2) For each 4k region which is being read or written obtain a cache page
42 * by calling CcRequestCachePage.
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.
48 * (4) Copy the data into or out of the page as necessary.
50 * (5) Release the cache page
52 /* INCLUDES ******************************************************************/
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>
61 #include <internal/debug.h>
63 /* GLOBALS *******************************************************************/
65 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
66 #define ROUND_DOWN(N, S) (ROUND_UP(N, S) - S)
68 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
69 #define TAG_BCB TAG('B', 'C', 'B', ' ')
71 /* FUNCTIONS *****************************************************************/
74 CcFlushCacheSegment(PCACHE_SEGMENT CacheSeg
)
76 * FUNCTION: Asks the FSD to flush the contents of the page back to disk
79 KeWaitForSingleObject(&CacheSeg
->Lock
,
84 /* FIXME: Build an IRP_MJ_WRITE and send it to the filesystem */
85 KeSetEvent(&CacheSeg
->Lock
, IO_NO_INCREMENT
, 0);
86 return(STATUS_NOT_IMPLEMENTED
);
90 CcReleaseCacheSegment(PBCB Bcb
,
91 PCACHE_SEGMENT CacheSeg
,
94 DPRINT("CcReleaseCachePage(Bcb %x, CacheSeg %x, Valid %d)\n",
95 Bcb
, CacheSeg
, Valid
);
97 CacheSeg
->ReferenceCount
--;
98 CacheSeg
->Valid
= Valid
;
99 KeSetEvent(&CacheSeg
->Lock
, IO_NO_INCREMENT
, FALSE
);
101 DPRINT("CcReleaseCachePage() finished\n");
103 return(STATUS_SUCCESS
);
107 CcRequestCacheSegment(PBCB Bcb
,
111 PCACHE_SEGMENT
* CacheSeg
)
113 * FUNCTION: Request a page mapping for a BCB
117 PLIST_ENTRY current_entry
;
118 PCACHE_SEGMENT current
;
121 if ((FileOffset
% Bcb
->CacheSegmentSize
) != 0)
126 DPRINT("CcRequestCachePage(Bcb %x, FileOffset %x, BaseAddress %x, "
127 "UptoDate %x, CacheSeg %x)\n", Bcb
, FileOffset
, BaseAddress
,
130 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
132 current_entry
= Bcb
->CacheSegmentListHead
.Flink
;
133 while (current_entry
!= &Bcb
->CacheSegmentListHead
)
135 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, ListEntry
);
136 if (current
->FileOffset
== FileOffset
)
138 DPRINT("Found existing segment at %x\n", current
);
139 current
->ReferenceCount
++;
140 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
141 DPRINT("Waiting for segment\n");
142 KeWaitForSingleObject(¤t
->Lock
,
147 *UptoDate
= current
->Valid
;
148 *BaseAddress
= current
->BaseAddress
;
150 DPRINT("Returning %x (UptoDate %d)\n", current
, current
->Valid
);
151 return(STATUS_SUCCESS
);
153 current_entry
= current_entry
->Flink
;
156 DPRINT("Creating new segment\n");
158 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
160 current
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CACHE_SEGMENT
),
162 current
->BaseAddress
= NULL
;
163 MmCreateMemoryArea(KernelMode
,
164 MmGetKernelAddressSpace(),
165 MEMORY_AREA_CACHE_SEGMENT
,
166 ¤t
->BaseAddress
,
167 Bcb
->CacheSegmentSize
,
169 (PMEMORY_AREA
*)¤t
->MemoryArea
);
170 current
->Valid
= FALSE
;
171 current
->FileOffset
= FileOffset
;
173 KeInitializeEvent(¤t
->Lock
, SynchronizationEvent
, FALSE
);
174 current
->ReferenceCount
= 1;
175 InsertTailList(&Bcb
->CacheSegmentListHead
, ¤t
->ListEntry
);
176 *UptoDate
= current
->Valid
;
177 *BaseAddress
= current
->BaseAddress
;
179 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGESIZE
); i
++)
181 MmCreateVirtualMapping(NULL
,
182 current
->BaseAddress
+ (i
* PAGESIZE
),
184 (ULONG
)MmAllocPage(0));
188 DPRINT("Returning %x (BaseAddress %x)\n", current
, *BaseAddress
);
190 return(STATUS_SUCCESS
);
194 CcFreeCachePage(PVOID Context
, PVOID Address
, ULONG PhysAddr
)
198 MmDereferencePage((PVOID
)PhysAddr
);
203 CcFreeCacheSegment(PBCB Bcb
, PCACHE_SEGMENT CacheSeg
)
205 * FUNCTION: Releases a cache segment associated with a BCB
208 DPRINT("Freeing cache segment %x\n", CacheSeg
);
209 MmFreeMemoryArea(MmGetKernelAddressSpace(),
210 CacheSeg
->BaseAddress
,
211 Bcb
->CacheSegmentSize
,
214 ExFreePool(CacheSeg
);
215 return(STATUS_SUCCESS
);
219 CcReleaseFileCache(PFILE_OBJECT FileObject
, PBCB Bcb
)
221 * FUNCTION: Releases the BCB associated with a file object
224 PLIST_ENTRY current_entry
;
225 PCACHE_SEGMENT current
;
227 DPRINT("CcReleaseFileCache(FileObject %x, Bcb %x)\n", FileObject
, Bcb
);
229 MmFreeSectionSegments(FileObject
);
231 current_entry
= Bcb
->CacheSegmentListHead
.Flink
;
232 while (current_entry
!= &Bcb
->CacheSegmentListHead
)
234 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
, ListEntry
);
235 current_entry
= current_entry
->Flink
;
236 CcFreeCacheSegment(Bcb
, current
);
241 DPRINT("CcReleaseFileCache() finished\n");
243 return(STATUS_SUCCESS
);
247 CcInitializeFileCache(PFILE_OBJECT FileObject
,
249 ULONG CacheSegmentSize
)
251 * FUNCTION: Initializes a BCB for a file object
254 DPRINT("CcInitializeFileCache(FileObject %x)\n",FileObject
);
256 (*Bcb
) = ExAllocatePoolWithTag(NonPagedPool
, sizeof(BCB
), TAG_BCB
);
259 return(STATUS_UNSUCCESSFUL
);
262 (*Bcb
)->FileObject
= FileObject
;
263 InitializeListHead(&(*Bcb
)->CacheSegmentListHead
);
264 KeInitializeSpinLock(&(*Bcb
)->BcbLock
);
265 (*Bcb
)->CacheSegmentSize
= CacheSegmentSize
;
267 DPRINT("Finished CcInitializeFileCache() = %x\n", *Bcb
);
269 return(STATUS_SUCCESS
);
273 /**********************************************************************
275 * CcMdlReadCompleteDev@8
287 * Used by CcMdlReadComplete@8 and FsRtl
290 CcMdlReadCompleteDev (IN PMDL MdlChain
,
291 IN PDEVICE_OBJECT DeviceObject
)
297 /**********************************************************************
299 * CcMdlReadComplete@8
309 * From Bo Branten's ntifs.h v13.
312 CcMdlReadComplete (IN PFILE_OBJECT FileObject
,
315 PDEVICE_OBJECT DeviceObject
= NULL
;
317 DeviceObject
= IoGetRelatedDeviceObject (FileObject
);
318 /* FIXME: try fast I/O first */
319 CcMdlReadCompleteDev (MdlChain
,