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