Modified some functions, changed the sequence for acquiring some of
[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.53 2002/10/02 19:20:51 hbirr Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/cc/view.c
23 * PURPOSE: Cache manager
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * PORTABILITY: Checked
26 * UPDATE HISTORY:
27 * Created 22/05/98
28 */
29
30 /* NOTES **********************************************************************
31 *
32 * This is not the NT implementation of a file cache nor anything much like
33 * it.
34 *
35 * The general procedure for a filesystem to implement a read or write
36 * dispatch routine is as follows
37 *
38 * (1) If caching for the FCB hasn't been initiated then so do by calling
39 * CcInitializeFileCache.
40 *
41 * (2) For each 4k region which is being read or written obtain a cache page
42 * by calling CcRequestCachePage.
43 *
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.
47 *
48 * (4) Copy the data into or out of the page as necessary.
49 *
50 * (5) Release the cache page
51 */
52 /* INCLUDES ******************************************************************/
53
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>
59 #include <ntos/minmax.h>
60
61 #define NDEBUG
62 #include <internal/debug.h>
63
64 /* GLOBALS *******************************************************************/
65
66 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
67 #define ROUND_DOWN(N, S) (((N) % (S)) ? ROUND_UP(N, S) - S : N)
68
69 #define TAG_CSEG TAG('C', 'S', 'E', 'G')
70 #define TAG_BCB TAG('B', 'C', 'B', ' ')
71
72 static LIST_ENTRY DirtySegmentListHead;
73 static LIST_ENTRY CacheSegmentListHead;
74 static LIST_ENTRY CacheSegmentLRUListHead;
75
76 static FAST_MUTEX ViewLock;
77
78 void * alloca(size_t size);
79
80 NTSTATUS STDCALL
81 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg);
82
83 /* FUNCTIONS *****************************************************************/
84
85 NTSTATUS STATIC
86 CcRosFlushCacheSegment(PCACHE_SEGMENT CacheSegment)
87 {
88 NTSTATUS Status;
89 KIRQL oldIrql;
90 Status = WriteCacheSegment(CacheSegment);
91 if (NT_SUCCESS(Status))
92 {
93 ExAcquireFastMutex(&ViewLock);
94 KeAcquireSpinLock(&CacheSegment->Bcb->BcbLock, &oldIrql);
95 CacheSegment->Dirty = FALSE;
96 RemoveEntryList(&CacheSegment->DirtySegmentListEntry);
97 CacheSegment->ReferenceCount--;
98 KeReleaseSpinLock(&CacheSegment->Bcb->BcbLock, oldIrql);
99 ExReleaseFastMutex(&ViewLock);
100 }
101 return(Status);
102 }
103
104 NTSTATUS
105 CcRosFlushDirtyPages(ULONG Target, PULONG Count)
106 {
107 PLIST_ENTRY current_entry;
108 PCACHE_SEGMENT current;
109 ULONG PagesPerSegment;
110 BOOLEAN Locked;
111 NTSTATUS Status;
112
113 DPRINT("CcRosFlushDirtyPages(Target %d)\n", Target);
114
115 (*Count) = 0;
116
117 ExAcquireFastMutex(&ViewLock);
118 current_entry = DirtySegmentListHead.Flink;
119 while (current_entry != &DirtySegmentListHead && Target > 0)
120 {
121 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
122 DirtySegmentListEntry);
123 current_entry = current_entry->Flink;
124 Locked = ExTryToAcquireFastMutex(&current->Lock);
125 if (!Locked)
126 {
127 continue;
128 }
129 assert(current->Dirty);
130 if (current->ReferenceCount > 1)
131 {
132 ExReleaseFastMutex(&current->Lock);
133 continue;
134 }
135 ExReleaseFastMutex(&ViewLock);
136 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
137 Status = CcRosFlushCacheSegment(current);
138 ExReleaseFastMutex(&current->Lock);
139 if (!NT_SUCCESS(Status))
140 {
141 DPRINT1("CC: Failed to flush cache segment.\n");
142 }
143 else
144 {
145 (*Count) += PagesPerSegment;
146 Target -= PagesPerSegment;
147 }
148 ExAcquireFastMutex(&ViewLock);
149 current_entry = DirtySegmentListHead.Flink;
150 }
151 ExReleaseFastMutex(&ViewLock);
152 DPRINT("CcRosTrimCache() finished\n");
153 return(STATUS_SUCCESS);
154 }
155
156 NTSTATUS
157 CcRosTrimCache(ULONG Target, ULONG Priority, PULONG NrFreed)
158 /*
159 * FUNCTION: Try to free some memory from the file cache.
160 * ARGUMENTS:
161 * Target - The number of pages to be freed.
162 * Priority - The priority of free (currently unused).
163 * NrFreed - Points to a variable where the number of pages
164 * actually freed is returned.
165 */
166 {
167 PLIST_ENTRY current_entry;
168 PCACHE_SEGMENT current;
169 ULONG PagesPerSegment;
170 ULONG PagesFreed;
171 KIRQL oldIrql;
172 LIST_ENTRY FreeList;
173
174 DPRINT("CcRosTrimCache(Target %d)\n", Target);
175
176 *NrFreed = 0;
177
178 InitializeListHead(&FreeList);
179
180 ExAcquireFastMutex(&ViewLock);
181 current_entry = CacheSegmentLRUListHead.Flink;
182 while (current_entry != &CacheSegmentLRUListHead && Target > 0)
183 {
184 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
185 CacheSegmentLRUListEntry);
186 current_entry = current_entry->Flink;
187
188 KeAcquireSpinLock(&current->Bcb->BcbLock, &oldIrql);
189 if (current->ReferenceCount == 0)
190 {
191 RemoveEntryList(&current->BcbSegmentListEntry);
192 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
193 RemoveEntryList(&current->CacheSegmentListEntry);
194 RemoveEntryList(&current->CacheSegmentLRUListEntry);
195 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
196 }
197 else
198 {
199 KeReleaseSpinLock(&current->Bcb->BcbLock, oldIrql);
200 }
201 }
202 ExReleaseFastMutex(&ViewLock);
203
204 while (!IsListEmpty(&FreeList))
205 {
206 current_entry = RemoveHeadList(&FreeList);
207 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
208 BcbSegmentListEntry);
209 PagesPerSegment = current->Bcb->CacheSegmentSize / PAGE_SIZE;
210 PagesFreed = min(PagesPerSegment, Target);
211 Target -= PagesFreed;
212 (*NrFreed) += PagesFreed;
213 CcRosInternalFreeCacheSegment(current);
214 }
215
216 DPRINT("CcRosTrimCache() finished\n");
217 return(STATUS_SUCCESS);
218 }
219
220 NTSTATUS STDCALL
221 CcRosReleaseCacheSegment(PBCB Bcb,
222 PCACHE_SEGMENT CacheSeg,
223 BOOLEAN Valid,
224 BOOLEAN Dirty,
225 BOOLEAN Mapped)
226 {
227 BOOLEAN WasDirty = CacheSeg->Dirty;
228 KIRQL oldIrql;
229
230 DPRINT("CcReleaseCacheSegment(Bcb %x, CacheSeg %x, Valid %d)\n",
231 Bcb, CacheSeg, Valid);
232
233 CacheSeg->Valid = Valid;
234 CacheSeg->Dirty = CacheSeg->Dirty || Dirty;
235
236 ExAcquireFastMutex(&ViewLock);
237 if (!WasDirty && CacheSeg->Dirty)
238 {
239 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
240 }
241 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
242 InsertTailList(&CacheSegmentLRUListHead, &CacheSeg->CacheSegmentLRUListEntry);
243
244 if (Mapped)
245 {
246 CacheSeg->MappedCount++;
247 }
248 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
249 CacheSeg->ReferenceCount--;
250 if (Mapped && CacheSeg->MappedCount == 1)
251 {
252 CacheSeg->ReferenceCount++;
253 }
254 if (!WasDirty && CacheSeg->Dirty)
255 {
256 CacheSeg->ReferenceCount++;
257 }
258 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
259 ExReleaseFastMutex(&ViewLock);
260 ExReleaseFastMutex(&CacheSeg->Lock);
261
262 return(STATUS_SUCCESS);
263 }
264
265 PCACHE_SEGMENT CcRosLookupCacheSegment(PBCB Bcb, ULONG FileOffset)
266 {
267 PLIST_ENTRY current_entry;
268 PCACHE_SEGMENT current;
269 KIRQL oldIrql;
270
271 DPRINT("CcRosLookupCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
272
273 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
274 current_entry = Bcb->BcbSegmentListHead.Flink;
275 while (current_entry != &Bcb->BcbSegmentListHead)
276 {
277 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
278 BcbSegmentListEntry);
279 if (current->FileOffset <= FileOffset &&
280 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
281 {
282 current->ReferenceCount++;
283 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
284 return(current);
285 }
286 current_entry = current_entry->Flink;
287 }
288 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
289 return(NULL);
290 }
291
292 NTSTATUS
293 CcRosMarkDirtyCacheSegment(PBCB Bcb, ULONG FileOffset)
294 {
295 PCACHE_SEGMENT CacheSeg;
296 KIRQL oldIrql;
297
298 DPRINT("CcRosMarkDirtyCacheSegment(Bcb %x, FileOffset %d)\n", Bcb, FileOffset);
299
300 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
301 if (CacheSeg == NULL)
302 {
303 KeBugCheck(0);
304 }
305 ExAcquireFastMutex(&CacheSeg->Lock);
306 if (!CacheSeg->Dirty)
307 {
308 ExAcquireFastMutex(&ViewLock);
309 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
310 ExReleaseFastMutex(&ViewLock);
311 }
312 else
313 {
314 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
315 CacheSeg->ReferenceCount--;
316 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
317 }
318
319
320 CacheSeg->Dirty = TRUE;
321 ExReleaseFastMutex(&CacheSeg->Lock);
322
323 return(STATUS_SUCCESS);
324 }
325
326 NTSTATUS
327 CcRosUnmapCacheSegment(PBCB Bcb, ULONG FileOffset, BOOLEAN NowDirty)
328 {
329 PCACHE_SEGMENT CacheSeg;
330 BOOLEAN WasDirty;
331 KIRQL oldIrql;
332
333 DPRINT("CcRosUnmapCacheSegment(Bcb %x, FileOffset %d, NowDirty %d)\n",
334 Bcb, FileOffset, NowDirty);
335
336 CacheSeg = CcRosLookupCacheSegment(Bcb, FileOffset);
337 if (CacheSeg == NULL)
338 {
339 return(STATUS_UNSUCCESSFUL);
340 }
341 ExAcquireFastMutex(&CacheSeg->Lock);
342
343 WasDirty = CacheSeg->Dirty;
344 CacheSeg->Dirty = CacheSeg->Dirty || NowDirty;
345
346 CacheSeg->MappedCount--;
347
348 if (!WasDirty && NowDirty)
349 {
350 ExAcquireFastMutex(&ViewLock);
351 InsertTailList(&DirtySegmentListHead, &CacheSeg->DirtySegmentListEntry);
352 ExReleaseFastMutex(&ViewLock);
353 }
354
355 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
356 CacheSeg->ReferenceCount--;
357 if (!WasDirty && NowDirty)
358 {
359 CacheSeg->ReferenceCount++;
360 }
361 if (CacheSeg->MappedCount == 0)
362 {
363 CacheSeg->ReferenceCount--;
364 }
365 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
366
367 ExReleaseFastMutex(&CacheSeg->Lock);
368 return(STATUS_SUCCESS);
369 }
370
371 NTSTATUS STATIC
372 CcRosCreateCacheSegment(PBCB Bcb,
373 ULONG FileOffset,
374 PCACHE_SEGMENT* CacheSeg,
375 BOOLEAN Lock)
376 {
377 ULONG i;
378 PCACHE_SEGMENT current;
379 PLIST_ENTRY current_entry;
380 NTSTATUS Status;
381 KIRQL oldIrql;
382
383 DPRINT("CcRosCreateCacheSegment()\n");
384
385 current = ExAllocatePoolWithTag(NonPagedPool, sizeof(CACHE_SEGMENT),
386 TAG_CSEG);
387 current->Valid = FALSE;
388 current->Dirty = FALSE;
389 current->FileOffset = ROUND_DOWN(FileOffset, Bcb->CacheSegmentSize);
390 current->Bcb = Bcb;
391 current->MappedCount = 0;
392 current->DirtySegmentListEntry.Flink = NULL;
393 current->DirtySegmentListEntry.Blink = NULL;
394 current->ReferenceCount = 1;
395 ExInitializeFastMutex(&current->Lock);
396 ExAcquireFastMutex(&current->Lock);
397 ExAcquireFastMutex(&ViewLock);
398
399 *CacheSeg = current;
400 /* There is window between the call to CcRosLookupCacheSegment
401 * and CcRosCreateCacheSegment. We must check if a segment on
402 * the fileoffset exist. If there exist a segment, we release
403 * our new created segment and return the existing one.
404 */
405 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
406 current_entry = Bcb->BcbSegmentListHead.Flink;
407 while (current_entry != &Bcb->BcbSegmentListHead)
408 {
409 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
410 BcbSegmentListEntry);
411 if (current->FileOffset <= FileOffset &&
412 (current->FileOffset + Bcb->CacheSegmentSize) > FileOffset)
413 {
414 current->ReferenceCount++;
415 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
416 ExReleaseFastMutex(&(*CacheSeg)->Lock);
417 ExReleaseFastMutex(&ViewLock);
418 ExFreePool(*CacheSeg);
419 *CacheSeg = current;
420 if (Lock)
421 {
422 ExAcquireFastMutex(&current->Lock);
423 }
424 return STATUS_SUCCESS;
425 }
426 current_entry = current_entry->Flink;
427 }
428 /* There was no existing segment. */
429 current = *CacheSeg;
430 InsertTailList(&Bcb->BcbSegmentListHead, &current->BcbSegmentListEntry);
431 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
432 InsertTailList(&CacheSegmentListHead, &current->CacheSegmentListEntry);
433 InsertTailList(&CacheSegmentLRUListHead, &current->CacheSegmentLRUListEntry);
434 ExReleaseFastMutex(&ViewLock);
435
436 MmLockAddressSpace(MmGetKernelAddressSpace());
437 current->BaseAddress = NULL;
438 Status = MmCreateMemoryArea(NULL,
439 MmGetKernelAddressSpace(),
440 MEMORY_AREA_CACHE_SEGMENT,
441 &current->BaseAddress,
442 Bcb->CacheSegmentSize,
443 PAGE_READWRITE,
444 (PMEMORY_AREA*)&current->MemoryArea,
445 FALSE);
446 MmUnlockAddressSpace(MmGetKernelAddressSpace());
447 if (!NT_SUCCESS(Status))
448 {
449 KeBugCheck(0);
450 }
451 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
452 {
453 PHYSICAL_ADDRESS Page;
454
455 Status = MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page);
456 if (!NT_SUCCESS(Status))
457 {
458 KeBugCheck(0);
459 }
460
461 Status = MmCreateVirtualMapping(NULL,
462 current->BaseAddress + (i * PAGE_SIZE),
463 PAGE_READWRITE,
464 Page,
465 TRUE);
466 if (!NT_SUCCESS(Status))
467 {
468 KeBugCheck(0);
469 }
470 }
471 if (!Lock)
472 {
473 ExReleaseFastMutex(&current->Lock);
474 }
475
476 return(STATUS_SUCCESS);
477 }
478
479 NTSTATUS
480 CcRosGetCacheSegmentChain(PBCB Bcb,
481 ULONG FileOffset,
482 ULONG Length,
483 PCACHE_SEGMENT* CacheSeg)
484 {
485 PCACHE_SEGMENT current;
486 ULONG i;
487 PCACHE_SEGMENT* CacheSegList;
488 PCACHE_SEGMENT Previous = NULL;
489
490 DPRINT("CcRosGetCacheSegmentChain()\n");
491
492 Length = ROUND_UP(Length, Bcb->CacheSegmentSize);
493
494 CacheSegList = alloca(sizeof(PCACHE_SEGMENT) *
495 (Length / Bcb->CacheSegmentSize));
496
497 /*
498 * Look for a cache segment already mapping the same data.
499 */
500 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
501 {
502 ULONG CurrentOffset = FileOffset + (i * Bcb->CacheSegmentSize);
503 current = CcRosLookupCacheSegment(Bcb, CurrentOffset);
504 if (current != NULL)
505 {
506 CacheSegList[i] = current;
507 }
508 else
509 {
510 CcRosCreateCacheSegment(Bcb, CurrentOffset, &current, FALSE);
511 CacheSegList[i] = current;
512 }
513 }
514
515 for (i = 0; i < (Length / Bcb->CacheSegmentSize); i++)
516 {
517 ExAcquireFastMutex(&CacheSegList[i]->Lock);
518 if (i == 0)
519 {
520 *CacheSeg = CacheSegList[i];
521 Previous = CacheSegList[i];
522 }
523 else
524 {
525 Previous->NextInChain = CacheSegList[i];
526 Previous = CacheSegList[i];
527 }
528 }
529 Previous->NextInChain = NULL;
530
531 return(STATUS_SUCCESS);
532 }
533
534 NTSTATUS
535 CcRosGetCacheSegment(PBCB Bcb,
536 ULONG FileOffset,
537 PULONG BaseOffset,
538 PVOID* BaseAddress,
539 PBOOLEAN UptoDate,
540 PCACHE_SEGMENT* CacheSeg)
541 {
542 PCACHE_SEGMENT current;
543 NTSTATUS Status;
544
545 DPRINT("CcRosGetCacheSegment()\n");
546
547 /*
548 * Look for a cache segment already mapping the same data.
549 */
550 current = CcRosLookupCacheSegment(Bcb, FileOffset);
551 if (current != NULL)
552 {
553 ExAcquireFastMutex(&current->Lock);
554 }
555 else
556 {
557 /*
558 * Otherwise create a new segment.
559 */
560 Status = CcRosCreateCacheSegment(Bcb, FileOffset, &current, TRUE);
561 }
562 /*
563 * Return information about the segment to the caller.
564 */
565 *UptoDate = current->Valid;
566 *BaseAddress = current->BaseAddress;
567 DPRINT("*BaseAddress 0x%.8X\n", *BaseAddress);
568 *CacheSeg = current;
569 *BaseOffset = current->FileOffset;
570 return(STATUS_SUCCESS);
571 }
572
573 NTSTATUS STDCALL
574 CcRosRequestCacheSegment(PBCB Bcb,
575 ULONG FileOffset,
576 PVOID* BaseAddress,
577 PBOOLEAN UptoDate,
578 PCACHE_SEGMENT* CacheSeg)
579 /*
580 * FUNCTION: Request a page mapping for a BCB
581 */
582 {
583 ULONG BaseOffset;
584
585 if ((FileOffset % Bcb->CacheSegmentSize) != 0)
586 {
587 CPRINT("Bad fileoffset %x should be multiple of %x",
588 FileOffset, Bcb->CacheSegmentSize);
589 KeBugCheck(0);
590 }
591
592 return(CcRosGetCacheSegment(Bcb,
593 FileOffset,
594 &BaseOffset,
595 BaseAddress,
596 UptoDate,
597 CacheSeg));
598 }
599
600 STATIC VOID
601 CcFreeCachePage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
602 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, BOOLEAN Dirty)
603 {
604 assert(SwapEntry == 0);
605 if (PhysAddr.QuadPart != 0)
606 {
607 MmReleasePageMemoryConsumer(MC_CACHE, PhysAddr);
608 }
609 }
610
611 NTSTATUS STDCALL
612 CcRosInternalFreeCacheSegment(PCACHE_SEGMENT CacheSeg)
613 /*
614 * FUNCTION: Releases a cache segment associated with a BCB
615 */
616 {
617
618 DPRINT("Freeing cache segment %x\n", CacheSeg);
619
620 MmLockAddressSpace(MmGetKernelAddressSpace());
621 MmFreeMemoryArea(MmGetKernelAddressSpace(),
622 CacheSeg->BaseAddress,
623 CacheSeg->Bcb->CacheSegmentSize,
624 CcFreeCachePage,
625 NULL);
626 MmUnlockAddressSpace(MmGetKernelAddressSpace());
627 ExFreePool(CacheSeg);
628 return(STATUS_SUCCESS);
629 }
630
631 NTSTATUS STDCALL
632 CcRosFreeCacheSegment(PBCB Bcb, PCACHE_SEGMENT CacheSeg)
633 {
634 NTSTATUS Status;
635 KIRQL oldIrql;
636
637 DPRINT("CcRosFreeCacheSegment(Bcb %x, CacheSeg %x)\n",
638 Bcb, CacheSeg);
639
640 ExAcquireFastMutex(&ViewLock);
641 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
642 RemoveEntryList(&CacheSeg->BcbSegmentListEntry);
643 RemoveEntryList(&CacheSeg->CacheSegmentListEntry);
644 RemoveEntryList(&CacheSeg->CacheSegmentLRUListEntry);
645 if (CacheSeg->Dirty)
646 {
647 RemoveEntryList(&CacheSeg->DirtySegmentListEntry);
648 }
649 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
650 ExReleaseFastMutex(&ViewLock);
651
652 Status = CcRosInternalFreeCacheSegment(CacheSeg);
653 return(Status);
654 }
655
656 VOID STDCALL
657 CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointers,
658 IN PLARGE_INTEGER FileOffset OPTIONAL,
659 IN ULONG Length,
660 OUT PIO_STATUS_BLOCK IoStatus)
661 {
662 PBCB Bcb;
663 LARGE_INTEGER Offset;
664 PCACHE_SEGMENT current;
665 NTSTATUS Status;
666 KIRQL oldIrql;
667
668 DPRINT("CcFlushCache(SectionObjectPointers %x, FileOffset %x, Length %d, IoStatus %x)\n",
669 SectionObjectPointers, FileOffset, Length, IoStatus);
670
671 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
672 {
673 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
674 if (FileOffset)
675 {
676 Offset = *FileOffset;
677 }
678 else
679 {
680 Offset.QuadPart = 0LL;
681 Length = Bcb->FileSize.u.LowPart;
682 }
683
684 if (IoStatus)
685 {
686 IoStatus->Status = STATUS_SUCCESS;
687 IoStatus->Information = 0;
688 }
689
690 while (Length > 0)
691 {
692 current = CcRosLookupCacheSegment (Bcb, Offset.u.LowPart);
693 if (current != NULL)
694 {
695 ExAcquireFastMutex(&current->Lock);
696 if (current->Dirty)
697 {
698 Status = CcRosFlushCacheSegment(current);
699 if (!NT_SUCCESS(Status) && IoStatus != NULL)
700 {
701 IoStatus->Status = Status;
702 }
703 }
704 ExReleaseFastMutex(&current->Lock);
705 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
706 current->ReferenceCount--;
707 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
708 }
709
710 Offset.QuadPart += Bcb->CacheSegmentSize;
711 if (Length > Bcb->CacheSegmentSize)
712 {
713 Length -= Bcb->CacheSegmentSize;
714 }
715 else
716 {
717 Length = 0;
718 }
719 }
720 }
721 else
722 {
723 if (IoStatus)
724 {
725 IoStatus->Status = STATUS_INVALID_PARAMETER;
726 }
727 }
728 }
729
730 NTSTATUS STDCALL
731 CcRosDeleteFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
732 /*
733 * FUNCTION: Releases the BCB associated with a file object
734 */
735 {
736 PLIST_ENTRY current_entry;
737 PCACHE_SEGMENT current;
738 NTSTATUS Status;
739 LIST_ENTRY FreeList;
740 KIRQL oldIrql;
741
742 DPRINT("CcRosDeleteFileCache(FileObject %x, Bcb %x)\n",
743 Bcb->FileObject, Bcb);
744
745 ExReleaseFastMutex(&ViewLock);
746
747 CcFlushCache(FileObject->SectionObjectPointers, NULL, 0, NULL);
748
749 ExAcquireFastMutex(&ViewLock);
750
751 if (Bcb->RefCount == 0)
752 {
753 MmFreeSectionSegments(Bcb->FileObject);
754
755 /*
756 * Release all cache segments.
757 */
758 InitializeListHead(&FreeList);
759
760 FileObject->SectionObjectPointers->SharedCacheMap = NULL;
761 KeAcquireSpinLock(&Bcb->BcbLock, &oldIrql);
762 current_entry = Bcb->BcbSegmentListHead.Flink;
763 while (!IsListEmpty(&Bcb->BcbSegmentListHead))
764 {
765 current_entry = RemoveTailList(&Bcb->BcbSegmentListHead);
766 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
767 RemoveEntryList(&current->CacheSegmentListEntry);
768 RemoveEntryList(&current->CacheSegmentLRUListEntry);
769 if (current->Dirty)
770 {
771 RemoveEntryList(&current->DirtySegmentListEntry);
772 }
773 InsertHeadList(&FreeList, &current->BcbSegmentListEntry);
774
775 }
776 KeReleaseSpinLock(&Bcb->BcbLock, oldIrql);
777 while (!IsListEmpty(&FreeList))
778 {
779 current_entry = RemoveTailList(&FreeList);
780 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT, BcbSegmentListEntry);
781 Status = CcRosInternalFreeCacheSegment(current);
782 }
783
784 ObDereferenceObject (Bcb->FileObject);
785 ExFreePool(Bcb);
786 }
787 return(STATUS_SUCCESS);
788 }
789
790 VOID CcRosReferenceCache(PFILE_OBJECT FileObject)
791 {
792 PBCB Bcb;
793 ExAcquireFastMutex(&ViewLock);
794 Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
795 Bcb->RefCount++;
796 ExReleaseFastMutex(&ViewLock);
797 }
798
799 VOID CcRosDereferenceCache(PFILE_OBJECT FileObject)
800 {
801 PBCB Bcb;
802 ExAcquireFastMutex(&ViewLock);
803 Bcb = (PBCB)FileObject->SectionObjectPointers->SharedCacheMap;
804 Bcb->RefCount--;
805 if (Bcb->RefCount == 0)
806 {
807 CcRosDeleteFileCache(FileObject, Bcb);
808 }
809 ExReleaseFastMutex(&ViewLock);
810 }
811
812 NTSTATUS STDCALL
813 CcRosReleaseFileCache(PFILE_OBJECT FileObject, PBCB Bcb)
814 /*
815 * FUNCTION: Called by the file system when a handle to a file object
816 * has been closed.
817 */
818 {
819 ExAcquireFastMutex(&ViewLock);
820
821 if (FileObject->SectionObjectPointers->SharedCacheMap != NULL)
822 {
823 if (FileObject->PrivateCacheMap != NULL)
824 {
825 FileObject->PrivateCacheMap = NULL;
826 Bcb->RefCount--;
827 }
828 if (Bcb->RefCount == 0)
829 {
830 CcRosDeleteFileCache(FileObject, Bcb);
831 }
832 }
833 ExReleaseFastMutex(&ViewLock);
834 return(STATUS_SUCCESS);
835 }
836
837 NTSTATUS STDCALL
838 CcRosInitializeFileCache(PFILE_OBJECT FileObject,
839 PBCB* Bcb,
840 ULONG CacheSegmentSize)
841 /*
842 * FUNCTION: Initializes a BCB for a file object
843 */
844 {
845 DPRINT("CcRosInitializeFileCache(FileObject %x, *Bcb %x, CacheSegmentSize %d)\n",
846 FileObject, Bcb, CacheSegmentSize);
847
848 ExAcquireFastMutex(&ViewLock);
849
850 if (*Bcb == NULL)
851 {
852 (*Bcb) = ExAllocatePoolWithTag(NonPagedPool, sizeof(BCB), TAG_BCB);
853 if ((*Bcb) == NULL)
854 {
855 return(STATUS_UNSUCCESSFUL);
856 }
857
858 ObReferenceObjectByPointer(FileObject,
859 FILE_ALL_ACCESS,
860 NULL,
861 KernelMode);
862 (*Bcb)->FileObject = FileObject;
863 (*Bcb)->CacheSegmentSize = CacheSegmentSize;
864 if (FileObject->FsContext)
865 {
866 (*Bcb)->AllocationSize =
867 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->AllocationSize;
868 (*Bcb)->FileSize =
869 ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->FileSize;
870 }
871 KeInitializeSpinLock(&(*Bcb)->BcbLock);
872 InitializeListHead(&(*Bcb)->BcbSegmentListHead);
873 FileObject->SectionObjectPointers->SharedCacheMap = *Bcb;
874 }
875 if (FileObject->PrivateCacheMap == NULL)
876 {
877 FileObject->PrivateCacheMap = *Bcb;
878 (*Bcb)->RefCount++;
879 }
880 ExReleaseFastMutex(&ViewLock);
881
882 return(STATUS_SUCCESS);
883 }
884
885 PFILE_OBJECT STDCALL
886 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointers)
887 {
888 PBCB Bcb;
889 if (SectionObjectPointers && SectionObjectPointers->SharedCacheMap)
890 {
891 Bcb = (PBCB)SectionObjectPointers->SharedCacheMap;
892 return Bcb->FileObject;
893 }
894 return NULL;
895 }
896
897 VOID
898 CcInitView(VOID)
899 {
900 DPRINT("CcInitView()\n");
901 InitializeListHead(&CacheSegmentListHead);
902 InitializeListHead(&DirtySegmentListHead);
903 InitializeListHead(&CacheSegmentLRUListHead);
904 ExInitializeFastMutex(&ViewLock);
905 MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
906 CcInitCacheZeroPage();
907 }
908
909 /* EOF */
910
911
912
913
914
915
916