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& Block(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 NumBlocks() const { return fNumBlocks; } 206 int32 Start() const { return fStart; } 207 208 private: 209 friend class BlockAllocator; 210 211 uint32 fNumBits; 212 uint32 fNumBlocks; 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 blocks 269 return Block(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, Block(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 Block(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, Block(block), 329 Block(block) & HOST_ENDIAN_TO_BFS_INT32(~mask))); 330 331 Block(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 //fNumBlocks = (fVolume->NumBlocks() + fVolume->BlockSize() * 8 - 1) 528 /// (fVolume->BlockSize() * 8); 529 fNumBlocks = fVolume->NumBitmapBlocks(); 530 531 fGroups = new(std::nothrow) AllocationGroup[fNumGroups]; 532 if (fGroups == NULL) 533 return B_NO_MEMORY; 534 535 if (!full) 536 return B_OK; 537 538 recursive_lock_lock(&fLock); 539 // the lock will be released by the _Initialize() method 540 541 thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::_Initialize, 542 "bfs block allocator", B_LOW_PRIORITY, this); 543 if (id < B_OK) 544 return _Initialize(this); 545 546 recursive_lock_transfer_lock(&fLock, id); 547 548 return resume_thread(id); 549 } 550 551 552 status_t 553 BlockAllocator::InitializeAndClearBitmap(Transaction& transaction) 554 { 555 status_t status = Initialize(false); 556 if (status != B_OK) 557 return status; 558 559 uint32 numBits = 8 * fBlocksPerGroup * fVolume->BlockSize(); 560 uint32 blockShift = fVolume->BlockShift(); 561 562 uint32* buffer = (uint32*)malloc(numBits >> 3); 563 if (buffer == NULL) 564 RETURN_ERROR(B_NO_MEMORY); 565 566 memset(buffer, 0, numBits >> 3); 567 568 off_t offset = 1; 569 // the bitmap starts directly after the superblock 570 571 // initialize the AllocationGroup objects and clear the on-disk bitmap 572 573 for (int32 i = 0; i < fNumGroups; i++) { 574 if (write_pos(fVolume->Device(), offset << blockShift, buffer, 575 fBlocksPerGroup << blockShift) < B_OK) { 576 free(buffer); 577 return B_ERROR; 578 } 579 580 // the last allocation group may contain less blocks than the others 581 if (i == fNumGroups - 1) { 582 fGroups[i].fNumBits = fVolume->NumBlocks() - i * numBits; 583 fGroups[i].fNumBlocks = 1 + ((fGroups[i].NumBits() - 1) 584 >> (blockShift + 3)); 585 } else { 586 fGroups[i].fNumBits = numBits; 587 fGroups[i].fNumBlocks = fBlocksPerGroup; 588 } 589 fGroups[i].fStart = offset; 590 fGroups[i].fFirstFree = fGroups[i].fLargestStart = 0; 591 fGroups[i].fFreeBits = fGroups[i].fLargestLength = fGroups[i].fNumBits; 592 fGroups[i].fLargestValid = true; 593 594 offset += fBlocksPerGroup; 595 } 596 free(buffer); 597 598 // reserve the boot block, the log area, and the block bitmap itself 599 uint32 reservedBlocks = fVolume->ToBlock(fVolume->Log()) + fVolume->Log().Length(); 600 uint32 blocksToReserve = reservedBlocks; 601 for (int32 i = 0; i < fNumGroups; i++) { 602 int32 reservedBlocksInGroup = min_c(blocksToReserve, numBits); 603 if (fGroups[i].Allocate(transaction, 0, reservedBlocksInGroup) < B_OK) { 604 FATAL(("could not allocate reserved space for block bitmap/log!\n")); 605 return B_ERROR; 606 } 607 blocksToReserve -= reservedBlocksInGroup; 608 if (blocksToReserve == 0) 609 break; 610 } 611 fVolume->SuperBlock().used_blocks 612 = HOST_ENDIAN_TO_BFS_INT64(reservedBlocks); 613 614 return B_OK; 615 } 616 617 618 status_t 619 BlockAllocator::_Initialize(BlockAllocator* allocator) 620 { 621 // The lock must already be held at this point 622 RecursiveLocker locker(allocator->fLock, true); 623 624 Volume* volume = allocator->fVolume; 625 uint32 blocks = allocator->fBlocksPerGroup; 626 uint32 blockShift = volume->BlockShift(); 627 off_t freeBlocks = 0; 628 629 uint32* buffer = (uint32*)malloc(blocks << blockShift); 630 if (buffer == NULL) 631 RETURN_ERROR(B_NO_MEMORY); 632 633 AllocationGroup* groups = allocator->fGroups; 634 off_t offset = 1; 635 uint32 bitsPerGroup = 8 * (blocks << blockShift); 636 int32 numGroups = allocator->fNumGroups; 637 638 for (int32 i = 0; i < numGroups; i++) { 639 if (read_pos(volume->Device(), offset << blockShift, buffer, 640 blocks << blockShift) < B_OK) 641 break; 642 643 // the last allocation group may contain less blocks than the others 644 if (i == numGroups - 1) { 645 groups[i].fNumBits = volume->NumBlocks() - i * bitsPerGroup; 646 groups[i].fNumBlocks = 1 + ((groups[i].NumBits() - 1) 647 >> (blockShift + 3)); 648 } else { 649 groups[i].fNumBits = bitsPerGroup; 650 groups[i].fNumBlocks = blocks; 651 } 652 groups[i].fStart = offset; 653 654 // finds all free ranges in this allocation group 655 int32 start = -1, range = 0; 656 int32 numBits = groups[i].fNumBits, bit = 0; 657 int32 count = (numBits + 31) / 32; 658 659 for (int32 k = 0; k < count; k++) { 660 for (int32 j = 0; j < 32 && bit < numBits; j++, bit++) { 661 if (buffer[k] & (1UL << j)) { 662 // block is in use 663 if (range > 0) { 664 groups[i].AddFreeRange(start, range); 665 range = 0; 666 } 667 } else if (range++ == 0) { 668 // block is free, start new free range 669 start = bit; 670 } 671 } 672 } 673 if (range) 674 groups[i].AddFreeRange(start, range); 675 676 freeBlocks += groups[i].fFreeBits; 677 678 offset += blocks; 679 } 680 free(buffer); 681 682 // check if block bitmap and log area are reserved 683 uint32 reservedBlocks = volume->ToBlock(volume->Log()) + volume->Log().Length(); 684 685 if (allocator->CheckBlocks(0, reservedBlocks) != B_OK) { 686 if (volume->IsReadOnly()) { 687 FATAL(("Space for block bitmap or log area is not reserved " 688 "(volume is mounted read-only)!\n")); 689 } else { 690 Transaction transaction(volume, 0); 691 if (groups[0].Allocate(transaction, 0, reservedBlocks) != B_OK) { 692 FATAL(("Could not allocate reserved space for block " 693 "bitmap/log!\n")); 694 volume->Panic(); 695 } else { 696 transaction.Done(); 697 FATAL(("Space for block bitmap or log area was not " 698 "reserved!\n")); 699 } 700 } 701 } 702 703 off_t usedBlocks = volume->NumBlocks() - freeBlocks; 704 if (volume->UsedBlocks() != usedBlocks) { 705 // If the disk in a dirty state at mount time, it's 706 // normal that the values don't match 707 INFORM(("volume reports %" B_PRIdOFF " used blocks, correct is %" 708 B_PRIdOFF "\n", volume->UsedBlocks(), usedBlocks)); 709 volume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks); 710 } 711 712 return B_OK; 713 } 714 715 716 void 717 BlockAllocator::Uninitialize() 718 { 719 // We only have to make sure that the initializer thread isn't running 720 // anymore. 721 recursive_lock_lock(&fLock); 722 } 723 724 725 /*! Tries to allocate between \a minimum, and \a maximum blocks starting 726 at group \a groupIndex with offset \a start. The resulting allocation 727 is put into \a run. 728 729 The number of allocated blocks is always a multiple of \a minimum which 730 has to be a power of two value. 731 */ 732 status_t 733 BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex, 734 uint16 start, uint16 maximum, uint16 minimum, block_run& run) 735 { 736 if (maximum == 0) 737 return B_BAD_VALUE; 738 739 FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16 740 ", maximum = %" B_PRIu16 ", minimum = %" B_PRIu16 "\n", 741 groupIndex, start, maximum, minimum)); 742 743 AllocationBlock cached(fVolume); 744 RecursiveLocker lock(fLock); 745 746 uint32 bitsPerFullBlock = fVolume->BlockSize() << 3; 747 748 // Find the block_run that can fulfill the request best 749 int32 bestGroup = -1; 750 int32 bestStart = -1; 751 int32 bestLength = -1; 752 753 for (int32 i = 0; i < fNumGroups + 1; i++, groupIndex++, start = 0) { 754 groupIndex = groupIndex % fNumGroups; 755 AllocationGroup& group = fGroups[groupIndex]; 756 757 CHECK_ALLOCATION_GROUP(groupIndex); 758 759 if (start >= group.NumBits() || group.IsFull()) 760 continue; 761 762 // The wanted maximum is smaller than the largest free block in the 763 // group or already smaller than the minimum 764 765 if (start < group.fFirstFree) 766 start = group.fFirstFree; 767 768 if (group.fLargestValid) { 769 if (group.fLargestLength < bestLength) 770 continue; 771 772 if (group.fLargestStart >= start) { 773 if (group.fLargestLength >= bestLength) { 774 bestGroup = groupIndex; 775 bestStart = group.fLargestStart; 776 bestLength = group.fLargestLength; 777 778 if (bestLength >= maximum) 779 break; 780 } 781 782 // We know everything about this group we have to, let's skip 783 // to the next 784 continue; 785 } 786 } 787 788 // There may be more than one block per allocation group - and 789 // we iterate through it to find a place for the allocation. 790 // (one allocation can't exceed one allocation group) 791 792 uint32 block = start / (fVolume->BlockSize() << 3); 793 int32 currentStart = 0, currentLength = 0; 794 int32 groupLargestStart = -1; 795 int32 groupLargestLength = -1; 796 int32 currentBit = start; 797 bool canFindGroupLargest = start == 0; 798 799 for (; block < group.NumBlocks(); block++) { 800 if (cached.SetTo(group, block) < B_OK) 801 RETURN_ERROR(B_ERROR); 802 803 T(Block("alloc-in", group.Start() + block, cached.Block(), 804 fVolume->BlockSize(), groupIndex, currentStart)); 805 806 // find a block large enough to hold the allocation 807 for (uint32 bit = start % bitsPerFullBlock; 808 bit < cached.NumBlockBits(); bit++) { 809 if (!cached.IsUsed(bit)) { 810 if (currentLength == 0) { 811 // start new range 812 currentStart = currentBit; 813 } 814 815 // have we found a range large enough to hold numBlocks? 816 if (++currentLength >= maximum) { 817 bestGroup = groupIndex; 818 bestStart = currentStart; 819 bestLength = currentLength; 820 break; 821 } 822 } else { 823 if (currentLength) { 824 // end of a range 825 if (currentLength > bestLength) { 826 bestGroup = groupIndex; 827 bestStart = currentStart; 828 bestLength = currentLength; 829 } 830 if (currentLength > groupLargestLength) { 831 groupLargestStart = currentStart; 832 groupLargestLength = currentLength; 833 } 834 currentLength = 0; 835 } 836 if ((int32)group.NumBits() - currentBit 837 <= groupLargestLength) { 838 // We can't find a bigger block in this group anymore, 839 // let's skip the rest. 840 block = group.NumBlocks(); 841 break; 842 } 843 } 844 currentBit++; 845 } 846 847 T(Block("alloc-out", block, cached.Block(), 848 fVolume->BlockSize(), groupIndex, currentStart)); 849 850 if (bestLength >= maximum) { 851 canFindGroupLargest = false; 852 break; 853 } 854 855 // start from the beginning of the next block 856 start = 0; 857 } 858 859 if (currentBit == (int32)group.NumBits()) { 860 if (currentLength > bestLength) { 861 bestGroup = groupIndex; 862 bestStart = currentStart; 863 bestLength = currentLength; 864 } 865 if (canFindGroupLargest && currentLength > groupLargestLength) { 866 groupLargestStart = currentStart; 867 groupLargestLength = currentLength; 868 } 869 } 870 871 if (canFindGroupLargest && !group.fLargestValid 872 && groupLargestLength >= 0) { 873 group.fLargestStart = groupLargestStart; 874 group.fLargestLength = groupLargestLength; 875 group.fLargestValid = true; 876 } 877 878 if (bestLength >= maximum) 879 break; 880 } 881 882 // If we found a suitable range, mark the blocks as in use, and 883 // write the updated block bitmap back to disk 884 if (bestLength < minimum) 885 return B_DEVICE_FULL; 886 887 if (bestLength > maximum) 888 bestLength = maximum; 889 else if (minimum > 1) { 890 // make sure bestLength is a multiple of minimum 891 bestLength = round_down(bestLength, minimum); 892 } 893 894 if (fGroups[bestGroup].Allocate(transaction, bestStart, bestLength) != B_OK) 895 RETURN_ERROR(B_IO_ERROR); 896 897 CHECK_ALLOCATION_GROUP(bestGroup); 898 899 run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(bestGroup); 900 run.start = HOST_ENDIAN_TO_BFS_INT16(bestStart); 901 run.length = HOST_ENDIAN_TO_BFS_INT16(bestLength); 902 903 fVolume->SuperBlock().used_blocks 904 = HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + bestLength); 905 // We are not writing back the disk's superblock - it's 906 // either done by the journaling code, or when the disk 907 // is unmounted. 908 // If the value is not correct at mount time, it will be 909 // fixed anyway. 910 911 // We need to flush any remaining blocks in the new allocation to make sure 912 // they won't interfere with the file cache. 913 block_cache_discard(fVolume->BlockCache(), fVolume->ToBlock(run), 914 run.Length()); 915 916 T(Allocate(run)); 917 return B_OK; 918 } 919 920 921 status_t 922 BlockAllocator::AllocateForInode(Transaction& transaction, 923 const block_run* parent, mode_t type, block_run& run) 924 { 925 // Apply some allocation policies here (AllocateBlocks() will break them 926 // if necessary) - we will start with those described in Dominic Giampaolo's 927 // "Practical File System Design", and see how good they work 928 929 // Files are going in the same allocation group as its parent, 930 // sub-directories will be inserted 8 allocation groups after 931 // the one of the parent 932 uint16 group = parent->AllocationGroup(); 933 if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY) 934 group += 8; 935 936 return AllocateBlocks(transaction, group, 0, 1, 1, run); 937 } 938 939 940 status_t 941 BlockAllocator::Allocate(Transaction& transaction, Inode* inode, 942 off_t numBlocks, block_run& run, uint16 minimum) 943 { 944 if (numBlocks <= 0) 945 return B_ERROR; 946 947 // one block_run can't hold more data than there is in one allocation group 948 if (numBlocks > fGroups[0].NumBits()) 949 numBlocks = fGroups[0].NumBits(); 950 951 // since block_run.length is uint16, the largest number of blocks that 952 // can be covered by a block_run is 65535 953 // TODO: if we drop compatibility, couldn't we do this any better? 954 // There are basically two possibilities: 955 // a) since a length of zero doesn't have any sense, take that for 65536 - 956 // but that could cause many problems (bugs) in other areas 957 // b) reduce the maximum amount of blocks per block_run, so that the 958 // remaining number of free blocks can be used in a useful manner 959 // (like 4 blocks) - but that would also reduce the maximum file size 960 // c) have BlockRun::Length() return (length + 1). 961 if (numBlocks > MAX_BLOCK_RUN_LENGTH) 962 numBlocks = MAX_BLOCK_RUN_LENGTH; 963 964 // Apply some allocation policies here (AllocateBlocks() will break them 965 // if necessary) 966 uint16 group = inode->BlockRun().AllocationGroup(); 967 uint16 start = 0; 968 969 // Are there already allocated blocks? (then just try to allocate near the 970 // last one) 971 if (inode->Size() > 0) { 972 const data_stream& data = inode->Node().data; 973 // TODO: we currently don't care for when the data stream 974 // is already grown into the indirect ranges 975 if (data.max_double_indirect_range == 0 976 && data.max_indirect_range == 0) { 977 // Since size > 0, there must be a valid block run in this stream 978 int32 last = 0; 979 for (; last < NUM_DIRECT_BLOCKS - 1; last++) 980 if (data.direct[last + 1].IsZero()) 981 break; 982 983 group = data.direct[last].AllocationGroup(); 984 start = data.direct[last].Start() + data.direct[last].Length(); 985 } 986 } else if (inode->IsContainer() || inode->IsSymLink()) { 987 // directory and symbolic link data will go in the same allocation 988 // group as the inode is in but after the inode data 989 start = inode->BlockRun().Start(); 990 } else { 991 // file data will start in the next allocation group 992 group = inode->BlockRun().AllocationGroup() + 1; 993 } 994 995 return AllocateBlocks(transaction, group, start, numBlocks, minimum, run); 996 } 997 998 999 status_t 1000 BlockAllocator::Free(Transaction& transaction, block_run run) 1001 { 1002 RecursiveLocker lock(fLock); 1003 1004 int32 group = run.AllocationGroup(); 1005 uint16 start = run.Start(); 1006 uint16 length = run.Length(); 1007 1008 FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16 1009 ", length = %" B_PRIu16 "\n", group, start, length)) 1010 T(Free(run)); 1011 1012 // doesn't use Volume::IsValidBlockRun() here because it can check better 1013 // against the group size (the last group may have a different length) 1014 if (group < 0 || group >= fNumGroups 1015 || start > fGroups[group].NumBits() 1016 || uint32(start + length) > fGroups[group].NumBits() 1017 || length == 0) { 1018 FATAL(("tried to free an invalid block_run" 1019 " (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n", 1020 group, start, length)); 1021 DEBUGGER(("tried to free invalid block_run")); 1022 return B_BAD_VALUE; 1023 } 1024 // check if someone tries to free reserved areas at the beginning of the 1025 // drive 1026 if (group < fVolume->Log().AllocationGroup() 1027 || (group == fVolume->Log().AllocationGroup() 1028 && start < uint32(fVolume->Log().Start()) + fVolume->Log().Length())) { 1029 FATAL(("tried to free a reserved block_run" 1030 " (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n", 1031 group, start, length)); 1032 DEBUGGER(("tried to free reserved block")); 1033 return B_BAD_VALUE; 1034 } 1035 #ifdef DEBUG 1036 if (CheckBlockRun(run) != B_OK) 1037 return B_BAD_DATA; 1038 #endif 1039 1040 CHECK_ALLOCATION_GROUP(group); 1041 1042 if (fGroups[group].Free(transaction, start, length) != B_OK) 1043 RETURN_ERROR(B_IO_ERROR); 1044 1045 CHECK_ALLOCATION_GROUP(group); 1046 1047 #ifdef DEBUG 1048 if (CheckBlockRun(run, NULL, false) != B_OK) { 1049 DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just " 1050 "freed)\n")); 1051 } 1052 #endif 1053 1054 fVolume->SuperBlock().used_blocks = 1055 HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() - run.Length()); 1056 return B_OK; 1057 } 1058 1059 1060 #ifdef DEBUG_FRAGMENTER 1061 void 1062 BlockAllocator::Fragment() 1063 { 1064 AllocationBlock cached(fVolume); 1065 RecursiveLocker lock(fLock); 1066 1067 // only leave 4 block holes 1068 static const uint32 kMask = 0x0f0f0f0f; 1069 uint32 valuesPerBlock = fVolume->BlockSize() / 4; 1070 1071 for (int32 i = 0; i < fNumGroups; i++) { 1072 AllocationGroup& group = fGroups[i]; 1073 1074 for (uint32 block = 0; block < group.NumBlocks(); block++) { 1075 Transaction transaction(fVolume, 0); 1076 1077 if (cached.SetToWritable(transaction, group, block) != B_OK) 1078 return; 1079 1080 for (int32 index = 0; index < valuesPerBlock; index++) { 1081 cached.Block(index) |= HOST_ENDIAN_TO_BFS_INT32(kMask); 1082 } 1083 1084 transaction.Done(); 1085 } 1086 } 1087 } 1088 #endif // DEBUG_FRAGMENTER 1089 1090 1091 #ifdef DEBUG_ALLOCATION_GROUPS 1092 void 1093 BlockAllocator::_CheckGroup(int32 groupIndex) const 1094 { 1095 AllocationBlock cached(fVolume); 1096 ASSERT_LOCKED_RECURSIVE(&fLock); 1097 1098 AllocationGroup& group = fGroups[groupIndex]; 1099 1100 int32 currentStart = 0, currentLength = 0; 1101 int32 firstFree = -1; 1102 int32 largestStart = -1; 1103 int32 largestLength = 0; 1104 int32 currentBit = 0; 1105 1106 for (uint32 block = 0; block < group.NumBlocks(); block++) { 1107 if (cached.SetTo(group, block) < B_OK) { 1108 panic("setting group block %d failed\n", (int)block); 1109 return; 1110 } 1111 1112 for (uint32 bit = 0; bit < cached.NumBlockBits(); bit++) { 1113 if (!cached.IsUsed(bit)) { 1114 if (firstFree < 0) { 1115 firstFree = currentBit; 1116 if (!group.fLargestValid) { 1117 if (firstFree >= 0 && firstFree < group.fFirstFree) { 1118 // mostly harmless but noteworthy 1119 dprintf("group %d first free too late: should be " 1120 "%d, is %d\n", (int)groupIndex, (int)firstFree, 1121 (int)group.fFirstFree); 1122 } 1123 return; 1124 } 1125 } 1126 1127 if (currentLength == 0) { 1128 // start new range 1129 currentStart = currentBit; 1130 } 1131 currentLength++; 1132 } else if (currentLength) { 1133 // end of a range 1134 if (currentLength > largestLength) { 1135 largestStart = currentStart; 1136 largestLength = currentLength; 1137 } 1138 currentLength = 0; 1139 } 1140 currentBit++; 1141 } 1142 } 1143 1144 if (currentLength > largestLength) { 1145 largestStart = currentStart; 1146 largestLength = currentLength; 1147 } 1148 1149 if (firstFree >= 0 && firstFree < group.fFirstFree) { 1150 // mostly harmless but noteworthy 1151 dprintf("group %d first free too late: should be %d, is %d\n", 1152 (int)groupIndex, (int)firstFree, (int)group.fFirstFree); 1153 } 1154 if (group.fLargestValid 1155 && (largestStart != group.fLargestStart 1156 || largestLength != group.fLargestLength)) { 1157 panic("bfs %p: group %d largest differs: %d.%d, checked %d.%d.\n", 1158 fVolume, (int)groupIndex, (int)group.fLargestStart, 1159 (int)group.fLargestLength, (int)largestStart, (int)largestLength); 1160 } 1161 } 1162 #endif // DEBUG_ALLOCATION_GROUPS 1163 1164 1165 status_t 1166 BlockAllocator::Trim(uint64 offset, uint64 size, uint64& trimmedSize) 1167 { 1168 // TODO: Remove this check when offset and size handling is implemented 1169 if (offset != 0 1170 || fVolume->NumBlocks() < 0 1171 || size < (uint64)fVolume->NumBlocks() * fVolume->BlockSize()) { 1172 INFORM(("BFS Trim: Ranges smaller than the file system size" 1173 " are not supported yet.\n")); 1174 return B_UNSUPPORTED; 1175 } 1176 1177 const uint32 kTrimRanges = 128; 1178 fs_trim_data* trimData = (fs_trim_data*)malloc(sizeof(fs_trim_data) 1179 + 2 * sizeof(uint64) * (kTrimRanges - 1)); 1180 if (trimData == NULL) 1181 return B_NO_MEMORY; 1182 1183 MemoryDeleter deleter(trimData); 1184 RecursiveLocker locker(fLock); 1185 1186 // TODO: take given offset and size into account! 1187 int32 lastGroup = fNumGroups - 1; 1188 uint32 firstBlock = 0; 1189 uint32 firstBit = 0; 1190 uint64 currentBlock = 0; 1191 uint32 blockShift = fVolume->BlockShift(); 1192 1193 uint64 firstFree = 0; 1194 uint64 freeLength = 0; 1195 1196 trimData->range_count = 0; 1197 trimmedSize = 0; 1198 1199 AllocationBlock cached(fVolume); 1200 for (int32 groupIndex = 0; groupIndex <= lastGroup; groupIndex++) { 1201 AllocationGroup& group = fGroups[groupIndex]; 1202 1203 for (uint32 block = firstBlock; block < group.NumBlocks(); block++) { 1204 cached.SetTo(group, block); 1205 1206 for (uint32 i = firstBit; i < cached.NumBlockBits(); i++) { 1207 if (cached.IsUsed(i)) { 1208 // Block is in use 1209 if (freeLength > 0) { 1210 // Overflow is unlikely to happen, but check it anyway 1211 if ((firstFree << blockShift) >> blockShift 1212 != firstFree 1213 || (freeLength << blockShift) >> blockShift 1214 != freeLength) { 1215 FATAL(("BlockAllocator::Trim:" 1216 " Overflow detected!\n")); 1217 return B_ERROR; 1218 } 1219 status_t status = _TrimNext(*trimData, kTrimRanges, 1220 firstFree << blockShift, freeLength << blockShift, 1221 false, trimmedSize); 1222 if (status != B_OK) 1223 return status; 1224 1225 freeLength = 0; 1226 } 1227 } else if (freeLength++ == 0) { 1228 // Block is free, start new free range 1229 firstFree = currentBlock; 1230 } 1231 1232 currentBlock++; 1233 } 1234 } 1235 1236 firstBlock = 0; 1237 firstBit = 0; 1238 } 1239 1240 return _TrimNext(*trimData, kTrimRanges, firstFree << blockShift, 1241 freeLength << blockShift, true, trimmedSize); 1242 } 1243 1244 1245 // #pragma mark - 1246 1247 1248 /*! Checks whether or not the specified block range is allocated or not, 1249 depending on the \a allocated argument. 1250 */ 1251 status_t 1252 BlockAllocator::CheckBlocks(off_t start, off_t length, bool allocated, 1253 off_t* firstError) 1254 { 1255 if (start < 0 || start + length > fVolume->NumBlocks()) 1256 return B_BAD_VALUE; 1257 1258 off_t block = start; 1259 1260 int32 group = start >> fVolume->AllocationGroupShift(); 1261 uint32 bitmapBlock = start / (fVolume->BlockSize() << 3); 1262 uint32 blockOffset = start % (fVolume->BlockSize() << 3); 1263 1264 uint32 groupBlock = bitmapBlock % fBlocksPerGroup; 1265 1266 AllocationBlock cached(fVolume); 1267 1268 while (groupBlock < fGroups[group].NumBlocks() && length > 0) { 1269 if (cached.SetTo(fGroups[group], groupBlock) != B_OK) 1270 RETURN_ERROR(B_IO_ERROR); 1271 1272 for (; blockOffset < cached.NumBlockBits() && length > 0; 1273 blockOffset++, length--, block++) { 1274 if (cached.IsUsed(blockOffset) != allocated) { 1275 PRINT(("CheckBlocks: Erroneous block (group = %" B_PRId32 1276 ", groupBlock = %" B_PRIu32 1277 ", blockOffset = %" B_PRIu32 ")!\n", 1278 group, groupBlock, blockOffset)); 1279 1280 if (firstError) 1281 *firstError = block; 1282 1283 return B_BAD_DATA; 1284 } 1285 } 1286 1287 blockOffset = 0; 1288 1289 if (++groupBlock >= fGroups[group].NumBlocks()) { 1290 groupBlock = 0; 1291 group++; 1292 } 1293 } 1294 1295 return B_OK; 1296 } 1297 1298 1299 bool 1300 BlockAllocator::IsValidBlockRun(block_run run, const char* type) 1301 { 1302 if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups 1303 || run.Start() > fGroups[run.AllocationGroup()].fNumBits 1304 || uint32(run.Start() + run.Length()) 1305 > fGroups[run.AllocationGroup()].fNumBits 1306 || run.length == 0) { 1307 PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")" 1308 " is invalid!\n", type, run.AllocationGroup(), run.Start(), 1309 run.Length())); 1310 return false; 1311 } 1312 return true; 1313 } 1314 1315 1316 status_t 1317 BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated) 1318 { 1319 if (!IsValidBlockRun(run, type)) 1320 return B_BAD_DATA; 1321 1322 status_t status = CheckBlocks(fVolume->ToBlock(run), run.Length(), 1323 allocated); 1324 if (status != B_OK) { 1325 PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")" 1326 " is only partially allocated!\n", type, run.AllocationGroup(), 1327 run.Start(), run.Length())); 1328 } 1329 1330 return status; 1331 } 1332 1333 1334 bool 1335 BlockAllocator::_AddTrim(fs_trim_data& trimData, uint32 maxRanges, 1336 uint64 offset, uint64 size) 1337 { 1338 ASSERT(trimData.range_count < maxRanges); 1339 if (size == 0) 1340 return false; 1341 1342 trimData.ranges[trimData.range_count].offset = offset; 1343 trimData.ranges[trimData.range_count].size = size; 1344 trimData.range_count++; 1345 1346 return (trimData.range_count == maxRanges); 1347 } 1348 1349 1350 status_t 1351 BlockAllocator::_TrimNext(fs_trim_data& trimData, uint32 maxRanges, 1352 uint64 offset, uint64 size, bool force, uint64& trimmedSize) 1353 { 1354 PRINT(("_TrimNext(index %" B_PRIu32 ", offset %" B_PRIu64 ", size %" 1355 B_PRIu64 ")\n", trimData.range_count, offset, size)); 1356 1357 const bool rangesFilled = _AddTrim(trimData, maxRanges, offset, size); 1358 1359 if (rangesFilled || force) { 1360 // Trim now 1361 trimData.trimmed_size = 0; 1362 #ifdef DEBUG_TRIM 1363 dprintf("TRIM: BFS: free ranges (bytes):\n"); 1364 for (uint32 i = 0; i < trimData.range_count; i++) { 1365 dprintf("[%3" B_PRIu32 "] %" B_PRIu64 " : %" B_PRIu64 "\n", i, 1366 trimData.ranges[i].offset, trimData.ranges[i].size); 1367 } 1368 #endif 1369 if (ioctl(fVolume->Device(), B_TRIM_DEVICE, &trimData, 1370 sizeof(fs_trim_data) 1371 + 2 * sizeof(uint64) * (trimData.range_count - 1)) != 0) { 1372 return errno; 1373 } 1374 1375 trimmedSize += trimData.trimmed_size; 1376 trimData.range_count = 0; 1377 } 1378 1379 return B_OK; 1380 } 1381 1382 1383 // #pragma mark - debugger commands 1384 1385 1386 #ifdef BFS_DEBUGGER_COMMANDS 1387 1388 void 1389 BlockAllocator::Dump(int32 index) 1390 { 1391 kprintf("allocation groups: %" B_PRId32 " (base %p)\n", fNumGroups, fGroups); 1392 kprintf("blocks per group: %" B_PRId32 "\n", fBlocksPerGroup); 1393 1394 for (int32 i = 0; i < fNumGroups; i++) { 1395 if (index != -1 && i != index) 1396 continue; 1397 1398 AllocationGroup& group = fGroups[i]; 1399 1400 kprintf("[%3" B_PRId32 "] num bits: %" B_PRIu32 " (%p)\n", i, 1401 group.NumBits(), &group); 1402 kprintf(" num blocks: %" B_PRIu32 "\n", group.NumBlocks()); 1403 kprintf(" start: %" B_PRId32 "\n", group.Start()); 1404 kprintf(" first free: %" B_PRId32 "\n", group.fFirstFree); 1405 kprintf(" largest start: %" B_PRId32 "%s\n", group.fLargestStart, 1406 group.fLargestValid ? "" : " (invalid)"); 1407 kprintf(" largest length: %" B_PRId32 "\n", group.fLargestLength); 1408 kprintf(" free bits: %" B_PRId32 "\n", group.fFreeBits); 1409 } 1410 } 1411 1412 1413 #if BFS_TRACING 1414 static char kTraceBuffer[256]; 1415 1416 1417 int 1418 dump_block_allocator_blocks(int argc, char** argv) 1419 { 1420 if (argc != 3 || !strcmp(argv[1], "--help")) { 1421 kprintf("usage: %s <ptr-to-volume> <block>\n", argv[0]); 1422 return 0; 1423 } 1424 1425 Volume* volume = (Volume*)parse_expression(argv[1]); 1426 off_t block = parse_expression(argv[2]); 1427 1428 // iterate over all tracing entries to find overlapping actions 1429 1430 using namespace BFSBlockTracing; 1431 1432 LazyTraceOutput out(kTraceBuffer, sizeof(kTraceBuffer), 0); 1433 TraceEntryIterator iterator; 1434 while (TraceEntry* _entry = iterator.Next()) { 1435 if (Allocate* entry = dynamic_cast<Allocate*>(_entry)) { 1436 off_t first = volume->ToBlock(entry->Run()); 1437 off_t last = first - 1 + entry->Run().Length(); 1438 if (block >= first && block <= last) { 1439 out.Clear(); 1440 const char* dump = out.DumpEntry(entry); 1441 kprintf("%5ld. %s\n", iterator.Index(), dump); 1442 } 1443 } else if (Free* entry = dynamic_cast<Free*>(_entry)) { 1444 off_t first = volume->ToBlock(entry->Run()); 1445 off_t last = first - 1 + entry->Run().Length(); 1446 if (block >= first && block <= last) { 1447 out.Clear(); 1448 const char* dump = out.DumpEntry(entry); 1449 kprintf("%5ld. %s\n", iterator.Index(), dump); 1450 } 1451 } 1452 } 1453 1454 return 0; 1455 } 1456 #endif 1457 1458 1459 int 1460 dump_block_allocator(int argc, char** argv) 1461 { 1462 int32 group = -1; 1463 if (argc == 3) { 1464 group = parse_expression(argv[2]); 1465 argc--; 1466 } 1467 1468 if (argc != 2 || !strcmp(argv[1], "--help")) { 1469 kprintf("usage: %s <ptr-to-volume> [group]\n", argv[0]); 1470 return 0; 1471 } 1472 1473 Volume* volume = (Volume*)parse_expression(argv[1]); 1474 BlockAllocator& allocator = volume->Allocator(); 1475 1476 allocator.Dump(group); 1477 return 0; 1478 } 1479 1480 #endif // BFS_DEBUGGER_COMMANDS 1481 1482