2 * Copyright 2001-2011, Haiku Inc. All rights reserved.
3 * This file may be used under the terms of the MIT License.
6 * Janito V. Ferreira Filho
11 #include "BlockAllocator.h"
13 #include <util/AutoLock.h>
15 #include "BitmapBlock.h"
22 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
23 # define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
25 # define TRACE(x...) ;
28 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
31 class AllocationBlockGroup
: public TransactionListener
{
33 AllocationBlockGroup();
34 ~AllocationBlockGroup();
36 status_t
Initialize(Volume
* volume
, uint32 blockGroup
,
41 status_t
Allocate(Transaction
& transaction
, fsblock_t start
,
43 status_t
Free(Transaction
& transaction
, uint32 start
,
45 status_t
FreeAll(Transaction
& transaction
);
46 status_t
Check(uint32 start
, uint32 length
);
48 uint32
NumBits() const;
49 uint32
FreeBits() const;
50 fsblock_t
Start() const;
52 fsblock_t
LargestStart() const;
53 uint32
LargestLength() const;
55 // TransactionListener implementation
56 void TransactionDone(bool success
);
57 void RemovedFromTransaction();
60 status_t
_ScanFreeRanges();
61 void _AddFreeRange(uint32 start
, uint32 length
);
62 void _LockInTransaction(Transaction
& transaction
);
63 status_t
_InitGroup(Transaction
& transaction
);
65 uint32
_FirstFreeBlock();
69 ext2_block_group
* fGroupDescriptor
;
72 mutex fTransactionLock
;
73 int32 fCurrentTransaction
;
77 fsblock_t fBitmapBlock
;
82 uint32 fLargestLength
;
84 uint32 fPreviousFreeBits
;
85 uint32 fPreviousFirstFree
;
86 uint32 fPreviousLargestStart
;
87 uint32 fPreviousLargestLength
;
91 AllocationBlockGroup::AllocationBlockGroup()
95 fGroupDescriptor(NULL
),
103 fPreviousFreeBits(0),
104 fPreviousFirstFree(0),
105 fPreviousLargestStart(0),
106 fPreviousLargestLength(0)
108 mutex_init(&fLock
, "ext2 allocation block group");
109 mutex_init(&fTransactionLock
, "ext2 allocation block group transaction");
113 AllocationBlockGroup::~AllocationBlockGroup()
115 mutex_destroy(&fLock
);
116 mutex_destroy(&fTransactionLock
);
121 AllocationBlockGroup::Initialize(Volume
* volume
, uint32 blockGroup
,
125 fBlockGroup
= blockGroup
;
127 fStart
= blockGroup
* numBits
+ fVolume
->FirstDataBlock();
129 status_t status
= fVolume
->GetBlockGroup(blockGroup
, &fGroupDescriptor
);
133 fBitmapBlock
= fGroupDescriptor
->BlockBitmap(fVolume
->Has64bitFeature());
135 if (fGroupDescriptor
->Flags() & EXT2_BLOCK_GROUP_BLOCK_UNINIT
) {
136 fFreeBits
= fGroupDescriptor
->FreeBlocks(fVolume
->Has64bitFeature());
137 fLargestLength
= fFreeBits
;
138 fLargestStart
= _FirstFreeBlock();
139 TRACE("Group %" B_PRIu32
" is uninit\n", fBlockGroup
);
143 status
= _ScanFreeRanges();
147 if (fGroupDescriptor
->FreeBlocks(fVolume
->Has64bitFeature())
149 ERROR("AllocationBlockGroup(%" B_PRIu32
",%" B_PRIu64
")::Initialize()"
150 ": Mismatch between counted free blocks (%" B_PRIu32
"/%" B_PRIu32
151 ") and what is set on the group descriptor (%" B_PRIu32
")\n",
152 fBlockGroup
, fBitmapBlock
, fFreeBits
, fNumBits
,
153 fGroupDescriptor
->FreeBlocks(fVolume
->Has64bitFeature()));
157 fPreviousFreeBits
= fFreeBits
;
158 fPreviousFirstFree
= fFirstFree
;
159 fPreviousLargestStart
= fLargestStart
;
160 fPreviousLargestLength
= fLargestLength
;
167 AllocationBlockGroup::_ScanFreeRanges()
169 TRACE("AllocationBlockGroup::_ScanFreeRanges() for group %" B_PRIu32
"\n",
171 BitmapBlock
block(fVolume
, fNumBits
);
173 if (!block
.SetTo(fBitmapBlock
))
180 while (end
< fNumBits
) {
181 block
.FindNextUnmarked(start
);
182 ASSERT(block
.CheckMarked(end
, start
- end
));
185 if (start
!= block
.NumBits()) {
186 block
.FindNextMarked(end
);
187 _AddFreeRange(start
, end
- start
);
188 ASSERT(block
.CheckUnmarked(fLargestStart
, fLargestLength
));
198 AllocationBlockGroup::IsFull() const
200 return fFreeBits
== 0;
205 AllocationBlockGroup::Allocate(Transaction
& transaction
, fsblock_t _start
,
208 uint32 start
= _start
- fStart
;
209 TRACE("AllocationBlockGroup::Allocate(%" B_PRIu32
",%" B_PRIu32
")\n",
214 uint32 end
= start
+ length
;
218 _LockInTransaction(transaction
);
219 _InitGroup(transaction
);
221 BitmapBlock
block(fVolume
, fNumBits
);
223 if (!block
.SetToWritable(transaction
, fBitmapBlock
))
226 TRACE("AllocationBlockGroup::Allocate(): Largest range in %" B_PRIu32
"-%"
227 B_PRIu32
"\n", fLargestStart
, fLargestStart
+ fLargestLength
);
228 ASSERT(block
.CheckUnmarked(fLargestStart
, fLargestLength
));
230 if (!block
.Mark(start
, length
)) {
231 ERROR("Failed to allocate blocks from %" B_PRIu32
" to %" B_PRIu32
232 ". Some were already allocated.\n", start
, start
+ length
);
237 fGroupDescriptor
->SetFreeBlocks(fFreeBits
, fVolume
->Has64bitFeature());
238 fVolume
->WriteBlockGroup(transaction
, fBlockGroup
);
240 if (start
== fLargestStart
) {
241 if (fFirstFree
== fLargestStart
)
242 fFirstFree
+= length
;
244 fLargestStart
+= length
;
245 fLargestLength
-= length
;
246 } else if (start
+ length
== fLargestStart
+ fLargestLength
) {
247 fLargestLength
-= length
;
248 } else if (start
< fLargestStart
+ fLargestLength
249 && start
> fLargestStart
) {
250 uint32 firstLength
= start
- fLargestStart
;
251 uint32 secondLength
= fLargestStart
+ fLargestLength
254 if (firstLength
>= secondLength
) {
255 fLargestLength
= firstLength
;
257 fLargestLength
= secondLength
;
258 fLargestStart
= start
+ length
;
261 // No need to revalidate the largest free range
265 TRACE("AllocationBlockGroup::Allocate(): Largest range in %" B_PRIu32
"-%"
266 B_PRIu32
"\n", fLargestStart
, fLargestStart
+ fLargestLength
);
267 ASSERT(block
.CheckUnmarked(fLargestStart
, fLargestLength
));
269 if (fLargestLength
< fNumBits
/ 2)
270 block
.FindLargestUnmarkedRange(fLargestStart
, fLargestLength
);
271 ASSERT(block
.CheckUnmarked(fLargestStart
, fLargestLength
));
278 AllocationBlockGroup::Free(Transaction
& transaction
, uint32 start
,
281 TRACE("AllocationBlockGroup::Free(): start: %" B_PRIu32
", length %"
282 B_PRIu32
"\n", start
, length
);
287 uint32 end
= start
+ length
;
291 _LockInTransaction(transaction
);
292 if (fGroupDescriptor
->Flags() & EXT2_BLOCK_GROUP_BLOCK_UNINIT
)
293 panic("AllocationBlockGroup::Free() can't free blocks if uninit\n");
295 BitmapBlock
block(fVolume
, fNumBits
);
297 if (!block
.SetToWritable(transaction
, fBitmapBlock
))
300 TRACE("AllocationBlockGroup::Free(): Largest range in %" B_PRIu32
"-%"
301 B_PRIu32
"\n", fLargestStart
, fLargestStart
+ fLargestLength
);
302 ASSERT(block
.CheckUnmarked(fLargestStart
, fLargestLength
));
304 if (!block
.Unmark(start
, length
)) {
305 ERROR("Failed to free blocks from %" B_PRIu32
" to %" B_PRIu32
306 ". Some were already freed.\n", start
, start
+ length
);
310 TRACE("AllocationGroup::Free(): Unmarked bits in bitmap\n");
312 if (fFirstFree
> start
)
315 if (start
+ length
== fLargestStart
) {
316 fLargestStart
= start
;
317 fLargestLength
+= length
;
318 } else if (start
== fLargestStart
+ fLargestLength
) {
319 fLargestLength
+= length
;
320 } else if (fLargestLength
<= fNumBits
/ 2) {
321 // May have merged with some free blocks, becoming the largest
322 uint32 newEnd
= start
+ length
;
323 block
.FindNextMarked(newEnd
);
325 uint32 newStart
= start
;
326 block
.FindPreviousMarked(newStart
);
329 if (newEnd
- newStart
> fLargestLength
) {
330 fLargestLength
= newEnd
- newStart
;
331 fLargestStart
= newStart
;
335 TRACE("AllocationBlockGroup::Free(): Largest range in %" B_PRIu32
"-%"
336 B_PRIu32
"\n", fLargestStart
, fLargestStart
+ fLargestLength
);
337 ASSERT(block
.CheckUnmarked(fLargestStart
, fLargestLength
));
340 fGroupDescriptor
->SetFreeBlocks(fFreeBits
, fVolume
->Has64bitFeature());
341 fVolume
->WriteBlockGroup(transaction
, fBlockGroup
);
348 AllocationBlockGroup::FreeAll(Transaction
& transaction
)
350 return Free(transaction
, 0, fNumBits
);
355 AllocationBlockGroup::NumBits() const
362 AllocationBlockGroup::FreeBits() const
369 AllocationBlockGroup::Start() const
376 AllocationBlockGroup::LargestStart() const
378 return fStart
+ fLargestStart
;
383 AllocationBlockGroup::LargestLength() const
385 return fLargestLength
;
390 AllocationBlockGroup::_AddFreeRange(uint32 start
, uint32 length
)
394 fLargestStart
= start
;
395 fLargestLength
= length
;
396 } else if (length
> fLargestLength
) {
397 fLargestStart
= start
;
398 fLargestLength
= length
;
406 AllocationBlockGroup::_LockInTransaction(Transaction
& transaction
)
410 if (transaction
.ID() != fCurrentTransaction
) {
411 mutex_unlock(&fLock
);
413 mutex_lock(&fTransactionLock
);
416 fCurrentTransaction
= transaction
.ID();
417 transaction
.AddListener(this);
420 mutex_unlock(&fLock
);
425 AllocationBlockGroup::_InitGroup(Transaction
& transaction
)
427 TRACE("AllocationBlockGroup::_InitGroup()\n");
428 uint16 flags
= fGroupDescriptor
->Flags();
429 if ((flags
& EXT2_BLOCK_GROUP_BLOCK_UNINIT
) == 0)
432 TRACE("AllocationBlockGroup::_InitGroup() initing\n");
434 BitmapBlock
blockBitmap(fVolume
, fNumBits
);
435 if (!blockBitmap
.SetToWritable(transaction
, fBitmapBlock
))
437 blockBitmap
.Mark(0, _FirstFreeBlock(), true);
438 blockBitmap
.Unmark(0, fNumBits
, true);
440 fGroupDescriptor
->SetFlags(flags
& ~EXT2_BLOCK_GROUP_BLOCK_UNINIT
);
441 fVolume
->WriteBlockGroup(transaction
, fBlockGroup
);
443 status_t status
= _ScanFreeRanges();
447 if (fGroupDescriptor
->FreeBlocks(fVolume
->Has64bitFeature())
449 ERROR("AllocationBlockGroup(%" B_PRIu32
",%" B_PRIu64
")::_InitGroup()"
450 ": Mismatch between counted free blocks (%" B_PRIu32
"/%" B_PRIu32
451 ") and what is set on the group descriptor (%" B_PRIu32
")\n",
452 fBlockGroup
, fBitmapBlock
, fFreeBits
, fNumBits
,
453 fGroupDescriptor
->FreeBlocks(fVolume
->Has64bitFeature()));
457 TRACE("AllocationBlockGroup::_InitGroup() init OK\n");
464 AllocationBlockGroup::_IsSparse()
466 if (fBlockGroup
<= 1)
468 if (fBlockGroup
& 0x1)
471 uint32 i
= fBlockGroup
;
494 AllocationBlockGroup::_FirstFreeBlock()
499 else if (!fVolume
->HasMetaGroupFeature()) {
500 first
+= fVolume
->SuperBlock().ReservedGDTBlocks();
501 first
+= fVolume
->NumGroups();
508 AllocationBlockGroup::TransactionDone(bool success
)
511 TRACE("AllocationBlockGroup::TransactionDone(): The transaction "
512 "succeeded, discarding previous state\n");
513 fPreviousFreeBits
= fFreeBits
;
514 fPreviousFirstFree
= fFirstFree
;
515 fPreviousLargestStart
= fLargestStart
;
516 fPreviousLargestLength
= fLargestLength
;
518 TRACE("AllocationBlockGroup::TransactionDone(): The transaction "
519 "failed, restoring to previous state\n");
520 fFreeBits
= fPreviousFreeBits
;
521 fFirstFree
= fPreviousFirstFree
;
522 fLargestStart
= fPreviousLargestStart
;
523 fLargestLength
= fPreviousLargestLength
;
529 AllocationBlockGroup::RemovedFromTransaction()
531 mutex_unlock(&fTransactionLock
);
532 fCurrentTransaction
= -1;
536 BlockAllocator::BlockAllocator(Volume
* volume
)
544 mutex_init(&fLock
, "ext2 block allocator");
548 BlockAllocator::~BlockAllocator()
550 mutex_destroy(&fLock
);
558 BlockAllocator::Initialize()
560 fBlocksPerGroup
= fVolume
->BlocksPerGroup();
561 fNumGroups
= fVolume
->NumGroups();
562 fFirstBlock
= fVolume
->FirstDataBlock();
563 fNumBlocks
= fVolume
->NumBlocks();
565 TRACE("BlockAllocator::Initialize(): blocks per group: %" B_PRIu32
566 ", block groups: %" B_PRIu32
", first block: %" B_PRIu64
567 ", num blocks: %" B_PRIu64
"\n", fBlocksPerGroup
, fNumGroups
,
568 fFirstBlock
, fNumBlocks
);
570 fGroups
= new(std::nothrow
) AllocationBlockGroup
[fNumGroups
];
574 TRACE("BlockAllocator::Initialize(): allocated allocation block groups\n");
577 // Released by _Initialize
579 thread_id id
= -1; // spawn_kernel_thread(
580 // (thread_func)BlockAllocator::_Initialize, "ext2 block allocator",
581 // B_LOW_PRIORITY, this);
583 return _Initialize(this);
585 // mutex_transfer_lock(&fLock, id);
587 // return resume_thread(id);
588 panic("Failed to fall back to synchronous block allocator"
589 "initialization.\n");
595 BlockAllocator::AllocateBlocks(Transaction
& transaction
, uint32 minimum
,
596 uint32 maximum
, uint32
& blockGroup
, fsblock_t
& start
, uint32
& length
)
598 TRACE("BlockAllocator::AllocateBlocks()\n");
599 MutexLocker
lock(fLock
);
600 TRACE("BlockAllocator::AllocateBlocks(): Acquired lock\n");
602 TRACE("BlockAllocator::AllocateBlocks(): transaction: %" B_PRId32
", min: "
603 "%" B_PRIu32
", max: %" B_PRIu32
", block group: %" B_PRIu32
", start:"
604 " %" B_PRIu64
", num groups: %" B_PRIu32
"\n", transaction
.ID(),
605 minimum
, maximum
, blockGroup
, start
, fNumGroups
);
607 fsblock_t bestStart
= 0;
608 uint32 bestLength
= 0;
609 uint32 bestGroup
= 0;
611 uint32 groupNum
= blockGroup
;
613 AllocationBlockGroup
* last
= &fGroups
[fNumGroups
];
614 AllocationBlockGroup
* group
= &fGroups
[blockGroup
];
616 for (int32 iterations
= 0; iterations
< 2; iterations
++) {
617 for (; group
< last
; ++group
, ++groupNum
) {
618 TRACE("BlockAllocator::AllocateBlocks(): Group %" B_PRIu32
619 " has largest length of %" B_PRIu32
"\n", groupNum
,
620 group
->LargestLength());
622 if (group
->LargestLength() > bestLength
) {
623 if (start
<= group
->LargestStart()) {
624 bestStart
= group
->LargestStart();
625 bestLength
= group
->LargestLength();
626 bestGroup
= groupNum
;
628 TRACE("BlockAllocator::AllocateBlocks(): Found a better "
629 "range: block group: %" B_PRIu32
", %" B_PRIu64
"-%"
630 B_PRIu64
"\n", groupNum
, bestStart
,
631 bestStart
+ bestLength
);
633 if (bestLength
>= maximum
)
641 if (bestLength
>= maximum
)
647 last
= &fGroups
[blockGroup
+ 1];
650 if (bestLength
< minimum
) {
651 TRACE("BlockAllocator::AllocateBlocks(): best range (length %" B_PRIu32
652 ") doesn't have minimum length of %" B_PRIu32
"\n", bestLength
,
654 return B_DEVICE_FULL
;
657 if (bestLength
> maximum
)
658 bestLength
= maximum
;
660 TRACE("BlockAllocator::AllocateBlocks(): Selected range: block group %"
661 B_PRIu32
", %" B_PRIu64
"-%" B_PRIu64
"\n", bestGroup
, bestStart
,
662 bestStart
+ bestLength
);
664 status_t status
= fGroups
[bestGroup
].Allocate(transaction
, bestStart
,
666 if (status
!= B_OK
) {
667 TRACE("BlockAllocator::AllocateBlocks(): Failed to allocate %" B_PRIu32
668 " blocks inside block group %" B_PRIu32
".\n", bestLength
, bestGroup
);
674 blockGroup
= bestGroup
;
681 BlockAllocator::Allocate(Transaction
& transaction
, Inode
* inode
,
682 off_t numBlocks
, uint32 minimum
, fsblock_t
& start
, uint32
& allocated
)
686 if (start
> fNumBlocks
)
689 uint32 group
= inode
->ID() / fVolume
->InodesPerGroup();
690 uint32 preferred
= 0;
692 if (inode
->Size() > 0) {
693 // Try to allocate near it's last blocks
694 ext2_data_stream
* dataStream
= &inode
->Node().stream
;
695 uint32 numBlocks
= inode
->Size() / fVolume
->BlockSize() + 1;
696 uint32 lastBlock
= 0;
698 // DANGER! What happens with sparse files?
699 if (numBlocks
< EXT2_DIRECT_BLOCKS
) {
700 // Only direct blocks
701 lastBlock
= dataStream
->direct
[numBlocks
];
703 numBlocks
-= EXT2_DIRECT_BLOCKS
- 1;
704 uint32 numIndexes
= fVolume
->BlockSize() / 4;
705 // block size / sizeof(int32)
706 uint32 numIndexes2
= numIndexes
* numIndexes
;
707 uint32 numIndexes3
= numIndexes2
* numIndexes
;
708 uint32 indexesInIndirect
= numIndexes
;
709 uint32 indexesInDoubleIndirect
= indexesInIndirect
711 // uint32 indexesInTripleIndirect = indexesInDoubleIndirect
714 uint32 doubleIndirectBlock
= EXT2_DIRECT_BLOCKS
+ 1;
715 uint32 indirectBlock
= EXT2_DIRECT_BLOCKS
;
717 CachedBlock
cached(fVolume
);
718 uint32
* indirectData
;
720 if (numBlocks
> indexesInDoubleIndirect
) {
721 // Triple indirect blocks
722 indirectData
= (uint32
*)cached
.SetTo(EXT2_DIRECT_BLOCKS
+ 2);
723 if (indirectData
== NULL
)
726 uint32 index
= (numBlocks
- indexesInDoubleIndirect
)
728 doubleIndirectBlock
= indirectData
[index
];
731 if (numBlocks
> indexesInIndirect
) {
732 // Double indirect blocks
733 indirectData
= (uint32
*)cached
.SetTo(doubleIndirectBlock
);
734 if (indirectData
== NULL
)
737 uint32 index
= (numBlocks
- indexesInIndirect
) / numIndexes2
;
738 indirectBlock
= indirectData
[index
];
741 indirectData
= (uint32
*)cached
.SetTo(indirectBlock
);
742 if (indirectData
== NULL
)
745 uint32 index
= numBlocks
/ numIndexes
;
746 lastBlock
= indirectData
[index
];
749 group
= (lastBlock
- fFirstBlock
) / fBlocksPerGroup
;
750 preferred
= (lastBlock
- fFirstBlock
) % fBlocksPerGroup
+ 1;
753 // TODO: Apply some more policies
755 return AllocateBlocks(transaction
, minimum
, minimum
+ 8, group
, start
,
761 BlockAllocator::Free(Transaction
& transaction
, fsblock_t start
, uint32 length
)
763 TRACE("BlockAllocator::Free(%" B_PRIu64
", %" B_PRIu32
")\n", start
,
765 MutexLocker
lock(fLock
);
767 if (start
<= fFirstBlock
) {
768 panic("Trying to free superblock!\n");
774 if (start
> fNumBlocks
|| length
> fNumBlocks
)
777 TRACE("BlockAllocator::Free(): first block: %" B_PRIu64
778 ", blocks per group: %" B_PRIu32
"\n", fFirstBlock
, fBlocksPerGroup
);
780 start
-= fFirstBlock
;
781 off_t end
= start
+ length
- 1;
783 uint32 group
= start
/ fBlocksPerGroup
;
784 if (group
>= fNumGroups
) {
785 panic("BlockAllocator::Free() group %" B_PRIu32
" too big (fNumGroups "
786 "%" B_PRIu32
")\n", group
, fNumGroups
);
788 uint32 lastGroup
= end
/ fBlocksPerGroup
;
789 start
= start
% fBlocksPerGroup
;
791 if (group
== lastGroup
)
792 return fGroups
[group
].Free(transaction
, start
, length
);
794 TRACE("BlockAllocator::Free(): Freeing from group %" B_PRIu32
": %"
795 B_PRIu64
", %" B_PRIu64
"\n", group
,
796 start
, fGroups
[group
].NumBits() - start
);
798 status_t status
= fGroups
[group
].Free(transaction
, start
,
799 fGroups
[group
].NumBits() - start
);
803 for (++group
; group
< lastGroup
; ++group
) {
804 TRACE("BlockAllocator::Free(): Freeing all from group %" B_PRIu32
"\n",
806 status
= fGroups
[group
].FreeAll(transaction
);
811 TRACE("BlockAllocator::Free(): Freeing from group %" B_PRIu32
": 0-%"
812 B_PRIu64
" \n", group
, end
% fBlocksPerGroup
);
813 return fGroups
[group
].Free(transaction
, 0, (end
+ 1) % fBlocksPerGroup
);
818 BlockAllocator::_Initialize(BlockAllocator
* allocator
)
820 TRACE("BlockAllocator::_Initialize()\n");
821 // fLock is already heald
822 Volume
* volume
= allocator
->fVolume
;
824 AllocationBlockGroup
* groups
= allocator
->fGroups
;
825 uint32 numGroups
= allocator
->fNumGroups
- 1;
827 off_t freeBlocks
= 0;
828 TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF
"\n",
831 for (uint32 i
= 0; i
< numGroups
; ++i
) {
832 status_t status
= groups
[i
].Initialize(volume
, i
,
833 allocator
->fBlocksPerGroup
);
834 if (status
!= B_OK
) {
835 mutex_unlock(&allocator
->fLock
);
839 freeBlocks
+= groups
[i
].FreeBits();
840 TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF
"\n",
844 // Last block group may have less blocks
845 status_t status
= groups
[numGroups
].Initialize(volume
, numGroups
,
846 allocator
->fNumBlocks
- allocator
->fBlocksPerGroup
* numGroups
847 - allocator
->fFirstBlock
);
848 if (status
!= B_OK
) {
849 mutex_unlock(&allocator
->fLock
);
853 freeBlocks
+= groups
[numGroups
].FreeBits();
855 TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF
"\n",
858 mutex_unlock(&allocator
->fLock
);
860 if (freeBlocks
!= volume
->NumFreeBlocks()) {
861 TRACE("Counted free blocks (%" B_PRIdOFF
") doesn't match value in the"
862 " superblock (%" B_PRIdOFF
").\n", freeBlocks
,
863 volume
->NumFreeBlocks());