// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include namespace minfs { blk_t GetBlockBitmapBlocks(const Superblock& info) { ZX_DEBUG_ASSERT(info.ino_block >= info.abm_block); blk_t bitmap_blocks = info.ino_block - info.abm_block; if (info.flags & kMinfsFlagFVM) { const blk_t kBlocksPerSlice = static_cast(info.slice_size / kMinfsBlockSize); bitmap_blocks = info.abm_slices * kBlocksPerSlice; } return bitmap_blocks; } zx_status_t GetRequiredBlockCount(size_t offset, size_t length, blk_t* num_req_blocks) { if (length == 0) { // Return early if no data needs to be written. *num_req_blocks = 0; return ZX_OK; } // Determine which range of direct blocks will be accessed given offset and length, // and add to total. blk_t first_direct = static_cast(offset / kMinfsBlockSize); blk_t last_direct = static_cast((offset + length - 1) / kMinfsBlockSize); blk_t reserve_blocks = last_direct - first_direct + 1; if (last_direct >= kMinfsDirect) { // If direct blocks go into indirect range, adjust the indices accordingly. first_direct = fbl::max(first_direct, kMinfsDirect) - kMinfsDirect; last_direct -= kMinfsDirect; // Calculate indirect blocks containing first and last direct blocks, and add to total. blk_t first_indirect = first_direct / kMinfsDirectPerIndirect; blk_t last_indirect = last_direct / kMinfsDirectPerIndirect; reserve_blocks += last_indirect - first_indirect + 1; if (last_indirect >= kMinfsIndirect) { // If indirect blocks go into doubly indirect range, adjust the indices accordingly. first_indirect = fbl::max(first_indirect, kMinfsIndirect) - kMinfsIndirect; last_indirect -= kMinfsIndirect; // Calculate doubly indirect blocks containing first/last indirect blocks, // and add to total blk_t first_dindirect = first_indirect / kMinfsDirectPerIndirect; blk_t last_dindirect = last_indirect / kMinfsDirectPerIndirect; reserve_blocks += last_dindirect - first_dindirect + 1; if (last_dindirect >= kMinfsDoublyIndirect) { // We cannot allocate blocks which exceed the doubly indirect range. return ZX_ERR_OUT_OF_RANGE; } } } *num_req_blocks = reserve_blocks; return ZX_OK; } TransactionLimits::TransactionLimits(const Superblock& info) { CalculateDataBlocks(); CalculateJournalBlocks(GetBlockBitmapBlocks(info)); } void TransactionLimits::CalculateDataBlocks() { // If we ever increase the number of doubly indirect blocks, we will need to update this offset // to be 1 byte before the end of the first doubly indirect block. constexpr blk_t kOffset = (kMinfsDirect + (kMinfsIndirect * kMinfsDirectPerIndirect)) * kMinfsBlockSize - 1; // This calculation ignores the fact that directory size is capped at |kMinfsMaxDirectorySize|, // because following that constraint makes it a little harder to predict where the most // significant cross-block write would be. This means we may overestimate the maximum number of // directory blocks by some amount, but this is better than an understimate. blk_t max_directory_blocks; ZX_ASSERT(GetRequiredBlockCount(kOffset, kMinfsMaxDirentSize, &max_directory_blocks) == ZX_OK); ZX_ASSERT(GetRequiredBlockCount(kOffset, kMaxWriteBytes, &max_data_blocks_) == ZX_OK); blk_t direct_blocks = (fbl::round_up(kMaxWriteBytes, kMinfsBlockSize) / kMinfsBlockSize) + 1; blk_t max_indirect_blocks = max_data_blocks_ - direct_blocks; max_meta_data_blocks_ = fbl::max(max_directory_blocks, max_indirect_blocks); } void TransactionLimits::CalculateJournalBlocks(blk_t block_bitmap_blocks) { max_entry_data_blocks_ = kMaxSuperblockBlocks + kMaxInodeBitmapBlocks + block_bitmap_blocks + kMaxInodeTableBlocks + max_meta_data_blocks_; // Ensure we have enough space to fit all the block numbers that may be updated in one // transaction. This may spill over into multiple blocks. blk_t header_blocks = 1; if (max_entry_data_blocks_ > kJournalEntryHeaderMaxBlocks) { header_blocks += fbl::round_up((max_entry_data_blocks_ - kJournalEntryHeaderMaxBlocks), kMinfsDirectPerIndirect) / kMinfsDirectPerIndirect; } // For revocation records, we need to know the maximum number of metadata blocks within the // data section of Minfs that can be deleted within one operation. This is either a directory // vnode's maximum possible number of data blocks + indirect blocks, or a data vnode's maximum // possible number of indirect blocks. blk_t maximum_directory_blocks; ZX_ASSERT(GetRequiredBlockCount(0, kMinfsMaxDirectorySize, &maximum_directory_blocks) == ZX_OK); blk_t maximum_indirect_blocks = kMinfsIndirect + kMinfsDoublyIndirect * kMinfsDirectPerIndirect; blk_t revocation_blocks = fbl::round_up(fbl::max(maximum_directory_blocks, maximum_indirect_blocks), kMinfsDirectPerIndirect) / kMinfsDirectPerIndirect; blk_t commit_blocks = 1; max_entry_blocks_ = header_blocks + revocation_blocks + max_entry_data_blocks_ + commit_blocks; min_journal_blocks_ = max_entry_blocks_ + kJournalMetadataBlocks; rec_journal_blocks_ = fbl::max(min_journal_blocks_, kDefaultJournalBlocks); } } // namespace minfs