/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    hivefree.c

Abstract:

    Hive free code


Revision History:

    Implementation of bin-size chunk loading of hives.

--*/

#include "cmp.h"

#define LogHiveFree(_hive_) //nothing

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HvFreeHive)
#pragma alloc_text(PAGE,HvFreeHivePartial)
#endif

VOID
HvFreeHive(
    PHHIVE Hive
    )
/*++

Routine Description:

    Free all of the pieces of a hive.

Arguments:

    Hive - supplies a pointer to hive control structure for hive to free.
            this structure itself will NOT be freed, but everything it
            points to will.

Return Value:

    NONE.

--*/
{
    PHMAP_DIRECTORY Dir;
    PHMAP_ENTRY     Me;
    HCELL_INDEX     Address;
    ULONG           Type;
    ULONG           Length;
    PHBIN           Bin;
    ULONG           Tables;
    PFREE_HBIN      FreeBin;

    ASSERT(Hive->Flat == FALSE);
    ASSERT(Hive->ReadOnly == FALSE);
    ASSERT(Stable == 0);
    ASSERT(Volatile == 1);

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvFreeHive(%ws) :\n", Hive->BaseBlock->FileName));
    //
    // Iterate through both types of storage
    //
    for (Type = 0; Type <= Volatile; Type++) {

        Address = HCELL_TYPE_MASK * Type;
        Length = Hive->Storage[Type].Length + (HCELL_TYPE_MASK * Type);

        if( Hive->Storage[Type].Map && (Length > (HCELL_TYPE_MASK * Type)) ) {

            //
            // Sweep through bin set
            //
            do {
                Me = HvpGetCellMap(Hive, Address);
                VALIDATE_CELL_MAP(__LINE__,Me,Hive,Address);
                    if (Me->BinAddress & HMAP_DISCARDABLE) {
                        //
                        // hbin is either discarded or discardable, check the tombstone
                        //
                        FreeBin = (PFREE_HBIN)Me->BlockAddress;
                        Address += FreeBin->Size;
                        if (FreeBin->Flags & FREE_HBIN_DISCARDABLE) {
                            CmpFree((PHBIN)HBIN_BASE(Me->BinAddress), FreeBin->Size);
                        } else if(Me->BinAddress & HMAP_INPAGEDPOOL) {
                            //
                            // The bin has been freed, but quota is still charged.
                            // Since the hive is being freed, the quota must be
                            // returned here.
                            //
                            CmpReleaseGlobalQuota(FreeBin->Size);
                        }

                        CmpFree(FreeBin, sizeof(FREE_HBIN));
                    } else {
                        if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
                            ASSERT( Me->BinAddress & HMAP_INPAGEDPOOL );

                            Bin = (PHBIN)HBIN_BASE(Me->BinAddress);
                            Address += HvpGetBinMemAlloc(Hive,Bin,Type);
#if DBG
                            if( Type == Stable ) {
                                CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"HvFreeHive: BinAddress = 0x%p\t Size = 0x%lx\n", Bin, HvpGetBinMemAlloc(Hive,Bin,Type)));
                            }
#endif

                            //
                            // free the actual bin only if it is allocated from paged pool
                            //
                            if(HvpGetBinMemAlloc(Hive,Bin,Type)) {
                                CmpFree(Bin, HvpGetBinMemAlloc(Hive,Bin,Type));
                            }
                        } else {
                            //
                            // bin was mapped into view; advance carefully
                            //
                            Address += HBLOCK_SIZE;
                        }

                    }
            } while (Address < Length);

            //
            // Free map table storage
            //
            ASSERT(Hive->Storage[Type].Length != (HCELL_TYPE_MASK * Type));
            Tables = (((Hive->Storage[Type].Length) / HBLOCK_SIZE)-1) / HTABLE_SLOTS;
            Dir = Hive->Storage[Type].Map;
            HvpFreeMap(Hive, Dir, 0, Tables);

            if (Tables > 0) {
                CmpFree(Hive->Storage[Type].Map, sizeof(HMAP_DIRECTORY));  // free dir if it exists
            }
        }
        Hive->Storage[Type].Length = 0;
    }

    CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"\n"));
    //
    // Free the base block
    //
    (Hive->Free)(Hive->BaseBlock,Hive->BaseBlockAlloc);
    Hive->BaseBlock = NULL;
    
    //
    // Free the dirty vector
    //
    if (Hive->DirtyVector.Buffer != NULL) {
        CmpFree((PVOID)(Hive->DirtyVector.Buffer), Hive->DirtyAlloc);
    }

    HvpFreeHiveFreeDisplay(Hive);
    LogHiveFree(Hive);
    return;
}

