382 lines
8.5 KiB
C
382 lines
8.5 KiB
C
/*++
|
|
|
|
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;
|
|
}
|
|
|