Windows2000/private/ntos/config/hivecell.c
2020-09-30 17:12:32 +02:00

1660 lines
42 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
hivecell.c
Abstract:
This module implements hive cell procedures.
Author:
Bryan M. Willman (bryanwi) 27-Mar-92
Environment:
Revision History:
Dragos C. Sambotin (dragoss) 22-Dec-98
Requests for cells bigger than 1K are doubled. This way
we avoid fragmentation and we make the value-growing
process more flexible.
Dragos C. Sambotin (dragoss) 13-Jan-99
At boot time, order the free cells list ascending.
--*/
#include "cmp.h"
// Private procedures
HCELL_INDEX
HvpDoAllocateCell(
PHHIVE Hive,
ULONG NewSize,
HSTORAGE_TYPE Type
);
ULONG
HvpAllocateInBin(
PHHIVE Hive,
PHBIN Bin,
ULONG Size,
ULONG Type
);
BOOLEAN
HvpMakeBinPresent(
IN PHHIVE Hive,
IN HCELL_INDEX Cell,
IN PHMAP_ENTRY Map
);
BOOLEAN
HvpIsFreeNeighbor(
PHHIVE Hive,
PHBIN Bin,
PHCELL FreeCell,
PHCELL *FreeNeighbor,
HSTORAGE_TYPE Type
);
VOID
HvpDelistBinFreeCells(
PHHIVE Hive,
PHBIN Bin,
HSTORAGE_TYPE Type,
PHCELL_INDEX TailDisplay OPTIONAL
);
#define CmpFindFirstSetRight KiFindFirstSetRight
extern CCHAR KiFindFirstSetRight[256];
#define CmpFindFirstSetLeft KiFindFirstSetLeft
extern CCHAR KiFindFirstSetLeft[256];
#define HvpComputeIndex(Index, Size) \
{ \
Index = (Size >> HHIVE_FREE_DISPLAY_SHIFT) - 1; \
if (Index >= HHIVE_LINEAR_INDEX ) { \
\
/* \
** Too big for the linear lists, compute the exponential \
** list. \
*/ \
\
if (Index > 255) { \
/* \
** Too big for all the lists, use the last index. \
*/ \
Index = HHIVE_FREE_DISPLAY_SIZE-1; \
} else { \
Index = CmpFindFirstSetLeft[Index] + \
HHIVE_FREE_DISPLAY_BIAS; \
} \
} \
}
#define ONE_K 1024
// Double requests bigger than 1KB
// CmpSetValueKeyExisting always allocates a bigger data
// value cell exactly the required size. This creates
// problems when somebody slowly grows a value one DWORD
// at a time to some enormous size. An easy fix for this
// would be to set a certain threshold (like 1K). Once a
// value size crosses that threshold, allocate a new cell
// that is twice the old size. So the actual allocated
// size would grow to 1k, then 2k, 4k, 8k, 16k, 32k,etc.
// This will reduce the fragmentation.
#define HvpAdjustCellSize(Size) \
{ \
ULONG onek = ONE_K; \
ULONG Limit = 0; \
\
while( Size > onek ) { \
onek<<=1; \
Limit++; \
} \
\
Size = Limit?onek:Size; \
}
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HvpGetCellPaged)
#pragma alloc_text(PAGE,HvpGetCellFlat)
#pragma alloc_text(PAGE,HvpGetCellMap)
#pragma alloc_text(PAGE,HvGetCellSize)
#pragma alloc_text(PAGE,HvAllocateCell)
#pragma alloc_text(PAGE,HvpDoAllocateCell)
#pragma alloc_text(PAGE,HvFreeCell)
#pragma alloc_text(PAGE,HvpIsFreeNeighbor)
#pragma alloc_text(PAGE,HvpEnlistFreeCell)
#pragma alloc_text(PAGE,HvpDelistFreeCell)
#pragma alloc_text(PAGE,HvReallocateCell)
#pragma alloc_text(PAGE,HvIsCellAllocated)
#pragma alloc_text(PAGE,HvpAllocateInBin)
#pragma alloc_text(PAGE,HvpMakeBinPresent)
#pragma alloc_text(PAGE,HvpDelistBinFreeCells)
#endif
// Cell Procedures
struct _CELL_DATA *
HvpGetCellPaged(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Returns the memory address for the specified Cell. Will never
return failure, but may assert. Use HvIsCellAllocated to check
validity of Cell.
This routine should never be called directly, always call it
via the HvGetCell() macro.
This routine provides GetCell support for hives with full maps.
It is the normal version of the routine.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - supplies HCELL_INDEX of cell to return address for
Return Value:
Address of Cell in memory. Assert or BugCheck if error.
--*/
{
ULONG Type;
ULONG Table;
ULONG Block;
ULONG Offset;
PHCELL pcell;
PHMAP_ENTRY Map;
CMLOG(CML_FLOW, CMS_MAP) {
KdPrint(("HvGetCellPaged:\n"));
KdPrint(("\tHive=%08lx Cell=%08lx\n",Hive,Cell));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Cell != HCELL_NIL);
ASSERT(Hive->Flat == FALSE);
ASSERT((Cell & (HCELL_PAD(Hive)-1))==0);
ASSERT_CM_LOCK_OWNED();
#if DBG
if (HvGetCellType(Cell) == Stable) {
ASSERT(Cell >= sizeof(HBIN));
} else {
ASSERT(Cell >= (HCELL_TYPE_MASK + sizeof(HBIN)));
}
#endif
Type = HvGetCellType(Cell);
Table = (Cell & HCELL_TABLE_MASK) >> HCELL_TABLE_SHIFT;
Block = (Cell & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
Offset = (Cell & HCELL_OFFSET_MASK);
ASSERT((Cell - (Type * HCELL_TYPE_MASK)) < Hive->Storage[Type].Length);
Map = &((Hive->Storage[Type].Map)->Directory[Table]->Table[Block]);
ASSERT((Map->BinAddress & HMAP_BASE) != 0);
ASSERT((Map->BinAddress & HMAP_DISCARDABLE) == 0);
pcell = (PHCELL)((ULONG_PTR)(Map->BlockAddress) + Offset);
if (USE_OLD_CELL(Hive)) {
return (struct _CELL_DATA *)&(pcell->u.OldCell.u.UserData);
} else {
return (struct _CELL_DATA *)&(pcell->u.NewCell.u.UserData);
}
}
struct _CELL_DATA *
HvpGetCellFlat(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Returns the memory address for the specified Cell. Will never
return failure, but may assert. Use HvIsCellAllocated to check
validity of Cell.
This routine should never be called directly, always call it
via the HvGetCell() macro.
This routine provides GetCell support for read only hives with
single allocation flat images. Such hives do not have cell
maps ("page tables"), instead, we compute addresses by
arithmetic against the base image address.
Such hives cannot have volatile cells.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - supplies HCELL_INDEX of cell to return address for
Return Value:
Address of Cell in memory. Assert or BugCheck if error.
--*/
{
PUCHAR base;
PHCELL pcell;
CMLOG(CML_FLOW, CMS_MAP) {
KdPrint(("HvGetCellFlat:\n"));
KdPrint(("\tHive=%08lx Cell=%08lx\n",Hive,Cell));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Cell != HCELL_NIL);
ASSERT(Hive->Flat == TRUE);
ASSERT(HvGetCellType(Cell) == Stable);
ASSERT(Cell >= sizeof(HBIN));
ASSERT(Cell < Hive->BaseBlock->Length);
ASSERT((Cell & 0x7)==0);
// Address is base of Hive image + Cell
base = (PUCHAR)(Hive->BaseBlock) + HBLOCK_SIZE;
pcell = (PHCELL)(base + Cell);
if (USE_OLD_CELL(Hive)) {
return (struct _CELL_DATA *)&(pcell->u.OldCell.u.UserData);
} else {
return (struct _CELL_DATA *)&(pcell->u.NewCell.u.UserData);
}
}
PHMAP_ENTRY
HvpGetCellMap(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Returns the address of the HMAP_ENTRY for the cell.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - supplies HCELL_INDEX of cell to return map entry address for
Return Value:
Address of MAP_ENTRY in memory. NULL if no such cell or other error.
--*/
{
ULONG Type;
ULONG Table;
ULONG Block;
PHMAP_TABLE ptab;
CMLOG(CML_FLOW, CMS_MAP) {
KdPrint(("HvpGetCellMapPaged:\n"));
KdPrint(("\tHive=%08lx Cell=%08lx\n",Hive,Cell));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->Flat == FALSE);
ASSERT((Cell & (HCELL_PAD(Hive)-1))==0);
Type = HvGetCellType(Cell);
Table = (Cell & HCELL_TABLE_MASK) >> HCELL_TABLE_SHIFT;
Block = (Cell & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
if ((Cell - (Type * HCELL_TYPE_MASK)) >= Hive->Storage[Type].Length) {
return NULL;
}
ptab = (Hive->Storage[Type].Map)->Directory[Table];
return &(ptab->Table[Block]);
}
LONG
HvGetCellSize(
IN PHHIVE Hive,
IN PVOID Address
)
/*++
Routine Description:
Returns the size of the specified Cell, based on its MEMORY
ADDRESS. Must always call HvGetCell first to get that
address.
NOTE: This should be a macro if speed is issue.
NOTE: If you pass in some random pointer, you will get some
random answer. Only pass in valid Cell addresses.
Arguments:
Hive - supplies hive control structure for the given cell
Address - address in memory of the cell, returned by HvGetCell()
Return Value:
Allocated size in bytes of the cell.
If Negative, Cell is free, or Address is bogus.
--*/
{
LONG size;
CMLOG(CML_FLOW, CMS_MAP) {
KdPrint(("HvGetCellSize:\n"));
KdPrint(("\tAddress=%08lx\n", Address));
}
if (USE_OLD_CELL(Hive)) {
size = ( (CONTAINING_RECORD(Address, HCELL, u.OldCell.u.UserData))->Size ) * -1;
size -= FIELD_OFFSET(HCELL, u.OldCell.u.UserData);
} else {
size = ( (CONTAINING_RECORD(Address, HCELL, u.NewCell.u.UserData))->Size ) * -1;
size -= FIELD_OFFSET(HCELL, u.NewCell.u.UserData);
}
return size;
}
HCELL_INDEX
HvAllocateCell(
PHHIVE Hive,
ULONG NewSize,
HSTORAGE_TYPE Type
)
/*++
Routine Description:
Allocates the space and the cell index for a new cell.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
NewSize - size in bytes of the cell to allocate
Type - indicates whether Stable or Volatile storage is desired.
Return Value:
New HCELL_INDEX if success, HCELL_NIL if failure.
--*/
{
HCELL_INDEX NewCell;
CMLOG(CML_MAJOR, CMS_HIVE) {
KdPrint(("HvAllocateCell:\n"));
KdPrint(("\tHive=%08lx NewSize=%08lx\n",Hive,NewSize));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
// Make room for overhead fields and round up to HCELL_PAD boundary
if (USE_OLD_CELL(Hive)) {
NewSize += FIELD_OFFSET(HCELL, u.OldCell.u.UserData);
} else {
NewSize += FIELD_OFFSET(HCELL, u.NewCell.u.UserData);
}
NewSize = ROUND_UP(NewSize, HCELL_PAD(Hive));
// Adjust the size (an easy fix for granularity)
HvpAdjustCellSize(NewSize);
// reject impossible/unreasonable values
if (NewSize > HSANE_CELL_MAX) {
return HCELL_NIL;
}
// Do the actual storage allocation
NewCell = HvpDoAllocateCell(Hive, NewSize, Type);
#if DBG
if (NewCell != HCELL_NIL) {
ASSERT(HvIsCellAllocated(Hive, NewCell));
}
#endif
CMLOG(CML_FLOW, CMS_HIVE) {
KdPrint(("\tNewCell=%08lx\n", NewCell));
}
return NewCell;
}
HCELL_INDEX
HvpDoAllocateCell(
PHHIVE Hive,
ULONG NewSize,
HSTORAGE_TYPE Type
)
/*++
Routine Description:
Allocates space in the hive. Does not affect cell map in any way.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
NewSize - size in bytes of the cell to allocate
Type - indicates whether Stable or Volatile storage is desired.
Return Value:
HCELL_INDEX of new cell, HCELL_NIL if failure
--*/
{
ULONG Index;
ULONG Summary;
HCELL_INDEX cell;
PHCELL pcell;
HCELL_INDEX tcell;
PHCELL ptcell;
PHBIN Bin;
PHMAP_ENTRY Me;
ULONG offset;
PHCELL next;
ULONG MinFreeSize;
CMLOG(CML_MINOR, CMS_HIVE) {
KdPrint(("HvDoAllocateCell:\n"));
KdPrint(("\tHive=%08lx NewSize=%08lx Type=%08lx\n",Hive,NewSize,Type));
}
ASSERT(Hive->ReadOnly == FALSE);
// Compute Index into Display
HvpComputeIndex(Index, NewSize);
// Compute Summary vector of Display entries that are non null
Summary = Hive->Storage[Type].FreeSummary;
Summary = Summary & ~((1 << Index) - 1);
// We now have a summary of lists that are non-null and may
// contain entries large enough to satisfy the request.
// Iterate through the list and pull the first cell that is
// big enough. If no cells are big enough, advance to the
// next non-null list.
ASSERT(HHIVE_FREE_DISPLAY_SIZE == 24);
while (Summary != 0) {
if (Summary & 0xff) {
Index = CmpFindFirstSetRight[Summary & 0xff];
} else if (Summary & 0xff00) {
Index = CmpFindFirstSetRight[(Summary & 0xff00) >> 8] + 8;
} else {
ASSERT(Summary & 0xff0000);
Index = CmpFindFirstSetRight[(Summary & 0xff0000) >> 16] + 16;
}
// Walk through the list until we find a cell large enough
// to satisfy the allocation. If we find one, pull it from
// the list and use it. If we don't find one, clear this
// list's bit in the Summary and try the next larger list.
// look for a large enough cell in the list
cell = Hive->Storage[Type].FreeDisplay[Index];
while (cell != HCELL_NIL) {
pcell = HvpGetHCell(Hive, cell);
if (NewSize <= (ULONG)pcell->Size) {
// Found a big enough cell.
if (! HvMarkCellDirty(Hive, cell)) {
return HCELL_NIL;
}
HvpDelistFreeCell(Hive, pcell, Type, NULL);
ASSERT(pcell->Size > 0);
ASSERT(NewSize <= (ULONG)pcell->Size);
goto UseIt;
}
// DbgPrint("cell %08lx (%lx) too small (%d < %d), trying next at",cell,pcell,pcell->Size,NewSize);
if (USE_OLD_CELL(Hive)) {
cell = pcell->u.OldCell.u.Next;
} else {
cell = pcell->u.NewCell.u.Next;
}
// DbgPrint(" %08lx\n",cell);
}
// No suitable cell was found on that list.
// Clear the bit in the summary and try the
// next biggest list.
// DbgPrint("No suitable cell, Index %d, Summary %08lx -> ",Index, Summary);
ASSERT(Summary & (1 << Index));
Summary = Summary & ~(1 << Index);
// DbgPrint("%08lx\n",Summary);
}
if (Summary == 0) {
// No suitable cells were found on any free list.
// Either there is no large enough cell, or we
// have no free cells left at all. In either case, allocate a
// new bin, with a new free cell certain to be large enough in
// it, and use that cell.
// Attempt to create a new bin
if ((Bin = HvpAddBin(Hive, NewSize, Type)) != NULL) {
// It worked. Use single large cell in Bin.
DHvCheckBin(Hive,Bin);
cell = (Bin->FileOffset) + sizeof(HBIN) + (Type*HCELL_TYPE_MASK);
pcell = HvpGetHCell(Hive, cell);
} else {
return HCELL_NIL;
}
}
UseIt:
// cell refers to a free cell we have pulled from its list
// if it is too big, give the residue back
// ("too big" means there is at least one HCELL of extra space)
// always mark it allocated
// return it as our function value
ASSERT(pcell->Size > 0);
if (USE_OLD_CELL(Hive)) {
MinFreeSize = FIELD_OFFSET(HCELL, u.OldCell.u.Next) + sizeof(HCELL_INDEX);
} else {
MinFreeSize = FIELD_OFFSET(HCELL, u.NewCell.u.Next) + sizeof(HCELL_INDEX);
}
if ((NewSize + MinFreeSize) < (ULONG)pcell->Size) {
// Crack the cell, use part we need, put rest on
// free list.
Me = HvpGetCellMap(Hive, cell);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,cell);
Bin = (PHBIN)((Me->BinAddress) & HMAP_BASE);
offset = (ULONG)((ULONG_PTR)pcell - (ULONG_PTR)Bin);
ptcell = (PHCELL)((PUCHAR)pcell + NewSize);
if (USE_OLD_CELL(Hive)) {
ptcell->u.OldCell.Last = offset;
}
ptcell->Size = pcell->Size - NewSize;
if ((offset + pcell->Size) < Bin->Size) {
next = (PHCELL)((PUCHAR)pcell + pcell->Size);
if (USE_OLD_CELL(Hive)) {
next->u.OldCell.Last = offset + NewSize;
}
}
pcell->Size = NewSize;
tcell = (HCELL_INDEX)((ULONG)cell + NewSize);
HvpEnlistFreeCell(Hive, tcell, ptcell->Size, Type, TRUE,NULL);
}
// return the cell we found.
#if DBG
if (USE_OLD_CELL(Hive)) {
RtlFillMemory(
&(pcell->u.OldCell.u.UserData),
(pcell->Size - FIELD_OFFSET(HCELL, u.OldCell.u.UserData)),
HCELL_ALLOCATE_FILL
);
} else {
RtlFillMemory(
&(pcell->u.NewCell.u.UserData),
(pcell->Size - FIELD_OFFSET(HCELL, u.NewCell.u.UserData)),
HCELL_ALLOCATE_FILL
);
}
#endif
pcell->Size *= -1;
return cell;
}
VOID
HvFreeCell(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Frees the storage for a cell.
NOTE: CALLER is expected to mark relevent data dirty, so as to
allow this call to always succeed.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - HCELL_INDEX of Cell to free.
Return Value:
FALSE - failed, presumably for want of log space.
TRUE - it worked
--*/
{
PHBIN Bin;
PHCELL tmp;
HCELL_INDEX newfreecell;
PHCELL freebase;
ULONG savesize;
PHCELL neighbor;
ULONG Type;
PHMAP_ENTRY Me;
CMLOG(CML_MINOR, CMS_HIVE) {
KdPrint(("HvFreeCell:\n"));
KdPrint(("\tHive=%08lx Cell=%08lx\n",Hive,Cell));
}
ASSERT(Hive->ReadOnly == FALSE);
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
// Get sizes and addresses
Me = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Me,Hive,Cell);
Type = HvGetCellType(Cell);
Bin = (PHBIN)((Me->BinAddress) & HMAP_BASE);
DHvCheckBin(Hive,Bin);
freebase = HvpGetHCell(Hive, Cell);
// go do actual frees, cannot fail from this point on
ASSERT(freebase->Size < 0);
freebase->Size *= -1;
savesize = freebase->Size;
// Look for free neighbors and coalesce them. We will never travel
// around this loop more than twice.
while (
HvpIsFreeNeighbor(
Hive,
Bin,
freebase,
&neighbor,
Type
) == TRUE
)
{
if (neighbor > freebase) {
// Neighboring free cell is immediately above us in memory.
if (USE_OLD_CELL(Hive)) {
tmp = (PHCELL)((PUCHAR)neighbor + neighbor->Size);
if ( ((ULONG)((ULONG_PTR)tmp - (ULONG_PTR)Bin)) < Bin->Size) {
tmp->u.OldCell.Last = (ULONG)((ULONG_PTR)freebase - (ULONG_PTR)Bin);
}
}
freebase->Size += neighbor->Size;
} else {
// Neighboring free cell is immediately below us in memory.
if (USE_OLD_CELL(Hive)) {
tmp = (PHCELL)((PUCHAR)freebase + freebase->Size);
if ( ((ULONG)((ULONG_PTR)tmp - (ULONG_PTR)Bin)) < Bin->Size ) {
tmp->u.OldCell.Last = (ULONG)((ULONG_PTR)neighbor - (ULONG_PTR)Bin);
}
}
neighbor->Size += freebase->Size;
freebase = neighbor;
}
}
// freebase now points to the biggest free cell we could make, none
// of which is on the free list. So put it on the list.
newfreecell = (Bin->FileOffset) +
((ULONG)((ULONG_PTR)freebase - (ULONG_PTR)Bin)) +
(Type*HCELL_TYPE_MASK);
ASSERT(HvpGetHCell(Hive, newfreecell) == freebase);
#if DBG
if (USE_OLD_CELL(Hive)) {
RtlFillMemory(
&(freebase->u.OldCell.u.UserData),
(freebase->Size - FIELD_OFFSET(HCELL, u.OldCell.u.UserData)),
HCELL_FREE_FILL
);
} else {
RtlFillMemory(
&(freebase->u.NewCell.u.UserData),
(freebase->Size - FIELD_OFFSET(HCELL, u.NewCell.u.UserData)),
HCELL_FREE_FILL
);
}
#endif
HvpEnlistFreeCell(Hive, newfreecell, freebase->Size, Type, TRUE,NULL);
return;
}
BOOLEAN
HvpIsFreeNeighbor(
PHHIVE Hive,
PHBIN Bin,
PHCELL FreeCell,
PHCELL *FreeNeighbor,
HSTORAGE_TYPE Type
)
/*++
Routine Description:
Reports on whether FreeCell has at least one free neighbor and
if so where. Free neighbor will be cut out of the free list.
Arguments:
Hive - hive we're working on
Bin - pointer to the storage bin
FreeCell - supplies a pointer to a cell that has been freed, or
the result of a coalesce.
FreeNeighbor - supplies a pointer to a variable to receive the address
of a free neigbhor of FreeCell, if such exists
Type - storage type of the cell
Return Value:
TRUE if a free neighbor was found, else false.
--*/
{
PHCELL ptcell;
CMLOG(CML_MINOR, CMS_HIVE) {
KdPrint(("HvpIsFreeNeighbor:\n\tBin=%08lx",Bin));
KdPrint(("FreeCell=%08lx\n", FreeCell));
}
ASSERT(Hive->ReadOnly == FALSE);
// Neighbor above us?
*FreeNeighbor = NULL;
ptcell = (PHCELL)((PUCHAR)FreeCell + FreeCell->Size);
ASSERT( ((ULONG)((ULONG_PTR)ptcell - (ULONG_PTR)Bin)) <= Bin->Size);
if (((ULONG)((ULONG_PTR)ptcell - (ULONG_PTR)Bin)) < Bin->Size) {
if (ptcell->Size > 0) {
*FreeNeighbor = ptcell;
goto FoundNeighbor;
}
}
// Neighbor below us?
if (USE_OLD_CELL(Hive)) {
if (FreeCell->u.OldCell.Last != HBIN_NIL) {
ptcell = (PHCELL)((PUCHAR)Bin + FreeCell->u.OldCell.Last);
if (ptcell->Size > 0) {
*FreeNeighbor = ptcell;
goto FoundNeighbor;
}
}
} else {
ptcell = (PHCELL)(Bin+1);
while (ptcell < FreeCell) {
// scan through the cells from the start of the bin looking for neighbor.
if (ptcell->Size > 0) {
if ((PHCELL)((PUCHAR)ptcell + ptcell->Size) == FreeCell) {
*FreeNeighbor = ptcell;
// Try and mark it dirty, since we will be changing
// the size field. If this fails, ignore
// the free neighbor, we will not fail the free
// just because we couldn't mark the cell dirty
// so it could be coalesced.
// Note we only bother doing this for new hives,
// for old format hives we always mark the whole
// bin dirty.
if ((Type == Volatile) ||
(HvMarkCellDirty(Hive, (ULONG)((ULONG_PTR)ptcell-(ULONG_PTR)Bin) + Bin->FileOffset))) {
goto FoundNeighbor;
} else {
return(FALSE);
}
} else {
ptcell = (PHCELL)((PUCHAR)ptcell + ptcell->Size);
}
} else {
ptcell = (PHCELL)((PUCHAR)ptcell - ptcell->Size);
}
}
}
return(FALSE);
FoundNeighbor:
HvpDelistFreeCell(Hive, *FreeNeighbor, Type, NULL);
return TRUE;
}
VOID
HvpEnlistFreeCell(
PHHIVE Hive,
HCELL_INDEX Cell,
ULONG Size,
HSTORAGE_TYPE Type,
BOOLEAN CoalesceForward,
PHCELL_INDEX TailDisplay OPTIONAL
)
/*++
Routine Description:
Puts the newly freed cell on the appropriate list.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - supplies index of cell to enlist
Size - size of cell
Type - indicates whether Stable or Volatile storage is desired.
CoalesceForward - indicates whether we can coalesce forward or not.
For the case where we have not finished scanning the hive and
enlisting free cells, we do not want to coalesce forward.
Return Value:
NONE.
--*/
{
PHCELL_INDEX Last;
PHCELL_INDEX First;
PHMAP_ENTRY Map;
PHCELL pcell;
PHCELL pcellLast;
PHCELL FirstCell;
ULONG Index;
PHBIN Bin;
HCELL_INDEX FreeCell;
PFREE_HBIN FreeBin;
PHBIN FirstBin;
PHBIN LastBin;
HvpComputeIndex(Index, Size);
First = &(Hive->Storage[Type].FreeDisplay[Index]);
pcell = HvpGetHCell(Hive, Cell);
ASSERT(pcell->Size > 0);
ASSERT(Size == (ULONG)pcell->Size);
if (ARGUMENT_PRESENT(TailDisplay)) {
// This should only happen once, at boot time; Then, we want to sort the list ascedending
Last = &TailDisplay[Index];
if( *Last != HCELL_NIL ) {
// there is a last cell; insert current cell right after it.
// and set the next cell pointer for the current cell to NIL
pcellLast = HvpGetHCell(Hive,*Last);
ASSERT(pcellLast->Size > 0);
if (USE_OLD_CELL(Hive)) {
pcellLast->u.OldCell.u.Next = Cell;
pcell->u.OldCell.u.Next = HCELL_NIL;
} else {
pcellLast->u.NewCell.u.Next = Cell;
pcell->u.NewCell.u.Next = HCELL_NIL;
}
} else {
// No Last cell; Insert it at the begining and set the last cell pointing to it as well
// Sanity check: First cell should be also NIL
ASSERT( *First == HCELL_NIL );
if (USE_OLD_CELL(Hive)) {
pcell->u.OldCell.u.Next = *First;
} else {
pcell->u.NewCell.u.Next = *First;
}
*First = Cell;
}
// The current cell becomes the last cell
*Last = Cell;
} else {
// Normal case, insert the cell at the begining of the list (speed reasons).
if (USE_OLD_CELL(Hive)) {
pcell->u.OldCell.u.Next = *First;
} else {
pcell->u.NewCell.u.Next = *First;
}
*First = Cell;
}
// Check to see if this is the first cell in the bin and if the entire
// bin consists just of this cell.
Map = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Map,Hive,Cell);
Bin = (PHBIN)(Map->BinAddress & ~HMAP_NEWALLOC);
if ((pcell == (PHCELL)(Bin + 1)) &&
(Size == Bin->Size-sizeof(HBIN))) {
// We have a bin that is entirely free. But we cannot do anything with it
// unless the memalloc that contains the bin is entirely free. Walk the
// bins backwards until we find the first one in the alloc, then walk forwards
// until we find the last one. If any of the other bins in the memalloc
// are not free, bail out.
FirstBin = Bin;
while (FirstBin->MemAlloc == 0) {
Map=HvpGetCellMap(Hive,(FirstBin->FileOffset - HBLOCK_SIZE) +
(Type * HCELL_TYPE_MASK));
VALIDATE_CELL_MAP(__LINE__,Map,Hive,(FirstBin->FileOffset - HBLOCK_SIZE) +(Type * HCELL_TYPE_MASK));
FirstBin = (PHBIN)(Map->BinAddress & HMAP_BASE);
FirstCell = (PHCELL)(FirstBin+1);
if ((ULONG)(FirstCell->Size) != FirstBin->Size-sizeof(HBIN)) {
// The first cell in the bin is either allocated, or not the only
// cell in the HBIN. We cannot free any HBINs.
goto Done;
}
}
// We can never discard the first bin of a hive as that always gets marked dirty
// and written out.
if (FirstBin->FileOffset == 0) {
goto Done;
}
LastBin = Bin;
while (LastBin->FileOffset+LastBin->Size < FirstBin->FileOffset+FirstBin->MemAlloc) {
if (!CoalesceForward) {
// We are at the end of what's been built up. Just return and this
// will get freed up when the next HBIN is added.
goto Done;
}
Map = HvpGetCellMap(Hive, (LastBin->FileOffset+LastBin->Size) +
(Type * HCELL_TYPE_MASK));
VALIDATE_CELL_MAP(__LINE__,Map,Hive,(LastBin->FileOffset+LastBin->Size) + (Type * HCELL_TYPE_MASK));
ASSERT(Map->BinAddress != 0);
LastBin = (PHBIN)(Map->BinAddress & HMAP_BASE);
FirstCell = (PHCELL)(LastBin + 1);
if ((ULONG)(FirstCell->Size) != LastBin->Size-sizeof(HBIN)) {
// The first cell in the bin is either allocated, or not the only
// cell in the HBIN. We cannot free any HBINs.
goto Done;
}
}
// All the bins in this alloc are freed. Coalesce all the bins into
// one alloc-sized bin, then either discard the bin or mark it as
// discardable.
if (FirstBin->Size != FirstBin->MemAlloc) {
// Mark the first HBLOCK of the first HBIN dirty, since
// we will need to update the on disk field for the bin size
if (!HvMarkDirty(Hive,
FirstBin->FileOffset + (Type * HCELL_TYPE_MASK),
sizeof(HBIN) + sizeof(HCELL))) {
goto Done;
}
}
FreeBin = (Hive->Allocate)(sizeof(FREE_HBIN), FALSE);
if (FreeBin == NULL) {
goto Done;
}
// Walk through the bins and delist each free cell
Bin = FirstBin;
do {
FirstCell = (PHCELL)(Bin+1);
HvpDelistFreeCell(Hive, FirstCell, Type, TailDisplay);
if (Bin==LastBin) {
break;
}
Map = HvpGetCellMap(Hive, (Bin->FileOffset+Bin->Size)+
(Type * HCELL_TYPE_MASK));
VALIDATE_CELL_MAP(__LINE__,Map,Hive,(Bin->FileOffset+Bin->Size)+(Type * HCELL_TYPE_MASK));
Bin = (PHBIN)(Map->BinAddress & HMAP_BASE);
} while ( TRUE );
// Coalesce them all into one bin.
FirstBin->Size = FirstBin->MemAlloc;
FreeBin->Size = FirstBin->Size;
FreeBin->FileOffset = FirstBin->FileOffset;
FirstCell = (PHCELL)(FirstBin+1);
FirstCell->Size = FirstBin->Size - sizeof(HBIN);
if (USE_OLD_CELL(Hive)) {
FirstCell->u.OldCell.Last = (ULONG)HBIN_NIL;
}
InsertHeadList(&Hive->Storage[Type].FreeBins, &FreeBin->ListEntry);
ASSERT_LISTENTRY(&FreeBin->ListEntry);
ASSERT_LISTENTRY(FreeBin->ListEntry.Flink);
FreeCell = FirstBin->FileOffset+(Type*HCELL_TYPE_MASK);
FreeBin->Flags = FREE_HBIN_DISCARDABLE;
while (FreeCell-FirstBin->FileOffset < FirstBin->Size) {
Map = HvpGetCellMap(Hive, FreeCell);
VALIDATE_CELL_MAP(__LINE__,Map,Hive,FreeCell);
if (Map->BinAddress & HMAP_NEWALLOC) {
Map->BinAddress = (ULONG_PTR)FirstBin | HMAP_DISCARDABLE | HMAP_NEWALLOC;
} else {
Map->BinAddress = (ULONG_PTR)FirstBin | HMAP_DISCARDABLE;
}
Map->BlockAddress = (ULONG_PTR)FreeBin;
FreeCell += HBLOCK_SIZE;
}
}
Done:
Hive->Storage[Type].FreeSummary |= (1 << Index);
return;
}
VOID
HvpDelistFreeCell(
PHHIVE Hive,
PHCELL Pcell,
HSTORAGE_TYPE Type,
PHCELL_INDEX TailDisplay OPTIONAL
)
/*++
Routine Description:
Removes a free cell from its list, and clears the Summary
bit for it if need be.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Pcell - supplies a pointer to the HCELL structure of interest
Type - Stable vs. Volatile
Return Value:
NONE.
--*/
{
PHCELL_INDEX List;
HCELL_INDEX Prev = HCELL_NIL;
ULONG Index;
HvpComputeIndex(Index, Pcell->Size);
List = &(Hive->Storage[Type].FreeDisplay[Index]);
// Find previous cell on list
ASSERT(*List != HCELL_NIL);
while (HvpGetHCell(Hive,*List) != Pcell) {
Prev = *List;
List = (PHCELL_INDEX)HvGetCell(Hive,*List);
ASSERT(*List != HCELL_NIL);
}
if (ARGUMENT_PRESENT(TailDisplay)) {
// This should only happen once, at boot time; Then, we want to sort the list ascedending
if( *List == TailDisplay[Index] ) {
// this cell is also the last cell on list; so it should be reset
#if DBG
// consistency checks
if (USE_OLD_CELL(Hive)) {
ASSERT(Pcell->u.OldCell.u.Next == HCELL_NIL);
} else {
ASSERT(Pcell->u.NewCell.u.Next == HCELL_NIL);
}
#endif
TailDisplay[Index] = Prev;
}
}
// Remove Pcell from list.
if (USE_OLD_CELL(Hive)) {
*List = Pcell->u.OldCell.u.Next;
} else {
*List = Pcell->u.NewCell.u.Next;
}
if (Hive->Storage[Type].FreeDisplay[Index] == HCELL_NIL) {
// consistency check
ASSERT(Prev == HCELL_NIL);
Hive->Storage[Type].FreeSummary &= ~(1 << Index);
}
return;
}
HCELL_INDEX
HvReallocateCell(
PHHIVE Hive,
HCELL_INDEX Cell,
ULONG NewSize
)
/*++
Routine Description:
Grows or shrinks a cell.
NOTE:
MUST NOT FAIL if the cell is being made smaller. Can be
a noop, but must work.
WARNING:
If the cell is grown, it will get a NEW and DIFFERENT HCELL_INDEX!!!
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Cell - supplies index of cell to grow or shrink
NewSize - desired size of cell (this is an absolute size, not an
increment or decrement.)
Return Value:
New HCELL_INDEX for cell, or HCELL_NIL if failure.
If return is HCELL_NIL, either old cell did not exist, or it did exist
and we could not make a new one. In either case, nothing has changed.
If return is NOT HCELL_NIL, then it is the HCELL_INDEX for the Cell,
which very probably moved.
--*/
{
PUCHAR oldaddress;
LONG oldsize;
ULONG oldalloc;
HCELL_INDEX NewCell; // return value
PUCHAR newaddress;
ULONG Type;
CMLOG(CML_MAJOR, CMS_HIVE) {
KdPrint(("HvReallocateCell:\n"));
KdPrint(("\tHive=%08lx Cell=%08lx NewSize=%08lx\n",Hive,Cell,NewSize));
}
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
ASSERT(Hive->ReadOnly == FALSE);
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
// Make room for overhead fields and round up to HCELL_PAD boundary
if (USE_OLD_CELL(Hive)) {
NewSize += FIELD_OFFSET(HCELL, u.OldCell.u.UserData);
} else {
NewSize += FIELD_OFFSET(HCELL, u.NewCell.u.UserData);
}
NewSize = ROUND_UP(NewSize, HCELL_PAD(Hive));
// Adjust the size (an easy fix for granularity)
HvpAdjustCellSize(NewSize);
// reject impossible/unreasonable values
if (NewSize > HSANE_CELL_MAX) {
CMLOG(CML_FLOW, CMS_HIVE) {
KdPrint(("\tNewSize=%08lx\n", NewSize));
}
return HCELL_NIL;
}
// Get sizes and addresses
oldaddress = (PUCHAR)HvGetCell(Hive, Cell);
oldsize = HvGetCellSize(Hive, oldaddress);
ASSERT(oldsize > 0);
if (USE_OLD_CELL(Hive)) {
oldalloc = (ULONG)(oldsize + FIELD_OFFSET(HCELL, u.OldCell.u.UserData));
} else {
oldalloc = (ULONG)(oldsize + FIELD_OFFSET(HCELL, u.NewCell.u.UserData));
}
Type = HvGetCellType(Cell);
DHvCheckHive(Hive);
if (NewSize == oldalloc) {
// This is a noop, return the same cell
NewCell = Cell;
} else if (NewSize < oldalloc) {
// This is a shrink.
// PERFNOTE - IMPLEMENT THIS. Do nothing for now.
NewCell = Cell;
} else {
// This is a grow.
// PERFNOTE - Someday we want to detect that there is a free neighbor
// above us and grow into that neighbor if possible.
// For now, always do the allocate, copy, free gig.
// Allocate a new block of memory to hold the cell
if ((NewCell = HvpDoAllocateCell(Hive, NewSize, Type)) == HCELL_NIL) {
return HCELL_NIL;
}
ASSERT(HvIsCellAllocated(Hive, NewCell));
newaddress = (PUCHAR)HvGetCell(Hive, NewCell);
// oldaddress points to the old data block for the cell,
// newaddress points to the new data block, copy the data
RtlMoveMemory(newaddress, oldaddress, oldsize);
// Free the old block of memory
HvFreeCell(Hive, Cell);
}
DHvCheckHive(Hive);
return NewCell;
}
// Procedure used for checking only (used in production systems, so
// must always be here.)
BOOLEAN
HvIsCellAllocated(
PHHIVE Hive,
HCELL_INDEX Cell
)
/*++
Routine Description:
Report whether the requested cell is allocated or not.
Arguments:
Hive - containing Hive.
Cell - cel of interest
Return Value:
TRUE if allocated, FALSE if not.
--*/
{
ULONG Type;
PHCELL Address;
PHCELL Below;
PHMAP_ENTRY Me;
PHBIN Bin;
ULONG Offset;
LONG Size;
ASSERT(Hive->Signature == HHIVE_SIGNATURE);
if (Hive->Flat == TRUE) {
return TRUE;
}
Type = HvGetCellType(Cell);
if ( ((Cell & ~HCELL_TYPE_MASK) > Hive->Storage[Type].Length) || // off end
(Cell % HCELL_PAD(Hive) != 0) // wrong alignment
)
{
return FALSE;
}
Address = HvpGetHCell(Hive, Cell);
Me = HvpGetCellMap(Hive, Cell);
if (Me == NULL) {
return FALSE;
}
Bin = (PHBIN)((ULONG_PTR)(Me->BinAddress) & HMAP_BASE);
Offset = (ULONG)((ULONG_PTR)Address - (ULONG_PTR)Bin);
Size = Address->Size * -1;
if ( (Address->Size > 0) || // not allocated
((Offset + (ULONG)Size) > Bin->Size) || // runs off bin, or too big
(Offset < sizeof(HBIN)) // pts into bin header
)
{
return FALSE;
}
if (USE_OLD_CELL(Hive)) {
if (Address->u.OldCell.Last != HBIN_NIL) {
if (Address->u.OldCell.Last > Bin->Size) { // bogus back pointer
return FALSE;
}
Below = (PHCELL)((PUCHAR)Bin + Address->u.OldCell.Last);
Size = (Below->Size < 0) ?
Below->Size * -1 :
Below->Size;
if ( ((ULONG_PTR)Below + Size) != (ULONG_PTR)Address ) { // no pt back
return FALSE;
}
}
}
return TRUE;
}
VOID
HvpDelistBinFreeCells(
PHHIVE Hive,
PHBIN Bin,
HSTORAGE_TYPE Type,
PHCELL_INDEX TailDisplay OPTIONAL
)
/*++
Routine Description:
If we are here, the hive needs recovery.
Walks through the entire bin and removes its free cells from the list.
If the bin is marked as free, it just delist it from the freebins list.
Arguments:
Hive - supplies a pointer to the hive control structure for the
hive of interest
Bin - supplies a pointer to the HBIN of interest
Type - Stable vs. Volatile
Return Value:
NONE.
--*/
{
PHCELL p;
ULONG size;
HCELL_INDEX Cell;
PHMAP_ENTRY Map;
PFREE_HBIN FreeBin;
PLIST_ENTRY Entry;
Cell = Bin->FileOffset+(Type*HCELL_TYPE_MASK);
Map = HvpGetCellMap(Hive, Cell);
VALIDATE_CELL_MAP(__LINE__,Map,Hive,Cell);
// When loading, bins are always in separate chunks (each bin in it's owns chunk)
ASSERT( Map->BinAddress == ((ULONG_PTR)Bin | HMAP_NEWALLOC) );
if( Map->BinAddress & HMAP_DISCARDABLE ) {
// The bin has been added to the freebins list
// we have to take it out. No free cell from this bin is on the
// freecells list, so we don't have to delist them.
Entry = Hive->Storage[Type].FreeBins.Flink;
while (Entry != &Hive->Storage[Type].FreeBins) {
FreeBin = CONTAINING_RECORD(Entry,
FREE_HBIN,
ListEntry);
if( FreeBin->FileOffset == Bin->FileOffset ){
// that's the bin we're looking for
// sanity checks
ASSERT( FreeBin->Size == Bin->Size );
ASSERT_LISTENTRY(&FreeBin->ListEntry);
RemoveEntryList(&FreeBin->ListEntry);
(Hive->Free)(FreeBin, sizeof(FREE_HBIN));
return;
}
// advance to the new bin
Entry = Entry->Flink;
}
// we shouldn't get here
CM_BUGCHECK(REGISTRY_ERROR,14,(ULONG)Cell,(ULONG_PTR)Map,0);
return;
}
// Scan all the cells in the bin, total free and allocated, check
// for impossible pointers.
p = (PHCELL)((PUCHAR)Bin + sizeof(HBIN));
while (p < (PHCELL)((PUCHAR)Bin + Bin->Size)) {
// if free cell, remove it from the list of the hive
if (p->Size >= 0) {
size = (ULONG)p->Size;
// Enlist this free cell, but do not coalesce with the next free cell
// as we haven't gotten that far yet.
HvpDelistFreeCell(Hive, p, Type, TailDisplay);
} else {
size = (ULONG)(p->Size * -1);
}
ASSERT(size >= 0);
p = (PHCELL)((PUCHAR)p + size);
}
return;
}