VOID
HvFreeHivePartial(
    PHHIVE      Hive,
    HCELL_INDEX Start,
    HSTORAGE_TYPE Type
    )
/*++

Routine Description:

    Free the memory and associated maps for the end of a hive
    starting at Start.  The baseblock, hive, etc will not be touched.

Arguments:

    Hive - supplies a pointer to hive control structure for hive to
            partially free.

    Start - HCELL_INDEX of first bin to free, will free from this
            bin (inclusive) to the end of the hives stable storage.

    Type - Type of storage (Stable or Volatile) to be freed.

Return Value:

    NONE.

--*/
{
    PHMAP_DIRECTORY Dir;
    PHMAP_ENTRY     Me;
    HCELL_INDEX     Address;
    ULONG           StartTable;
    ULONG           Length;
    PHBIN           Bin;
    ULONG           Tables;
    ULONG           FirstBit;
    ULONG           LastBit;
    PFREE_HBIN      FreeBin;

    ASSERT(Hive->Flat == FALSE);
    ASSERT(Hive->ReadOnly == FALSE);

    Address = Start;
    Length = Hive->Storage[Type].Length;
    ASSERT(Address <= Length);

    if (Address == Length) {
        return;
    }

    //
    // Sweep through bin set
    //
    do {
        Me = HvpGetCellMap(Hive, Address + (Type*HCELL_TYPE_MASK));
        VALIDATE_CELL_MAP(__LINE__,Me,Hive,Address + (Type*HCELL_TYPE_MASK));
        if (Me->BinAddress & HMAP_DISCARDABLE) {
            FreeBin = (PFREE_HBIN)Me->BlockAddress;
            if (FreeBin->Flags & FREE_HBIN_DISCARDABLE) {
                CmpFree((PVOID)HBIN_BASE(Me->BinAddress), FreeBin->Size);
            } else {
                //
                // The bin has been freed, but quota is still charged.
                // Since the file will now shrink, the quota must be
                // returned here.
                //
                if( Me->BinAddress & HMAP_INPAGEDPOOL) {
                    //
                    // we charge quota only for bins in paged-pool
                    //
                    CmpReleaseGlobalQuota(FreeBin->Size);
                }
            }
            RemoveEntryList(&FreeBin->ListEntry);
            Address += FreeBin->Size;
            CmpFree(FreeBin, sizeof(FREE_HBIN));

        } else {
            Bin = (PHBIN)HBIN_BASE(Me->BinAddress);
            Address += HvpGetBinMemAlloc(Hive,Bin,Type);
            
            if( Me->BinAddress & HMAP_INPAGEDPOOL && HvpGetBinMemAlloc(Hive,Bin,Type) ) {
                //
                // free the bin only if it is allocated from paged pool
                //
                CmpFree(Bin, HvpGetBinMemAlloc(Hive,Bin,Type));
            }
        }
    } while (Address < Length);

    //
    // Free map table storage
    //
    Tables = (((Hive->Storage[Type].Length) / HBLOCK_SIZE) - 1) / HTABLE_SLOTS;
    Dir = Hive->Storage[Type].Map;
    if (Start > 0) {
        StartTable = ((Start-1) / HBLOCK_SIZE) / HTABLE_SLOTS;
    } else {
        StartTable = (ULONG)-1;
    }
    HvpFreeMap(Hive, Dir, StartTable+1, Tables);

    //
    // update hysteresis (eventually queue work item)
    //
    if( Type == Stable) {
        CmpUpdateSystemHiveHysteresis(Hive,(Start&(~HCELL_TYPE_MASK)),Hive->Storage[Type].Length);
    }

    Hive->Storage[Type].Length = (Start&(~HCELL_TYPE_MASK));

    if (Type==Stable) {
        //
        // Clear dirty vector for data past Hive->Storage[Stable].Length
        //
        FirstBit = Start / HSECTOR_SIZE;
        LastBit = Hive->DirtyVector.SizeOfBitMap;
        ASSERT(Hive->DirtyCount == RtlNumberOfSetBits(&Hive->DirtyVector));
        RtlClearBits(&Hive->DirtyVector, FirstBit, LastBit-FirstBit);
        Hive->DirtyCount = RtlNumberOfSetBits(&Hive->DirtyVector);
    }

    HvpAdjustHiveFreeDisplay(Hive,Hive->Storage[Type].Length,Type);

    return;
}

