|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
addrsup.c
Abstract:
This module implements a new version of the generic table package based on balanced binary trees (later named AVL), as described in Knuth, "The Art of Computer Programming, Volume 3, Sorting and Searching", and refers directly to algorithms as they are presented in the second edition Copyrighted in 1973.
Used rtl\avltable.c as a starting point, adding the following:
- Use less memory for structures as these are nonpaged & heavily used. - Caller allocates the pool to reduce mutex hold times. - Various VAD-specific customizations/optimizations. - Hints.
Author:
Landy Wang (landyw) 20-Aug-2001
Environment:
Kernel mode only, working set mutex held, APCs disabled.
Revision History:
--*/
#include "mi.h"
#if !defined (_USERMODE)
#define PRINT
#define COUNT_BALANCE_MAX(a)
#else
extern MM_AVL_TABLE MmSectionBasedRoot; #endif
#if (_MSC_VER >= 800)
#pragma warning(disable:4010) // Allow pretty pictures without the noise
#endif
TABLE_SEARCH_RESULT MiFindNodeOrParent ( IN PMM_AVL_TABLE Table, IN ULONG_PTR StartingVpn, OUT PMMADDRESS_NODE *NodeOrParent );
VOID MiPromoteNode ( IN PMMADDRESS_NODE C );
ULONG MiRebalanceNode ( IN PMMADDRESS_NODE S );
PMMADDRESS_NODE MiRealSuccessor ( IN PMMADDRESS_NODE Links );
PMMADDRESS_NODE MiRealPredecessor ( IN PMMADDRESS_NODE Links );
VOID MiInitializeVadTableAvl ( IN PMM_AVL_TABLE Table );
PVOID MiEnumerateGenericTableWithoutSplayingAvl ( IN PMM_AVL_TABLE Table, IN PVOID *RestartKey );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,MiCheckForConflictingNode)
#pragma alloc_text(PAGE,MiGetFirstNode)
#pragma alloc_text(PAGE,MiRealSuccessor)
#pragma alloc_text(PAGE,MiRealPredecessor)
#pragma alloc_text(PAGE,MiInitializeVadTableAvl)
#if 0
//
// These routines are called with the PFN lock held but would otherwise be
// pagable.
//
#pragma alloc_text(PAGE,MiPromoteNode)
#pragma alloc_text(PAGE,MiInsertNode)
#pragma alloc_text(PAGE,MiRebalanceNode)
#pragma alloc_text(PAGE,MiRemoveNode)
#endif
#pragma alloc_text(PAGE,MiEnumerateGenericTableWithoutSplayingAvl)
#pragma alloc_text(PAGE,MiGetNextNode)
#pragma alloc_text(PAGE,MiGetPreviousNode)
#pragma alloc_text(PAGE,MiFindEmptyAddressRangeInTree)
#pragma alloc_text(PAGE,MiFindEmptyAddressRangeDownTree)
#pragma alloc_text(PAGE,MiFindEmptyAddressRangeDownBasedTree)
#endif
//
// Various Rtl macros that reference Parent use private versions here since
// Parent is overloaded with Balance.
//
//
// The macro function Parent takes as input a pointer to a splay link in a
// tree and returns a pointer to the splay link of the parent of the input
// node. If the input node is the root of the tree the return value is
// equal to the input value.
//
// PRTL_SPLAY_LINKS
// MiParent (
// PRTL_SPLAY_LINKS Links
// );
//
#define MiParent(Links) ( \
(PRTL_SPLAY_LINKS)(SANITIZE_PARENT_NODE((Links)->u1.Parent)) \ )
//
// The macro function IsLeftChild takes as input a pointer to a splay link
// in a tree and returns TRUE if the input node is the left child of its
// parent, otherwise it returns FALSE.
//
// BOOLEAN
// MiIsLeftChild (
// PRTL_SPLAY_LINKS Links
// );
//
#define MiIsLeftChild(Links) ( \
(RtlLeftChild(MiParent(Links)) == (PRTL_SPLAY_LINKS)(Links)) \ )
//
// The macro function IsRightChild takes as input a pointer to a splay link
// in a tree and returns TRUE if the input node is the right child of its
// parent, otherwise it returns FALSE.
//
// BOOLEAN
// MiIsRightChild (
// PRTL_SPLAY_LINKS Links
// );
//
#define MiIsRightChild(Links) ( \
(RtlRightChild(MiParent(Links)) == (PRTL_SPLAY_LINKS)(Links)) \ )
#if DBG
//
// Build a table of the best case efficiency of a balanced binary tree,
// holding the most possible nodes that can possibly be held in a binary
// tree with a given number of levels. The answer is always (2**n) - 1.
//
// (Used for debug only.)
//
ULONG MiBestCaseFill[33] = { 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff };
//
// Build a table of the worst case efficiency of a balanced binary tree,
// holding the fewest possible nodes that can possibly be contained in a
// balanced binary tree with the given number of levels. After the first
// two levels, each level n is obviously occupied by a root node, plus
// one subtree the size of level n-1, and another subtree which is the
// size of n-2, i.e.:
//
// MiWorstCaseFill[n] = 1 + MiWorstCaseFill[n-1] + MiWorstCaseFill[n-2]
//
// The efficiency of a typical balanced binary tree will normally fall
// between the two extremes, typically closer to the best case. Note
// however that even with the worst case, it only takes 32 compares to
// find an element in a worst case tree populated with ~3.5M nodes.
//
// Unbalanced trees and splay trees, on the other hand, can and will sometimes
// degenerate to a straight line, requiring on average n/2 compares to
// find a node.
//
// A specific case is one where the nodes are inserted in collated order.
// In this case an unbalanced or a splay tree will generate a straight
// line, yet the balanced binary tree will always create a perfectly
// balanced tree (best-case fill) in this situation.
//
// (Used for debug only.)
//
ULONG MiWorstCaseFill[33] = { 0, 1, 2, 4, 7, 12, 20, 33, 54, 88, 143, 232, 376, 609, 986, 1596, 2583, 4180, 6764, 10945, 17710, 28656, 46367, 75024, 121392, 196417, 317810, 514228, 832039, 1346268, 2178308, 3524577, 5702886 };
#endif
TABLE_SEARCH_RESULT MiFindNodeOrParent ( IN PMM_AVL_TABLE Table, IN ULONG_PTR StartingVpn, OUT PMMADDRESS_NODE *NodeOrParent )
/*++
Routine Description:
This routine is used by all of the routines of the generic table package to locate the a node in the tree. It will find and return (via the NodeOrParent parameter) the node with the given key, or if that node is not in the tree it will return (via the NodeOrParent parameter) a pointer to the parent.
Arguments:
Table - The generic table to search for the key.
StartingVpn - The starting virtual page number.
NodeOrParent - Will be set to point to the node containing the the key or what should be the parent of the node if it were in the tree. Note that this will *NOT* be set if the search result is TableEmptyTree.
Return Value:
TABLE_SEARCH_RESULT - TableEmptyTree: The tree was empty. NodeOrParent is *not* altered.
TableFoundNode: A node with the key is in the tree. NodeOrParent points to that node.
TableInsertAsLeft: Node with key was not found. NodeOrParent points to what would be parent. The node would be the left child.
TableInsertAsRight: Node with key was not found. NodeOrParent points to what would be parent. The node would be the right child.
Environment:
Kernel mode. The PFN lock is held for some of the tables.
--*/
{ #if DBG
ULONG NumberCompares = 0; #endif
PMMADDRESS_NODE Child; PMMADDRESS_NODE NodeToExamine;
if (Table->NumberGenericTableElements == 0) { return TableEmptyTree; }
NodeToExamine = (PMMADDRESS_NODE) Table->BalancedRoot.RightChild;
do {
//
// Make sure the depth of tree is correct.
//
ASSERT(++NumberCompares <= Table->DepthOfTree);
//
// Compare the buffer with the key in the tree element.
//
if (StartingVpn < NodeToExamine->StartingVpn) {
Child = NodeToExamine->LeftChild;
if (Child != NULL) { NodeToExamine = Child; } else {
//
// Node is not in the tree. Set the output
// parameter to point to what would be its
// parent and return which child it would be.
//
*NodeOrParent = NodeToExamine; return TableInsertAsLeft; } } else if (StartingVpn <= NodeToExamine->EndingVpn) {
//
// This is the node.
//
*NodeOrParent = NodeToExamine; return TableFoundNode; } else {
Child = NodeToExamine->RightChild;
if (Child != NULL) { NodeToExamine = Child; } else {
//
// Node is not in the tree. Set the output
// parameter to point to what would be its
// parent and return which child it would be.
//
*NodeOrParent = NodeToExamine; return TableInsertAsRight; } }
} while (TRUE); }
PMMADDRESS_NODE MiCheckForConflictingNode ( IN ULONG_PTR StartVpn, IN ULONG_PTR EndVpn, IN PMM_AVL_TABLE Table )
/*++
Routine Description:
The function determines if any addresses between a given starting and ending address is contained within a virtual address descriptor.
Arguments:
StartVpn - Supplies the virtual address to locate a containing descriptor.
EndVpn - Supplies the virtual address to locate a containing descriptor.
Return Value:
Returns a pointer to the first conflicting virtual address descriptor if one is found, otherwise a NULL value is returned.
--*/
{ PMMADDRESS_NODE Node;
if (Table->NumberGenericTableElements == 0) { return NULL; }
Node = (PMMADDRESS_NODE) Table->BalancedRoot.RightChild; ASSERT (Node != NULL);
do {
if (Node == NULL) { return NULL; }
if (StartVpn > Node->EndingVpn) { Node = Node->RightChild; } else if (EndVpn < Node->StartingVpn) { Node = Node->LeftChild; } else {
//
// The starting address is less than or equal to the end VA
// and the ending address is greater than or equal to the
// start va. Return this node.
//
return Node; }
} while (TRUE); }
PMMADDRESS_NODE FASTCALL MiGetFirstNode ( IN PMM_AVL_TABLE Table )
/*++
Routine Description:
This function locates the virtual address descriptor which contains the address range which logically is first within the address space.
Arguments:
None.
Return Value:
Returns a pointer to the virtual address descriptor containing the first address range, NULL if none.
--*/
{ PMMADDRESS_NODE First;
if (Table->NumberGenericTableElements == 0) { return NULL; }
First = (PMMADDRESS_NODE) Table->BalancedRoot.RightChild;
ASSERT (First != NULL);
while (First->LeftChild != NULL) { First = First->LeftChild; }
return First; }
VOID MiPromoteNode ( IN PMMADDRESS_NODE C )
/*++
Routine Description:
This routine performs the fundamental adjustment required for balancing the binary tree during insert and delete operations. Simply put, the designated node is promoted in such a way that it rises one level in the tree and its parent drops one level in the tree, becoming now the child of the designated node. Generally the path length to the subtree "opposite" the original parent. Balancing occurs as the caller chooses which nodes to promote according to the balanced tree algorithms from Knuth.
This is not the same as a splay operation, typically a splay "promotes" a designated node twice.
Note that the pointer to the root node of the tree is assumed to be contained in a MMADDRESS_NODE structure itself, to allow the algorithms below to change the root of the tree without checking for special cases. Note also that this is an internal routine, and the caller guarantees that it never requests to promote the root itself.
This routine only updates the tree links; the caller must update the balance factors as appropriate.
Arguments:
C - pointer to the child node to be promoted in the tree.
Return Value:
None.
--*/
{ PMMADDRESS_NODE P; PMMADDRESS_NODE G;
//
// Capture the current parent and grandparent (may be the root).
//
P = SANITIZE_PARENT_NODE (C->u1.Parent); G = SANITIZE_PARENT_NODE (P->u1.Parent);
//
// Break down the promotion into two cases based upon whether C
// is a left or right child.
//
if (P->LeftChild == C) {
//
// This promotion looks like this:
//
// G G
// | |
// P C
// / \ => / \ // C z x P
// / \ / \ // x y y z
//
P->LeftChild = C->RightChild;
if (P->LeftChild != NULL) {
P->LeftChild->u1.Parent = MI_MAKE_PARENT (P, P->LeftChild->u1.Balance); }
C->RightChild = P;
//
// Fall through to update parent and G <-> C relationship in
// common code.
//
} else {
ASSERT(P->RightChild == C);
//
// This promotion looks like this:
//
// G G
// | |
// P C
// / \ => / \ // x C P z
// / \ / \ // y z x y
//
P->RightChild = C->LeftChild;
if (P->RightChild != NULL) { P->RightChild->u1.Parent = MI_MAKE_PARENT (P, P->RightChild->u1.Balance); }
C->LeftChild = P; }
//
// Update parent of P, for either case above.
//
P->u1.Parent = MI_MAKE_PARENT (C, P->u1.Balance);
//
// Finally update G <-> C links for either case above.
//
if (G->LeftChild == P) { G->LeftChild = C; } else { ASSERT(G->RightChild == P); G->RightChild = C; } C->u1.Parent = MI_MAKE_PARENT (G, C->u1.Balance); }
ULONG MiRebalanceNode ( IN PMMADDRESS_NODE S )
/*++
Routine Description:
This routine performs a rebalance around the input node S, for which the Balance factor has just effectively become +2 or -2. When called, the Balance factor still has a value of +1 or -1, but the respective longer side has just become one longer as the result of an insert or delete operation.
This routine effectively implements steps A7.iii (test for Case 1 or Case 2) and steps A8 and A9 of Knuth's balanced insertion algorithm, plus it handles Case 3 identified in the delete section, which can only happen on deletes.
The trick is, to convince yourself that while traveling from the insertion point at the bottom of the tree up, that there are only these two cases, and that when traveling up from the deletion point, that there are just these three cases. Knuth says it is obvious!
Arguments:
S - pointer to the node which has just become unbalanced.
Return Value:
TRUE if Case 3 was detected (causes delete algorithm to terminate).
Environment:
Kernel mode. The PFN lock is held for some of the tables.
--*/
{ PMMADDRESS_NODE R, P; SCHAR a;
PRINT("rebalancing node %p bal=%x start=%x end=%x\n", S, S->u1.Balance, S->StartingVpn, S->EndingVpn);
//
// The parent node is never the argument node.
//
ASSERT (SANITIZE_PARENT_NODE(S->u1.Parent) != S);
//
// Capture which side is unbalanced.
//
a = (SCHAR) S->u1.Balance;
if (a == +1) { R = S->RightChild; } else { R = S->LeftChild; }
//
// If the balance of R and S are the same (Case 1 in Knuth) then a single
// promotion of R will do the single rotation. (Step A8, A10)
//
// Here is a diagram of the Case 1 transformation, for a == +1 (a mirror
// image transformation occurs when a == -1), and where the subtree
// heights are h and h+1 as shown (++ indicates the node out of balance):
//
// | |
// S++ R
// / \ / \ // (h) R+ ==> S (h+1)
// / \ / \ // (h) (h+1) (h) (h)
//
// Note that on an insert we can hit this case by inserting an item in the
// right subtree of R. The original height of the subtree before the insert
// was h+2, and it is still h+2 after the rebalance, so insert rebalancing
// may terminate.
//
// On a delete we can hit this case by deleting a node from the left subtree
// of S. The height of the subtree before the delete was h+3, and after the
// rebalance it is h+2, so rebalancing must continue up the tree.
//
if ((SCHAR) R->u1.Balance == a) {
MiPromoteNode (R); R->u1.Balance = 0; S->u1.Balance = 0;
return FALSE; }
//
// Otherwise, we have to promote the appropriate child of R twice (Case 2
// in Knuth). (Step A9, A10)
//
// Here is a diagram of the Case 2 transformation, for a == +1 (a mirror
// image transformation occurs when a == -1), and where the subtree
// heights are h and h-1 as shown. There are actually two minor subcases,
// differing only in the original balance of P (++ indicates the node out
// of balance).
//
// | |
// S++ P
// / \ / \ // / \ / \ // / \ / \ // (h) R- ==> S- R
// / \ / \ / \ // P+ (h) (h)(h-1)(h) (h)
// / \ // (h-1) (h)
//
//
// | |
// S++ P
// / \ / \ // / \ / \ // / \ / \ // (h) R- ==> S R+
// / \ / \ / \ // P- (h) (h) (h)(h-1)(h)
// / \ // (h) (h-1)
//
// Note that on an insert we can hit this case by inserting an item in the
// left subtree of R. The original height of the subtree before the insert
// was h+2, and it is still h+2 after the rebalance, so insert rebalancing
// may terminate.
//
// On a delete we can hit this case by deleting a node from the left subtree
// of S. The height of the subtree before the delete was h+3, and after the
// rebalance it is h+2, so rebalancing must continue up the tree.
//
if ((SCHAR) R->u1.Balance == -a) {
//
// Pick up the appropriate child P for the double rotation (Link(-a,R)).
//
if (a == 1) { P = R->LeftChild; } else { P = R->RightChild; }
//
// Promote him twice to implement the double rotation.
//
MiPromoteNode (P); MiPromoteNode (P);
//
// Now adjust the balance factors.
//
S->u1.Balance = 0; R->u1.Balance = 0; if ((SCHAR) P->u1.Balance == a) { PRINT("REBADJ A: Node %p, Bal %x -> %x\n", S, S->u1.Balance, -a); COUNT_BALANCE_MAX ((SCHAR)-a); S->u1.Balance = (ULONG_PTR) -a; } else if ((SCHAR) P->u1.Balance == -a) { PRINT("REBADJ B: Node %p, Bal %x -> %x\n", R, R->u1.Balance, a); COUNT_BALANCE_MAX ((SCHAR)a); R->u1.Balance = (ULONG_PTR) a; }
P->u1.Balance = 0; return FALSE; }
//
// Otherwise this is Case 3 which can only happen on Delete (identical
// to Case 1 except R->u1.Balance == 0). We do a single rotation, adjust
// the balance factors appropriately, and return TRUE. Note that the
// balance of S stays the same.
//
// Here is a diagram of the Case 3 transformation, for a == +1 (a mirror
// image transformation occurs when a == -1), and where the subtree
// heights are h and h+1 as shown (++ indicates the node out of balance):
//
// | |
// S++ R-
// / \ / \ // (h) R ==> S+ (h+1)
// / \ / \ // (h+1)(h+1) (h) (h+1)
//
// This case can not occur on an insert, because it is impossible for
// a single insert to balance R, yet somehow grow the right subtree of
// S at the same time. As we move up the tree adjusting balance factors
// after an insert, we terminate the algorithm if a node becomes balanced,
// because that means the subtree length did not change!
//
// On a delete we can hit this case by deleting a node from the left
// subtree of S. The height of the subtree before the delete was h+3,
// and after the rebalance it is still h+3, so rebalancing may terminate
// in the delete path.
//
MiPromoteNode (R); PRINT("REBADJ C: Node %p, Bal %x -> %x\n", R, R->u1.Balance, -a); COUNT_BALANCE_MAX ((SCHAR)-a); R->u1.Balance = -a; return TRUE; }
VOID FASTCALL MiRemoveNode ( IN PMMADDRESS_NODE NodeToDelete, IN PMM_AVL_TABLE Table )
/*++
Routine Description:
This routine deletes the specified node from the balanced tree, rebalancing as necessary. If the NodeToDelete has at least one NULL child pointers, then it is chosen as the EasyDelete, otherwise a subtree predecessor or successor is found as the EasyDelete. In either case the EasyDelete is deleted and the tree is rebalanced. Finally if the NodeToDelete was different than the EasyDelete, then the EasyDelete is linked back into the tree in place of the NodeToDelete.
Arguments:
NodeToDelete - Pointer to the node which the caller wishes to delete.
Table - The generic table in which the delete is to occur.
Return Value:
None.
Environment:
Kernel mode. The PFN lock is held for some of the tables.
--*/
{ PMMADDRESS_NODE Parent; PMMADDRESS_NODE EasyDelete; PMMADDRESS_NODE P; SCHAR a;
//
// If the NodeToDelete has at least one NULL child pointer, then we can
// delete it directly.
//
if ((NodeToDelete->LeftChild == NULL) || (NodeToDelete->RightChild == NULL)) {
EasyDelete = NodeToDelete; }
//
// Otherwise, we may as well pick the longest side to delete from (if one is
// is longer), as that reduces the probability that we will have to
// rebalance.
//
else if ((SCHAR) NodeToDelete->u1.Balance >= 0) {
//
// Pick up the subtree successor.
//
EasyDelete = NodeToDelete->RightChild; while (EasyDelete->LeftChild != NULL) { EasyDelete = EasyDelete->LeftChild; } } else {
//
// Pick up the subtree predecessor.
//
EasyDelete = NodeToDelete->LeftChild; while (EasyDelete->RightChild != NULL) { EasyDelete = EasyDelete->RightChild; } }
//
// Rebalancing must know which side of the first parent the delete occurred
// on. Assume it is the left side and otherwise correct below.
//
a = -1;
//
// Now we can do the simple deletion for the no left child case.
//
if (EasyDelete->LeftChild == NULL) {
Parent = SANITIZE_PARENT_NODE (EasyDelete->u1.Parent);
if (MiIsLeftChild(EasyDelete)) { Parent->LeftChild = EasyDelete->RightChild; } else { Parent->RightChild = EasyDelete->RightChild; a = 1; }
if (EasyDelete->RightChild != NULL) { EasyDelete->RightChild->u1.Parent = MI_MAKE_PARENT (Parent, EasyDelete->RightChild->u1.Balance); }
//
// Now we can do the simple deletion for the no right child case,
// plus we know there is a left child.
//
} else {
Parent = SANITIZE_PARENT_NODE (EasyDelete->u1.Parent);
if (MiIsLeftChild(EasyDelete)) { Parent->LeftChild = EasyDelete->LeftChild; } else { Parent->RightChild = EasyDelete->LeftChild; a = 1; }
EasyDelete->LeftChild->u1.Parent = MI_MAKE_PARENT (Parent, EasyDelete->LeftChild->u1.Balance); }
//
// For delete rebalancing, set the balance at the root to 0 to properly
// terminate the rebalance without special tests, and to be able to detect
// if the depth of the tree actually decreased.
//
Table->BalancedRoot.u1.Balance = 0; P = SANITIZE_PARENT_NODE (EasyDelete->u1.Parent);
//
// Loop until the tree is balanced.
//
while (TRUE) {
//
// First handle the case where the tree became more balanced. Zero
// the balance factor, calculate a for the next loop and move on to
// the parent.
//
if ((SCHAR) P->u1.Balance == a) {
P->u1.Balance = 0;
//
// If this node is curently balanced, we can show it is now unbalanced
// and terminate the scan since the subtree length has not changed.
// (This may be the root, since we set Balance to 0 above!)
//
} else if (P->u1.Balance == 0) {
PRINT("REBADJ D: Node %p, Bal %x -> %x\n", P, P->u1.Balance, -a); COUNT_BALANCE_MAX ((SCHAR)-a); P->u1.Balance = -a;
//
// If we shortened the depth all the way back to the root, then
// the tree really has one less level.
//
if (Table->BalancedRoot.u1.Balance != 0) { Table->DepthOfTree -= 1; }
break;
//
// Otherwise we made the short side 2 levels less than the long side,
// and rebalancing is required. On return, some node has been promoted
// to above node P. If Case 3 from Knuth was not encountered, then we
// want to effectively resume rebalancing from P's original parent which
// is effectively its grandparent now.
//
} else {
//
// We are done if Case 3 was hit, i.e., the depth of this subtree is
// now the same as before the delete.
//
if (MiRebalanceNode(P)) { break; }
P = SANITIZE_PARENT_NODE (P->u1.Parent); }
a = -1; if (MiIsRightChild(P)) { a = 1; } P = SANITIZE_PARENT_NODE (P->u1.Parent); }
//
// Finally, if we actually deleted a predecessor/successor of the
// NodeToDelete, we will link him back into the tree to replace
// NodeToDelete before returning. Note that NodeToDelete did have
// both child links filled in, but that may no longer be the case
// at this point.
//
if (NodeToDelete != EasyDelete) {
//
// Note carefully - VADs are of differing sizes therefore it is not safe
// to just overlay the EasyDelete node with the NodeToDelete like the
// rtl avl code does.
//
// Copy just the links, preserving the rest of the original EasyDelete
// VAD.
//
EasyDelete->u1.Parent = NodeToDelete->u1.Parent; EasyDelete->LeftChild = NodeToDelete->LeftChild; EasyDelete->RightChild = NodeToDelete->RightChild;
if (MiIsLeftChild(NodeToDelete)) { Parent = SANITIZE_PARENT_NODE (EasyDelete->u1.Parent); Parent->LeftChild = EasyDelete; } else { ASSERT(MiIsRightChild(NodeToDelete)); Parent = SANITIZE_PARENT_NODE (EasyDelete->u1.Parent); Parent->RightChild = EasyDelete; } if (EasyDelete->LeftChild != NULL) { EasyDelete->LeftChild->u1.Parent = MI_MAKE_PARENT (EasyDelete, EasyDelete->LeftChild->u1.Balance); } if (EasyDelete->RightChild != NULL) { EasyDelete->RightChild->u1.Parent = MI_MAKE_PARENT (EasyDelete, EasyDelete->RightChild->u1.Balance); } }
Table->NumberGenericTableElements -= 1;
//
// Sanity check tree size and depth.
//
ASSERT((Table->NumberGenericTableElements >= MiWorstCaseFill[Table->DepthOfTree]) && (Table->NumberGenericTableElements <= MiBestCaseFill[Table->DepthOfTree]));
return; }
PMMADDRESS_NODE MiRealSuccessor ( IN PMMADDRESS_NODE Links )
/*++
Routine Description:
This function takes as input a pointer to a balanced link in a tree and returns a pointer to the successor of the input node within the entire tree. If there is not a successor, the return value is NULL.
Arguments:
Links - Supplies a pointer to a balanced link in a tree.
Return Value:
PMMADDRESS_NODE - returns a pointer to the successor in the entire tree
--*/
{ PMMADDRESS_NODE Ptr;
/*
First check to see if there is a right subtree to the input link if there is then the real successor is the left most node in the right subtree. That is find and return S in the following diagram
Links \ . . . / S \ */
if ((Ptr = Links->RightChild) != NULL) {
while (Ptr->LeftChild != NULL) { Ptr = Ptr->LeftChild; }
return Ptr; }
/*
We do not have a right child so check to see if have a parent and if so find the first ancestor that we are a left decendant of. That is find and return S in the following diagram
S / . . . Links
Note that this code depends on how the BalancedRoot is initialized, which is Parent points to self, and the RightChild points to an actual node which is the root of the tree, and LeftChild does not point to self. */
Ptr = Links; while (MiIsRightChild(Ptr)) { Ptr = SANITIZE_PARENT_NODE (Ptr->u1.Parent); }
if (MiIsLeftChild(Ptr)) { return SANITIZE_PARENT_NODE (Ptr->u1.Parent); }
//
// Otherwise we are do not have a real successor so we simply return NULL.
//
// This can only occur when we get back to the root, and we can tell
// that since the Root is its own parent.
//
ASSERT (SANITIZE_PARENT_NODE(Ptr->u1.Parent) == Ptr);
return NULL; }
PMMADDRESS_NODE MiRealPredecessor ( IN PMMADDRESS_NODE Links )
/*++
Routine Description:
The RealPredecessor function takes as input a pointer to a balanced link in a tree and returns a pointer to the predecessor of the input node within the entire tree. If there is not a predecessor, the return value is NULL.
Arguments:
Links - Supplies a pointer to a balanced link in a tree.
Return Value:
PMMADDRESS_NODE - returns a pointer to the predecessor in the entire tree
--*/
{ PMMADDRESS_NODE Ptr; PMMADDRESS_NODE Parent; PMMADDRESS_NODE GrandParent;
/*
First check to see if there is a left subtree to the input link if there is then the real predecessor is the right most node in the left subtree. That is find and return P in the following diagram
Links / . . . P / */
if ((Ptr = Links->LeftChild) != NULL) {
while (Ptr->RightChild != NULL) { Ptr = Ptr->RightChild; }
return Ptr;
}
/*
We do not have a left child so check to see if have a parent and if so find the first ancestor that we are a right decendant of. That is find and return P in the following diagram
P \ . . . Links
Note that this code depends on how the BalancedRoot is initialized, which is Parent points to self, and the RightChild points to an actual node which is the root of the tree. */
Ptr = Links; while (MiIsLeftChild(Ptr)) { Ptr = SANITIZE_PARENT_NODE (Ptr->u1.Parent); }
if (MiIsRightChild(Ptr)) { Parent = SANITIZE_PARENT_NODE (Ptr->u1.Parent); GrandParent = SANITIZE_PARENT_NODE (Parent->u1.Parent); if (GrandParent != Parent) { return Parent; } }
//
// Otherwise we are do not have a real predecessor so we simply return
// NULL.
//
return NULL; }
VOID MiInitializeVadTableAvl ( IN PMM_AVL_TABLE Table )
/*++
Routine Description:
This routine initializes a table.
Arguments:
Table - Pointer to the generic table to be initialized.
Return Value:
None.
--*/
{
#if DBG
ULONG i;
for (i = 2; i < 33; i += 1) { ASSERT(MiWorstCaseFill[i] == (1 + MiWorstCaseFill[i - 1] + MiWorstCaseFill[i - 2])); } #endif
//
// Initialize each field in the argument Table.
//
RtlZeroMemory (Table, sizeof(MM_AVL_TABLE));
Table->BalancedRoot.u1.Parent = MI_MAKE_PARENT (&Table->BalancedRoot, 0); }
VOID FASTCALL MiInsertNode ( IN PMMADDRESS_NODE NodeToInsert, IN PMM_AVL_TABLE Table )
/*++
Routine Description:
This function inserts a new element in a table.
Arguments:
NodeToInsert - The initialized address node to insert.
Table - Pointer to the table in which to insert the new node.
Return Value:
None.
Environment:
Kernel mode. The PFN lock is held for some of the tables.
--*/
{ //
// Holds a pointer to the node in the table or what would be the
// parent of the node.
//
PMMADDRESS_NODE NodeOrParent; TABLE_SEARCH_RESULT SearchResult;
ASSERT((Table->NumberGenericTableElements >= MiWorstCaseFill[Table->DepthOfTree]) && (Table->NumberGenericTableElements <= MiBestCaseFill[Table->DepthOfTree]));
SearchResult = MiFindNodeOrParent (Table, NodeToInsert->StartingVpn, &NodeOrParent);
ASSERT (SearchResult != TableFoundNode);
//
// The node wasn't in the (possibly empty) tree.
//
// We just check that the table isn't getting too big.
//
ASSERT (Table->NumberGenericTableElements != (MAXULONG-1));
NodeToInsert->LeftChild = NULL; NodeToInsert->RightChild = NULL;
Table->NumberGenericTableElements += 1;
//
// Insert the new node in the tree.
//
if (SearchResult == TableEmptyTree) {
Table->BalancedRoot.RightChild = NodeToInsert; NodeToInsert->u1.Parent = &Table->BalancedRoot; ASSERT (NodeToInsert->u1.Balance == 0); ASSERT(Table->DepthOfTree == 0); Table->DepthOfTree = 1;
ASSERT((Table->NumberGenericTableElements >= MiWorstCaseFill[Table->DepthOfTree]) && (Table->NumberGenericTableElements <= MiBestCaseFill[Table->DepthOfTree]));
} else {
PMMADDRESS_NODE R = NodeToInsert; PMMADDRESS_NODE S = NodeOrParent;
if (SearchResult == TableInsertAsLeft) { NodeOrParent->LeftChild = NodeToInsert; } else { NodeOrParent->RightChild = NodeToInsert; }
NodeToInsert->u1.Parent = NodeOrParent; ASSERT (NodeToInsert->u1.Balance == 0);
//
// The above completes the standard binary tree insertion, which
// happens to correspond to steps A1-A5 of Knuth's "balanced tree
// search and insertion" algorithm. Now comes the time to adjust
// balance factors and possibly do a single or double rotation as
// in steps A6-A10.
//
// Set the Balance factor in the root to a convenient value
// to simplify loop control.
//
PRINT("REBADJ E: Table %p, Bal %x -> %x\n", Table, Table->BalancedRoot.u1.Balance, -1); COUNT_BALANCE_MAX ((SCHAR)-1); Table->BalancedRoot.u1.Balance = (ULONG_PTR) -1;
//
// Now loop to adjust balance factors and see if any balance operations
// must be performed, using NodeOrParent to ascend the tree.
//
do {
SCHAR a;
//
// Calculate the next adjustment.
//
a = 1; if (MiIsLeftChild (R)) { a = -1; }
PRINT("LW 0: Table %p, Bal %x, %x\n", Table, Table->BalancedRoot.u1.Balance, a); PRINT("LW 0: R Node %p, Bal %x, %x\n", R, R->u1.Balance, 1); PRINT("LW 0: S Node %p, Bal %x, %x\n", S, S->u1.Balance, 1);
//
// If this node was balanced, show that it is no longer and
// keep looping. This is essentially A6 of Knuth's algorithm,
// where he updates all of the intermediate nodes on the
// insertion path which previously had balance factors of 0.
// We are looping up the tree via Parent pointers rather than
// down the tree as in Knuth.
//
if (S->u1.Balance == 0) {
PRINT("REBADJ F: Node %p, Bal %x -> %x\n", S, S->u1.Balance, a); COUNT_BALANCE_MAX ((SCHAR)a); S->u1.Balance = a; R = S; S = SANITIZE_PARENT_NODE (S->u1.Parent); } else if ((SCHAR) S->u1.Balance != a) {
PRINT("LW 1: Table %p, Bal %x, %x\n", Table, Table->BalancedRoot.u1.Balance, -1);
//
// If this node has the opposite balance, then the tree got
// more balanced (or we hit the root) and we are done.
//
// Step A7.ii
//
S->u1.Balance = 0;
//
// If S is actually the root, then this means the depth
// of the tree just increased by 1! (This is essentially
// A7.i, but we just initialized the root balance to force
// it through here.)
//
if (Table->BalancedRoot.u1.Balance == 0) { Table->DepthOfTree += 1; }
break; } else {
PRINT("LW 2: Table %p, Bal %x, %x\n", Table, Table->BalancedRoot.u1.Balance, -1);
//
// The tree became unbalanced (path length differs
// by 2 below us) and we need to do one of the balancing
// operations, and then we are done. The RebalanceNode routine
// does steps A7.iii, A8 and A9.
//
MiRebalanceNode (S); break; } PRINT("LW 3: Table %p, Bal %x, %x\n", Table, Table->BalancedRoot.u1.Balance, -1); } while (TRUE); PRINT("LW 4: Table %p, Bal %x, %x\n", Table, Table->BalancedRoot.u1.Balance, -1); }
//
// Sanity check tree size and depth.
//
ASSERT((Table->NumberGenericTableElements >= MiWorstCaseFill[Table->DepthOfTree]) && (Table->NumberGenericTableElements <= MiBestCaseFill[Table->DepthOfTree]));
return; }
PVOID MiEnumerateGenericTableWithoutSplayingAvl ( IN PMM_AVL_TABLE Table, IN PVOID *RestartKey )
/*++
Routine Description:
The function EnumerateGenericTableWithoutSplayingAvl will return to the caller one-by-one the elements of of a table. The return value is a pointer to the user defined structure associated with the element. The input parameter RestartKey indicates if the enumeration should start from the beginning or should return the next element. If the are no more new elements to return the return value is NULL. As an example of its use, to enumerate all of the elements in a table the user would write:
*RestartKey = NULL;
for (ptr = EnumerateGenericTableWithoutSplayingAvl(Table, &RestartKey); ptr != NULL; ptr = EnumerateGenericTableWithoutSplayingAvl(Table, &RestartKey)) { : }
Arguments:
Table - Pointer to the generic table to enumerate.
RestartKey - Pointer that indicates if we should restart or return the next element. If the contents of RestartKey is NULL, the search will be started from the beginning.
Return Value:
PVOID - Pointer to the user data.
--*/
{ PMMADDRESS_NODE NodeToReturn;
if (Table->NumberGenericTableElements == 0) {
//
// Nothing to do if the table is empty.
//
return NULL;
}
//
// If the restart flag is true then go to the least element
// in the tree.
//
if (*RestartKey == NULL) {
//
// Loop until we find the leftmost child of the root.
//
for (NodeToReturn = Table->BalancedRoot.RightChild; NodeToReturn->LeftChild; NodeToReturn = NodeToReturn->LeftChild) {
NOTHING; }
*RestartKey = NodeToReturn;
} else {
//
// The caller has passed in the previous entry found
// in the table to enable us to continue the search. We call
// RealSuccessor to step to the next element in the tree.
//
NodeToReturn = MiRealSuccessor (*RestartKey);
if (NodeToReturn) { *RestartKey = NodeToReturn; } }
//
// Return the found element.
//
return NodeToReturn; }
PMMADDRESS_NODE FASTCALL MiGetNextNode ( IN PMMADDRESS_NODE Node )
/*++
Routine Description:
This function locates the virtual address descriptor which contains the address range which logically follows the specified address range.
Arguments:
Node - Supplies a pointer to a virtual address descriptor.
Return Value:
Returns a pointer to the virtual address descriptor containing the next address range, NULL if none.
--*/
{ PMMADDRESS_NODE Next; PMMADDRESS_NODE Parent; PMMADDRESS_NODE Left;
Next = Node;
if (Next->RightChild == NULL) {
do { Parent = SANITIZE_PARENT_NODE (Next->u1.Parent);
ASSERT (Parent != NULL);
if (Parent == Next) { return NULL; }
//
// Locate the first ancestor of this node of which this
// node is the left child of and return that node as the
// next element.
//
if (Parent->LeftChild == Next) { return Parent; }
Next = Parent;
} while (TRUE); }
//
// A right child exists, locate the left most child of that right child.
//
Next = Next->RightChild;
do {
Left = Next->LeftChild;
if (Left == NULL) { break; }
Next = Left;
} while (TRUE);
return Next;
}
PMMADDRESS_NODE FASTCALL MiGetPreviousNode ( IN PMMADDRESS_NODE Node )
/*++
Routine Description:
This function locates the virtual address descriptor which contains the address range which logically precedes the specified virtual address descriptor.
Arguments:
Node - Supplies a pointer to a virtual address descriptor.
Return Value:
Returns a pointer to the virtual address descriptor containing the next address range, NULL if none.
--*/
{ PMMADDRESS_NODE Previous; PMMADDRESS_NODE Parent;
Previous = Node;
if (Previous->LeftChild == NULL) {
ASSERT (Previous->u1.Parent != NULL);
Parent = SANITIZE_PARENT_NODE (Previous->u1.Parent);
while (Parent != Previous) {
//
// Locate the first ancestor of this node of which this
// node is the right child of and return that node as the
// Previous element.
//
if (Parent->RightChild == Previous) {
if (Parent == SANITIZE_PARENT_NODE (Parent->u1.Parent)) { return NULL; }
return Parent; }
Previous = Parent; Parent = SANITIZE_PARENT_NODE (Previous->u1.Parent); } return NULL; }
//
// A left child exists, locate the right most child of that left child.
//
Previous = Previous->LeftChild;
while (Previous->RightChild != NULL) { Previous = Previous->RightChild; }
return Previous; }
PMMADDRESS_NODE FASTCALL MiLocateAddressInTree ( IN ULONG_PTR Vpn, IN PMM_AVL_TABLE Table )
/*++
Routine Description:
The function locates the virtual address descriptor which describes a given address.
Arguments:
Vpn - Supplies the virtual page number to locate a descriptor for.
Return Value:
Returns a pointer to the virtual address descriptor which contains the supplied virtual address or NULL if none was located.
--*/
{ PVOID NodeOrParent; TABLE_SEARCH_RESULT SearchResult;
//
// Lookup the element and save the result.
//
SearchResult = MiFindNodeOrParent (Table, Vpn, (PMMADDRESS_NODE *) &NodeOrParent);
if (SearchResult == TableFoundNode) {
//
// Return the VAD.
//
return (PMMADDRESS_NODE) NodeOrParent; }
return NULL; }
NTSTATUS MiFindEmptyAddressRangeInTree ( IN SIZE_T SizeOfRange, IN ULONG_PTR Alignment, IN PMM_AVL_TABLE Table, OUT PMMADDRESS_NODE *PreviousVad, OUT PVOID *Base )
/*++
Routine Description:
The function examines the virtual address descriptors to locate an unused range of the specified size and returns the starting address of the range.
Arguments:
SizeOfRange - Supplies the size in bytes of the range to locate.
Alignment - Supplies the alignment for the address. Must be a power of 2 and greater than the page_size.
Table - Supplies the root of the tree to search through.
PreviousVad - Supplies the Vad which is before this the found address range.
Base - Receives the starting address of a suitable range on success.
Return Value:
NTSTATUS.
--*/
{ PMMADDRESS_NODE Node; PMMADDRESS_NODE NextNode; ULONG_PTR AlignmentVpn; ULONG_PTR SizeOfRangeVpn;
AlignmentVpn = Alignment >> PAGE_SHIFT;
//
// Locate the node with the lowest starting address.
//
ASSERT (SizeOfRange != 0); SizeOfRangeVpn = (SizeOfRange + (PAGE_SIZE - 1)) >> PAGE_SHIFT; ASSERT (SizeOfRangeVpn != 0);
if (Table->NumberGenericTableElements == 0) { *Base = MM_LOWEST_USER_ADDRESS; return STATUS_SUCCESS; }
Node = Table->BalancedRoot.RightChild;
while (Node->LeftChild != NULL) { Node = Node->LeftChild; }
//
// Check to see if a range exists between the lowest address VAD
// and lowest user address.
//
if (Node->StartingVpn > MI_VA_TO_VPN (MM_LOWEST_USER_ADDRESS)) {
if (SizeOfRangeVpn < (Node->StartingVpn - MI_VA_TO_VPN (MM_LOWEST_USER_ADDRESS))) {
*PreviousVad = NULL; *Base = MM_LOWEST_USER_ADDRESS; return STATUS_SUCCESS; } }
do {
NextNode = MiGetNextNode (Node);
if (NextNode != NULL) {
if (SizeOfRangeVpn <= ((ULONG_PTR)NextNode->StartingVpn - MI_ROUND_TO_SIZE(1 + Node->EndingVpn, AlignmentVpn))) {
//
// Check to ensure that the ending address aligned upwards
// is not greater than the starting address.
//
if ((ULONG_PTR)NextNode->StartingVpn > MI_ROUND_TO_SIZE(1 + Node->EndingVpn, AlignmentVpn)) {
*PreviousVad = Node; *Base = (PVOID) MI_ROUND_TO_SIZE( (ULONG_PTR)MI_VPN_TO_VA_ENDING(Node->EndingVpn), Alignment); return STATUS_SUCCESS; } }
} else {
//
// No more descriptors, check to see if this fits into the remainder
// of the address space.
//
if ((((ULONG_PTR)Node->EndingVpn + MI_VA_TO_VPN(X64K)) < MI_VA_TO_VPN (MM_HIGHEST_VAD_ADDRESS)) && (SizeOfRange <= ((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)MI_ROUND_TO_SIZE( (ULONG_PTR)MI_VPN_TO_VA(Node->EndingVpn), Alignment)))) {
*PreviousVad = Node; *Base = (PVOID) MI_ROUND_TO_SIZE( (ULONG_PTR)MI_VPN_TO_VA_ENDING(Node->EndingVpn), Alignment); return STATUS_SUCCESS; } return STATUS_NO_MEMORY; } Node = NextNode;
} while (TRUE); }
NTSTATUS MiFindEmptyAddressRangeDownTree ( IN SIZE_T SizeOfRange, IN PVOID HighestAddressToEndAt, IN ULONG_PTR Alignment, IN PMM_AVL_TABLE Table, OUT PVOID *Base )
/*++
Routine Description:
The function examines the virtual address descriptors to locate an unused range of the specified size and returns the starting address of the range. The function examines from the high addresses down and ensures that starting address is less than the specified address.
Note this cannot be used for the based section tree because only the nodes in that tree are stored as VAs instead of VPNs.
Arguments:
SizeOfRange - Supplies the size in bytes of the range to locate.
HighestAddressToEndAt - Supplies the virtual address that limits the value of the ending address. The ending address of the located range must be less than this address.
Alignment - Supplies the alignment for the address. Must be a power of 2 and greater than the page_size.
Table - Supplies the root of the tree to search through.
Base - Receives the starting address of a suitable range on success.
Return Value:
NTSTATUS.
--*/
{ PMMADDRESS_NODE Node; PMMADDRESS_NODE PreviousNode; ULONG_PTR AlignedEndingVa; PVOID OptimalStart; ULONG_PTR OptimalStartVpn; ULONG_PTR HighestVpn; ULONG_PTR AlignmentVpn;
//
// Note this cannot be used for the based section tree because only
// the nodes in that tree are stored as VAs instead of VPNs.
//
ASSERT (Table != &MmSectionBasedRoot);
SizeOfRange = MI_ROUND_TO_SIZE (SizeOfRange, PAGE_SIZE);
if (((ULONG_PTR)HighestAddressToEndAt + 1) < SizeOfRange) { return STATUS_NO_MEMORY; }
ASSERT (HighestAddressToEndAt != NULL); ASSERT (HighestAddressToEndAt <= (PVOID)((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
HighestVpn = MI_VA_TO_VPN (HighestAddressToEndAt);
//
// Locate the Node with the highest starting address.
//
OptimalStart = (PVOID)(MI_ALIGN_TO_SIZE( (((ULONG_PTR)HighestAddressToEndAt + 1) - SizeOfRange), Alignment));
if (Table->NumberGenericTableElements == 0) {
//
// The tree is empty, any range is okay.
//
*Base = OptimalStart; return STATUS_SUCCESS; }
Node = (PMMADDRESS_NODE) Table->BalancedRoot.RightChild;
//
// See if an empty slot exists to hold this range, locate the largest
// element in the tree.
//
while (Node->RightChild != NULL) { Node = Node->RightChild; }
//
// Check to see if a range exists between the highest address VAD
// and the highest address to end at.
//
AlignedEndingVa = (ULONG_PTR)MI_ROUND_TO_SIZE ((ULONG_PTR)MI_VPN_TO_VA_ENDING (Node->EndingVpn), Alignment);
if (AlignedEndingVa < (ULONG_PTR)HighestAddressToEndAt) {
if ( SizeOfRange < ((ULONG_PTR)HighestAddressToEndAt - AlignedEndingVa)) {
*Base = MI_ALIGN_TO_SIZE( ((ULONG_PTR)HighestAddressToEndAt - SizeOfRange), Alignment); return STATUS_SUCCESS; } }
//
// Walk the tree backwards looking for a fit.
//
OptimalStartVpn = MI_VA_TO_VPN (OptimalStart); AlignmentVpn = MI_VA_TO_VPN (Alignment);
do {
PreviousNode = MiGetPreviousNode (Node);
if (PreviousNode != NULL) {
//
// Is the ending Va below the top of the address to end at.
//
if (PreviousNode->EndingVpn < OptimalStartVpn) { if ((SizeOfRange >> PAGE_SHIFT) <= ((ULONG_PTR)Node->StartingVpn - (ULONG_PTR)MI_ROUND_TO_SIZE(1 + PreviousNode->EndingVpn, AlignmentVpn))) {
//
// See if the optimal start will fit between these
// two VADs.
//
if ((OptimalStartVpn > PreviousNode->EndingVpn) && (HighestVpn < Node->StartingVpn)) { *Base = OptimalStart; return STATUS_SUCCESS; }
//
// Check to ensure that the ending address aligned upwards
// is not greater than the starting address.
//
if ((ULONG_PTR)Node->StartingVpn > (ULONG_PTR)MI_ROUND_TO_SIZE(1 + PreviousNode->EndingVpn, AlignmentVpn)) {
*Base = MI_ALIGN_TO_SIZE( (ULONG_PTR)MI_VPN_TO_VA (Node->StartingVpn) - SizeOfRange, Alignment); return STATUS_SUCCESS; } } } } else {
//
// No more descriptors, check to see if this fits into the remainder
// of the address space.
//
if (Node->StartingVpn > MI_VA_TO_VPN (MM_LOWEST_USER_ADDRESS)) { if ((SizeOfRange >> PAGE_SHIFT) <= ((ULONG_PTR)Node->StartingVpn - MI_VA_TO_VPN (MM_LOWEST_USER_ADDRESS))) {
//
// See if the optimal start will fit between these
// two VADs.
//
if (HighestVpn < Node->StartingVpn) { *Base = OptimalStart; return STATUS_SUCCESS; }
*Base = MI_ALIGN_TO_SIZE( (ULONG_PTR)MI_VPN_TO_VA (Node->StartingVpn) - SizeOfRange, Alignment); return STATUS_SUCCESS; } } return STATUS_NO_MEMORY; } Node = PreviousNode;
} while (TRUE); }
NTSTATUS MiFindEmptyAddressRangeDownBasedTree ( IN SIZE_T SizeOfRange, IN PVOID HighestAddressToEndAt, IN ULONG_PTR Alignment, IN PMM_AVL_TABLE Table, OUT PVOID *Base )
/*++
Routine Description:
The function examines the virtual address descriptors to locate an unused range of the specified size and returns the starting address of the range. The function examines from the high addresses down and ensures that starting address is less than the specified address.
Note this is only used for the based section tree because only the nodes in that tree are stored as VAs instead of VPNs.
Arguments:
SizeOfRange - Supplies the size in bytes of the range to locate.
HighestAddressToEndAt - Supplies the virtual address that limits the value of the ending address. The ending address of the located range must be less than this address.
Alignment - Supplies the alignment for the address. Must be a power of 2 and greater than the page_size.
Table - Supplies the root of the tree to search through.
Base - Receives the starting address of a suitable range on success.
Return Value:
NTSTATUS.
--*/
{ PMMADDRESS_NODE Node; PMMADDRESS_NODE PreviousNode; ULONG_PTR AlignedEndingVa; ULONG_PTR OptimalStart;
//
// Note this is only used for the based section tree because only
// the nodes in that tree are stored as VAs instead of VPNs.
//
ASSERT (Table == &MmSectionBasedRoot);
SizeOfRange = MI_ROUND_TO_SIZE (SizeOfRange, PAGE_SIZE);
if (((ULONG_PTR)HighestAddressToEndAt + 1) < SizeOfRange) { return STATUS_NO_MEMORY; }
ASSERT (HighestAddressToEndAt != NULL); ASSERT (HighestAddressToEndAt <= (PVOID)((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1));
//
// Locate the node with the highest starting address.
//
OptimalStart = (ULONG_PTR) MI_ALIGN_TO_SIZE ( (((ULONG_PTR)HighestAddressToEndAt + 1) - SizeOfRange), Alignment);
if (Table->NumberGenericTableElements == 0) {
//
// The tree is empty, any range is okay.
//
*Base = (PVOID) OptimalStart; return STATUS_SUCCESS; }
Node = (PMMADDRESS_NODE) Table->BalancedRoot.RightChild;
//
// See if an empty slot exists to hold this range, locate the largest
// element in the tree.
//
while (Node->RightChild != NULL) { Node = Node->RightChild; }
//
// Check to see if a range exists between the highest address VAD
// and the highest address to end at.
//
AlignedEndingVa = MI_ROUND_TO_SIZE (Node->EndingVpn, Alignment);
PRINT("search down0: %p %p %p\n", AlignedEndingVa, HighestAddressToEndAt, SizeOfRange);
if ((AlignedEndingVa < (ULONG_PTR)HighestAddressToEndAt) && (SizeOfRange < ((ULONG_PTR)HighestAddressToEndAt - AlignedEndingVa))) {
*Base = MI_ALIGN_TO_SIZE( ((ULONG_PTR)HighestAddressToEndAt - SizeOfRange), Alignment); return STATUS_SUCCESS; }
//
// Walk the tree backwards looking for a fit.
//
do {
PreviousNode = MiGetPreviousNode (Node);
PRINT("search down1: %p %p %p %p\n", PreviousNode, Node, OptimalStart, Alignment);
if (PreviousNode == NULL) { break; }
//
// Is the ending Va below the top of the address to end at.
//
if (PreviousNode->EndingVpn < OptimalStart) {
if (SizeOfRange <= (Node->StartingVpn - MI_ROUND_TO_SIZE(1 + PreviousNode->EndingVpn, Alignment))) {
//
// See if the optimal start will fit between these two VADs.
//
if ((OptimalStart > PreviousNode->EndingVpn) && ((ULONG_PTR) HighestAddressToEndAt < Node->StartingVpn)) { *Base = (PVOID) OptimalStart; return STATUS_SUCCESS; }
//
// Check to ensure that the ending address aligned upwards
// is not greater than the starting address.
//
if (Node->StartingVpn > MI_ROUND_TO_SIZE(1 + PreviousNode->EndingVpn, Alignment)) {
*Base = MI_ALIGN_TO_SIZE (Node->StartingVpn - SizeOfRange, Alignment);
return STATUS_SUCCESS; } } }
Node = PreviousNode;
} while (TRUE);
//
// No more descriptors, check to see if this fits into the remainder
// of the address space.
//
if (Node->StartingVpn > (ULONG_PTR) MM_LOWEST_USER_ADDRESS) {
if (SizeOfRange <= (Node->StartingVpn - (ULONG_PTR) MM_LOWEST_USER_ADDRESS)) {
//
// See if the optimal start will fit between these two VADs.
//
if ((ULONG_PTR) HighestAddressToEndAt < Node->StartingVpn) { *Base = (PVOID) OptimalStart; return STATUS_SUCCESS; }
*Base = MI_ALIGN_TO_SIZE (Node->StartingVpn - SizeOfRange, Alignment);
return STATUS_SUCCESS; } } return STATUS_NO_MEMORY; }
#if !defined (_USERMODE)
PMMVAD FASTCALL MiLocateAddress ( IN PVOID VirtualAddress )
/*++
Routine Description:
The function locates the virtual address descriptor which describes a given address.
Arguments:
VirtualAddress - Supplies the virtual address to locate a descriptor for.
Table - Supplies the table describing the tree.
Return Value:
Returns a pointer to the virtual address descriptor which contains the supplied virtual address or NULL if none was located.
--*/
{ PMMVAD FoundVad; ULONG_PTR Vpn; PMM_AVL_TABLE Table; TABLE_SEARCH_RESULT SearchResult;
Table = &PsGetCurrentProcess ()->VadRoot;
//
// Note the NodeHint *MUST* be captured locally - see the synchronization
// comment below for details.
//
FoundVad = (PMMVAD) Table->NodeHint;
if (FoundVad == NULL) { return NULL; }
Vpn = MI_VA_TO_VPN (VirtualAddress);
if ((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn)) { return FoundVad; }
//
// Lookup the element and save the result.
//
SearchResult = MiFindNodeOrParent (Table, Vpn, (PMMADDRESS_NODE *) &FoundVad);
if (SearchResult != TableFoundNode) { return NULL; }
ASSERT (FoundVad != NULL);
ASSERT ((Vpn >= FoundVad->StartingVpn) && (Vpn <= FoundVad->EndingVpn));
//
// Note the NodeHint field update is not synchronized in all cases, ie:
// some callers hold the address space mutex and others hold the working
// set pushlock. It is ok that the update is not synchronized - as long
// as care is taken above that it is read into a local variable and then
// referenced. Because no VAD can be removed from the tree without holding
// both the address space & working set.
//
Table->NodeHint = (PVOID) FoundVad;
//
// Return the VAD.
//
return FoundVad; } #endif
#if DBG
VOID MiNodeTreeWalk ( IN PMM_AVL_TABLE Table ) { PVOID RestartKey; PMMADDRESS_NODE NewNode; PMMADDRESS_NODE PrevNode; PMMADDRESS_NODE NextNode;
RestartKey = NULL;
do {
NewNode = MiEnumerateGenericTableWithoutSplayingAvl (Table, &RestartKey);
if (NewNode == NULL) { break; }
PrevNode = MiGetPreviousNode (NewNode); NextNode = MiGetNextNode (NewNode);
PRINT ("Node %p %x %x\n", NewNode, NewNode->StartingVpn, NewNode->EndingVpn);
if (PrevNode != NULL) { PRINT ("\tPrevNode %p %x %x\n", PrevNode, PrevNode->StartingVpn, PrevNode->EndingVpn); }
if (NextNode != NULL) { PRINT ("\tNextNode %p %x %x\n", NextNode, NextNode->StartingVpn, NextNode->EndingVpn); }
} while (TRUE);
PRINT ("NumberGenericTableElements = 0x%x, Depth = 0x%x\n", Table->NumberGenericTableElements, Table->DepthOfTree);
return; } #endif
#if defined (_USERMODE)
MMADDRESS_NODE MiBalancedLinks;
MM_AVL_TABLE MiAvlTable; MM_AVL_TABLE MmSectionBasedRoot;
ULONG DeleteRandom = 1;
#if RANDOM
#define NUMBER_OF_VADS 32
#else
#define NUMBER_OF_VADS 4
#endif
int __cdecl main( int argc, PCHAR argv[] ) { ULONG i; PVOID StartingAddress; PVOID EndingAddress; NTSTATUS Status; PMMADDRESS_NODE NewNode; #if RANDOM
PMMADDRESS_NODE PrevNode; ULONG RandomNumber = 0x99887766; ULONG_PTR DeleteVpn = 0; #endif
PMM_AVL_TABLE Table; SIZE_T CapturedRegionSize;
UNREFERENCED_PARAMETER (argc); UNREFERENCED_PARAMETER (argv);
#if RANDOM
Table = &MiAvlTable; #else
Table = &MmSectionBasedRoot; #endif
MiInitializeVadTableAvl (Table);
for (i = 0; i < NUMBER_OF_VADS; i += 1) { NewNode = malloc (sizeof (MMADDRESS_NODE)); ASSERT (((ULONG_PTR)NewNode & 0x3) == 0);
if (NewNode == NULL) { PRINT ("Malloc failed\n"); exit (1); }
NewNode->u1.Parent = NULL; NewNode->LeftChild = NULL; NewNode->RightChild = NULL; NewNode->u1.Balance = 0;
#if RANDOM
RandomNumber = RtlRandom (&RandomNumber);
CapturedRegionSize = (SIZE_T) (RandomNumber & 0x1FFFFF);
Status = MiFindEmptyAddressRangeInTree (CapturedRegionSize, 64 * 1024, // align
Table, &PrevNode, &StartingAddress);
#else
CapturedRegionSize = 0x800000;
Status = MiFindEmptyAddressRangeDownBasedTree (CapturedRegionSize, (PVOID) 0x7f7effff, // highest addr
64 * 1024, // align
Table, &StartingAddress); #endif
if (!NT_SUCCESS (Status)) { PRINT ("Could not find empty addr range in tree for size %p\n", CapturedRegionSize); free (NewNode); continue; }
#if RANDOM
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + CapturedRegionSize - 1L) | (PAGE_SIZE - 1L)); #else
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + CapturedRegionSize - 1L)); #endif
printf ("Inserting addr range in tree @ %p %p\n", StartingAddress, EndingAddress);
#if RANDOM
NewNode->StartingVpn = MI_VA_TO_VPN (StartingAddress); NewNode->EndingVpn = MI_VA_TO_VPN (EndingAddress); #else
NewNode->StartingVpn = (ULONG_PTR) StartingAddress; NewNode->EndingVpn = (ULONG_PTR) EndingAddress; #endif
MiInsertNode (NewNode, Table);
#if RANDOM
RandomNumber = RtlRandom (&RandomNumber);
if (RandomNumber & 0x3) { DeleteVpn = NewNode->StartingVpn; }
if (DeleteRandom && ((i & 0x3) == 0)) { NewNode = MiLocateAddressInTree (DeleteVpn, Table); printf ("Located node for random deletion - vpn %p @ %p\n", DeleteVpn, NewNode);
if (NewNode != NULL) { MiRemoveNode (NewNode, Table); printf ("Removed random node for vpn %p @ %p %p %p\n", DeleteVpn, NewNode, NewNode->StartingVpn, NewNode->EndingVpn); } } #endif
printf ("\n"); }
MiNodeTreeWalk (Table);
NewNode = MiLocateAddressInTree (5, Table); printf ("Located node for vpn 5 @ %p\n", NewNode);
if (NewNode != NULL) { MiRemoveNode (NewNode, Table); printf ("Removed node for vpn 5 @ %p\n", NewNode); }
NewNode = MiLocateAddressInTree (5, Table); printf("Located node for vpn 5 @ %p\n", NewNode);
printf("all done, balmin=%x, balmax=%x\n", BalMin, BalMax);
return 0; }
#endif
|