https://git.reactos.org/?p=reactos.git;a=commitdiff;h=74c5d8b6bd6e8668eaab5f...
commit 74c5d8b6bd6e8668eaab5f6467c80cfc2d899825 Author: Pierre Schweitzer pierre@reactos.org AuthorDate: Mon Apr 30 12:10:24 2018 +0200 Commit: Pierre Schweitzer pierre@reactos.org CommitDate: Mon Apr 30 12:10:24 2018 +0200
[NTOSKRNL] Free unused VACB when required.
Same mechanism exists in Windows (even their Cc is way different from ours...) where when Cc is out of memory (in their case, out of VACB), we will start scavenge old & unused VACB to free some of the memory.
It's useful in case we're operating we big files operations, we may run out of memory where to map VACB for them, so start to scavenge VACB to free some of that memory.
With this, I am able to install Qt 4.8.6 with 2,5GB of RAM, scavenging acting when needed!
CORE-12081 CORE-14582 --- ntoskrnl/cc/view.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+)
diff --git a/ntoskrnl/cc/view.c b/ntoskrnl/cc/view.c index 5bd1f41411..d3c32f9046 100644 --- a/ntoskrnl/cc/view.c +++ b/ntoskrnl/cc/view.c @@ -699,6 +699,88 @@ CcRosMapVacbInKernelSpace( return STATUS_SUCCESS; }
+static +BOOLEAN +CcRosFreeUnusedVacb ( + PULONG Count) +{ + ULONG cFreed; + BOOLEAN Freed; + KIRQL oldIrql; + PROS_VACB current; + LIST_ENTRY FreeList; + PLIST_ENTRY current_entry; + + cFreed = 0; + Freed = FALSE; + InitializeListHead(&FreeList); + + KeAcquireGuardedMutex(&ViewLock); + + /* Browse all the available VACB */ + current_entry = VacbLruListHead.Flink; + while (current_entry != &VacbLruListHead) + { + ULONG Refs; + + current = CONTAINING_RECORD(current_entry, + ROS_VACB, + VacbLruListEntry); + current_entry = current_entry->Flink; + + KeAcquireSpinLock(¤t->SharedCacheMap->CacheMapLock, &oldIrql); + + /* Only deal with unused VACB, we will free them */ + Refs = CcRosVacbGetRefCount(current); + if (Refs < 2) + { + ASSERT(!current->Dirty); + ASSERT(!current->MappedCount); + ASSERT(Refs == 1); + + /* Reset and move to free list */ + RemoveEntryList(¤t->CacheMapVacbListEntry); + RemoveEntryList(¤t->VacbLruListEntry); + InitializeListHead(¤t->VacbLruListEntry); + InsertHeadList(&FreeList, ¤t->CacheMapVacbListEntry); + } + + KeReleaseSpinLock(¤t->SharedCacheMap->CacheMapLock, oldIrql); + + } + + KeReleaseGuardedMutex(&ViewLock); + + /* And now, free any of the found VACB, that'll free memory! */ + while (!IsListEmpty(&FreeList)) + { + ULONG Refs; + + current_entry = RemoveHeadList(&FreeList); + current = CONTAINING_RECORD(current_entry, + ROS_VACB, + CacheMapVacbListEntry); + InitializeListHead(¤t->CacheMapVacbListEntry); + Refs = CcRosVacbDecRefCount(current); + ASSERT(Refs == 0); + ++cFreed; + } + + /* If we freed at least one VACB, return success */ + if (cFreed != 0) + { + Freed = TRUE; + } + + /* If caller asked for free count, return it */ + if (Count != NULL) + { + *Count = cFreed; + } + + return Freed; +} + static NTSTATUS CcRosCreateVacb ( @@ -712,6 +794,7 @@ CcRosCreateVacb ( NTSTATUS Status; KIRQL oldIrql; ULONG Refs; + BOOLEAN Retried;
ASSERT(SharedCacheMap);
@@ -745,9 +828,25 @@ CcRosCreateVacb (
CcRosVacbIncRefCount(current);
+ Retried = FALSE; +Retry: + /* Map VACB in kernel space */ Status = CcRosMapVacbInKernelSpace(current); if (!NT_SUCCESS(Status)) { + ULONG Freed; + /* If no space left, try to prune unused VACB + * to recover space to map our VACB + * If it succeed, retry to map, otherwise + * just fail. + */ + if (!Retried && CcRosFreeUnusedVacb(&Freed)) + { + DPRINT("Prunned %d VACB, trying again\n", Freed); + Retried = TRUE; + goto Retry; + } + CcRosVacbDecRefCount(current); ExFreeToNPagedLookasideList(&VacbLookasideList, current); return Status;