| // 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 <zircon/device/block.h> |
| #include <fbl/algorithm.h> |
| #include <fbl/macros.h> |
| #include <fbl/vector.h> |
| |
| #include <fs/block-txn.h> |
| #include <fs/vfs.h> |
| |
| #include <utility> |
| |
| namespace fs { |
| |
| BlockTxn::BlockTxn(TransactionHandler* handler) : handler_(handler) {} |
| |
| BlockTxn::~BlockTxn() { |
| Transact(); |
| } |
| |
| #ifdef __Fuchsia__ |
| |
| void BlockTxn::EnqueueOperation(uint32_t op, vmoid_t id, uint64_t vmo_offset, |
| uint64_t dev_offset, uint64_t nblocks) { |
| // TODO(ZX-2253): Remove this assertion. |
| ZX_ASSERT_MSG(nblocks < UINT32_MAX, "Too many blocks"); |
| uint32_t blocks = static_cast<uint32_t>(nblocks); |
| for (size_t i = 0; i < requests_.size(); i++) { |
| if (requests_[i].vmoid != id || requests_[i].opcode != op) { |
| continue; |
| } |
| |
| if (requests_[i].vmo_offset == vmo_offset) { |
| // Take the longer of the operations (if operating on the same |
| // blocks). |
| if (requests_[i].length <= blocks) { |
| requests_[i].length = blocks; |
| } |
| return; |
| } else if ((requests_[i].vmo_offset + requests_[i].length == vmo_offset) && |
| (requests_[i].dev_offset + requests_[i].length == dev_offset)) { |
| // Combine with the previous request, if immediately following. |
| requests_[i].length += blocks; |
| return; |
| } |
| } |
| |
| block_fifo_request_t request; |
| request.opcode = op; |
| request.group = handler_->BlockGroupID(); |
| request.vmoid = id; |
| // NOTE: It's easier to compare everything when dealing |
| // with blocks (not offsets!) so the following are described in |
| // terms of blocks until we Transact(). |
| request.length = blocks; |
| request.vmo_offset = vmo_offset; |
| request.dev_offset = dev_offset; |
| requests_.push_back(std::move(request)); |
| } |
| |
| zx_status_t BlockTxn::Transact() { |
| // Fast-path for already completed transactions. |
| if (requests_.is_empty()) { |
| return ZX_OK; |
| } |
| // Convert 'filesystem block' units to 'disk block' units. |
| const size_t kBlockFactor = handler_->FsBlockSize() / handler_->DeviceBlockSize(); |
| for (size_t i = 0; i < requests_.size(); i++) { |
| requests_[i].vmo_offset *= kBlockFactor; |
| requests_[i].dev_offset *= kBlockFactor; |
| // TODO(ZX-2253): Remove this assertion. |
| uint64_t length = requests_[i].length * kBlockFactor; |
| ZX_ASSERT_MSG(length < UINT32_MAX, "Too many blocks"); |
| requests_[i].length = static_cast<uint32_t>(length); |
| } |
| zx_status_t status = ZX_OK; |
| if (requests_.size() != 0) { |
| status = handler_->Transaction(requests_.get(), requests_.size()); |
| } |
| requests_.reset(); |
| return status; |
| } |
| |
| #else |
| |
| void BlockTxn::EnqueueOperation(uint32_t op, const void* id, uint64_t vmo_offset, |
| uint64_t dev_offset, uint64_t nblocks) { |
| for (size_t b = 0; b < nblocks; b++) { |
| void* data = GetBlock(handler_->FsBlockSize(), id, vmo_offset + b); |
| if (op == BLOCKIO_WRITE) { |
| handler_->Writeblk(static_cast<uint32_t>(dev_offset + b), data); |
| } else if (op == BLOCKIO_READ) { |
| handler_->Readblk(static_cast<uint32_t>(dev_offset + b), data); |
| } else if (op == BLOCKIO_FLUSH) { |
| // No-op. |
| } else { |
| ZX_ASSERT(false); // Invalid operation. |
| } |
| } |
| } |
| |
| // Activate the transaction (do nothing) |
| zx_status_t BlockTxn::Transact() { |
| return ZX_OK; |
| } |
| |
| #endif |
| |
| } // namespace fs |