btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / ext2 / BlockAllocator.cpp
blobbb4f85edfc608af9f2d5b610bee40ca9941c1732
1 /*
2 * Copyright 2001-2011, Haiku Inc. All rights reserved.
3 * This file may be used under the terms of the MIT License.
5 * Authors:
6 * Janito V. Ferreira Filho
7 * Jérôme Duval
8 */
11 #include "BlockAllocator.h"
13 #include <util/AutoLock.h>
15 #include "BitmapBlock.h"
16 #include "Inode.h"
19 #undef ASSERT
20 //#define TRACE_EXT2
21 #ifdef TRACE_EXT2
22 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
23 # define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); }
24 #else
25 # define TRACE(x...) ;
26 # define ASSERT(x) ;
27 #endif
28 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x)
31 class AllocationBlockGroup : public TransactionListener {
32 public:
33 AllocationBlockGroup();
34 ~AllocationBlockGroup();
36 status_t Initialize(Volume* volume, uint32 blockGroup,
37 uint32 numBits);
39 bool IsFull() const;
41 status_t Allocate(Transaction& transaction, fsblock_t start,
42 uint32 length);
43 status_t Free(Transaction& transaction, uint32 start,
44 uint32 length);
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();
59 private:
60 status_t _ScanFreeRanges();
61 void _AddFreeRange(uint32 start, uint32 length);
62 void _LockInTransaction(Transaction& transaction);
63 status_t _InitGroup(Transaction& transaction);
64 bool _IsSparse();
65 uint32 _FirstFreeBlock();
67 Volume* fVolume;
68 uint32 fBlockGroup;
69 ext2_block_group* fGroupDescriptor;
71 mutex fLock;
72 mutex fTransactionLock;
73 int32 fCurrentTransaction;
75 fsblock_t fStart;
76 uint32 fNumBits;
77 fsblock_t fBitmapBlock;
79 uint32 fFreeBits;
80 uint32 fFirstFree;
81 uint32 fLargestStart;
82 uint32 fLargestLength;
84 uint32 fPreviousFreeBits;
85 uint32 fPreviousFirstFree;
86 uint32 fPreviousLargestStart;
87 uint32 fPreviousLargestLength;
91 AllocationBlockGroup::AllocationBlockGroup()
93 fVolume(NULL),
94 fBlockGroup(0),
95 fGroupDescriptor(NULL),
96 fStart(0),
97 fNumBits(0),
98 fBitmapBlock(0),
99 fFreeBits(0),
100 fFirstFree(0),
101 fLargestStart(0),
102 fLargestLength(0),
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);
120 status_t
121 AllocationBlockGroup::Initialize(Volume* volume, uint32 blockGroup,
122 uint32 numBits)
124 fVolume = volume;
125 fBlockGroup = blockGroup;
126 fNumBits = numBits;
127 fStart = blockGroup * numBits + fVolume->FirstDataBlock();
129 status_t status = fVolume->GetBlockGroup(blockGroup, &fGroupDescriptor);
130 if (status != B_OK)
131 return status;
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);
140 return B_OK;
143 status = _ScanFreeRanges();
144 if (status != B_OK)
145 return status;
147 if (fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature())
148 != fFreeBits) {
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()));
154 return B_BAD_DATA;
157 fPreviousFreeBits = fFreeBits;
158 fPreviousFirstFree = fFirstFree;
159 fPreviousLargestStart = fLargestStart;
160 fPreviousLargestLength = fLargestLength;
162 return status;
166 status_t
167 AllocationBlockGroup::_ScanFreeRanges()
169 TRACE("AllocationBlockGroup::_ScanFreeRanges() for group %" B_PRIu32 "\n",
170 fBlockGroup);
171 BitmapBlock block(fVolume, fNumBits);
173 if (!block.SetTo(fBitmapBlock))
174 return B_ERROR;
176 fFreeBits = 0;
177 uint32 start = 0;
178 uint32 end = 0;
180 while (end < fNumBits) {
181 block.FindNextUnmarked(start);
182 ASSERT(block.CheckMarked(end, start - end));
183 end = start;
185 if (start != block.NumBits()) {
186 block.FindNextMarked(end);
187 _AddFreeRange(start, end - start);
188 ASSERT(block.CheckUnmarked(fLargestStart, fLargestLength));
189 start = end;
193 return B_OK;
197 bool
198 AllocationBlockGroup::IsFull() const
200 return fFreeBits == 0;
204 status_t
205 AllocationBlockGroup::Allocate(Transaction& transaction, fsblock_t _start,
206 uint32 length)
208 uint32 start = _start - fStart;
209 TRACE("AllocationBlockGroup::Allocate(%" B_PRIu32 ",%" B_PRIu32 ")\n",
210 start, length);
211 if (length == 0)
212 return B_OK;
214 uint32 end = start + length;
215 if (end > fNumBits)
216 return B_BAD_VALUE;
218 _LockInTransaction(transaction);
219 _InitGroup(transaction);
221 BitmapBlock block(fVolume, fNumBits);
223 if (!block.SetToWritable(transaction, fBitmapBlock))
224 return B_ERROR;
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);
233 return B_ERROR;
236 fFreeBits -= 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
252 - (start + length);
254 if (firstLength >= secondLength) {
255 fLargestLength = firstLength;
256 } else {
257 fLargestLength = secondLength;
258 fLargestStart = start + length;
260 } else {
261 // No need to revalidate the largest free range
262 return B_OK;
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));
273 return B_OK;
277 status_t
278 AllocationBlockGroup::Free(Transaction& transaction, uint32 start,
279 uint32 length)
281 TRACE("AllocationBlockGroup::Free(): start: %" B_PRIu32 ", length %"
282 B_PRIu32 "\n", start, length);
284 if (length == 0)
285 return B_OK;
287 uint32 end = start + length;
288 if (end > fNumBits)
289 return B_BAD_VALUE;
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))
298 return B_ERROR;
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);
307 return B_ERROR;
310 TRACE("AllocationGroup::Free(): Unmarked bits in bitmap\n");
312 if (fFirstFree > start)
313 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);
327 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));
339 fFreeBits += length;
340 fGroupDescriptor->SetFreeBlocks(fFreeBits, fVolume->Has64bitFeature());
341 fVolume->WriteBlockGroup(transaction, fBlockGroup);
343 return B_OK;
347 status_t
348 AllocationBlockGroup::FreeAll(Transaction& transaction)
350 return Free(transaction, 0, fNumBits);
354 uint32
355 AllocationBlockGroup::NumBits() const
357 return fNumBits;
361 uint32
362 AllocationBlockGroup::FreeBits() const
364 return fFreeBits;
368 fsblock_t
369 AllocationBlockGroup::Start() const
371 return fStart;
375 fsblock_t
376 AllocationBlockGroup::LargestStart() const
378 return fStart + fLargestStart;
382 uint32
383 AllocationBlockGroup::LargestLength() const
385 return fLargestLength;
389 void
390 AllocationBlockGroup::_AddFreeRange(uint32 start, uint32 length)
392 if (IsFull()) {
393 fFirstFree = start;
394 fLargestStart = start;
395 fLargestLength = length;
396 } else if (length > fLargestLength) {
397 fLargestStart = start;
398 fLargestLength = length;
401 fFreeBits += length;
405 void
406 AllocationBlockGroup::_LockInTransaction(Transaction& transaction)
408 mutex_lock(&fLock);
410 if (transaction.ID() != fCurrentTransaction) {
411 mutex_unlock(&fLock);
413 mutex_lock(&fTransactionLock);
414 mutex_lock(&fLock);
416 fCurrentTransaction = transaction.ID();
417 transaction.AddListener(this);
420 mutex_unlock(&fLock);
424 status_t
425 AllocationBlockGroup::_InitGroup(Transaction& transaction)
427 TRACE("AllocationBlockGroup::_InitGroup()\n");
428 uint16 flags = fGroupDescriptor->Flags();
429 if ((flags & EXT2_BLOCK_GROUP_BLOCK_UNINIT) == 0)
430 return B_OK;
432 TRACE("AllocationBlockGroup::_InitGroup() initing\n");
434 BitmapBlock blockBitmap(fVolume, fNumBits);
435 if (!blockBitmap.SetToWritable(transaction, fBitmapBlock))
436 return B_ERROR;
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();
444 if (status != B_OK)
445 return status;
447 if (fGroupDescriptor->FreeBlocks(fVolume->Has64bitFeature())
448 != fFreeBits) {
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()));
454 return B_BAD_DATA;
457 TRACE("AllocationBlockGroup::_InitGroup() init OK\n");
459 return B_OK;
463 bool
464 AllocationBlockGroup::_IsSparse()
466 if (fBlockGroup <= 1)
467 return true;
468 if (fBlockGroup & 0x1)
469 return false;
471 uint32 i = fBlockGroup;
472 while (i % 7 == 0)
473 i /= 7;
474 if (i == 1)
475 return true;
477 i = fBlockGroup;
478 while (i % 5 == 0)
479 i /= 5;
480 if (i == 1)
481 return true;
483 i = fBlockGroup;
484 while (i % 3 == 0)
485 i /= 3;
486 if (i == 1)
487 return true;
489 return false;
493 uint32
494 AllocationBlockGroup::_FirstFreeBlock()
496 uint32 first = 1;
497 if (_IsSparse())
498 first = 0;
499 else if (!fVolume->HasMetaGroupFeature()) {
500 first += fVolume->SuperBlock().ReservedGDTBlocks();
501 first += fVolume->NumGroups();
503 return first;
507 void
508 AllocationBlockGroup::TransactionDone(bool success)
510 if (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;
517 } else {
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;
528 void
529 AllocationBlockGroup::RemovedFromTransaction()
531 mutex_unlock(&fTransactionLock);
532 fCurrentTransaction = -1;
536 BlockAllocator::BlockAllocator(Volume* volume)
538 fVolume(volume),
539 fGroups(NULL),
540 fBlocksPerGroup(0),
541 fNumBlocks(0),
542 fNumGroups(0)
544 mutex_init(&fLock, "ext2 block allocator");
548 BlockAllocator::~BlockAllocator()
550 mutex_destroy(&fLock);
552 if (fGroups != NULL)
553 delete [] fGroups;
557 status_t
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];
571 if (fGroups == NULL)
572 return B_NO_MEMORY;
574 TRACE("BlockAllocator::Initialize(): allocated allocation block groups\n");
576 mutex_lock(&fLock);
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);
582 if (id < B_OK)
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");
590 return B_ERROR;
594 status_t
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)
634 break;
638 start = 0;
641 if (bestLength >= maximum)
642 break;
644 groupNum = 0;
646 group = &fGroups[0];
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,
653 minimum);
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,
665 bestLength);
666 if (status != B_OK) {
667 TRACE("BlockAllocator::AllocateBlocks(): Failed to allocate %" B_PRIu32
668 " blocks inside block group %" B_PRIu32 ".\n", bestLength, bestGroup);
669 return status;
672 start = bestStart;
673 length = bestLength;
674 blockGroup = bestGroup;
676 return B_OK;
680 status_t
681 BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
682 off_t numBlocks, uint32 minimum, fsblock_t& start, uint32& allocated)
684 if (numBlocks <= 0)
685 return B_ERROR;
686 if (start > fNumBlocks)
687 return B_BAD_VALUE;
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];
702 } else {
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
710 + numIndexes2;
711 // uint32 indexesInTripleIndirect = indexesInDoubleIndirect
712 // + numIndexes3;
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)
724 return B_IO_ERROR;
726 uint32 index = (numBlocks - indexesInDoubleIndirect)
727 / numIndexes3;
728 doubleIndirectBlock = indirectData[index];
731 if (numBlocks > indexesInIndirect) {
732 // Double indirect blocks
733 indirectData = (uint32*)cached.SetTo(doubleIndirectBlock);
734 if (indirectData == NULL)
735 return B_IO_ERROR;
737 uint32 index = (numBlocks - indexesInIndirect) / numIndexes2;
738 indirectBlock = indirectData[index];
741 indirectData = (uint32*)cached.SetTo(indirectBlock);
742 if (indirectData == NULL)
743 return B_IO_ERROR;
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,
756 allocated);
760 status_t
761 BlockAllocator::Free(Transaction& transaction, fsblock_t start, uint32 length)
763 TRACE("BlockAllocator::Free(%" B_PRIu64 ", %" B_PRIu32 ")\n", start,
764 length);
765 MutexLocker lock(fLock);
767 if (start <= fFirstBlock) {
768 panic("Trying to free superblock!\n");
769 return B_BAD_VALUE;
772 if (length == 0)
773 return B_OK;
774 if (start > fNumBlocks || length > fNumBlocks)
775 return B_BAD_VALUE;
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);
800 if (status != B_OK)
801 return status;
803 for (++group; group < lastGroup; ++group) {
804 TRACE("BlockAllocator::Free(): Freeing all from group %" B_PRIu32 "\n",
805 group);
806 status = fGroups[group].FreeAll(transaction);
807 if (status != B_OK)
808 return status;
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);
817 /*static*/ status_t
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",
829 freeBlocks);
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);
836 return status;
839 freeBlocks += groups[i].FreeBits();
840 TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF "\n",
841 freeBlocks);
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);
850 return status;
853 freeBlocks += groups[numGroups].FreeBits();
855 TRACE("BlockAllocator::_Initialize(): free blocks: %" B_PRIdOFF "\n",
856 freeBlocks);
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());
864 return B_BAD_DATA;
867 return B_OK;