blob: ff13b29379b014ac8c9f9212fce534f34db12049 [file] [log] [blame]
// 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