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

1384 lines
35 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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:
--*/
#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
);
#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; \
} \
} \
}
#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)
#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)(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));
//
// reject impossible/unreasonable values
//
if (NewSize >= HSANE_CELL_MAX) {
return HCELL_NIL;
}
if (NewSize > HCELL_BIG_ROUND) {
NewSize = ROUND_UP(NewSize, HBLOCK_SIZE);
}
//
// 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);
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);
ASSERT(Me != NULL);
Bin = (PHBIN)((ULONG)(Me->BinAddress) & HMAP_BASE);
offset = (ULONG)pcell - (ULONG)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);
}
//
// return the cell we found.
//
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
);
}
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);
ASSERT(Me != NULL);
Type = HvGetCellType(Cell);
Bin = (PHBIN)((ULONG)(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)tmp - (ULONG)Bin) < Bin->Size) {
tmp->u.OldCell.Last = (ULONG)freebase - (ULONG)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)tmp - (ULONG)Bin) < Bin->Size ) {
tmp->u.OldCell.Last = (ULONG)neighbor - (ULONG)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)freebase - (ULONG)Bin) +
(Type*HCELL_TYPE_MASK);
ASSERT(HvpGetHCell(Hive, newfreecell) == freebase);
HvpEnlistFreeCell(Hive, newfreecell, freebase->Size, Type);
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)ptcell - (ULONG)Bin) <= Bin->Size);
if (((ULONG)ptcell - (ULONG)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)ptcell-(ULONG)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);
return TRUE;
}
VOID
HvpEnlistFreeCell(
PHHIVE Hive,
HCELL_INDEX Cell,
ULONG Size,
HSTORAGE_TYPE Type
)
/*++
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.
Return Value:
NONE.
--*/
{
PHCELL_INDEX Next;
PHMAP_ENTRY Map;
PHCELL pcell;
PHCELL FirstCell;
ULONG Index;
PHBIN Bin;
HCELL_INDEX FreeCell;
PFREE_HBIN FreeBin;
PHBIN FirstBin;
PHBIN LastBin;
HvpComputeIndex(Index, Size);
Next = &(Hive->Storage[Type].FreeDisplay[Index]);
pcell = HvpGetHCell(Hive, Cell);
ASSERT(pcell->Size > 0);
ASSERT(Size == (ULONG)pcell->Size);
if (USE_OLD_CELL(Hive)) {
pcell->u.OldCell.u.Next = *Next;
} else {
pcell->u.NewCell.u.Next = *Next;
}
*Next = 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);
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));
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;
}
}
LastBin = Bin;
while (LastBin->FileOffset+LastBin->Size < FirstBin->FileOffset+FirstBin->MemAlloc) {
Map = HvpGetCellMap(Hive, (LastBin->FileOffset+LastBin->Size) +
(Type * HCELL_TYPE_MASK));
if (Map->BinAddress==0) {
//
// Map is still being built up and 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;
}
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.
//
//
// Mark the first HBLOCK of the first HBIN dirty, since
// we may 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);
if (Bin==LastBin) {
break;
}
Map = HvpGetCellMap(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);
if (Map->BinAddress | HMAP_NEWALLOC) {
Map->BinAddress = (ULONG)FirstBin | HMAP_DISCARDABLE | HMAP_NEWALLOC;
} else {
Map->BinAddress = (ULONG)FirstBin | HMAP_DISCARDABLE;
}
Map->BlockAddress = (ULONG)FreeBin;
FreeCell += HBLOCK_SIZE;
}
}
Done:
Hive->Storage[Type].FreeSummary |= (1 << Index);
return;
}
VOID
HvpDelistFreeCell(
PHHIVE Hive,
PHCELL Pcell,
HSTORAGE_TYPE Type
)
/*++
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;
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) {
List = (PHCELL_INDEX)HvGetCell(Hive,*List);
ASSERT(*List != HCELL_NIL);
}
//
// 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) {
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.
--*/
{
PHMAP_ENTRY me;
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));
//
// reject impossible/unreasonable values
//
if (NewSize >= HSANE_CELL_MAX) {
CMLOG(CML_FLOW, CMS_HIVE) {
KdPrint(("\tNewSize=%08lx\n", NewSize));
}
return HCELL_NIL;
}
if (NewSize > HCELL_BIG_ROUND) {
NewSize = ROUND_UP(NewSize, HBLOCK_SIZE);
}
//
// Get sizes and addresses
//
me = HvpGetCellMap(Hive, Cell);
ASSERT(me != NULL);
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)(Me->BinAddress) & HMAP_BASE);
Offset = (ULONG)Address - (ULONG)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)Below + Size) != (ULONG)Address ) { // no pt back
return FALSE;
}
}
}
return TRUE;
}