/*++ Copyright (c) 1999-2000 Microsoft Corporation Module Name: pplasl9x.c Abstract: This file contains the implementation of lookaside list manager. Author: Scott Holden (sholden) 14-Apr-2000 --*/ #include "wdm.h" #include "ndis.h" #include "cxport.h" #include "pplasl.h" // Keep scan period at one second -- this will make TCP/IP more responsive to // short bursts. #define MAXIMUM_SCAN_PERIOD 1 #define MINIMUM_ALLOCATION_THRESHOLD 25 #define MINIMUM_LOOKASIDE_DEPTH 10 LIST_ENTRY PplLookasideListHead; KSPIN_LOCK PplLookasideLock; CTETimer PplTimer; ULONG PplCurrentScanPeriod = 1; VOID PplTimeout(CTEEvent * Timer, PVOID Context); BOOLEAN PplInit(VOID) { InitializeListHead(&PplLookasideListHead); CTEInitTimer(&PplTimer); KeInitializeSpinLock(&PplLookasideLock); CTEStartTimer(&PplTimer, 1000L, PplTimeout, NULL); return TRUE; } VOID PplDeinit(VOID) { CTEStopTimer(&PplTimer); return; } HANDLE PplCreatePool( IN PALLOCATE_FUNCTION Allocate, IN PFREE_FUNCTION Free, IN ULONG Flags, IN SIZE_T Size, IN ULONG Tag, IN USHORT Depth ) { HANDLE PoolHandle; SIZE_T PoolSize; PNPAGED_LOOKASIDE_LIST Lookaside; PoolSize = sizeof(NPAGED_LOOKASIDE_LIST); PoolHandle = ExAllocatePoolWithTag(NonPagedPool, PoolSize, Tag); if (PoolHandle) { Lookaside = (PNPAGED_LOOKASIDE_LIST)PoolHandle; ExInitializeSListHead(&Lookaside->L.ListHead); Lookaside->L.Depth = MINIMUM_LOOKASIDE_DEPTH; Lookaside->L.MaximumDepth = Depth; Lookaside->L.TotalAllocates = 0; Lookaside->L.AllocateMisses = 0; Lookaside->L.TotalFrees = 0; Lookaside->L.FreeMisses = 0; Lookaside->L.Type = NonPagedPool | Flags; Lookaside->L.Tag = Tag; Lookaside->L.Size = Size; if (Allocate == NULL) { Lookaside->L.Allocate = ExAllocatePoolWithTag; } else { Lookaside->L.Allocate = Allocate; } if (Free == NULL) { Lookaside->L.Free = ExFreePool; } else { Lookaside->L.Free = Free; } Lookaside->L.LastTotalAllocates = 0; Lookaside->L.LastAllocateMisses = 0; KeInitializeSpinLock(&Lookaside->Lock); // // Insert the lookaside list structure the PPL lookaside list. // ExInterlockedInsertTailList(&PplLookasideListHead, &Lookaside->L.ListEntry, &PplLookasideLock); } return PoolHandle; } VOID PplDestroyPool( IN HANDLE PoolHandle ) { PNPAGED_LOOKASIDE_LIST Lookaside; PVOID Entry; KIRQL OldIrql; if (PoolHandle == NULL) { return; } Lookaside = (PNPAGED_LOOKASIDE_LIST)PoolHandle; // // Acquire the nonpaged system lookaside list lock and remove the // specified lookaside list structure from the list. // ExAcquireSpinLock(&PplLookasideLock, &OldIrql); RemoveEntryList(&Lookaside->L.ListEntry); ExReleaseSpinLock(&PplLookasideLock, OldIrql); // // Remove all pool entries from the specified lookaside structure // and free them. // while ((Entry = ExAllocateFromNPagedLookasideList(Lookaside)) != NULL) { (Lookaside->L.Free)(Entry); } ExFreePool(PoolHandle); return; } PVOID PplAllocate( IN HANDLE PoolHandle, OUT LOGICAL *FromList ) { PNPAGED_LOOKASIDE_LIST Lookaside; PVOID Entry; // Assume we'll get the item from the lookaside list. // *FromList = TRUE; Lookaside = (PNPAGED_LOOKASIDE_LIST)PoolHandle; Lookaside->L.TotalAllocates += 1; Entry = ExInterlockedPopEntrySList(&Lookaside->L.ListHead, &Lookaside->Lock); if (Entry == NULL) { Lookaside->L.AllocateMisses += 1; Entry = (Lookaside->L.Allocate)(Lookaside->L.Type, Lookaside->L.Size, Lookaside->L.Tag); *FromList = FALSE; } return Entry; } VOID PplFree( IN HANDLE PoolHandle, IN PVOID Entry ) { PNPAGED_LOOKASIDE_LIST Lookaside = (PNPAGED_LOOKASIDE_LIST)PoolHandle; Lookaside->L.TotalFrees += 1; if (ExQueryDepthSList(&Lookaside->L.ListHead) >= Lookaside->L.Depth) { Lookaside->L.FreeMisses += 1; (Lookaside->L.Free)(Entry); } else { ExInterlockedPushEntrySList(&Lookaside->L.ListHead, (PSINGLE_LIST_ENTRY)Entry, &Lookaside->Lock); } return; } LOGICAL PplComputeLookasideDepth ( IN ULONG Allocates, IN ULONG Misses, IN USHORT MaximumDepth, IN OUT PUSHORT Depth ) /*++ Routine Description: This function computes the target depth of a lookaside list given the total allocations and misses during the last scan period and the current depth. Arguments: Allocates - Supplies the total number of allocations during the last scan period. Misses - Supplies the total number of allocate misses during the last scan period. MaximumDepth - Supplies the maximum depth the lookaside list is allowed to reach. Depth - Supplies a pointer to the current lookaside list depth which receives the target depth. Return Value: If the target depth is greater than the current depth, then a value of TRUE is returned as the function value. Otherwise, a value of FALSE is returned. --*/ { LOGICAL Changes; ULONG Ratio; ULONG Target; // // If the allocate rate is less than the mimimum threshold, then lower // the maximum depth of the lookaside list. Otherwise, if the miss rate // is less than .5%, then lower the maximum depth. Otherwise, raise the // maximum depth based on the miss rate. // Changes = FALSE; if (Misses >= Allocates) { Misses = Allocates; } if (Allocates == 0) { Allocates = 1; } Ratio = (Misses * 1000) / Allocates; Target = *Depth; if ((Allocates / PplCurrentScanPeriod) < MINIMUM_ALLOCATION_THRESHOLD) { if (Target > (MINIMUM_LOOKASIDE_DEPTH + 10)) { Target -= 10; } else { Target = MINIMUM_LOOKASIDE_DEPTH; } } else if (Ratio < 5) { if (Target > (MINIMUM_LOOKASIDE_DEPTH + 1)) { Target -= 1; } else { Target = MINIMUM_LOOKASIDE_DEPTH; } } else { Changes = TRUE; Target += ((Ratio * MaximumDepth) / (1000 * 2)) + 5; if (Target > MaximumDepth) { Target = MaximumDepth; } } *Depth = (USHORT)Target; return Changes; } LOGICAL PplScanLookasideList( PNPAGED_LOOKASIDE_LIST Lookaside ) { LOGICAL Changes; ULONG Allocates; ULONG Misses; Allocates = Lookaside->L.TotalAllocates - Lookaside->L.LastTotalAllocates; Lookaside->L.LastTotalAllocates = Lookaside->L.TotalAllocates; Misses = Lookaside->L.AllocateMisses - Lookaside->L.LastAllocateMisses; Lookaside->L.LastAllocateMisses = Lookaside->L.AllocateMisses; Changes = PplComputeLookasideDepth( Allocates, Misses, Lookaside->L.MaximumDepth, &Lookaside->L.Depth); return Changes; } VOID PplTimeout( CTEEvent * Timer, PVOID Context) { LOGICAL Changes; KIRQL OldIrql; PLIST_ENTRY Entry; PNPAGED_LOOKASIDE_LIST Lookaside; // // Decrement the scan period and check if it is time to dynamically // adjust the maximum depth of lookaside lists. // Changes = FALSE; // // Scan our Ppl lists. // ExAcquireSpinLock(&PplLookasideLock, &OldIrql); Entry = PplLookasideListHead.Flink; while (Entry != &PplLookasideListHead) { Lookaside = CONTAINING_RECORD(Entry, NPAGED_LOOKASIDE_LIST, L.ListEntry); Changes |= PplScanLookasideList(Lookaside); Entry = Entry->Flink; } // // If any changes were made to the depth of any lookaside list during // this scan period, then lower the scan period to the minimum value. // Otherwise, attempt to raise the scan period. // if (Changes != FALSE) { PplCurrentScanPeriod = 1; } else { if (PplCurrentScanPeriod != MAXIMUM_SCAN_PERIOD) { PplCurrentScanPeriod += 1; } } ExReleaseSpinLock(&PplLookasideLock, OldIrql); // // Restart the timer. // CTEStartTimer(&PplTimer, PplCurrentScanPeriod * 1000L, PplTimeout, NULL); return; }