Fixed warnings and errors so ReactOS can be compiled with GCC 3.2.
[reactos.git] / reactos / 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: view.c,v 1.38 2002/05/05 14:57:42 chorns Exp $
20 *
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
27 * UPDATE HISTORY:
28 * Created 22/05/98
29 */
30
31 /* NOTES **********************************************************************
32 *
33 * This is not the NT implementation of a file cache nor anything much like
34 * it.
35 *
36 * The general procedure for a filesystem to implement a read or write
37 * dispatch routine is as follows
38 *
39 * (1) If caching for the FCB hasn't been initiated then so do by calling
40 * CcInitializeFileCache.
41 *
42 * (2) For each 4k region which is being read or written obtain a cache page
43 * by calling CcRequestCachePage.
44 *
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.
48 *
49 * (4) Copy the data into or out of the page as necessary.
50 *
51 * (5) Release the cache page
52 */
53 /* INCLUDES ******************************************************************/
54
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>
61
62 #define NDEBUG
63 #include <internal/debug.h>
64
65 extern void * alloca(size_t);
66
67 /* GLOBALS *******************************************************************/
68
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)
71
72 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
73 #define TAG_BCB TAG('B', 'C', 'B', ' ')
74
75 static LIST_ENTRY DirtySegmentListHead;
76 static LIST_ENTRY CacheSegmentListHead;
77 static LIST_ENTRY CacheSegmentLRUListHead;
78
79 static FAST_MUTEX ViewLock;
80
81 NTSTATUS STDCALL
82 CcRosInternalFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg);
83
84 /* FUNCTIONS *****************************************************************/
85
86 NTSTATUS
87 CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
88 {
89 PLIST_ENTRY current_entry;
90 PCACHE_SEGMENT current;
91 ULONG PagesPerSegment;
92 ULONG PagesFreed;
93 BOOLEAN Locked;
94
95 DPRINT("CcRosTrimCache(Target %d)\n", Target);
96
97 *NrFreed = 0;
98
99 ExAcquireFastMutex(&ViewLock);
100 current_entry = CacheSegmentLRUListHead.Flink;
101 while (current_entry != &CacheSegmentLRUListHead && Target > 0)
102 {
103 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
104 CacheSegmentLRUListEntry);
105 current_entry = current_entry->Flink;
106 Locked = ExTryToAcquireFastMutex(&current->Lock);
107 if (!Locked)
108 {
109 continue;
110 }
111 if (current->MappedCount > 0 || current->Dirty ||
112 current->ReferenceCount > 0)
113 {
114 ExReleaseFastMutex(&current->Lock);
115 continue;
116 }
117 ExReleaseFastMutex(&current->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;
126 }
127 ExReleaseFastMutex(&ViewLock);
128 DPRINT("CcRosTrimCache() finished\n");
129 return(STATUS_SUCCESS);
130 }
131
132 NTSTATUS STDCALL
133 CcRosReleaseCacheSegment(PBCB Bcb,
134 PCACHE_SEGMENT CacheSeg,
135 BOOLEAN Valid,
136 BOOLEAN Dirty,
137 BOOLEAN Mapped)
138 {
139 DPRINT("CcReleaseCachePage(Bcb %x, CacheSeg %x, Valid %d)\n",
140 Bcb, CacheSeg, Valid);
141
142 CacheSeg->Valid = Valid;
143 CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
144 if (Mapped)
145 {
146 CacheSeg->MappedCount++;
147 }
148 ExReleaseFastMutex(&CacheSeg->Lock);
149 ExAcquireFastMutex(&ViewLock);
150 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
151 InsertTailList(&CacheSegmentLRUListHead,
152 &CacheSeg->CacheSegmentLRUListEntry);
153 ExReleaseFastMutex(&ViewLock);
154 InterlockedDecrement(&CacheSeg->ReferenceCount);
155
156 DPRINT("CcReleaseCachePage() finished\n");
157
158 return(STATUS_SUCCESS);
159 }
160
161 PCACHE_SEGMENT CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
162 {
163 PLIST_ENTRY current_entry;
164 PCACHE_SEGMENT current;
165 KIRQL oldIrql;
166
167 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
168 current_entry = Bcb->BcbSegmentListHead.Flink;
169 while (current_entry != &Bcb->BcbSegmentListHead)
170 {
171 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
172 BcbSegmentListEntry);
173 if (current->FileOffset <= FileOffset &&
174 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
175 {
176 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
177 return(current);
178 }
179 current_entry = current_entry->Flink;
180 }
181 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
182 return(NULL);
183 }
184
185 NTSTATUS
186 CcRosSuggestFreeCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
187 {
188 PCACHE_SEGMENT CacheSeg;
189
190 ExAcquireFastMutex(&ViewLock);
191 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
192 if (CacheSeg == NULL)
193 {
194 KeBugCheck(0);
195 }
196 ExAcquireFastMutex(&CacheSeg->Lock);
197 if (CacheSeg->MappedCount > 0)
198 {
199 KeBugCheck(0);
200 }
201 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
202 if (CacheSeg->Dirty || CacheSeg->ReferenceCount > 0)
203 {
204 ExReleaseFastMutex(&CacheSeg->Lock);
205 ExReleaseFastMutex(&ViewLock);
206 return(STATUS_UNSUCCESSFUL);
207 }
208 ExReleaseFastMutex(&CacheSeg->Lock);
209 CcRosInternalFreeCacheSegment(CacheSeg->Bcb, CacheSeg);
210 ExReleaseFastMutex(&ViewLock);
211 return(STATUS_SUCCESS);
212 }
213
214 NTSTATUS
215 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
216 {
217 PCACHE_SEGMENT CacheSeg;
218
219 ExAcquireFastMutex(&ViewLock);
220 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
221 if (CacheSeg == NULL)
222 {
223 ExReleaseFastMutex(&ViewLock);
224 return(STATUS_UNSUCCESSFUL);
225 }
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);
233 }
234
235 NTSTATUS STATIC
236 CcRosCreateCacheSegment(PBCB Bcb,
237 ULONG FileOffset,
238 PCACHE_SEGMENT* CacheSeg,
239 BOOLEAN Lock)
240 {
241 ULONG i;
242 PCACHE_SEGMENT current;
243 NTSTATUS Status;
244 KIRQL oldIrql;
245
246 current = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_SEGMENT),
247 TAG_CSEG);
248
249 MmLockAddressSpace(MmGetKernelAddressSpace());
250 current->BaseAddress = NULL;
251 Status = MmCreateMemoryArea(KernelMode,
252 MmGetKernelAddressSpace(),
253 MEMORY_AREA_CACHE_SEGMENT,
254 &current->BaseAddress,
255 Bcb->CacheSegmentSize,
256 PAGE_READWRITE,
257 (PMEMORY_AREA*)&current->MemoryArea,
258 FALSE);
259 if (!NT_SUCCESS(Status))
260 {
261 MmUnlockAddressSpace(MmGetKernelAddressSpace());
262 KeBugCheck(0);
263 }
264 MmUnlockAddressSpace(MmGetKernelAddressSpace());
265 current->Valid = FALSE;
266 current->Dirty = FALSE;
267 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
268 current->Bcb = Bcb;
269 current->MappedCount = 0;
270 ExInitializeFastMutex(&current->Lock);
271 current->ReferenceCount = 1;
272 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
273 InsertTailList(&Bcb->BcbSegmentListHead, &current->BcbSegmentListEntry);
274 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
275 InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
276 InsertTailList(&CacheSegmentLRUListHead,
277 &current->CacheSegmentLRUListEntry);
278 current->DirtySegmentListEntry.Flink =
279 current->DirtySegmentListEntry.Blink = NULL;
280 if (Lock)
281 {
282 ExAcquireFastMutex(&current->Lock);
283 }
284 ExReleaseFastMutex(&ViewLock);
285 for (i = 0; i < (Bcb->CacheSegmentSize / PAGESIZE); i++)
286 {
287 PVOID Page;
288
289 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
290 if (!NT_SUCCESS(Status))
291 {
292 KeBugCheck(0);
293 }
294
295 Status = MmCreateVirtualMapping(NULL,
296 current->BaseAddress + (i * PAGESIZE),
297 PAGE_READWRITE,
298 (ULONG)Page,
299 TRUE);
300 if (!NT_SUCCESS(Status))
301 {
302 KeBugCheck(0);
303 }
304 }
305 *CacheSeg = current;
306 return(STATUS_SUCCESS);
307 }
308
309 NTSTATUS
310 CcRosGetCacheSegmentChain(PBCB Bcb,
311 ULONG FileOffset,
312 ULONG Length,
313 PCACHE_SEGMENT* CacheSeg)
314 {
315 PCACHE_SEGMENT current;
316 ULONG i;
317 PCACHE_SEGMENT* CacheSegList;
318 PCACHE_SEGMENT Previous;
319
320 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
321
322 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
323 (Length / Bcb->CacheSegmentSize));
324
325 /*
326 * Acquire the global lock.
327 */
328 ExAcquireFastMutex(&ViewLock);
329
330 /*
331 * Look for a cache segment already mapping the same data.
332 */
333 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
334 {
335 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
336 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
337 if (current != NULL)
338 {
339 /*
340 * Make sure the cache segment can't go away outside of our control.
341 */
342 current->ReferenceCount++;
343 CacheSegList[i] = current;
344 }
345 else
346 {
347 CcRosCreateCacheSegment(Bcb, CurrentOffset, &current, FALSE);
348 CacheSegList[i] = current;
349 ExAcquireFastMutex(&ViewLock);
350 }
351 }
352 ExReleaseFastMutex(&ViewLock);
353
354 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
355 {
356 ExAcquireFastMutex(&CacheSegList[i]->Lock);
357 if (i == 0)
358 {
359 *CacheSeg = CacheSegList[i];
360 Previous = CacheSegList[i];
361 }
362 else
363 {
364 Previous->NextInChain = CacheSegList[i];
365 Previous = CacheSegList[i];
366 }
367 }
368 Previous->NextInChain = NULL;
369
370 return(STATUS_SUCCESS);
371 }
372
373 NTSTATUS
374 CcRosGetCacheSegment(PBCB Bcb,
375 ULONG FileOffset,
376 PULONG BaseOffset,
377 PVOID* BaseAddress,
378 PBOOLEAN UptoDate,
379 PCACHE_SEGMENT* CacheSeg)
380 {
381 PCACHE_SEGMENT current;
382 NTSTATUS Status;
383
384 /*
385 * Acquire the global lock.
386 */
387 ExAcquireFastMutex(&ViewLock);
388
389 /*
390 * Look for a cache segment already mapping the same data.
391 */
392 current = CcRosLookupCacheSegment(Bcb, FileOffset);
393 if (current != NULL)
394 {
395 /*
396 * Make sure the cache segment can't go away outside of our control.
397 */
398 current->ReferenceCount++;
399 /*
400 * Release the global lock and lock the cache segment.
401 */
402 ExReleaseFastMutex(&ViewLock);
403 ExAcquireFastMutex(&current->Lock);
404 /*
405 * Return information about the segment to the caller.
406 */
407 *UptoDate = current->Valid;
408 *BaseAddress = current->BaseAddress;
409 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
410 *CacheSeg = current;
411 *BaseOffset = current->FileOffset;
412 return(STATUS_SUCCESS);
413 }
414
415 /*
416 * Otherwise create a new segment.
417 */
418 Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current, TRUE);
419 *UptoDate = current->Valid;
420 *BaseAddress = current->BaseAddress;
421 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
422 *CacheSeg = current;
423 *BaseOffset = current->FileOffset;
424
425 return(STATUS_SUCCESS);
426 }
427
428 NTSTATUS STDCALL
429 CcRosRequestCacheSegment(PBCB Bcb,
430 ULONG FileOffset,
431 PVOID* BaseAddress,
432 PBOOLEAN UptoDate,
433 PCACHE_SEGMENT* CacheSeg)
434 /*
435 * FUNCTION: Request a page mapping for a BCB
436 */
437 {
438 ULONG BaseOffset;
439
440 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
441 {
442 CPRINT("Bad fileoffset %x should be multiple of %x",
443 FileOffset, Bcb->CacheSegmentSize);
444 KeBugCheck(0);
445 }
446
447 return(CcRosGetCacheSegment(Bcb,
448 FileOffset,
449 &BaseOffset,
450 BaseAddress,
451 UptoDate,
452 CacheSeg));
453 }
454
455 STATIC VOID
456 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
457 ULONG PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
458 {
459 assert(SwapEntry == 0);
460 if (PhysAddr != 0)
461 {
462 MmReleasePageMemoryConsumer(MC_CACHE, (PVOID)PhysAddr);
463 }
464 }
465
466 NTSTATUS STDCALL
467 CcRosInternalFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
468 /*
469 * FUNCTION: Releases a cache segment associated with a BCB
470 */
471 {
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,
480 CcFreeCachePage,
481 NULL);
482 MmUnlockAddressSpace(MmGetKernelAddressSpace());
483 ExFreePool(CacheSeg);
484 return(STATUS_SUCCESS);
485 }
486
487 NTSTATUS STDCALL
488 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
489 {
490 NTSTATUS Status;
491 ExAcquireFastMutex(&ViewLock);
492 Status = CcRosInternalFreeCacheSegment(Bcb, CacheSeg);
493 ExReleaseFastMutex(&ViewLock);
494 return(Status);
495 }
496
497 NTSTATUS STDCALL
498 CcRosReleaseFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
499 /*
500 * FUNCTION: Releases the BCB associated with a file object
501 */
502 {
503 PLIST_ENTRY current_entry;
504 PCACHE_SEGMENT current;
505
506 DPRINT("CcRosReleaseFileCache(FileObject %x, Bcb %x)\n", Bcb->FileObject,
507 Bcb);
508
509 MmFreeSectionSegments(Bcb->FileObject);
510
511 /*
512 * Release all cache segments.
513 */
514 current_entry = Bcb->BcbSegmentListHead.Flink;
515 while (current_entry != &Bcb->BcbSegmentListHead)
516 {
517 current =
518 CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
519 current_entry = current_entry->Flink;
520 CcRosFreeCacheSegment(Bcb, current);
521 }
522
523 ObDereferenceObject (Bcb->FileObject);
524 ExFreePool(Bcb);
525
526 return(STATUS_SUCCESS);
527 }
528
529 NTSTATUS STDCALL
530 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
531 PBCB* Bcb,
532 ULONG CacheSegmentSize)
533 /*
534 * FUNCTION: Initializes a BCB for a file object
535 */
536 {
537 (*Bcb) = ExAllocatePoolWithTag(NonPagedPool, sizeof(BCB), TAG_BCB);
538 if ((*Bcb) == NULL)
539 {
540 return(STATUS_UNSUCCESSFUL);
541 }
542
543 ObReferenceObjectByPointer(FileObject,
544 FILE_ALL_ACCESS,
545 NULL,
546 KernelMode);
547 (*Bcb)->FileObject = FileObject;
548 (*Bcb)->CacheSegmentSize = CacheSegmentSize;
549 if (FileObject->FsContext)
550 {
551 (*Bcb)->AllocationSize =
552 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->AllocationSize;
553 (*Bcb)->FileSize =
554 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->FileSize;
555 }
556 KeInitializeSpinLock(&(*Bcb)->BcbLock);
557 InitializeListHead(&(*Bcb)->BcbSegmentListHead);
558
559 return(STATUS_SUCCESS);
560 }
561
562 VOID
563 CcInitView(VOID)
564 {
565 DPRINT("CcInitView()\n");
566 InitializeListHead(&CacheSegmentListHead);
567 InitializeListHead(&DirtySegmentListHead);
568 InitializeListHead(&CacheSegmentLRUListHead);
569 ExInitializeFastMutex(&ViewLock);
570 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
571 }
572
573 /* EOF */
574
575
576
577
578
579
580