You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
539 lines
11 KiB
539 lines
11 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
lookasid.c
|
|
|
|
Abstract:
|
|
|
|
This module implements heap lookaside list function.
|
|
|
|
Author:
|
|
|
|
David N. Cutler (davec) 19-Feb-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "ntrtlp.h"
|
|
#include "heap.h"
|
|
#include "heappriv.h"
|
|
|
|
// begin_ntslist
|
|
|
|
#if !defined(NTSLIST_ASSERT)
|
|
#define NTSLIST_ASSERT(x) ASSERT(x)
|
|
#endif // !defined(NTSLIST_ASSERT)
|
|
|
|
#ifdef _NTSLIST_DIRECT_
|
|
#define INLINE_SLIST __inline
|
|
#define RtlInitializeSListHead _RtlInitializeSListHead
|
|
#define _RtlFirstEntrySList FirstEntrySList
|
|
|
|
PSLIST_ENTRY
|
|
FirstEntrySList (
|
|
const SLIST_HEADER *ListHead
|
|
);
|
|
|
|
#define RtlInterlockedPopEntrySList _RtlInterlockedPopEntrySList
|
|
#define RtlInterlockedPushEntrySList _RtlInterlockedPushEntrySList
|
|
#define RtlInterlockedFlushSList _RtlInterlockedFlushSList
|
|
#define _RtlQueryDepthSList RtlpQueryDepthSList
|
|
#else
|
|
#define INLINE_SLIST
|
|
#endif // _NTSLIST_DIRECT_
|
|
|
|
// end_ntslist
|
|
|
|
//
|
|
// Define minimum allocation threshold.
|
|
//
|
|
|
|
#define MINIMUM_ALLOCATION_THRESHOLD 25
|
|
|
|
|
|
// begin_ntslist
|
|
|
|
//
|
|
// Define forward referenced function prototypes.
|
|
//
|
|
|
|
VOID
|
|
RtlpInitializeSListHead (
|
|
IN PSLIST_HEADER ListHead
|
|
);
|
|
|
|
PSLIST_ENTRY
|
|
FASTCALL
|
|
RtlpInterlockedPopEntrySList (
|
|
IN PSLIST_HEADER ListHead
|
|
);
|
|
|
|
PSLIST_ENTRY
|
|
FASTCALL
|
|
RtlpInterlockedPushEntrySList (
|
|
IN PSLIST_HEADER ListHead,
|
|
IN PSLIST_ENTRY ListEntry
|
|
);
|
|
|
|
PSLIST_ENTRY
|
|
FASTCALL
|
|
RtlpInterlockedFlushSList (
|
|
IN PSLIST_HEADER ListHead
|
|
);
|
|
|
|
USHORT
|
|
RtlpQueryDepthSList (
|
|
IN PSLIST_HEADER SListHead
|
|
);
|
|
|
|
// end_ntslist
|
|
|
|
VOID
|
|
RtlpInitializeHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside,
|
|
IN USHORT Depth
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes a heap lookaside list structure
|
|
|
|
Arguments:
|
|
|
|
Lookaside - Supplies a pointer to a heap lookaside list structure.
|
|
|
|
Allocate - Supplies a pointer to an allocate function.
|
|
|
|
Free - Supplies a pointer to a free function.
|
|
|
|
HeapHandle - Supplies a pointer to the heap that backs this lookaside list
|
|
|
|
Flags - Supplies a set of heap flags.
|
|
|
|
Size - Supplies the size for the lookaside list entries.
|
|
|
|
Depth - Supplies the maximum depth of the lookaside list.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Initialize the lookaside list structure.
|
|
//
|
|
|
|
RtlInitializeSListHead(&Lookaside->ListHead);
|
|
|
|
Lookaside->Depth = MINIMUM_LOOKASIDE_DEPTH;
|
|
Lookaside->MaximumDepth = 256; //Depth;
|
|
Lookaside->TotalAllocates = 0;
|
|
Lookaside->AllocateMisses = 0;
|
|
Lookaside->TotalFrees = 0;
|
|
Lookaside->FreeMisses = 0;
|
|
|
|
Lookaside->LastTotalAllocates = 0;
|
|
Lookaside->LastAllocateMisses = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
RtlpDeleteHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function frees any entries specified by the lookaside structure.
|
|
|
|
Arguments:
|
|
|
|
Lookaside - Supplies a pointer to a heap lookaside list structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PVOID Entry;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
RtlpAdjustHeapLookasideDepth (
|
|
IN PHEAP_LOOKASIDE Lookaside
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called periodically to adjust the maximum depth of
|
|
a single heap lookaside list.
|
|
|
|
Arguments:
|
|
|
|
Lookaside - Supplies a pointer to a heap lookaside list structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Allocates;
|
|
ULONG Misses;
|
|
|
|
//
|
|
// Compute the total number of allocations and misses for this scan
|
|
// period.
|
|
//
|
|
|
|
Allocates = Lookaside->TotalAllocates - Lookaside->LastTotalAllocates;
|
|
Lookaside->LastTotalAllocates = Lookaside->TotalAllocates;
|
|
Misses = Lookaside->AllocateMisses - Lookaside->LastAllocateMisses;
|
|
Lookaside->LastAllocateMisses = Lookaside->AllocateMisses;
|
|
|
|
//
|
|
// Compute target depth of lookaside list.
|
|
//
|
|
|
|
{
|
|
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.
|
|
//
|
|
|
|
if (Misses >= Allocates) {
|
|
Misses = Allocates;
|
|
}
|
|
|
|
if (Allocates == 0) {
|
|
Allocates = 1;
|
|
}
|
|
|
|
Ratio = (Misses * 1000) / Allocates;
|
|
Target = Lookaside->Depth;
|
|
if (Allocates < 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 {
|
|
Target += ((Ratio * Lookaside->MaximumDepth) / (1000 * 2)) + 5;
|
|
if (Target > Lookaside->MaximumDepth) {
|
|
Target = Lookaside->MaximumDepth;
|
|
}
|
|
}
|
|
|
|
Lookaside->Depth = (USHORT)Target;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
PVOID
|
|
RtlpAllocateFromHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function removes (pops) the first entry from the specified
|
|
heap lookaside list.
|
|
|
|
Arguments:
|
|
|
|
Lookaside - Supplies a pointer to a paged lookaside list structure.
|
|
|
|
Return Value:
|
|
|
|
If an entry is removed from the specified lookaside list, then the
|
|
address of the entry is returned as the function value. Otherwise,
|
|
NULL is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PVOID Entry;
|
|
|
|
Lookaside->TotalAllocates += 1;
|
|
|
|
//
|
|
// We need to protect ourselves from a second thread that can cause us
|
|
// to fault on the pop. If we do fault then we'll just do a regular pop
|
|
// operation
|
|
//
|
|
|
|
__try {
|
|
Entry = RtlpInterlockedPopEntrySList(&Lookaside->ListHead);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Entry = NULL;
|
|
}
|
|
|
|
if (Entry != NULL) {
|
|
return Entry;
|
|
}
|
|
|
|
Lookaside->AllocateMisses += 1;
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
RtlpFreeToHeapLookaside (
|
|
IN PHEAP_LOOKASIDE Lookaside,
|
|
IN PVOID Entry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function inserts (pushes) the specified entry into the specified
|
|
paged lookaside list.
|
|
|
|
Arguments:
|
|
|
|
Lookaside - Supplies a pointer to a paged lookaside list structure.
|
|
|
|
Entry - Supples a pointer to the entry that is inserted in the
|
|
lookaside list.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the entry was put on the lookaside list and FALSE
|
|
otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
Lookaside->TotalFrees += 1;
|
|
if (RtlpQueryDepthSList(&Lookaside->ListHead) < Lookaside->Depth) {
|
|
RtlpInterlockedPushEntrySList(&Lookaside->ListHead,
|
|
(PSLIST_ENTRY)Entry);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Lookaside->FreeMisses += 1;
|
|
return FALSE;
|
|
}
|
|
|
|
// begin_ntslist
|
|
|
|
INLINE_SLIST
|
|
VOID
|
|
RtlInitializeSListHead (
|
|
IN PSLIST_HEADER SListHead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes a sequenced singly linked listhead.
|
|
|
|
Arguments:
|
|
|
|
SListHead - Supplies a pointer to a sequenced singly linked listhead.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
RtlpInitializeSListHead(SListHead);
|
|
return;
|
|
}
|
|
|
|
INLINE_SLIST
|
|
PSLIST_ENTRY
|
|
RtlInterlockedPopEntrySList (
|
|
IN PSLIST_HEADER ListHead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function removes an entry from the front of a sequenced singly
|
|
linked list so that access to the list is synchronized in a MP system.
|
|
If there are no entries in the list, then a value of NULL is returned.
|
|
Otherwise, the address of the entry that is removed is returned as the
|
|
function value.
|
|
|
|
Arguments:
|
|
|
|
ListHead - Supplies a pointer to the sequenced listhead from which
|
|
an entry is to be removed.
|
|
|
|
Return Value:
|
|
|
|
The address of the entry removed from the list, or NULL if the list is
|
|
empty.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Count;
|
|
|
|
//
|
|
// It is posible during the pop of the sequenced list that an access
|
|
// violation can occur if a stale pointer is dereferenced. This is an
|
|
// acceptable result and the operation can be retried.
|
|
//
|
|
// N.B. The count is used to distinguish the case where the list head
|
|
// itself causes the access violation and therefore no progress
|
|
// can be made by repeating the operation.
|
|
//
|
|
|
|
Count = 0;
|
|
do {
|
|
__try {
|
|
return RtlpInterlockedPopEntrySList(ListHead);
|
|
|
|
} __except (Count++ < 20 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
|
|
continue;
|
|
}
|
|
|
|
} while (TRUE);
|
|
}
|
|
|
|
INLINE_SLIST
|
|
PSLIST_ENTRY
|
|
RtlInterlockedPushEntrySList (
|
|
IN PSLIST_HEADER ListHead,
|
|
IN PSLIST_ENTRY ListEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function inserts an entry at the head of a sequenced singly linked
|
|
list so that access to the list is synchronized in an MP system.
|
|
|
|
Arguments:
|
|
|
|
ListHead - Supplies a pointer to the sequenced listhead into which
|
|
an entry is to be inserted.
|
|
|
|
ListEntry - Supplies a pointer to the entry to be inserted at the
|
|
head of the list.
|
|
|
|
Return Value:
|
|
|
|
The address of the previous firt entry in the list. NULL implies list
|
|
went from empty to not empty.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSLIST_ASSERT(((ULONG_PTR)ListEntry & 0x7) == 0);
|
|
|
|
return RtlpInterlockedPushEntrySList(ListHead, ListEntry);
|
|
}
|
|
|
|
INLINE_SLIST
|
|
PSLIST_ENTRY
|
|
RtlInterlockedFlushSList (
|
|
IN PSLIST_HEADER ListHead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function flushes the entire list of entries on a sequenced singly
|
|
linked list so that access to the list is synchronized in a MP system.
|
|
If there are no entries in the list, then a value of NULL is returned.
|
|
Otherwise, the address of the firt entry on the list is returned as the
|
|
function value.
|
|
|
|
Arguments:
|
|
|
|
ListHead - Supplies a pointer to the sequenced listhead from which
|
|
an entry is to be removed.
|
|
|
|
Return Value:
|
|
|
|
The address of the entry removed from the list, or NULL if the list is
|
|
empty.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return RtlpInterlockedFlushSList(ListHead);
|
|
}
|
|
|
|
// end_ntslist
|
|
|
|
USHORT
|
|
RtlQueryDepthSList (
|
|
IN PSLIST_HEADER SListHead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the depth of the specified SLIST.
|
|
|
|
Arguments:
|
|
|
|
SListHead - Supplies a pointer to a sequenced singly linked listhead.
|
|
|
|
Return Value:
|
|
|
|
The current depth of the specified SLIST is returned as the function
|
|
value.
|
|
|
|
--*/
|
|
|
|
{
|
|
return RtlpQueryDepthSList(SListHead);
|
|
}
|