| // Copyright 2022 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 "src/storage/f2fs/f2fs.h" |
| |
| namespace f2fs { |
| |
| StorageBuffer::StorageBuffer(BcacheMapper *bc, size_t blocks, uint32_t block_size, |
| std::string_view label, uint32_t allocation_unit) |
| : max_blocks_(bc->Maxblk()), allocation_unit_(allocation_unit) { |
| ZX_DEBUG_ASSERT(allocation_unit >= 1 && allocation_unit <= blocks); |
| blocks = fbl::round_up(blocks, allocation_unit); |
| ZX_ASSERT(buffer_.Initialize(bc, blocks, block_size, label.data()) == ZX_OK); |
| Init(); |
| } |
| |
| void StorageBuffer::Init() { |
| std::lock_guard lock(mutex_); |
| for (size_t i = 0; i < buffer_.capacity(); i += allocation_unit_) { |
| auto key = std::make_unique<VmoBufferKey>(i); |
| free_keys_.push_back(std::move(key)); |
| } |
| } |
| |
| zx::result<size_t> StorageBuffer::ReserveWriteOperation(Page &page) { |
| ZX_DEBUG_ASSERT(page.GetBlockAddr() != kNullAddr); |
| if (page.GetBlockAddr() >= max_blocks_) { |
| return zx::error(ZX_ERR_OUT_OF_RANGE); |
| } |
| std::lock_guard lock(mutex_); |
| // No room in |buffer_|. |
| if (free_keys_.is_empty()) { |
| return zx::error(ZX_ERR_UNAVAILABLE); |
| } |
| |
| auto key = free_keys_.pop_front(); |
| storage::OperationType type = storage::OperationType::kWrite; |
| if (page.IsCommit()) { |
| type = storage::OperationType::kWritePreflushAndFua; |
| } else if (page.IsSync()) { |
| type = storage::OperationType::kWriteFua; |
| } |
| storage::Operation op = { |
| .type = type, |
| .vmo_offset = key->GetKey(), |
| .dev_offset = page.GetBlockAddr(), |
| .length = 1, |
| }; |
| // Copy |page| to |buffer| at |key|. |
| page.Read(buffer_.Data(op.vmo_offset)); |
| // Here, |operation| can be merged into a previous operation. |
| builder_.Add(op, &buffer_); |
| reserved_keys_.push_back(std::move(key)); |
| return zx::ok(reserved_keys_.size()); |
| } |
| |
| zx::result<StorageOperations> StorageBuffer::MakeReadOperations(const std::vector<block_t> &addrs) { |
| VmoKeyList keys; |
| uint32_t allocate_index = 0; |
| fs::BufferedOperationsBuilder builder; |
| std::lock_guard lock(mutex_); |
| for (auto addr : addrs) { |
| if (addr != kNullAddr && addr != kNewAddr) { |
| // If addr is invalid, free allocated keys. |
| if (addr >= max_blocks_) { |
| free_keys_.splice(free_keys_.end(), keys); |
| return zx::error(ZX_ERR_OUT_OF_RANGE); |
| } |
| |
| if (allocate_index % allocation_unit_ == 0) { |
| allocate_index = 0; |
| // Wait until there is a room in |buffer_|. |
| while (free_keys_.is_empty()) { |
| if (auto wait_result = cvar_.wait_for(mutex_, std::chrono::seconds(kWriteTimeOut)); |
| wait_result == std::cv_status::timeout) { |
| return zx::error(ZX_ERR_TIMED_OUT); |
| } |
| } |
| keys.push_back(free_keys_.pop_front()); |
| } |
| |
| storage::Operation op = { |
| .type = storage::OperationType::kRead, |
| .vmo_offset = keys.back().GetKey() + allocate_index, |
| .dev_offset = addr, |
| .length = 1, |
| }; |
| builder.Add(op, &buffer_); |
| ++allocate_index; |
| } |
| } |
| return zx::ok(StorageOperations(*this, builder, keys)); |
| } |
| |
| void StorageBuffer::ReleaseBuffers(const StorageOperations &operation) { |
| if (!operation.IsEmpty()) { |
| auto keys = operation.TakeVmoKeys(); |
| std::lock_guard lock(mutex_); |
| ZX_DEBUG_ASSERT(!keys.is_empty()); |
| // Add vmo buffers of |operation| to |free_keys_| to allow waiters to reserve buffer_. |
| free_keys_.splice(free_keys_.end(), keys); |
| // TODO: When multi-qd is available, consider notify_all(). |
| cvar_.notify_all(); |
| } |
| } |
| |
| StorageOperations StorageBuffer::TakeWriteOperations() { |
| std::lock_guard lock(mutex_); |
| return StorageOperations(*this, builder_, reserved_keys_); |
| } |
| |
| StorageBuffer::~StorageBuffer() { |
| { |
| std::lock_guard lock(mutex_); |
| [[maybe_unused]] size_t num_keys = 0; |
| while (!free_keys_.is_empty()) { |
| ++num_keys; |
| free_keys_.pop_front(); |
| } |
| ZX_DEBUG_ASSERT(num_keys == buffer_.capacity() / allocation_unit_); |
| } |
| } |
| |
| } // namespace f2fs |