NTOS:CC Free some VACBs when we're under memory pressure

This commit is contained in:
Jérôme Gardou 2022-10-01 22:48:46 +02:00 committed by Timo Kreuzer
parent 470ad18825
commit 69daff72d2
3 changed files with 44 additions and 58 deletions

View file

@ -130,6 +130,16 @@ CcWriteBehind(VOID)
++CcLazyWriteIos; ++CcLazyWriteIos;
DPRINT("Lazy writer done (%d)\n", Count); DPRINT("Lazy writer done (%d)\n", Count);
} }
/* Make sure we're not throttling writes after this */
while (MmAvailablePages < MmThrottleTop)
{
/* Break if we can't even find one to free */
if (!CcRosFreeOneUnusedVacb())
{
break;
}
}
} }
VOID VOID

View file

@ -595,34 +595,26 @@ CcRosUnmarkDirtyVacb (
} }
} }
static
BOOLEAN BOOLEAN
CcRosFreeUnusedVacb ( CcRosFreeOneUnusedVacb(
PULONG Count) VOID)
{ {
ULONG cFreed;
BOOLEAN Freed;
KIRQL oldIrql; KIRQL oldIrql;
PROS_VACB current;
LIST_ENTRY FreeList;
PLIST_ENTRY current_entry; PLIST_ENTRY current_entry;
PROS_VACB to_free = NULL;
cFreed = 0;
Freed = FALSE;
InitializeListHead(&FreeList);
oldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); oldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
/* Browse all the available VACB */ /* Browse all the available VACB */
current_entry = VacbLruListHead.Flink; current_entry = VacbLruListHead.Flink;
while (current_entry != &VacbLruListHead) while ((current_entry != &VacbLruListHead) && (to_free == NULL))
{ {
ULONG Refs; ULONG Refs;
PROS_VACB current;
current = CONTAINING_RECORD(current_entry, current = CONTAINING_RECORD(current_entry,
ROS_VACB, ROS_VACB,
VacbLruListEntry); VacbLruListEntry);
current_entry = current_entry->Flink;
KeAcquireSpinLockAtDpcLevel(&current->SharedCacheMap->CacheMapLock); KeAcquireSpinLockAtDpcLevel(&current->SharedCacheMap->CacheMapLock);
@ -634,47 +626,32 @@ CcRosFreeUnusedVacb (
ASSERT(!current->MappedCount); ASSERT(!current->MappedCount);
ASSERT(Refs == 1); ASSERT(Refs == 1);
/* Reset and move to free list */ /* Reset it, this is the one we want to free */
RemoveEntryList(&current->CacheMapVacbListEntry); RemoveEntryList(&current->CacheMapVacbListEntry);
InitializeListHead(&current->CacheMapVacbListEntry);
RemoveEntryList(&current->VacbLruListEntry); RemoveEntryList(&current->VacbLruListEntry);
InitializeListHead(&current->VacbLruListEntry); InitializeListHead(&current->VacbLruListEntry);
InsertHeadList(&FreeList, &current->CacheMapVacbListEntry);
to_free = current;
} }
KeReleaseSpinLockFromDpcLevel(&current->SharedCacheMap->CacheMapLock); KeReleaseSpinLockFromDpcLevel(&current->SharedCacheMap->CacheMapLock);
current_entry = current_entry->Flink;
} }
KeReleaseQueuedSpinLock(LockQueueMasterLock, oldIrql); KeReleaseQueuedSpinLock(LockQueueMasterLock, oldIrql);
/* And now, free any of the found VACB, that'll free memory! */ /* And now, free the VACB that we found, if any. */
while (!IsListEmpty(&FreeList)) if (to_free == NULL)
{ {
ULONG Refs; return FALSE;
current_entry = RemoveHeadList(&FreeList);
current = CONTAINING_RECORD(current_entry,
ROS_VACB,
CacheMapVacbListEntry);
InitializeListHead(&current->CacheMapVacbListEntry);
Refs = CcRosVacbDecRefCount(current);
ASSERT(Refs == 0);
++cFreed;
} }
/* If we freed at least one VACB, return success */ /* This must be its last ref */
if (cFreed != 0) NT_VERIFY(CcRosVacbDecRefCount(to_free) == 0);
{
Freed = TRUE;
}
/* If caller asked for free count, return it */ return TRUE;
if (Count != NULL)
{
*Count = cFreed;
}
return Freed;
} }
static static
@ -690,7 +667,6 @@ CcRosCreateVacb (
NTSTATUS Status; NTSTATUS Status;
KIRQL oldIrql; KIRQL oldIrql;
ULONG Refs; ULONG Refs;
BOOLEAN Retried;
SIZE_T ViewSize = VACB_MAPPING_GRANULARITY; SIZE_T ViewSize = VACB_MAPPING_GRANULARITY;
ASSERT(SharedCacheMap); ASSERT(SharedCacheMap);
@ -711,28 +687,24 @@ CcRosCreateVacb (
CcRosVacbIncRefCount(current); CcRosVacbIncRefCount(current);
Retried = FALSE; while (TRUE)
Retry:
/* Map VACB in system space */
Status = MmMapViewInSystemSpaceEx(SharedCacheMap->Section, &current->BaseAddress, &ViewSize, &current->FileOffset, 0);
if (!NT_SUCCESS(Status))
{ {
ULONG Freed; /* Map VACB in system space */
/* If no space left, try to prune unused VACB Status = MmMapViewInSystemSpaceEx(SharedCacheMap->Section, &current->BaseAddress, &ViewSize, &current->FileOffset, 0);
* to recover space to map our VACB if (NT_SUCCESS(Status))
* If it succeed, retry to map, otherwise
* just fail.
*/
if (!Retried && CcRosFreeUnusedVacb(&Freed))
{ {
DPRINT("Prunned %d VACB, trying again\n", Freed); break;
Retried = TRUE;
goto Retry;
} }
ExFreeToNPagedLookasideList(&VacbLookasideList, current); /*
return Status; * If no space left, try to prune one unused VACB to recover space to map our VACB.
* If it succeeds, retry to map, otherwise just fail.
*/
if (!CcRosFreeOneUnusedVacb())
{
ExFreeToNPagedLookasideList(&VacbLookasideList, current);
return Status;
}
} }
#if DBG #if DBG

View file

@ -509,3 +509,7 @@ CcRosVacbDecRefCount(
} }
#define CcRosVacbGetRefCount(vacb) InterlockedCompareExchange((PLONG)&(vacb)->ReferenceCount, 0, 0) #define CcRosVacbGetRefCount(vacb) InterlockedCompareExchange((PLONG)&(vacb)->ReferenceCount, 0, 0)
#endif #endif
BOOLEAN
CcRosFreeOneUnusedVacb(
VOID);