0715e33504699f53330f083bc1f4353d7e034d1b
[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.43 2002/06/10 21:11:56 hbirr 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 * FUNCTION: Try to free some memory from the file cache.
90 * ARGUMENTS:
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.
95 */
96 {
97 PLIST_ENTRY current_entry;
98 PCACHE_SEGMENT current;
99 ULONG PagesPerSegment;
100 ULONG PagesFreed;
101 BOOLEAN Locked;
102
103 DPRINT("CcRosTrimCache(Target %d)\n", Target);
104
105 *NrFreed = 0;
106
107 ExAcquireFastMutex(&ViewLock);
108 current_entry = CacheSegmentLRUListHead.Flink;
109 while (current_entry != &CacheSegmentLRUListHead && Target > 0)
110 {
111 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
112 CacheSegmentLRUListEntry);
113 current_entry = current_entry->Flink;
114 Locked = ExTryToAcquireFastMutex(&current->Lock);
115 if (!Locked)
116 {
117 continue;
118 }
119 if (current->MappedCount > 0 || current->Dirty ||
120 current->ReferenceCount > 0)
121 {
122 ExReleaseFastMutex(&current->Lock);
123 continue;
124 }
125 ExReleaseFastMutex(&current->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;
134 }
135 ExReleaseFastMutex(&ViewLock);
136 DPRINT("CcRosTrimCache() finished\n");
137 return(STATUS_SUCCESS);
138 }
139
140 NTSTATUS STDCALL
141 CcRosReleaseCacheSegment(PBCB Bcb,
142 PCACHE_SEGMENT CacheSeg,
143 BOOLEAN Valid,
144 BOOLEAN Dirty,
145 BOOLEAN Mapped)
146 {
147 DPRINT("CcReleaseCachePage(Bcb %x, CacheSeg %x, Valid %d)\n",
148 Bcb, CacheSeg, Valid);
149
150 CacheSeg->Valid = Valid;
151 CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
152 if (Mapped)
153 {
154 CacheSeg->MappedCount++;
155 }
156 ExReleaseFastMutex(&CacheSeg->Lock);
157 ExAcquireFastMutex(&ViewLock);
158 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
159 InsertTailList(&CacheSegmentLRUListHead,
160 &CacheSeg->CacheSegmentLRUListEntry);
161 ExReleaseFastMutex(&ViewLock);
162 InterlockedDecrement(&CacheSeg->ReferenceCount);
163
164 DPRINT("CcReleaseCachePage() finished\n");
165
166 return(STATUS_SUCCESS);
167 }
168
169 PCACHE_SEGMENT CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
170 {
171 PLIST_ENTRY current_entry;
172 PCACHE_SEGMENT current;
173 KIRQL oldIrql;
174
175 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
176 current_entry = Bcb->BcbSegmentListHead.Flink;
177 while (current_entry != &Bcb->BcbSegmentListHead)
178 {
179 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
180 BcbSegmentListEntry);
181 if (current->FileOffset <= FileOffset &&
182 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
183 {
184 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
185 return(current);
186 }
187 current_entry = current_entry->Flink;
188 }
189 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
190 return(NULL);
191 }
192
193 NTSTATUS
194 CcRosSuggestFreeCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
195 {
196 PCACHE_SEGMENT CacheSeg;
197
198 ExAcquireFastMutex(&ViewLock);
199 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
200 if (CacheSeg == NULL)
201 {
202 KeBugCheck(0);
203 }
204 ExAcquireFastMutex(&CacheSeg->Lock);
205 if (CacheSeg->MappedCount > 0)
206 {
207 KeBugCheck(0);
208 }
209 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
210 if (CacheSeg->Dirty || CacheSeg->ReferenceCount > 0)
211 {
212 ExReleaseFastMutex(&CacheSeg->Lock);
213 ExReleaseFastMutex(&ViewLock);
214 return(STATUS_UNSUCCESSFUL);
215 }
216 ExReleaseFastMutex(&CacheSeg->Lock);
217 CcRosInternalFreeCacheSegment(CacheSeg->Bcb, CacheSeg);
218 ExReleaseFastMutex(&ViewLock);
219 return(STATUS_SUCCESS);
220 }
221
222 NTSTATUS
223 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
224 {
225 PCACHE_SEGMENT CacheSeg;
226
227 ExAcquireFastMutex(&ViewLock);
228 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
229 if (CacheSeg == NULL)
230 {
231 ExReleaseFastMutex(&ViewLock);
232 return(STATUS_UNSUCCESSFUL);
233 }
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);
241 }
242
243 NTSTATUS STATIC
244 CcRosCreateCacheSegment(PBCB Bcb,
245 ULONG FileOffset,
246 PCACHE_SEGMENT* CacheSeg,
247 BOOLEAN Lock)
248 {
249 ULONG i;
250 PCACHE_SEGMENT current;
251 NTSTATUS Status;
252 KIRQL oldIrql;
253
254 current = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_SEGMENT),
255 TAG_CSEG);
256
257 MmLockAddressSpace(MmGetKernelAddressSpace());
258 current->BaseAddress = NULL;
259 Status = MmCreateMemoryArea(KernelMode,
260 MmGetKernelAddressSpace(),
261 MEMORY_AREA_CACHE_SEGMENT,
262 &current->BaseAddress,
263 Bcb->CacheSegmentSize,
264 PAGE_READWRITE,
265 (PMEMORY_AREA*)&current->MemoryArea,
266 FALSE);
267 if (!NT_SUCCESS(Status))
268 {
269 MmUnlockAddressSpace(MmGetKernelAddressSpace());
270 KeBugCheck(0);
271 }
272 MmUnlockAddressSpace(MmGetKernelAddressSpace());
273 current->Valid = FALSE;
274 current->Dirty = FALSE;
275 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
276 current->Bcb = Bcb;
277 current->MappedCount = 0;
278 ExInitializeFastMutex(&current->Lock);
279 current->ReferenceCount = 1;
280 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
281 InsertTailList(&Bcb->BcbSegmentListHead, &current->BcbSegmentListEntry);
282 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
283 InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
284 InsertTailList(&CacheSegmentLRUListHead,
285 &current->CacheSegmentLRUListEntry);
286 current->DirtySegmentListEntry.Flink =
287 current->DirtySegmentListEntry.Blink = NULL;
288 if (Lock)
289 {
290 ExAcquireFastMutex(&current->Lock);
291 }
292 ExReleaseFastMutex(&ViewLock);
293 for (i = 0; i < (Bcb->CacheSegmentSize / PAGESIZE); i++)
294 {
295 PHYSICAL_ADDRESS Page;
296
297 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
298 if (!NT_SUCCESS(Status))
299 {
300 KeBugCheck(0);
301 }
302
303 Status = MmCreateVirtualMapping(NULL,
304 current->BaseAddress + (i * PAGESIZE),
305 PAGE_READWRITE,
306 Page,
307 TRUE);
308 if (!NT_SUCCESS(Status))
309 {
310 KeBugCheck(0);
311 }
312 }
313 *CacheSeg = current;
314 return(STATUS_SUCCESS);
315 }
316
317 NTSTATUS
318 CcRosGetCacheSegmentChain(PBCB Bcb,
319 ULONG FileOffset,
320 ULONG Length,
321 PCACHE_SEGMENT* CacheSeg)
322 {
323 PCACHE_SEGMENT current;
324 ULONG i;
325 PCACHE_SEGMENT* CacheSegList;
326 PCACHE_SEGMENT Previous;
327
328 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
329
330 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
331 (Length / Bcb->CacheSegmentSize));
332
333 /*
334 * Acquire the global lock.
335 */
336 ExAcquireFastMutex(&ViewLock);
337
338 /*
339 * Look for a cache segment already mapping the same data.
340 */
341 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
342 {
343 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
344 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
345 if (current != NULL)
346 {
347 /*
348 * Make sure the cache segment can't go away outside of our control.
349 */
350 current->ReferenceCount++;
351 CacheSegList[i] = current;
352 }
353 else
354 {
355 CcRosCreateCacheSegment(Bcb, CurrentOffset, &current, FALSE);
356 CacheSegList[i] = current;
357 ExAcquireFastMutex(&ViewLock);
358 }
359 }
360 ExReleaseFastMutex(&ViewLock);
361
362 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
363 {
364 ExAcquireFastMutex(&CacheSegList[i]->Lock);
365 if (i == 0)
366 {
367 *CacheSeg = CacheSegList[i];
368 Previous = CacheSegList[i];
369 }
370 else
371 {
372 Previous->NextInChain = CacheSegList[i];
373 Previous = CacheSegList[i];
374 }
375 }
376 Previous->NextInChain = NULL;
377
378 return(STATUS_SUCCESS);
379 }
380
381 NTSTATUS
382 CcRosGetCacheSegment(PBCB Bcb,
383 ULONG FileOffset,
384 PULONG BaseOffset,
385 PVOID* BaseAddress,
386 PBOOLEAN UptoDate,
387 PCACHE_SEGMENT* CacheSeg)
388 {
389 PCACHE_SEGMENT current;
390 NTSTATUS Status;
391
392 /*
393 * Acquire the global lock.
394 */
395 ExAcquireFastMutex(&ViewLock);
396
397 /*
398 * Look for a cache segment already mapping the same data.
399 */
400 current = CcRosLookupCacheSegment(Bcb, FileOffset);
401 if (current != NULL)
402 {
403 /*
404 * Make sure the cache segment can't go away outside of our control.
405 */
406 current->ReferenceCount++;
407 /*
408 * Release the global lock and lock the cache segment.
409 */
410 ExReleaseFastMutex(&ViewLock);
411 ExAcquireFastMutex(&current->Lock);
412 /*
413 * Return information about the segment to the caller.
414 */
415 *UptoDate = current->Valid;
416 *BaseAddress = current->BaseAddress;
417 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
418 *CacheSeg = current;
419 *BaseOffset = current->FileOffset;
420 return(STATUS_SUCCESS);
421 }
422
423 /*
424 * Otherwise create a new segment.
425 */
426 Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current, TRUE);
427 *UptoDate = current->Valid;
428 *BaseAddress = current->BaseAddress;
429 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
430 *CacheSeg = current;
431 *BaseOffset = current->FileOffset;
432
433 return(STATUS_SUCCESS);
434 }
435
436 NTSTATUS STDCALL
437 CcRosRequestCacheSegment(PBCB Bcb,
438 ULONG FileOffset,
439 PVOID* BaseAddress,
440 PBOOLEAN UptoDate,
441 PCACHE_SEGMENT* CacheSeg)
442 /*
443 * FUNCTION: Request a page mapping for a BCB
444 */
445 {
446 ULONG BaseOffset;
447
448 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
449 {
450 CPRINT("Bad fileoffset %x should be multiple of %x",
451 FileOffset, Bcb->CacheSegmentSize);
452 KeBugCheck(0);
453 }
454
455 return(CcRosGetCacheSegment(Bcb,
456 FileOffset,
457 &BaseOffset,
458 BaseAddress,
459 UptoDate,
460 CacheSeg));
461 }
462
463 STATIC VOID
464 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
465 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
466 {
467 assert(SwapEntry == 0);
468 if (PhysAddr.QuadPart != 0)
469 {
470 MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
471 }
472 }
473
474 NTSTATUS STDCALL
475 CcRosInternalFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
476 /*
477 * FUNCTION: Releases a cache segment associated with a BCB
478 */
479 {
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,
488 CcFreeCachePage,
489 NULL);
490 MmUnlockAddressSpace(MmGetKernelAddressSpace());
491 ExFreePool(CacheSeg);
492 return(STATUS_SUCCESS);
493 }
494
495 NTSTATUS STDCALL
496 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
497 {
498 NTSTATUS Status;
499 ExAcquireFastMutex(&ViewLock);
500 Status = CcRosInternalFreeCacheSegment(Bcb, CacheSeg);
501 ExReleaseFastMutex(&ViewLock);
502 return(Status);
503 }
504
505 NTSTATUS STDCALL
506 CcRosReleaseFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
507 /*
508 * FUNCTION: Releases the BCB associated with a file object
509 */
510 {
511 PLIST_ENTRY current_entry;
512 PCACHE_SEGMENT current;
513
514 DPRINT("CcRosReleaseFileCache(FileObject %x, Bcb %x)\n", Bcb->FileObject,
515 Bcb);
516
517 MmFreeSectionSegments(Bcb->FileObject);
518
519 /*
520 * Release all cache segments.
521 */
522 current_entry = Bcb->BcbSegmentListHead.Flink;
523 while (current_entry != &Bcb->BcbSegmentListHead)
524 {
525 current =
526 CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
527 current_entry = current_entry->Flink;
528 CcRosFreeCacheSegment(Bcb, current);
529 }
530
531 ObDereferenceObject (Bcb->FileObject);
532 ExFreePool(Bcb);
533
534 return(STATUS_SUCCESS);
535 }
536
537 NTSTATUS STDCALL
538 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
539 PBCB* Bcb,
540 ULONG CacheSegmentSize)
541 /*
542 * FUNCTION: Initializes a BCB for a file object
543 */
544 {
545 (*Bcb) = ExAllocatePoolWithTag(NonPagedPool, sizeof(BCB), TAG_BCB);
546 if ((*Bcb) == NULL)
547 {
548 return(STATUS_UNSUCCESSFUL);
549 }
550
551 ObReferenceObjectByPointer(FileObject,
552 FILE_ALL_ACCESS,
553 NULL,
554 KernelMode);
555 (*Bcb)->FileObject = FileObject;
556 (*Bcb)->CacheSegmentSize = CacheSegmentSize;
557 if (FileObject->FsContext)
558 {
559 (*Bcb)->AllocationSize =
560 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->AllocationSize;
561 (*Bcb)->FileSize =
562 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->FileSize;
563 }
564 KeInitializeSpinLock(&(*Bcb)->BcbLock);
565 InitializeListHead(&(*Bcb)->BcbSegmentListHead);
566
567 return(STATUS_SUCCESS);
568 }
569
570 VOID
571 CcInitView(VOID)
572 {
573 DPRINT("CcInitView()\n");
574 InitializeListHead(&CacheSegmentListHead);
575 InitializeListHead(&DirtySegmentListHead);
576 InitializeListHead(&CacheSegmentLRUListHead);
577 ExInitializeFastMutex(&ViewLock);
578 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
579 InitCacheZeroPage();
580 }
581
582 /* EOF */
583
584
585
586
587
588
589