// CheckBlock bool BlockAllocator::Area::CheckBlock(Block *checkBlock, size_t minSize) { for (Block *block = fFirstBlock; block; block = block->GetNextBlock()) { if (block == checkBlock) return (block->GetUsableSize() >= minSize); } FATAL(("Block %p is not in area %p!\n", checkBlock, this)); BA_PANIC("Invalid Block."); return false; }
// _MakeUsedBlock inline Block * BlockAllocator::Area::_MakeUsedBlock(void *address, ssize_t offset, Block *previous, size_t size, bool hasNext) { Block *block = (Block*)((char*)address + offset); block->SetTo(previous, size, false, hasNext, NULL); if (hasNext) block->GetNextBlock()->SetPreviousBlock(block); else fLastBlock = block; return block; }
// _Defragment void BlockAllocator::Area::_Defragment() { D(SanityCheck()); //PRINT(("BlockAllocator::Area::_Defragment()\n")); // A trivial strategy for now: Keep the last free block and move the // others so that they can be joined with it. This is done iteratively // by moving the first free block to adjoin to the second one and // coalescing them. A free block is moved by moving the data blocks in // between. TFreeBlock *nextFree = NULL; while (fFirstFree && (nextFree = fFirstFree->GetNextFreeBlock()) != NULL) { Block *prevBlock = fFirstFree->GetPreviousBlock(); Block *nextBlock = fFirstFree->GetNextBlock(); size_t size = fFirstFree->GetSize(); // Used blocks are relatively position independed. We can move them // en bloc and only need to adjust the previous pointer of the first // one. if (!nextBlock->IsFree()) { // move the used blocks size_t chunkSize = (char*)nextFree - (char*)nextBlock; Block *nextFreePrev = nextFree->GetPreviousBlock(); Block *movedBlock = fFirstFree; memmove(movedBlock, nextBlock, chunkSize); movedBlock->SetPreviousBlock(prevBlock); // init the first free block Block *movedNextFreePrev = (Block*)((char*)nextFreePrev - size); fFirstFree = _MakeFreeBlock(movedBlock, chunkSize, movedNextFreePrev, size, true, NULL, nextFree); nextFree->SetPreviousFreeBlock(fFirstFree); // fix the references of the moved blocks for (Block *block = movedBlock; block != fFirstFree; block = block->GetNextBlock()) { block->FixReference(); } } else { // uncoalesced adjoining free block: That should never happen, // since we always coalesce as early as possible. INFORM(("Warning: Found uncoalesced adjoining free blocks!\n")); } // coalesce the first two blocks D(SanityCheck()); _CoalesceWithNext(fFirstFree); D(SanityCheck()); } //D(SanityCheck()); //PRINT(("BlockAllocator::Area::_Defragment() done\n")); }
// SanityCheck bool BlockAllocator::Area::SanityCheck() const { // area ID if (fID < 0) { FATAL(("Area ID < 0: %lx\n", fID)); BA_PANIC("Bad area ID."); return false; } // size size_t areaHeaderSize = block_align_ceil(sizeof(Area)); if (fSize < areaHeaderSize + sizeof(TFreeBlock)) { FATAL(("Area too small to contain area header and at least one free " "block: %lu bytes\n", fSize)); BA_PANIC("Bad area size."); return false; } // free bytes if (fFreeBytes > fSize) { FATAL(("Free size greater than area size: %lu vs %lu\n", fFreeBytes, fSize)); BA_PANIC("Bad area free bytes."); return false; } // block count if (fFreeBlockCount + fUsedBlockCount == 0) { FATAL(("Area contains no blocks at all.\n")); BA_PANIC("Bad area block count."); return false; } // block list uint32 usedBlockCount = 0; uint32 freeBlockCount = 0; size_t freeBytes = 0; if (!fFirstBlock || !fLastBlock) { FATAL(("Invalid block list: first or last block NULL: first: %p, " "last: %p\n", fFirstBlock, fLastBlock)); BA_PANIC("Bad area block list."); return false; } else { // iterate through block list and also check free list int32 blockCount = fFreeBlockCount + fUsedBlockCount; Block *block = fFirstBlock; Block *prevBlock = NULL; Block *prevFree = NULL; Block *nextFree = fFirstFree; bool blockListOK = true; for (int32 i = 0; i < blockCount; i++) { blockListOK = false; if (!block) { FATAL(("Encountered NULL in block list at index %ld, although " "list should have %ld blocks\n", i, blockCount)); BA_PANIC("Bad area block list."); return false; } uint64 address = (uint32)block; // block within area? if (address < (uint32)this + areaHeaderSize || address + sizeof(TFreeBlock) > (uint32)this + fSize) { FATAL(("Utterly mislocated block: %p, area: %p, " "size: %lu\n", block, this, fSize)); BA_PANIC("Bad area block."); return false; } // block too large for area? size_t blockSize = block->GetSize(); if (blockSize < sizeof(TFreeBlock) || address + blockSize > (uint32)this + fSize) { FATAL(("Mislocated block: %p, size: %lu, area: %p, " "size: %lu\n", block, blockSize, this, fSize)); BA_PANIC("Bad area block."); return false; } // alignment if (block_align_floor(address) != address || block_align_floor(blockSize) != blockSize) { FATAL(("Block %ld not properly aligned: %p, size: %lu\n", i, block, blockSize)); BA_PANIC("Bad area block."); return false; } // previous block if (block->GetPreviousBlock() != prevBlock) { FATAL(("Previous block of block %ld was not the previous " "block in list: %p vs %p\n", i, block->GetPreviousBlock(), prevBlock)); BA_PANIC("Bad area block list."); return false; } // additional checks for free block list if (block->IsFree()) { freeBlockCount++; TFreeBlock *freeBlock = block->ToFreeBlock(); if (prevFree) freeBytes += freeBlock->GetSize(); else freeBytes += freeBlock->GetUsableSize(); // block == next free block of previous free block if (freeBlock != nextFree) { FATAL(("Free block %ld is not the next block in free " "list: %p vs %p\n", i, freeBlock, nextFree)); BA_PANIC("Bad area free list."); return false; } // previous free block if (freeBlock->GetPreviousFreeBlock() != prevFree) { FATAL(("Previous free block of block %ld was not the " " previous block in free list: %p vs %p\n", i, freeBlock->GetPreviousFreeBlock(), prevFree)); BA_PANIC("Bad area free list."); return false; } prevFree = freeBlock; nextFree = freeBlock->GetNextFreeBlock(); } else usedBlockCount++; prevBlock = block; block = block->GetNextBlock(); blockListOK = true; } // final checks on block list if (blockListOK) { if (block) { FATAL(("More blocks in block list than expected\n")); BA_PANIC("Bad area block count."); return false; } else if (fLastBlock != prevBlock) { FATAL(("last block in block list was %p, but should be " "%p\n", fLastBlock, prevBlock)); BA_PANIC("Bad area last block."); return false; } else if (prevFree != fLastFree) { FATAL(("last block in free list was %p, but should be %p\n", fLastFree, prevFree)); BA_PANIC("Bad area last free block."); return false; } // block counts (a bit reduntant) if (freeBlockCount != fFreeBlockCount) { FATAL(("Free block count is %ld, but should be %ld\n", fFreeBlockCount, freeBlockCount)); BA_PANIC("Bad area free block count."); return false; } if (usedBlockCount != fUsedBlockCount) { FATAL(("Used block count is %ld, but should be %ld\n", fUsedBlockCount, usedBlockCount)); BA_PANIC("Bad area used block count."); return false; } // free bytes if (fFreeBytes != freeBytes) { FATAL(("Free bytes is %lu, but should be %lu\n", fFreeBytes, freeBytes)); BA_PANIC("Bad area free bytes."); return false; } } } return true; }