1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <zircon/device/block.h> 6#include <fbl/algorithm.h> 7#include <fbl/macros.h> 8#include <fbl/vector.h> 9 10#include <fs/block-txn.h> 11#include <fs/vfs.h> 12 13namespace fs { 14 15BlockTxn::BlockTxn(TransactionHandler* handler) : handler_(handler) {} 16 17BlockTxn::~BlockTxn() { 18 Transact(); 19} 20 21#ifdef __Fuchsia__ 22 23void BlockTxn::EnqueueOperation(uint32_t op, vmoid_t id, uint64_t vmo_offset, 24 uint64_t dev_offset, uint64_t nblocks) { 25 // TODO(ZX-2253): Remove this assertion. 26 ZX_ASSERT_MSG(nblocks < UINT32_MAX, "Too many blocks"); 27 uint32_t blocks = static_cast<uint32_t>(nblocks); 28 for (size_t i = 0; i < requests_.size(); i++) { 29 if (requests_[i].vmoid != id || requests_[i].opcode != op) { 30 continue; 31 } 32 33 if (requests_[i].vmo_offset == vmo_offset) { 34 // Take the longer of the operations (if operating on the same 35 // blocks). 36 if (requests_[i].length <= blocks) { 37 requests_[i].length = blocks; 38 } 39 return; 40 } else if ((requests_[i].vmo_offset + requests_[i].length == vmo_offset) && 41 (requests_[i].dev_offset + requests_[i].length == dev_offset)) { 42 // Combine with the previous request, if immediately following. 43 requests_[i].length += blocks; 44 return; 45 } 46 } 47 48 block_fifo_request_t request; 49 request.opcode = op; 50 request.group = handler_->BlockGroupID(); 51 request.vmoid = id; 52 // NOTE: It's easier to compare everything when dealing 53 // with blocks (not offsets!) so the following are described in 54 // terms of blocks until we Transact(). 55 request.length = blocks; 56 request.vmo_offset = vmo_offset; 57 request.dev_offset = dev_offset; 58 requests_.push_back(fbl::move(request)); 59} 60 61zx_status_t BlockTxn::Transact() { 62 // Fast-path for already completed transactions. 63 if (requests_.is_empty()) { 64 return ZX_OK; 65 } 66 // Convert 'filesystem block' units to 'disk block' units. 67 const size_t kBlockFactor = handler_->FsBlockSize() / handler_->DeviceBlockSize(); 68 for (size_t i = 0; i < requests_.size(); i++) { 69 requests_[i].vmo_offset *= kBlockFactor; 70 requests_[i].dev_offset *= kBlockFactor; 71 // TODO(ZX-2253): Remove this assertion. 72 uint64_t length = requests_[i].length * kBlockFactor; 73 ZX_ASSERT_MSG(length < UINT32_MAX, "Too many blocks"); 74 requests_[i].length = static_cast<uint32_t>(length); 75 } 76 zx_status_t status = ZX_OK; 77 if (requests_.size() != 0) { 78 status = handler_->Transaction(requests_.get(), requests_.size()); 79 } 80 requests_.reset(); 81 return status; 82} 83 84#else 85 86void BlockTxn::EnqueueOperation(uint32_t op, const void* id, uint64_t vmo_offset, 87 uint64_t dev_offset, uint64_t nblocks) { 88 for (size_t b = 0; b < nblocks; b++) { 89 void* data = GetBlock(handler_->FsBlockSize(), id, vmo_offset + b); 90 if (op == BLOCKIO_WRITE) { 91 handler_->Writeblk(dev_offset + b, data); 92 } else if (op == BLOCKIO_READ) { 93 handler_->Readblk(dev_offset + b, data); 94 } else if (op == BLOCKIO_FLUSH) { 95 // No-op. 96 } else { 97 ZX_ASSERT(false); // Invalid operation. 98 } 99 } 100} 101 102// Activate the transaction (do nothing) 103zx_status_t BlockTxn::Transact() { 104 return ZX_OK; 105} 106 107#endif 108 109} // namespace fs 110