1 /* 2 * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de. 3 * This file may be used under the terms of the MIT License. 4 */ 5 6 7 //! Block bitmap handling and allocation policies 8 9 10 #include "BlockAllocator.h" 11 12 #include "Debug.h" 13 #include "Inode.h" 14 #include "Volume.h" 15 16 17 // Things the BlockAllocator should do: 18 19 // - find a range of blocks of a certain size nearby a specific position 20 // - allocating an unsharp range of blocks for pre-allocation 21 // - free blocks 22 // - know how to deal with each allocation, special handling for directories, 23 // files, symlinks, etc. (type sensitive allocation policies) 24 25 // What makes the code complicated is the fact that we are not just reading 26 // in the whole bitmap and operate on that in memory - e.g. a 13 GB partition 27 // with a block size of 2048 bytes already has a 800kB bitmap, and the size 28 // of partitions will grow even more - so that's not an option. 29 // Instead we are reading in every block when it's used - since an allocation 30 // group can span several blocks in the block bitmap, the AllocationBlock 31 // class is there to make handling those easier. 32 33 // The current implementation is only slightly optimized and could probably 34 // be improved a lot. Furthermore, the allocation policies used here should 35 // have some real world tests. 36 37 #if BFS_TRACING && !defined(FS_SHELL) 38 namespace BFSBlockTracing { 39 40 41 class Allocate : public AbstractTraceEntry { 42 public: 43 Allocate(block_run run) 44 : 45 fRun(run) 46 { 47 Initialized(); 48 } 49 50 virtual void AddDump(TraceOutput& out) 51 { 52 out.Print("bfs:alloc %" B_PRId32 ".%" B_PRIu16 ".%" B_PRIu16, 53 fRun.AllocationGroup(), fRun.Start(), fRun.Length()); 54 } 55 56 const block_run& Run() const { return fRun; } 57 58 private: 59 block_run fRun; 60 }; 61 62 63 class Free : public AbstractTraceEntry { 64 public: 65 Free(block_run run) 66 : 67 fRun(run) 68 { 69 Initialized(); 70 } 71 72 virtual void AddDump(TraceOutput& out) 73 { 74 out.Print("bfs:free %" B_PRId32 ".%" B_PRIu16 ".%" B_PRIu16, 75 fRun.AllocationGroup(), fRun.Start(), fRun.Length()); 76 } 77 78 const block_run& Run() const { return fRun; } 79 80 private: 81 block_run fRun; 82 }; 83 84 85 static uint32 86 checksum(const uint8* data, size_t size) 87 { 88 const uint32* data4 = (const uint32*)data; 89 uint32 sum = 0; 90 while (size >= 4) { 91 sum += *data4; 92 data4++; 93 size -= 4; 94 } 95 return sum; 96 } 97 98 99 class Block : public AbstractTraceEntry { 100 public: 101 Block(const char* label, off_t blockNumber, const uint8* data, 102 size_t size, uint32 start = 0, uint32 length = 0) 103 : 104 fBlock(blockNumber), 105 fData(data), 106 fStart(start), 107 fLength(length), 108 fLabel(label) 109 { 110 fSum = checksum(data, size); 111 Initialized(); 112 } 113 114 virtual void AddDump(TraceOutput& out) 115 { 116 out.Print("bfs:%s: block %lld (%p), sum %lu, s/l %lu/%lu", fLabel, 117 fBlock, fData, fSum, fStart, fLength); 118 } 119 120 private: 121 off_t fBlock; 122 const uint8* fData; 123 uint32 fStart; 124 uint32 fLength; 125 uint32 fSum; 126 const char* fLabel; 127 }; 128 129 130 class BlockChange : public AbstractTraceEntry { 131 public: 132 BlockChange(const char* label, int32 block, uint32 oldData, uint32 newData) 133 : 134 fBlock(block), 135 fOldData(oldData), 136 fNewData(newData), 137 fLabel(label) 138 { 139 Initialized(); 140 } 141 142 virtual void AddDump(TraceOutput& out) 143 { 144 out.Print("bfs:%s: block %ld, %08lx -> %08lx", fLabel, 145 fBlock, fOldData, fNewData); 146 } 147 148 private: 149 int32 fBlock; 150 uint32 fOldData; 151 uint32 fNewData; 152 const char* fLabel; 153 }; 154 155 156 } // namespace BFSBlockTracing 157 158 # define T(x) new(std::nothrow) BFSBlockTracing::x; 159 #else 160 # define T(x) ; 161 #endif 162 163 #ifdef DEBUG_ALLOCATION_GROUPS 164 # define CHECK_ALLOCATION_GROUP(group) _CheckGroup(group) 165 #else 166 # define CHECK_ALLOCATION_GROUP(group) ; 167 #endif 168 169 170 class AllocationBlock : public CachedBlock { 171 public: 172 AllocationBlock(Volume* volume); 173 174 inline void Allocate(uint16 start, uint16 numBlocks); 175 inline void Free(uint16 start, uint16 numBlocks); 176 inline bool IsUsed(uint16 block); 177 inline uint32 NextFree(uint16 startBlock); 178 179 status_t SetTo(AllocationGroup& group, uint16 block); 180 status_t SetToWritable(Transaction& transaction, AllocationGroup& group, 181 uint16 block); 182 183 uint32 NumBlockBits() const { return fNumBits; } 184 uint32& Chunk(int32 index) { return ((uint32*)fBlock)[index]; } 185 uint8* Block() const { return (uint8*)fBlock; } 186 187 private: 188 uint32 fNumBits; 189 #ifdef DEBUG 190 bool fWritable; 191 #endif 192 }; 193 194 195 class AllocationGroup { 196 public: 197 AllocationGroup(); 198 199 void AddFreeRange(int32 start, int32 blocks); 200 bool IsFull() const { return fFreeBits == 0; } 201 202 status_t Allocate(Transaction& transaction, uint16 start, int32 length); 203 status_t Free(Transaction& transaction, uint16 start, int32 length); 204 205 uint32 NumBits() const { return fNumBits; } 206 uint32 NumBitmapBlocks() const { return fNumBitmapBlocks; } 207 int32 Start() const { return fStart; } 208 209 private: 210 friend class BlockAllocator; 211 212 uint32 fNumBits; 213 uint32 fNumBitmapBlocks; 214 int32 fStart; 215 int32 fFirstFree; 216 int32 fFreeBits; 217 218 int32 fLargestStart; 219 int32 fLargestLength; 220 bool fLargestValid; 221 }; 222 223 224 AllocationBlock::AllocationBlock(Volume* volume) 225 : CachedBlock(volume) 226 { 227 } 228 229 230 status_t 231 AllocationBlock::SetTo(AllocationGroup& group, uint16 block) 232 { 233 // 8 blocks per byte 234 fNumBits = fVolume->BlockSize() << 3; 235 // the last group may have less bits than the others 236 if ((block + 1) * fNumBits > group.NumBits()) 237 fNumBits = group.NumBits() % fNumBits; 238 239 #ifdef DEBUG 240 fWritable = false; 241 #endif 242 return CachedBlock::SetTo(group.Start() + block); 243 } 244 245 246 status_t 247 AllocationBlock::SetToWritable(Transaction& transaction, AllocationGroup& group, 248 uint16 block) 249 { 250 // 8 blocks per byte 251 fNumBits = fVolume->BlockSize() << 3; 252 // the last group may have less bits in the last block 253 if ((block + 1) * fNumBits > group.NumBits()) 254 fNumBits = group.NumBits() % fNumBits; 255 256 #ifdef DEBUG 257 fWritable = true; 258 #endif 259 return CachedBlock::SetToWritable(transaction, group.Start() + block); 260 } 261 262 263 bool 264 AllocationBlock::IsUsed(uint16 block) 265 { 266 if (block > fNumBits) 267 return true; 268 269 // the block bitmap is accessed in 32-bit chunks 270 return Chunk(block >> 5) & HOST_ENDIAN_TO_BFS_INT32(1UL << (block % 32)); 271 } 272 273 274 uint32 275 AllocationBlock::NextFree(uint16 startBlock) 276 { 277 // Set all bits below the start block in the current chunk, to ignore them. 278 uint32 ignoreNext = (1UL << (startBlock % 32)) - 1; 279 280 for (uint32 offset = ROUNDDOWN(startBlock, 32); 281 offset < fNumBits; offset += 32) { 282 uint32 chunk = Chunk(offset >> 5); 283 chunk |= ignoreNext; 284 ignoreNext = 0; 285 286 if (chunk == UINT32_MAX) 287 continue; 288 uint32 result = offset + ffs(~chunk) - 1; 289 if (result >= fNumBits) 290 break; 291 return result; 292 } 293 294 return fNumBits; 295 } 296 297 298 void 299 AllocationBlock::Allocate(uint16 start, uint16 numBlocks) 300 { 301 ASSERT(start < fNumBits); 302 ASSERT(uint32(start + numBlocks) <= fNumBits); 303 #ifdef DEBUG 304 ASSERT(fWritable); 305 #endif 306 307 T(Block("b-alloc-in", fBlockNumber, fBlock, fVolume->BlockSize(), 308 start, numBlocks)); 309 310 int32 block = start >> 5; 311 312 while (numBlocks > 0) { 313 uint32 mask = 0; 314 for (int32 i = start % 32; i < 32 && numBlocks; i++, numBlocks--) 315 mask |= 1UL << i; 316 317 T(BlockChange("b-alloc", block, Chunk(block), 318 Block(block) | HOST_ENDIAN_TO_BFS_INT32(mask))); 319 320 #if KDEBUG 321 // check for already set blocks 322 if (HOST_ENDIAN_TO_BFS_INT32(mask) & ((uint32*)fBlock)[block]) { 323 FATAL(("AllocationBlock::Allocate(): some blocks are already " 324 "allocated, start = %u, numBlocks = %u\n", start, numBlocks)); 325 panic("blocks already set!"); 326 } 327 #endif 328 329 Chunk(block++) |= HOST_ENDIAN_TO_BFS_INT32(mask); 330 start = 0; 331 } 332 T(Block("b-alloc-out", fBlockNumber, fBlock, fVolume->BlockSize(), 333 start, numBlocks)); 334 } 335 336 337 void 338 AllocationBlock::Free(uint16 start, uint16 numBlocks) 339 { 340 ASSERT(start < fNumBits); 341 ASSERT(uint32(start + numBlocks) <= fNumBits); 342 #ifdef DEBUG 343 ASSERT(fWritable); 344 #endif 345 346 int32 block = start >> 5; 347 348 while (numBlocks > 0) { 349 uint32 mask = 0; 350 for (int32 i = start % 32; i < 32 && numBlocks; i++, numBlocks--) 351 mask |= 1UL << (i % 32); 352 353 T(BlockChange("b-free", block, Chunk(block), 354 Block(block) & HOST_ENDIAN_TO_BFS_INT32(~mask))); 355 356 Chunk(block++) &= HOST_ENDIAN_TO_BFS_INT32(~mask); 357 start = 0; 358 } 359 } 360 361 362 // #pragma mark - 363 364 365 /*! The allocation groups are created and initialized in 366 BlockAllocator::Initialize() and BlockAllocator::InitializeAndClearBitmap() 367 respectively. 368 */ 369 AllocationGroup::AllocationGroup() 370 : 371 fFirstFree(-1), 372 fFreeBits(0), 373 fLargestValid(false) 374 { 375 } 376 377 378 void 379 AllocationGroup::AddFreeRange(int32 start, int32 blocks) 380 { 381 //D(if (blocks > 512) 382 // PRINT(("range of %ld blocks starting at %ld\n",blocks,start))); 383 384 if (fFirstFree == -1) 385 fFirstFree = start; 386 387 if (!fLargestValid || fLargestLength < blocks) { 388 fLargestStart = start; 389 fLargestLength = blocks; 390 fLargestValid = true; 391 } 392 393 fFreeBits += blocks; 394 } 395 396 397 /*! Allocates the specified run in the allocation group. 398 Doesn't check if the run is valid or already allocated partially, nor 399 does it maintain the free ranges hints or the volume's used blocks count. 400 It only does the low-level work of allocating some bits in the block bitmap. 401 Assumes that the block bitmap lock is hold. 402 */ 403 status_t 404 AllocationGroup::Allocate(Transaction& transaction, uint16 start, int32 length) 405 { 406 ASSERT(start + length <= (int32)fNumBits); 407 408 // Update the allocation group info 409 // TODO: this info will be incorrect if something goes wrong later 410 // Note, the fFirstFree block doesn't have to be really free 411 if (start == fFirstFree) 412 fFirstFree = start + length; 413 fFreeBits -= length; 414 415 if (fLargestValid) { 416 bool cut = false; 417 if (fLargestStart == start) { 418 // cut from start 419 fLargestStart += length; 420 fLargestLength -= length; 421 cut = true; 422 } else if (start > fLargestStart 423 && start < fLargestStart + fLargestLength) { 424 // cut from end 425 fLargestLength = start - fLargestStart; 426 cut = true; 427 } 428 if (cut && (fLargestLength < fLargestStart 429 || fLargestLength 430 < (int32)fNumBits - (fLargestStart + fLargestLength))) { 431 // might not be the largest block anymore 432 fLargestValid = false; 433 } 434 } 435 436 Volume* volume = transaction.GetVolume(); 437 438 // calculate block in the block bitmap and position within 439 uint32 bitsPerBlock = volume->BlockSize() << 3; 440 uint32 block = start / bitsPerBlock; 441 start = start % bitsPerBlock; 442 443 AllocationBlock cached(volume); 444 445 while (length > 0) { 446 if (cached.SetToWritable(transaction, *this, block) < B_OK) { 447 fLargestValid = false; 448 RETURN_ERROR(B_IO_ERROR); 449 } 450 451 uint32 numBlocks = length; 452 if (start + numBlocks > cached.NumBlockBits()) 453 numBlocks = cached.NumBlockBits() - start; 454 455 cached.Allocate(start, numBlocks); 456 457 length -= numBlocks; 458 start = 0; 459 block++; 460 } 461 462 return B_OK; 463 } 464 465 466 /*! Frees the specified run in the allocation group. 467 Doesn't check if the run is valid or was not completely allocated, nor 468 does it maintain the free ranges hints or the volume's used blocks count. 469 It only does the low-level work of freeing some bits in the block bitmap. 470 Assumes that the block bitmap lock is hold. 471 */ 472 status_t 473 AllocationGroup::Free(Transaction& transaction, uint16 start, int32 length) 474 { 475 ASSERT(start + length <= (int32)fNumBits); 476 477 // Update the allocation group info 478 // TODO: this info will be incorrect if something goes wrong later 479 if (fFirstFree > start) 480 fFirstFree = start; 481 fFreeBits += length; 482 483 // The range to be freed cannot be part of the valid largest range 484 ASSERT(!fLargestValid || start + length <= fLargestStart 485 || start > fLargestStart); 486 487 if (fLargestValid 488 && (start + length == fLargestStart 489 || fLargestStart + fLargestLength == start 490 || (start < fLargestStart && fLargestStart > fLargestLength) 491 || (start > fLargestStart 492 && (int32)fNumBits - (fLargestStart + fLargestLength) 493 > fLargestLength))) { 494 fLargestValid = false; 495 } 496 497 Volume* volume = transaction.GetVolume(); 498 499 // calculate block in the block bitmap and position within 500 uint32 bitsPerBlock = volume->BlockSize() << 3; 501 uint32 block = start / bitsPerBlock; 502 start = start % bitsPerBlock; 503 504 AllocationBlock cached(volume); 505 506 while (length > 0) { 507 if (cached.SetToWritable(transaction, *this, block) < B_OK) 508 RETURN_ERROR(B_IO_ERROR); 509 510 T(Block("free-1", block, cached.Block(), volume->BlockSize())); 511 uint16 freeLength = length; 512 if (uint32(start + length) > cached.NumBlockBits()) 513 freeLength = cached.NumBlockBits() - start; 514 515 cached.Free(start, freeLength); 516 517 length -= freeLength; 518 start = 0; 519 T(Block("free-2", block, cached.Block(), volume->BlockSize())); 520 block++; 521 } 522 return B_OK; 523 } 524 525 526 // #pragma mark - 527 528 529 BlockAllocator::BlockAllocator(Volume* volume) 530 : 531 fVolume(volume), 532 fGroups(NULL) 533 //fCheckBitmap(NULL), 534 //fCheckCookie(NULL) 535 { 536 recursive_lock_init(&fLock, "bfs allocator"); 537 } 538 539 540 BlockAllocator::~BlockAllocator() 541 { 542 recursive_lock_destroy(&fLock); 543 delete[] fGroups; 544 } 545 546 547 status_t 548 BlockAllocator::Initialize(bool full) 549 { 550 fNumGroups = fVolume->AllocationGroups(); 551 fBlocksPerGroup = fVolume->SuperBlock().BlocksPerAllocationGroup(); 552 fNumBitmapBlocks = fVolume->NumBitmapBlocks(); 553 554 fGroups = new(std::nothrow) AllocationGroup[fNumGroups]; 555 if (fGroups == NULL) 556 return B_NO_MEMORY; 557 558 if (!full) 559 return B_OK; 560 561 recursive_lock_lock(&fLock); 562 // the lock will be released by the _Initialize() method 563 564 thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::_Initialize, 565 "bfs block allocator", B_LOW_PRIORITY, this); 566 if (id < B_OK) 567 return _Initialize(this); 568 569 recursive_lock_transfer_lock(&fLock, id); 570 571 return resume_thread(id); 572 } 573 574 575 status_t 576 BlockAllocator::InitializeAndClearBitmap(Transaction& transaction) 577 { 578 status_t status = Initialize(false); 579 if (status != B_OK) 580 return status; 581 582 uint32 numBits = 8 * fBlocksPerGroup * fVolume->BlockSize(); 583 uint32 blockShift = fVolume->BlockShift(); 584 585 uint32* buffer = (uint32*)malloc(numBits >> 3); 586 if (buffer == NULL) 587 RETURN_ERROR(B_NO_MEMORY); 588 589 memset(buffer, 0, numBits >> 3); 590 591 off_t offset = 1; 592 // the bitmap starts directly after the superblock 593 594 // initialize the AllocationGroup objects and clear the on-disk bitmap 595 596 for (int32 i = 0; i < fNumGroups; i++) { 597 if (write_pos(fVolume->Device(), offset << blockShift, buffer, 598 fBlocksPerGroup << blockShift) < B_OK) { 599 free(buffer); 600 return B_ERROR; 601 } 602 603 // the last allocation group may contain less blocks than the others 604 if (i == fNumGroups - 1) { 605 fGroups[i].fNumBits = fVolume->NumBlocks() - i * numBits; 606 fGroups[i].fNumBitmapBlocks = 1 + ((fGroups[i].NumBits() - 1) 607 >> (blockShift + 3)); 608 } else { 609 fGroups[i].fNumBits = numBits; 610 fGroups[i].fNumBitmapBlocks = fBlocksPerGroup; 611 } 612 fGroups[i].fStart = offset; 613 fGroups[i].fFirstFree = fGroups[i].fLargestStart = 0; 614 fGroups[i].fFreeBits = fGroups[i].fLargestLength = fGroups[i].fNumBits; 615 fGroups[i].fLargestValid = true; 616 617 offset += fBlocksPerGroup; 618 } 619 free(buffer); 620 621 // reserve the boot block, the log area, and the block bitmap itself 622 uint32 reservedBlocks = fVolume->ToBlock(fVolume->Log()) + fVolume->Log().Length(); 623 uint32 blocksToReserve = reservedBlocks; 624 for (int32 i = 0; i < fNumGroups; i++) { 625 int32 reservedBlocksInGroup = min_c(blocksToReserve, numBits); 626 if (fGroups[i].Allocate(transaction, 0, reservedBlocksInGroup) < B_OK) { 627 FATAL(("could not allocate reserved space for block bitmap/log!\n")); 628 return B_ERROR; 629 } 630 blocksToReserve -= reservedBlocksInGroup; 631 if (blocksToReserve == 0) 632 break; 633 } 634 fVolume->SuperBlock().used_blocks 635 = HOST_ENDIAN_TO_BFS_INT64(reservedBlocks); 636 637 return B_OK; 638 } 639 640 641 status_t 642 BlockAllocator::_Initialize(BlockAllocator* allocator) 643 { 644 // The lock must already be held at this point 645 RecursiveLocker locker(allocator->fLock, true); 646 647 Volume* volume = allocator->fVolume; 648 uint32 blocks = allocator->fBlocksPerGroup; 649 uint32 blockShift = volume->BlockShift(); 650 off_t freeBlocks = 0; 651 652 uint32* buffer = (uint32*)malloc(blocks << blockShift); 653 if (buffer == NULL) 654 RETURN_ERROR(B_NO_MEMORY); 655 656 AllocationGroup* groups = allocator->fGroups; 657 off_t offset = 1; 658 uint32 bitsPerGroup = 8 * (blocks << blockShift); 659 int32 numGroups = allocator->fNumGroups; 660 661 for (int32 i = 0; i < numGroups; i++) { 662 if (read_pos(volume->Device(), offset << blockShift, buffer, 663 blocks << blockShift) < B_OK) 664 break; 665 666 // the last allocation group may contain less blocks than the others 667 if (i == numGroups - 1) { 668 groups[i].fNumBits = volume->NumBlocks() - i * bitsPerGroup; 669 groups[i].fNumBitmapBlocks = 1 + ((groups[i].NumBits() - 1) 670 >> (blockShift + 3)); 671 } else { 672 groups[i].fNumBits = bitsPerGroup; 673 groups[i].fNumBitmapBlocks = blocks; 674 } 675 groups[i].fStart = offset; 676 677 // finds all free ranges in this allocation group 678 int32 start = -1, range = 0; 679 int32 numBits = groups[i].fNumBits, bit = 0; 680 int32 count = (numBits + 31) / 32; 681 682 for (int32 k = 0; k < count; k++) { 683 for (int32 j = 0; j < 32 && bit < numBits; j++, bit++) { 684 if (buffer[k] & (1UL << j)) { 685 // block is in use 686 if (range > 0) { 687 groups[i].AddFreeRange(start, range); 688 range = 0; 689 } 690 } else if (range++ == 0) { 691 // block is free, start new free range 692 start = bit; 693 } 694 } 695 } 696 if (range) 697 groups[i].AddFreeRange(start, range); 698 699 freeBlocks += groups[i].fFreeBits; 700 701 offset += blocks; 702 } 703 free(buffer); 704 705 // check if block bitmap and log area are reserved 706 uint32 reservedBlocks = volume->ToBlock(volume->Log()) + volume->Log().Length(); 707 708 if (allocator->CheckBlocks(0, reservedBlocks) != B_OK) { 709 if (volume->IsReadOnly()) { 710 FATAL(("Space for block bitmap or log area is not reserved " 711 "(volume is mounted read-only)!\n")); 712 } else { 713 Transaction transaction(volume, 0); 714 if (groups[0].Allocate(transaction, 0, reservedBlocks) != B_OK) { 715 FATAL(("Could not allocate reserved space for block " 716 "bitmap/log!\n")); 717 volume->Panic(); 718 } else { 719 transaction.Done(); 720 FATAL(("Space for block bitmap or log area was not " 721 "reserved!\n")); 722 } 723 } 724 } 725 726 off_t usedBlocks = volume->NumBlocks() - freeBlocks; 727 if (volume->UsedBlocks() != usedBlocks) { 728 // If the disk in a dirty state at mount time, it's 729 // normal that the values don't match 730 INFORM(("volume reports %" B_PRIdOFF " used blocks, correct is %" 731 B_PRIdOFF "\n", volume->UsedBlocks(), usedBlocks)); 732 volume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks); 733 } 734 735 return B_OK; 736 } 737 738 739 void 740 BlockAllocator::Uninitialize() 741 { 742 // We only have to make sure that the initializer thread isn't running 743 // anymore. 744 recursive_lock_lock(&fLock); 745 } 746 747 748 /*! Tries to allocate between \a minimum, and \a maximum blocks starting 749 at group \a groupIndex with offset \a start. The resulting allocation 750 is put into \a run. 751 752 The number of allocated blocks is always a multiple of \a minimum which 753 has to be a power of two value. 754 */ 755 status_t 756 BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, 757 uint16 start, uint16 maximum, uint16 minimum, block_run& run) 758 { 759 if (maximum == 0) 760 return B_BAD_VALUE; 761 762 FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16 763 ", maximum = %" B_PRIu16 ", minimum = %" B_PRIu16 "\n", 764 groupIndex, start, maximum, minimum)); 765 766 AllocationBlock cached(fVolume); 767 RecursiveLocker lock(fLock); 768 769 uint32 bitsPerFullBlock = fVolume->BlockSize() << 3; 770 771 // Find the block_run that can fulfill the request best 772 int32 bestGroup = -1; 773 int32 bestStart = -1; 774 int32 bestLength = -1; 775 776 for (int32 i = 0; i < fNumGroups + 1; i++, groupIndex++, start = 0) { 777 groupIndex = groupIndex % fNumGroups; 778 AllocationGroup& group = fGroups[groupIndex]; 779 780 CHECK_ALLOCATION_GROUP(groupIndex); 781 782 if (start >= group.NumBits() || group.IsFull()) 783 continue; 784 785 // The wanted maximum is smaller than the largest free block in the 786 // group or already smaller than the minimum 787 788 if (start < group.fFirstFree) 789 start = group.fFirstFree; 790 791 if (group.fLargestValid) { 792 if (group.fLargestLength < bestLength) 793 continue; 794 795 if (group.fLargestStart >= start) { 796 if (group.fLargestLength >= bestLength) { 797 bestGroup = groupIndex; 798 bestStart = group.fLargestStart; 799 bestLength = group.fLargestLength; 800 801 if (bestLength >= maximum) 802 break; 803 } 804 805 // We know everything about this group we have to, let's skip 806 // to the next 807 continue; 808 } 809 } 810 811 // There may be more than one block per allocation group - and 812 // we iterate through it to find a place for the allocation. 813 // (one allocation can't exceed one allocation group) 814 815 uint32 block = start / (fVolume->BlockSize() << 3); 816 int32 currentStart = 0, currentLength = 0; 817 int32 groupLargestStart = -1; 818 int32 groupLargestLength = -1; 819 int32 currentBit = start; 820 bool canFindGroupLargest = start == 0; 821 822 for (; block < group.NumBitmapBlocks(); block++) { 823 if (cached.SetTo(group, block) < B_OK) 824 RETURN_ERROR(B_ERROR); 825 826 T(Block("alloc-in", group.Start() + block, cached.Block(), 827 fVolume->BlockSize(), groupIndex, currentStart)); 828 829 // find a block large enough to hold the allocation 830 for (uint32 bit = start % bitsPerFullBlock; 831 bit < cached.NumBlockBits(); bit++) { 832 if (!cached.IsUsed(bit)) { 833 if (currentLength == 0) { 834 // start new range 835 currentStart = currentBit; 836 } 837 838 // have we found a range large enough to hold numBlocks? 839 if (++currentLength >= maximum) { 840 bestGroup = groupIndex; 841 bestStart = currentStart; 842 bestLength = currentLength; 843 break; 844 } 845 } else { 846 if (currentLength) { 847 // end of a range 848 if (currentLength > bestLength) { 849 bestGroup = groupIndex; 850 bestStart = currentStart; 851 bestLength = currentLength; 852 } 853 if (currentLength > groupLargestLength) { 854 groupLargestStart = currentStart; 855 groupLargestLength = currentLength; 856 } 857 currentLength = 0; 858 } 859 if (((int32)group.NumBits() - currentBit) 860 <= groupLargestLength) { 861 // We can't find a bigger block in this group anymore, 862 // let's skip the rest. 863 block = group.NumBitmapBlocks(); 864 break; 865 } 866 867 // Advance the current bit to one before the next free (or last) bit, 868 // so that the next loop iteration will check the next free bit. 869 const uint32 nextFreeOffset = cached.NextFree(bit) - bit; 870 bit += nextFreeOffset - 1; 871 currentBit += nextFreeOffset - 1; 872 } 873 currentBit++; 874 } 875 876 T(Block("alloc-out", block, cached.Block(), 877 fVolume->BlockSize(), groupIndex, currentStart)); 878 879 if (bestLength >= maximum) { 880 canFindGroupLargest = false; 881 break; 882 } 883 884 // start from the beginning of the next block 885 start = 0; 886 } 887 888 if (currentBit == (int32)group.NumBits()) { 889 if (currentLength > bestLength) { 890 bestGroup = groupIndex; 891 bestStart = currentStart; 892 bestLength = currentLength; 893 } 894 if (canFindGroupLargest && currentLength > groupLargestLength) { 895 groupLargestStart = currentStart; 896 groupLargestLength = currentLength; 897 } 898 } 899 900 if (canFindGroupLargest && !group.fLargestValid 901 && groupLargestLength >= 0) { 902 group.fLargestStart = groupLargestStart; 903 group.fLargestLength = groupLargestLength; 904 group.fLargestValid = true; 905 } 906 907 if (bestLength >= maximum) 908 break; 909 } 910 911 // If we found a suitable range, mark the blocks as in use, and 912 // write the updated block bitmap back to disk 913 if (bestLength < minimum) 914 return B_DEVICE_FULL; 915 916 if (bestLength > maximum) 917 bestLength = maximum; 918 else if (minimum > 1) { 919 // make sure bestLength is a multiple of minimum 920 bestLength = round_down(bestLength, minimum); 921 } 922 923 if (fGroups[bestGroup].Allocate(transaction, bestStart, bestLength) != B_OK) 924 RETURN_ERROR(B_IO_ERROR); 925 926 CHECK_ALLOCATION_GROUP(bestGroup); 927 928 run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(bestGroup); 929 run.start = HOST_ENDIAN_TO_BFS_INT16(bestStart); 930 run.length = HOST_ENDIAN_TO_BFS_INT16(bestLength); 931 932 fVolume->SuperBlock().used_blocks 933 = HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + bestLength); 934 // We are not writing back the disk's superblock - it's 935 // either done by the journaling code, or when the disk 936 // is unmounted. 937 // If the value is not correct at mount time, it will be 938 // fixed anyway. 939 940 // We need to flush any remaining blocks in the new allocation to make sure 941 // they won't interfere with the file cache. 942 block_cache_discard(fVolume->BlockCache(), fVolume->ToBlock(run), 943 run.Length()); 944 945 T(Allocate(run)); 946 return B_OK; 947 } 948 949 950 status_t 951 BlockAllocator::AllocateForInode(Transaction& transaction, 952 const block_run* parent, mode_t type, block_run& run) 953 { 954 // Apply some allocation policies here (AllocateBlocks() will break them 955 // if necessary) - we will start with those described in Dominic Giampaolo's 956 // "Practical File System Design", and see how good they work 957 958 // Files are going in the same allocation group as its parent, 959 // sub-directories will be inserted 8 allocation groups after 960 // the one of the parent 961 uint16 group = parent->AllocationGroup(); 962 if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY) 963 group += 8; 964 965 return AllocateBlocks(transaction, group, 0, 1, 1, run); 966 } 967 968 969 status_t 970 BlockAllocator::Allocate(Transaction& transaction, Inode* inode, 971 off_t numBlocks, block_run& run, uint16 minimum) 972 { 973 if (numBlocks <= 0) 974 return B_ERROR; 975 976 // one block_run can't hold more data than there is in one allocation group 977 if (numBlocks > fGroups[0].NumBits()) 978 numBlocks = fGroups[0].NumBits(); 979 980 // since block_run.length is uint16, the largest number of blocks that 981 // can be covered by a block_run is 65535 982 // TODO: if we drop compatibility, couldn't we do this any better? 983 // There are basically two possibilities: 984 // a) since a length of zero doesn't have any sense, take that for 65536 - 985 // but that could cause many problems (bugs) in other areas 986 // b) reduce the maximum amount of blocks per block_run, so that the 987 // remaining number of free blocks can be used in a useful manner 988 // (like 4 blocks) - but that would also reduce the maximum file size 989 // c) have BlockRun::Length() return (length + 1). 990 if (numBlocks > MAX_BLOCK_RUN_LENGTH) 991 numBlocks = MAX_BLOCK_RUN_LENGTH; 992 993 // Apply some allocation policies here (AllocateBlocks() will break them 994 // if necessary) 995 uint16 group = inode->BlockRun().AllocationGroup(); 996 uint16 start = 0; 997 998 // Are there already allocated blocks? (then just try to allocate near the 999 // last one) 1000 if (inode->Size() > 0) { 1001 const data_stream& data = inode->Node().data; 1002 // TODO: we currently don't care for when the data stream 1003 // is already grown into the indirect ranges 1004 if (data.max_double_indirect_range == 0 1005 && data.max_indirect_range == 0) { 1006 // Since size > 0, there must be a valid block run in this stream 1007 int32 last = 0; 1008 for (; last < NUM_DIRECT_BLOCKS - 1; last++) 1009 if (data.direct[last + 1].IsZero()) 1010 break; 1011 1012 group = data.direct[last].AllocationGroup(); 1013 start = data.direct[last].Start() + data.direct[last].Length(); 1014 } 1015 } else if (inode->IsContainer() || inode->IsSymLink()) { 1016 // directory and symbolic link data will go in the same allocation 1017 // group as the inode is in but after the inode data 1018 start = inode->BlockRun().Start(); 1019 } else { 1020 // file data will start in the next allocation group 1021 group = inode->BlockRun().AllocationGroup() + 1; 1022 } 1023 1024 return AllocateBlocks(transaction, group, start, numBlocks, minimum, run); 1025 } 1026 1027 1028 status_t 1029 BlockAllocator::Free(Transaction& transaction, block_run run) 1030 { 1031 RecursiveLocker lock(fLock); 1032 1033 int32 group = run.AllocationGroup(); 1034 uint16 start = run.Start(); 1035 uint16 length = run.Length(); 1036 1037 FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16 1038 ", length = %" B_PRIu16 "\n", group, start, length)) 1039 T(Free(run)); 1040 1041 // doesn't use Volume::IsValidBlockRun() here because it can check better 1042 // against the group size (the last group may have a different length) 1043 if (group < 0 || group >= fNumGroups 1044 || start > fGroups[group].NumBits() 1045 || uint32(start + length) > fGroups[group].NumBits() 1046 || length == 0) { 1047 FATAL(("tried to free an invalid block_run" 1048 " (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n", 1049 group, start, length)); 1050 DEBUGGER(("tried to free invalid block_run")); 1051 return B_BAD_VALUE; 1052 } 1053 // check if someone tries to free reserved areas at the beginning of the 1054 // drive 1055 if (group < fVolume->Log().AllocationGroup() 1056 || (group == fVolume->Log().AllocationGroup() 1057 && start < uint32(fVolume->Log().Start()) + fVolume->Log().Length())) { 1058 FATAL(("tried to free a reserved block_run" 1059 " (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n", 1060 group, start, length)); 1061 DEBUGGER(("tried to free reserved block")); 1062 return B_BAD_VALUE; 1063 } 1064 #ifdef DEBUG 1065 if (CheckBlockRun(run) != B_OK) 1066 return B_BAD_DATA; 1067 #endif 1068 1069 CHECK_ALLOCATION_GROUP(group); 1070 1071 if (fGroups[group].Free(transaction, start, length) != B_OK) 1072 RETURN_ERROR(B_IO_ERROR); 1073 1074 CHECK_ALLOCATION_GROUP(group); 1075 1076 #ifdef DEBUG 1077 if (CheckBlockRun(run, NULL, false) != B_OK) { 1078 DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just " 1079 "freed)\n")); 1080 } 1081 #endif 1082 1083 fVolume->SuperBlock().used_blocks = 1084 HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() - run.Length()); 1085 return B_OK; 1086 } 1087 1088 1089 #ifdef DEBUG_FRAGMENTER 1090 void 1091 BlockAllocator::Fragment() 1092 { 1093 AllocationBlock cached(fVolume); 1094 RecursiveLocker lock(fLock); 1095 1096 // only leave 4 block holes 1097 static const uint32 kMask = 0x0f0f0f0f; 1098 uint32 valuesPerBlock = fVolume->BlockSize() / 4; 1099 1100 for (int32 i = 0; i < fNumGroups; i++) { 1101 AllocationGroup& group = fGroups[i]; 1102 1103 for (uint32 block = 0; block < group.NumBlocks(); block++) { 1104 Transaction transaction(fVolume, 0); 1105 1106 if (cached.SetToWritable(transaction, group, block) != B_OK) 1107 return; 1108 1109 for (int32 index = 0; index < valuesPerBlock; index++) { 1110 cached.Block(index) |= HOST_ENDIAN_TO_BFS_INT32(kMask); 1111 } 1112 1113 transaction.Done(); 1114 } 1115 } 1116 } 1117 #endif // DEBUG_FRAGMENTER 1118 1119 1120 #ifdef DEBUG_ALLOCATION_GROUPS 1121 void 1122 BlockAllocator::_CheckGroup(int32 groupIndex) const 1123 { 1124 AllocationBlock cached(fVolume); 1125 ASSERT_LOCKED_RECURSIVE(&fLock); 1126 1127 AllocationGroup& group = fGroups[groupIndex]; 1128 1129 int32 currentStart = 0, currentLength = 0; 1130 int32 firstFree = -1; 1131 int32 largestStart = -1; 1132 int32 largestLength = 0; 1133 int32 currentBit = 0; 1134 1135 for (uint32 block = 0; block < group.NumBlocks(); block++) { 1136 if (cached.SetTo(group, block) < B_OK) { 1137 panic("setting group block %d failed\n", (int)block); 1138 return; 1139 } 1140 1141 for (uint32 bit = 0; bit < cached.NumBlockBits(); bit++) { 1142 if (!cached.IsUsed(bit)) { 1143 if (firstFree < 0) { 1144 firstFree = currentBit; 1145 if (!group.fLargestValid) { 1146 if (firstFree >= 0 && firstFree < group.fFirstFree) { 1147 // mostly harmless but noteworthy 1148 dprintf("group %d first free too late: should be " 1149 "%d, is %d\n", (int)groupIndex, (int)firstFree, 1150 (int)group.fFirstFree); 1151 } 1152 return; 1153 } 1154 } 1155 1156 if (currentLength == 0) { 1157 // start new range 1158 currentStart = currentBit; 1159 } 1160 currentLength++; 1161 } else if (currentLength) { 1162 // end of a range 1163 if (currentLength > largestLength) { 1164 largestStart = currentStart; 1165 largestLength = currentLength; 1166 } 1167 currentLength = 0; 1168 } 1169 currentBit++; 1170 } 1171 } 1172 1173 if (currentLength > largestLength) { 1174 largestStart = currentStart; 1175 largestLength = currentLength; 1176 } 1177 1178 if (firstFree >= 0 && firstFree < group.fFirstFree) { 1179 // mostly harmless but noteworthy 1180 dprintf("group %d first free too late: should be %d, is %d\n", 1181 (int)groupIndex, (int)firstFree, (int)group.fFirstFree); 1182 } 1183 if (group.fLargestValid 1184 && (largestStart != group.fLargestStart 1185 || largestLength != group.fLargestLength)) { 1186 panic("bfs %p: group %d largest differs: %d.%d, checked %d.%d.\n", 1187 fVolume, (int)groupIndex, (int)group.fLargestStart, 1188 (int)group.fLargestLength, (int)largestStart, (int)largestLength); 1189 } 1190 } 1191 #endif // DEBUG_ALLOCATION_GROUPS 1192 1193 1194 status_t 1195 BlockAllocator::Trim(uint64 offset, uint64 size, uint64& trimmedSize) 1196 { 1197 // TODO: Remove this check when offset and size handling is implemented 1198 if (offset != 0 1199 || fVolume->NumBlocks() < 0 1200 || size < (uint64)fVolume->NumBlocks() * fVolume->BlockSize()) { 1201 INFORM(("BFS Trim: Ranges smaller than the file system size" 1202 " are not supported yet.\n")); 1203 return B_UNSUPPORTED; 1204 } 1205 1206 const uint32 kTrimRanges = 128; 1207 fs_trim_data* trimData = (fs_trim_data*)malloc(sizeof(fs_trim_data) 1208 + 2 * sizeof(uint64) * (kTrimRanges - 1)); 1209 if (trimData == NULL) 1210 return B_NO_MEMORY; 1211 1212 MemoryDeleter deleter(trimData); 1213 RecursiveLocker locker(fLock); 1214 1215 // TODO: take given offset and size into account! 1216 int32 lastGroup = fNumGroups - 1; 1217 uint32 firstBlock = 0; 1218 uint32 firstBit = 0; 1219 uint64 currentBlock = 0; 1220 uint32 blockShift = fVolume->BlockShift(); 1221 1222 uint64 firstFree = 0; 1223 uint64 freeLength = 0; 1224 1225 trimData->range_count = 0; 1226 trimmedSize = 0; 1227 1228 AllocationBlock cached(fVolume); 1229 for (int32 groupIndex = 0; groupIndex <= lastGroup; groupIndex++) { 1230 AllocationGroup& group = fGroups[groupIndex]; 1231 1232 for (uint32 block = firstBlock; block < group.NumBitmapBlocks(); block++) { 1233 cached.SetTo(group, block); 1234 1235 for (uint32 i = firstBit; i < cached.NumBlockBits(); i++) { 1236 if (cached.IsUsed(i)) { 1237 // Block is in use 1238 if (freeLength > 0) { 1239 // Overflow is unlikely to happen, but check it anyway 1240 if ((firstFree << blockShift) >> blockShift 1241 != firstFree 1242 || (freeLength << blockShift) >> blockShift 1243 != freeLength) { 1244 FATAL(("BlockAllocator::Trim:" 1245 " Overflow detected!\n")); 1246 return B_ERROR; 1247 } 1248 status_t status = _TrimNext(*trimData, kTrimRanges, 1249 firstFree << blockShift, freeLength << blockShift, 1250 false, trimmedSize); 1251 if (status != B_OK) 1252 return status; 1253 1254 freeLength = 0; 1255 } 1256 } else if (freeLength++ == 0) { 1257 // Block is free, start new free range 1258 firstFree = currentBlock; 1259 } 1260 1261 currentBlock++; 1262 } 1263 } 1264 1265 firstBlock = 0; 1266 firstBit = 0; 1267 } 1268 1269 return _TrimNext(*trimData, kTrimRanges, firstFree << blockShift, 1270 freeLength << blockShift, true, trimmedSize); 1271 } 1272 1273 1274 // #pragma mark - 1275 1276 1277 /*! Checks whether or not the specified block range is allocated or not, 1278 depending on the \a allocated argument. 1279 */ 1280 status_t 1281 BlockAllocator::CheckBlocks(off_t start, off_t length, bool allocated, 1282 off_t* firstError) 1283 { 1284 if (start < 0 || start + length > fVolume->NumBlocks()) 1285 return B_BAD_VALUE; 1286 1287 off_t block = start; 1288 1289 int32 group = start >> fVolume->AllocationGroupShift(); 1290 uint32 bitmapBlock = start / (fVolume->BlockSize() << 3); 1291 uint32 blockOffset = start % (fVolume->BlockSize() << 3); 1292 1293 uint32 groupBlock = bitmapBlock % fBlocksPerGroup; 1294 1295 AllocationBlock cached(fVolume); 1296 1297 while (groupBlock < fGroups[group].NumBitmapBlocks() && length > 0) { 1298 if (cached.SetTo(fGroups[group], groupBlock) != B_OK) 1299 RETURN_ERROR(B_IO_ERROR); 1300 1301 for (; blockOffset < cached.NumBlockBits() && length > 0; 1302 blockOffset++, length--, block++) { 1303 if (cached.IsUsed(blockOffset) != allocated) { 1304 PRINT(("CheckBlocks: Erroneous block (group = %" B_PRId32 1305 ", groupBlock = %" B_PRIu32 1306 ", blockOffset = %" B_PRIu32 ")!\n", 1307 group, groupBlock, blockOffset)); 1308 1309 if (firstError) 1310 *firstError = block; 1311 1312 return B_BAD_DATA; 1313 } 1314 } 1315 1316 blockOffset = 0; 1317 1318 if (++groupBlock >= fGroups[group].NumBitmapBlocks()) { 1319 groupBlock = 0; 1320 group++; 1321 } 1322 } 1323 1324 return B_OK; 1325 } 1326 1327 1328 bool 1329 BlockAllocator::IsValidBlockRun(block_run run, const char* type) 1330 { 1331 if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups 1332 || run.Start() > fGroups[run.AllocationGroup()].fNumBits 1333 || uint32(run.Start() + run.Length()) 1334 > fGroups[run.AllocationGroup()].fNumBits 1335 || run.length == 0) { 1336 PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")" 1337 " is invalid!\n", type, run.AllocationGroup(), run.Start(), 1338 run.Length())); 1339 return false; 1340 } 1341 return true; 1342 } 1343 1344 1345 status_t 1346 BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated) 1347 { 1348 if (!IsValidBlockRun(run, type)) 1349 return B_BAD_DATA; 1350 1351 status_t status = CheckBlocks(fVolume->ToBlock(run), run.Length(), 1352 allocated); 1353 if (status != B_OK) { 1354 PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")" 1355 " is only partially allocated!\n", type, run.AllocationGroup(), 1356 run.Start(), run.Length())); 1357 } 1358 1359 return status; 1360 } 1361 1362 1363 bool 1364 BlockAllocator::_AddTrim(fs_trim_data& trimData, uint32 maxRanges, 1365 uint64 offset, uint64 size) 1366 { 1367 ASSERT(trimData.range_count < maxRanges); 1368 if (size == 0) 1369 return false; 1370 1371 trimData.ranges[trimData.range_count].offset = offset; 1372 trimData.ranges[trimData.range_count].size = size; 1373 trimData.range_count++; 1374 1375 return (trimData.range_count == maxRanges); 1376 } 1377 1378 1379 status_t 1380 BlockAllocator::_TrimNext(fs_trim_data& trimData, uint32 maxRanges, 1381 uint64 offset, uint64 size, bool force, uint64& trimmedSize) 1382 { 1383 PRINT(("_TrimNext(index %" B_PRIu32 ", offset %" B_PRIu64 ", size %" 1384 B_PRIu64 ")\n", trimData.range_count, offset, size)); 1385 1386 const bool rangesFilled = _AddTrim(trimData, maxRanges, offset, size); 1387 1388 if (rangesFilled || force) { 1389 // Trim now 1390 trimData.trimmed_size = 0; 1391 #ifdef DEBUG_TRIM 1392 dprintf("TRIM: BFS: free ranges (bytes):\n"); 1393 for (uint32 i = 0; i < trimData.range_count; i++) { 1394 dprintf("[%3" B_PRIu32 "] %" B_PRIu64 " : %" B_PRIu64 "\n", i, 1395 trimData.ranges[i].offset, trimData.ranges[i].size); 1396 } 1397 #endif 1398 if (ioctl(fVolume->Device(), B_TRIM_DEVICE, &trimData, 1399 sizeof(fs_trim_data) 1400 + 2 * sizeof(uint64) * (trimData.range_count - 1)) != 0) { 1401 return errno; 1402 } 1403 1404 trimmedSize += trimData.trimmed_size; 1405 trimData.range_count = 0; 1406 } 1407 1408 return B_OK; 1409 } 1410 1411 1412 // #pragma mark - debugger commands 1413 1414 1415 #ifdef BFS_DEBUGGER_COMMANDS 1416 1417 void 1418 BlockAllocator::Dump(int32 index) 1419 { 1420 kprintf("allocation groups: %" B_PRId32 " (base %p)\n", fNumGroups, fGroups); 1421 kprintf("blocks per group: %" B_PRId32 "\n", fBlocksPerGroup); 1422 1423 for (int32 i = 0; i < fNumGroups; i++) { 1424 if (index != -1 && i != index) 1425 continue; 1426 1427 AllocationGroup& group = fGroups[i]; 1428 1429 kprintf("[%3" B_PRId32 "] num bits: %" B_PRIu32 " (%p)\n", i, 1430 group.NumBits(), &group); 1431 kprintf(" num blocks: %" B_PRIu32 "\n", group.NumBitmapBlocks()); 1432 kprintf(" start: %" B_PRId32 "\n", group.Start()); 1433 kprintf(" first free: %" B_PRId32 "\n", group.fFirstFree); 1434 kprintf(" largest start: %" B_PRId32 "%s\n", group.fLargestStart, 1435 group.fLargestValid ? "" : " (invalid)"); 1436 kprintf(" largest length: %" B_PRId32 "\n", group.fLargestLength); 1437 kprintf(" free bits: %" B_PRId32 "\n", group.fFreeBits); 1438 } 1439 } 1440 1441 1442 #if BFS_TRACING 1443 static char kTraceBuffer[256]; 1444 1445 1446 int 1447 dump_block_allocator_blocks(int argc, char** argv) 1448 { 1449 if (argc != 3 || !strcmp(argv[1], "--help")) { 1450 kprintf("usage: %s <ptr-to-volume> <block>\n", argv[0]); 1451 return 0; 1452 } 1453 1454 Volume* volume = (Volume*)parse_expression(argv[1]); 1455 off_t block = parse_expression(argv[2]); 1456 1457 // iterate over all tracing entries to find overlapping actions 1458 1459 using namespace BFSBlockTracing; 1460 1461 LazyTraceOutput out(kTraceBuffer, sizeof(kTraceBuffer), 0); 1462 TraceEntryIterator iterator; 1463 while (TraceEntry* _entry = iterator.Next()) { 1464 if (Allocate* entry = dynamic_cast<Allocate*>(_entry)) { 1465 off_t first = volume->ToBlock(entry->Run()); 1466 off_t last = first - 1 + entry->Run().Length(); 1467 if (block >= first && block <= last) { 1468 out.Clear(); 1469 const char* dump = out.DumpEntry(entry); 1470 kprintf("%5ld. %s\n", iterator.Index(), dump); 1471 } 1472 } else if (Free* entry = dynamic_cast<Free*>(_entry)) { 1473 off_t first = volume->ToBlock(entry->Run()); 1474 off_t last = first - 1 + entry->Run().Length(); 1475 if (block >= first && block <= last) { 1476 out.Clear(); 1477 const char* dump = out.DumpEntry(entry); 1478 kprintf("%5ld. %s\n", iterator.Index(), dump); 1479 } 1480 } 1481 } 1482 1483 return 0; 1484 } 1485 #endif 1486 1487 1488 int 1489 dump_block_allocator(int argc, char** argv) 1490 { 1491 int32 group = -1; 1492 if (argc == 3) { 1493 group = parse_expression(argv[2]); 1494 argc--; 1495 } 1496 1497 if (argc != 2 || !strcmp(argv[1], "--help")) { 1498 kprintf("usage: %s <ptr-to-volume> [group]\n", argv[0]); 1499 return 0; 1500 } 1501 1502 Volume* volume = (Volume*)parse_expression(argv[1]); 1503 BlockAllocator& allocator = volume->Allocator(); 1504 1505 allocator.Dump(group); 1506 return 0; 1507 } 1508 1509 #endif // BFS_DEBUGGER_COMMANDS 1510 1511