/*
* Copyright 2001-2011, Haiku Inc. All rights reserved.
* This file may be used under the terms of the MIT License.
*
* Authors:
* Janito V. Ferreira Filho
* Jérôme Duval
*/
#include "BlockAllocator.h"
#include <util/AutoLock.h>
#include "BitmapBlock.h"
#include "Inode.h"
#undef ASSERT
//#define TRACE_EXT2
#ifdef TRACE_EXT2
# define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
# define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
#else
# define TRACE(x...) ;
# define ASSERT(x) ;
#endif
#define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
class AllocationBlockGroup : public TransactionListener {
public:
AllocationBlockGroup();
~AllocationBlockGroup();
status_t Initialize(Volume* volume, uint32 blockGroup,
uint32 numBits);
bool IsFull() const;
status_t Allocate(Transaction& transaction, fsblock_t start,
uint32 length);
status_t Free(Transaction& transaction, uint32 start,
uint32 length);
status_t FreeAll(Transaction& transaction);
status_t Check(uint32 start, uint32 length);
uint32 NumBits() const;
uint32 FreeBits() const;
fsblock_t Start() const;
fsblock_t LargestStart() const;
uint32 LargestLength() const;
// TransactionListener implementation
void TransactionDone(bool success);
void RemovedFromTransaction();
private:
status_t _ScanFreeRanges();
void _AddFreeRange(uint32 start, uint32 length);
void _LockInTransaction(Transaction& transaction);
status_t _InitGroup(Transaction& transaction);
bool _IsSparse();
uint32 _FirstFreeBlock();
Volume* fVolume;
uint32 fBlockGroup;
ext2_block_group* fGroupDescriptor;
mutex fLock;
mutex fTransactionLock;
int32 fCurrentTransaction;
fsblock_t fStart;
uint32 fNumBits;
fsblock_t fBitmapBlock;
uint32 fFreeBits;
uint32 fFirstFree;
uint32 fLargestStart;
uint32 fLargestLength;
uint32 fPreviousFreeBits;
uint32 fPreviousFirstFree;
uint32 fPreviousLargestStart;
uint32 fPreviousLargestLength;
};
AllocationBlockGroup::AllocationBlockGroup()
:
fVolume(NULL),
fBlockGroup(0),
fGroupDescriptor(NULL),
fStart(0),
fNumBits(0),
fBitmapBlock(0),
fFreeBits(0),
fFirstFree(0),
fLargestStart(0),
fLargestLength(0),
fPreviousFreeBits(0),
fPreviousFirstFree(0),
fPreviousLargestStart(0),
fPreviousLargestLength(0)
{
mutex_init(&fLock, "ext2 allocation block group");
mutex_init(&fTransactionLock, "ext2 allocation block group transaction");
}
AllocationBlockGroup::~AllocationBlockGroup()
{
mutex_destroy(&fLock);
mutex_destroy(&fTransactionLock);
}
status_t
AllocationBlockGroup::Initialize(Volume* volume, uint32 blockGroup,
uint32 numBits)
{
fVolume = volume;
fBlockGroup = blockGroup;
fNumBits = numBits;
fStart = blockGroup * numBits + fVolume->FirstDataBlock();
status_t status = fVolume->GetBlockGroup(blockGroup, &fGroupDescriptor);
if (status != B_OK)
return status;
fBitmapBlock = fGroupDescriptor->BlockBitmap(fVolume->Has64bitFeature());
if (fGroupDescriptor->Flags() & EXT2_BLOCK_GROUP_BLOCK_UNINIT) {
fFreeBits = fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature());
fLargestLength = fFreeBits;
fLargestStart = _FirstFreeBlock();
TRACE("Group %" B_PRIu32 " is uninit\n", fBlockGroup);
return B_OK;
}
status = _ScanFreeRanges();
if (status != B_OK)
return status;
if (fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature())
!= fFreeBits) {
ERROR("AllocationBlockGroup(%" B_PRIu32 ",%" B_PRIu64 ")::Initialize()"
": Mismatch between counted free blocks (%" B_PRIu32 "/%" B_PRIu32
") and what is set on the group descriptor (%" B_PRIu32 ")\n",
fBlockGroup, fBitmapBlock, fFreeBits, fNumBits,
fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature()));
return B_BAD_DATA;
}
fPreviousFreeBits = fFreeBits;
fPreviousFirstFree = fFirstFree;
fPreviousLargestStart = fLargestStart;
fPreviousLargestLength = fLargestLength;
return status;
}
status_t
AllocationBlockGroup::_ScanFreeRanges()
{
TRACE("AllocationBlockGroup::_ScanFreeRanges() for group %" B_PRIu32 "\n",
fBlockGroup);
BitmapBlock block(fVolume, fNumBits);
if (!block.SetTo(fBitmapBlock))
return B_ERROR;
fFreeBits = 0;
uint32 start = 0;
uint32 end = 0;
while (end < fNumBits) {
block.FindNextUnmarked(start);
ASSERT(block.CheckMarked(end, start - end));
end = start;
if (start != block.NumBits()) {
block.FindNextMarked(end);
_AddFreeRange(start, end - start);
ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
start = end;
}
}
return B_OK;
}
bool
AllocationBlockGroup::IsFull() const
{
return fFreeBits == 0;
}
status_t
AllocationBlockGroup::Allocate(Transaction& transaction, fsblock_t _start,
uint32 length)
{
uint32 start = _start - fStart;
TRACE("AllocationBlockGroup::Allocate(%" B_PRIu32 ",%" B_PRIu32 ")\n",
start, length);
if (length == 0)
return B_OK;
uint32 end = start + length;
if (end > fNumBits)
return B_BAD_VALUE;
_LockInTransaction(transaction);
_InitGroup(transaction);
BitmapBlock block(fVolume, fNumBits);
if (!block.SetToWritable(transaction, fBitmapBlock))
return B_ERROR;
TRACE("AllocationBlockGroup::Allocate(): Largest range in %" B_PRIu32 "-%"
B_PRIu32 "\n", fLargestStart, fLargestStart + fLargestLength);
ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
if (!block.Mark(start, length)) {
ERROR("Failed to allocate blocks from %" B_PRIu32 " to %" B_PRIu32
". Some were already allocated.\n", start, start + length);
return B_ERROR;
}
fFreeBits -= length;
fGroupDescriptor->SetFreeBlocks(fFreeBits, fVolume->Has64bitFeature());
fVolume->WriteBlockGroup(transaction, fBlockGroup);
if (start == fLargestStart) {
if (fFirstFree == fLargestStart)
fFirstFree += length;
fLargestStart += length;
fLargestLength -= length;
} else if (start + length == fLargestStart + fLargestLength) {
fLargestLength -= length;
} else if (start < fLargestStart + fLargestLength
&& start > fLargestStart) {
uint32 firstLength = start - fLargestStart;
uint32 secondLength = fLargestStart + fLargestLength
- (start + length);
if (firstLength >= secondLength) {
fLargestLength = firstLength;
} else {
fLargestLength = secondLength;
fLargestStart = start + length;
}
} else {
// No need to revalidate the largest free range
return B_OK;
}
TRACE("AllocationBlockGroup::Allocate(): Largest range in %" B_PRIu32 "-%"
B_PRIu32 "\n", fLargestStart, fLargestStart + fLargestLength);
ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
if (fLargestLength < fNumBits / 2)
block.FindLargestUnmarkedRange(fLargestStart, fLargestLength);
ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
return B_OK;
}
status_t
AllocationBlockGroup::Free(Transaction& transaction, uint32 start,
uint32 length)
{
TRACE("AllocationBlockGroup::Free(): start: %" B_PRIu32 ", length %"
B_PRIu32 "\n", start, length);
if (length == 0)
return B_OK;
uint32 end = start + length;
if (end > fNumBits)
return B_BAD_VALUE;
_LockInTransaction(transaction);
if (fGroupDescriptor->Flags() & EXT2_BLOCK_GROUP_BLOCK_UNINIT)
panic("AllocationBlockGroup::Free() can't free blocks if uninit\n");
BitmapBlock block(fVolume, fNumBits);
if (!block.SetToWritable(transaction, fBitmapBlock))
return B_ERROR;
TRACE("AllocationBlockGroup::Free(): Largest range in %" B_PRIu32 "-%"
B_PRIu32 "\n", fLargestStart, fLargestStart + fLargestLength);
ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
if (!block.Unmark(start, length)) {
ERROR("Failed to free blocks from %" B_PRIu32 " to %" B_PRIu32
". Some were already freed.\n", start, start + length);
return B_ERROR;
}
TRACE("AllocationGroup::Free(): Unmarked bits in bitmap\n");
if (fFirstFree > start)
fFirstFree = start;
if (start + length == fLargestStart) {
fLargestStart = start;
fLargestLength += length;
} else if (start == fLargestStart + fLargestLength) {
fLargestLength += length;
} else if (fLargestLength <= fNumBits / 2) {
// May have merged with some free blocks, becoming the largest
uint32 newEnd = start + length;
block.FindNextMarked(newEnd);
uint32 newStart = start;
block.FindPreviousMarked(newStart);
newStart++;
if (newEnd - newStart > fLargestLength) {
fLargestLength = newEnd - newStart;
fLargestStart = newStart;
}
}
TRACE("AllocationBlockGroup::Free(): Largest range in %" B_PRIu32 "-%"
B_PRIu32 "\n", fLargestStart, fLargestStart + fLargestLength);
ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
fFreeBits += length;
fGroupDescriptor->SetFreeBlocks(fFreeBits, fVolume->Has64bitFeature());
fVolume->WriteBlockGroup(transaction, fBlockGroup);
return B_OK;
}
status_t
AllocationBlockGroup::FreeAll(Transaction& transaction)
{
return Free(transaction, 0, fNumBits);
}
uint32
AllocationBlockGroup::NumBits() const
{
return fNumBits;
}
uint32
AllocationBlockGroup::FreeBits() const
{
return fFreeBits;
}
fsblock_t
AllocationBlockGroup::Start() const
{
return fStart;
}
fsblock_t
AllocationBlockGroup::LargestStart() const
{
return fStart + fLargestStart;
}
uint32
AllocationBlockGroup::LargestLength() const
{
return fLargestLength;
}
void
AllocationBlockGroup::_AddFreeRange(uint32 start, uint32 length)
{
if (IsFull()) {
fFirstFree = start;
fLargestStart = start;
fLargestLength = length;
} else if (length > fLargestLength) {
fLargestStart = start;
fLargestLength = length;
}
fFreeBits += length;
}
void
AllocationBlockGroup::_LockInTransaction(Transaction& transaction)
{
mutex_lock(&fLock);
if (transaction.ID() != fCurrentTransaction) {
mutex_unlock(&fLock);
mutex_lock(&fTransactionLock);
mutex_lock(&fLock);
fCurrentTransaction = transaction.ID();
transaction.AddListener(this);
}
mutex_unlock(&fLock);
}
status_t
AllocationBlockGroup::_InitGroup(Transaction& transaction)
{
TRACE("AllocationBlockGroup::_InitGroup()\n");
uint16 flags = fGroupDescriptor->Flags();
if ((flags & EXT2_BLOCK_GROUP_BLOCK_UNINIT) == 0)
return B_OK;
TRACE("AllocationBlockGroup::_InitGroup() initing\n");
BitmapBlock blockBitmap(fVolume, fNumBits);
if (!blockBitmap.SetToWritable(transaction, fBitmapBlock))
return B_ERROR;
blockBitmap.Mark(0, _FirstFreeBlock(), true);
blockBitmap.Unmark(0, fNumBits, true);
fGroupDescriptor->SetFlags(flags & ~EXT2_BLOCK_GROUP_BLOCK_UNINIT);
fVolume->WriteBlockGroup(transaction, fBlockGroup);
status_t status = _ScanFreeRanges();
if (status != B_OK)
return status;
if (fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature())
!= fFreeBits) {
ERROR("AllocationBlockGroup(%" B_PRIu32 ",%" B_PRIu64 ")::_InitGroup()"
": Mismatch between counted free blocks (%" B_PRIu32 "/%" B_PRIu32
") and what is set on the group descriptor (%" B_PRIu32 ")\n",
fBlockGroup, fBitmapBlock, fFreeBits, fNumBits,
fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature()));
return B_BAD_DATA;
}
TRACE("AllocationBlockGroup::_InitGroup() init OK\n");
return B_OK;
}
bool
AllocationBlockGroup::_IsSparse()
{
if (fBlockGroup <= 1)
return true;
if (fBlockGroup & 0x1)
return false;
uint32 i = fBlockGroup;
while (i % 7 == 0)
i /= 7;
if (i == 1)
return true;
i = fBlockGroup;
while (i % 5 == 0)
i /= 5;
if (i == 1)
return true;
i = fBlockGroup;
while (i % 3 == 0)
i /= 3;
if (i == 1)
return true;
return false;
}
uint32
AllocationBlockGroup::_FirstFreeBlock()
{
uint32 first = 1;
if (_IsSparse())
first = 0;
else if (!fVolume->HasMetaGroupFeature()) {
first += fVolume->SuperBlock().ReservedGDTBlocks();
first += fVolume->NumGroups();
}
return first;
}
void
AllocationBlockGroup::TransactionDone(bool success)
{
if (success) {
TRACE("AllocationBlockGroup::TransactionDone(): The transaction "
"succeeded, discarding previous state\n");
fPreviousFreeBits = fFreeBits;
fPreviousFirstFree = fFirstFree;
fPreviousLargestStart = fLargestStart;
fPreviousLargestLength = fLargestLength;
} else {
TRACE("AllocationBlockGroup::TransactionDone(): The transaction "
"failed, restoring to previous state\n");
fFreeBits = fPreviousFreeBits;
fFirstFree = fPreviousFirstFree;
fLargestStart = fPreviousLargestStart;
fLargestLength = fPreviousLargestLength;
}
}
void
AllocationBlockGroup::RemovedFromTransaction()
{
mutex_unlock(&fTransactionLock);
fCurrentTransaction = -1;
}
BlockAllocator::BlockAllocator(Volume* volume)
:
fVolume(volume),
fGroups(NULL),
fBlocksPerGroup(0),
fNumBlocks(0),
fNumGroups(0)
{
mutex_init(&fLock, "ext2 block allocator");
}
BlockAllocator::~BlockAllocator()
{
mutex_destroy(&fLock);
if (fGroups != NULL)
delete [] fGroups;
}
status_t
BlockAllocator::Initialize()
{
fBlocksPerGroup = fVolume->BlocksPerGroup();
fNumGroups = fVolume->NumGroups();
fFirstBlock = fVolume->FirstDataBlock();
fNumBlocks = fVolume->NumBlocks();
TRACE("BlockAllocator::Initialize(): blocks per group: %" B_PRIu32
", block groups: %" B_PRIu32 ", first block: %" B_PRIu64
", num blocks: %" B_PRIu64 "\n", fBlocksPerGroup, fNumGroups,
fFirstBlock, fNumBlocks);
fGroups = new(std::nothrow) AllocationBlockGroup[fNumGroups];
if (fGroups == NULL)
return B_NO_MEMORY;
TRACE("BlockAllocator::Initialize(): allocated allocation block groups\n");
mutex_lock(&fLock);
// Released by _Initialize
thread_id id = -1; // spawn_kernel_thread(
// (thread_func)BlockAllocator::_Initialize, "ext2 block allocator",
// B_LOW_PRIORITY, this);
if (id < B_OK)
return _Initialize(this);
// mutex_transfer_lock(&fLock, id);
// return resume_thread(id);
panic("Failed to fall back to synchronous block allocator"
"initialization.\n");
return B_ERROR;
}
status_t
BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum,
uint32 maximum, uint32& blockGroup, fsblock_t& start, uint32& length)
{
TRACE("BlockAllocator::AllocateBlocks()\n");
MutexLocker lock(fLock);
TRACE("BlockAllocator::AllocateBlocks(): Acquired lock\n");
TRACE("BlockAllocator::AllocateBlocks(): transaction: %" B_PRId32 ", min: "
"%" B_PRIu32 ", max: %" B_PRIu32 ", block group: %" B_PRIu32 ", start:"
" %" B_PRIu64 ", num groups: %" B_PRIu32 "\n", transaction.ID(),
minimum, maximum, blockGroup, start, fNumGroups);
fsblock_t bestStart = 0;
uint32 bestLength = 0;
uint32 bestGroup = 0;
uint32 groupNum = blockGroup;
AllocationBlockGroup* last = &fGroups[fNumGroups];
AllocationBlockGroup* group = &fGroups[blockGroup];
for (int32 iterations = 0; iterations < 2; iterations++) {
for (; group < last; ++group, ++groupNum) {
TRACE("BlockAllocator::AllocateBlocks(): Group %" B_PRIu32
" has largest length of %" B_PRIu32 "\n", groupNum,
group->LargestLength());
if (group->LargestLength() > bestLength) {
if (start <= group->LargestStart()) {
bestStart = group->LargestStart();
bestLength = group->LargestLength();
bestGroup = groupNum;
TRACE("BlockAllocator::AllocateBlocks(): Found a better "
"range: block group: %" B_PRIu32 ", %" B_PRIu64 "-%"
B_PRIu64 "\n", groupNum, bestStart,
bestStart + bestLength);
if (bestLength >= maximum)
break;
}
}
start = 0;
}
if (bestLength >= maximum)
break;
groupNum = 0;
group = &fGroups[0];
last = &fGroups[blockGroup + 1];
}
if (bestLength < minimum) {
TRACE("BlockAllocator::AllocateBlocks(): best range (length %" B_PRIu32
") doesn't have minimum length of %" B_PRIu32 "\n", bestLength,
minimum);
return B_DEVICE_FULL;
}
if (bestLength > maximum)
bestLength = maximum;
TRACE("BlockAllocator::AllocateBlocks(): Selected range: block group %"
B_PRIu32 ", %" B_PRIu64 "-%" B_PRIu64 "\n", bestGroup, bestStart,
bestStart + bestLength);
status_t status = fGroups[bestGroup].Allocate(transaction, bestStart,
bestLength);
if (status != B_OK) {
TRACE("BlockAllocator::AllocateBlocks(): Failed to allocate %" B_PRIu32
" blocks inside block group %" B_PRIu32 ".\n", bestLength, bestGroup);
return status;
}
start = bestStart;
length = bestLength;
blockGroup = bestGroup;
return B_OK;
}
status_t
BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
off_t numBlocks, uint32 minimum, fsblock_t& start, uint32& allocated)
{
if (numBlocks <= 0)
return B_ERROR;
if (start > fNumBlocks)
return B_BAD_VALUE;
uint32 group = inode->ID() / fVolume->InodesPerGroup();
uint32 preferred = 0;
if (inode->Size() > 0) {
// Try to allocate near it's last blocks
ext2_data_stream* dataStream = &inode->Node().stream;
uint32 numBlocks = inode->Size() / fVolume->BlockSize() + 1;
uint32 lastBlock = 0;
// DANGER! What happens with sparse files?
if (numBlocks < EXT2_DIRECT_BLOCKS) {
// Only direct blocks
lastBlock = dataStream->direct[numBlocks];
} else {
numBlocks -= EXT2_DIRECT_BLOCKS - 1;
uint32 numIndexes = fVolume->BlockSize() / 4;
// block size / sizeof(int32)
uint32 numIndexes2 = numIndexes * numIndexes;
uint32 numIndexes3 = numIndexes2 * numIndexes;
uint32 indexesInIndirect = numIndexes;
uint32 indexesInDoubleIndirect = indexesInIndirect
+ numIndexes2;
// uint32 indexesInTripleIndirect = indexesInDoubleIndirect
// + numIndexes3;
uint32 doubleIndirectBlock = EXT2_DIRECT_BLOCKS + 1;
uint32 indirectBlock = EXT2_DIRECT_BLOCKS;
CachedBlock cached(fVolume);
uint32* indirectData;
if (numBlocks > indexesInDoubleIndirect) {
// Triple indirect blocks
indirectData = (uint32*)cached.SetTo(EXT2_DIRECT_BLOCKS + 2);
if (indirectData == NULL)
return B_IO_ERROR;
uint32 index = (numBlocks - indexesInDoubleIndirect)
/ numIndexes3;
doubleIndirectBlock = indirectData[index];
}
if (numBlocks > indexesInIndirect) {
// Double indirect blocks
indirectData = (uint32*)cached.SetTo(doubleIndirectBlock);
if (indirectData == NULL)
return B_IO_ERROR;
uint32 index = (numBlocks - indexesInIndirect) / numIndexes2;
indirectBlock = indirectData[index];
}
indirectData = (uint32*)cached.SetTo(indirectBlock);
if (indirectData == NULL)
return B_IO_ERROR;
uint32 index = numBlocks / numIndexes;
lastBlock = indirectData[index];
}
group = (lastBlock - fFirstBlock) / fBlocksPerGroup;
preferred = (lastBlock - fFirstBlock) % fBlocksPerGroup + 1;
}
// TODO: Apply some more policies
return AllocateBlocks(transaction, minimum, minimum + 8, group, start,
allocated);
}
status_t
BlockAllocator::Free(Transaction& transaction, fsblock_t start, uint32 length)
{
TRACE("BlockAllocator::Free(%" B_PRIu64 ", %" B_PRIu32 ")\n", start,
length);
MutexLocker lock(fLock);
if (start <= fFirstBlock) {
panic("Trying to free superblock!\n");
return B_BAD_VALUE;
}
if (length == 0)
return B_OK;
if (start > fNumBlocks || length > fNumBlocks)
return B_BAD_VALUE;
TRACE("BlockAllocator::Free(): first block: %" B_PRIu64
", blocks per group: %" B_PRIu32 "\n", fFirstBlock, fBlocksPerGroup);
start -= fFirstBlock;
off_t end = start + length - 1;
uint32 group = start / fBlocksPerGroup;
if (group >= fNumGroups) {
panic("BlockAllocator::Free() group %" B_PRIu32 " too big (fNumGroups "
"%" B_PRIu32 ")\n", group, fNumGroups);
}
uint32 lastGroup = end / fBlocksPerGroup;
start = start % fBlocksPerGroup;
if (group == lastGroup)
return fGroups[group].Free(transaction, start, length);
TRACE("BlockAllocator::Free(): Freeing from group %" B_PRIu32 ": %"
B_PRIu64 ", %" B_PRIu64 "\n", group,
start, fGroups[group].NumBits() - start);
status_t status = fGroups[group].Free(transaction, start,
fGroups[group].NumBits() - start);
if (status != B_OK)
return status;
for (++group; group < lastGroup; ++group) {
TRACE("BlockAllocator::Free(): Freeing all from group %" B_PRIu32 "\n",
group);
status = fGroups[group].FreeAll(transaction);
if (status != B_OK)
return status;
}
TRACE("BlockAllocator::Free(): Freeing from group %" B_PRIu32 ": 0-%"
B_PRIu64 " \n", group, end % fBlocksPerGroup);
return fGroups[group].Free(transaction, 0, (end + 1) % fBlocksPerGroup);
}
/*static*/ status_t
BlockAllocator::_Initialize(BlockAllocator* allocator)
{
TRACE("BlockAllocator::_Initialize()\n");
// fLock is already heald
Volume* volume = allocator->fVolume;
AllocationBlockGroup* groups = allocator->fGroups;
uint32 numGroups = allocator->fNumGroups - 1;
off_t freeBlocks = 0;
TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF "\n",
freeBlocks);
for (uint32 i = 0; i < numGroups; ++i) {
status_t status = groups[i].Initialize(volume, i,
allocator->fBlocksPerGroup);
if (status != B_OK) {
mutex_unlock(&allocator->fLock);
return status;
}
freeBlocks += groups[i].FreeBits();
TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF "\n",
freeBlocks);
}
// Last block group may have less blocks
status_t status = groups[numGroups].Initialize(volume, numGroups,
allocator->fNumBlocks - allocator->fBlocksPerGroup * numGroups
- allocator->fFirstBlock);
if (status != B_OK) {
mutex_unlock(&allocator->fLock);
return status;
}
freeBlocks += groups[numGroups].FreeBits();
TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF "\n",
freeBlocks);
mutex_unlock(&allocator->fLock);
if (freeBlocks != volume->NumFreeBlocks()) {
TRACE("Counted free blocks (%" B_PRIdOFF ") doesn't match value in the"
" superblock (%" B_PRIdOFF ").\n", freeBlocks,
volume->NumFreeBlocks());
return B_BAD_DATA;
}
return B_OK;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fCurrentTransaction.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fFirstBlock.
↑ V547 Expression 'id < ((int) 0)' is always true.