blob: 614ba0d88a6296e27284095596a3bf559f0b8f92 [file] [log] [blame]
// Copyright 2020 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/blobfs/transfer_buffer.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <safemath/safe_conversions.h>
namespace blobfs {
StorageBackedTransferBuffer::StorageBackedTransferBuffer(zx::vmo vmo, size_t size,
storage::OwnedVmoid vmoid,
TransactionManager* txn_manager,
BlockIteratorProvider* block_iter_provider,
BlobfsMetrics* metrics)
: txn_manager_(txn_manager),
block_iter_provider_(block_iter_provider),
vmo_(std::move(vmo)),
size_(size),
vmoid_(std::move(vmoid)),
metrics_(metrics) {}
zx::result<std::unique_ptr<StorageBackedTransferBuffer>> StorageBackedTransferBuffer::Create(
size_t size, TransactionManager* txn_manager, BlockIteratorProvider* block_iter_provider,
BlobfsMetrics* metrics) {
ZX_DEBUG_ASSERT(metrics != nullptr && txn_manager != nullptr && block_iter_provider != nullptr);
if (size % kBlobfsBlockSize != 0 || size % PAGE_SIZE != 0) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
zx::vmo vmo;
zx_status_t status = zx::vmo::create(size, 0, &vmo);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot create pager transfer buffer: " << zx_status_get_string(status);
return zx::error(status);
}
storage::OwnedVmoid vmoid(txn_manager);
status = vmoid.AttachVmo(vmo);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to attach pager transfer vmo: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok(std::unique_ptr<StorageBackedTransferBuffer>(new StorageBackedTransferBuffer(
std::move(vmo), size, std::move(vmoid), txn_manager, block_iter_provider, metrics)));
}
zx::result<> StorageBackedTransferBuffer::Populate(uint64_t offset, uint64_t length,
const LoaderInfo& info) {
// Currently our block size is saved as a variable in some places and uses a constant in others.
// These should always match.
ZX_ASSERT(info.layout->blobfs_block_size() == kBlobfsBlockSize);
fs::Ticker ticker;
if (offset % kBlobfsBlockSize != 0) {
// The block math below relies on the offset being block-aligned.
return zx::error(ZX_ERR_INVALID_ARGS);
}
auto block_iter = block_iter_provider_->BlockIteratorByNodeIndex(info.node_index);
if (block_iter.is_error()) {
return block_iter.take_error();
}
uint64_t start_block = (info.layout->DataOffset() + offset) / kBlobfsBlockSize;
uint64_t block_count = fbl::round_up(length, kBlobfsBlockSize) / kBlobfsBlockSize;
TRACE_DURATION("blobfs", "StorageBackedTransferBuffer::Populate", "offset",
start_block * kBlobfsBlockSize, "length", block_count * kBlobfsBlockSize);
// Navigate to the start block.
zx_status_t status = IterateToBlock(&block_iter.value(), start_block);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to navigate to start block " << start_block << ": "
<< zx_status_get_string(status);
return zx::error(status);
}
std::vector<storage::BufferedOperation> operations;
// Enqueue operations to read in the required blocks to the transfer buffer.
const uint64_t data_start = DataStartBlock(txn_manager_->Info());
status = StreamBlocks(&block_iter.value(), block_count,
[&](uint64_t vmo_offset, uint64_t dev_offset, uint64_t length) {
operations.push_back({.vmoid = vmoid_.get(),
.op = {
.type = storage::OperationType::kRead,
.vmo_offset = vmo_offset - start_block,
.dev_offset = dev_offset + data_start,
.length = length,
}});
return ZX_OK;
});
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to enqueue read operations: " << zx_status_get_string(status);
return zx::error(status);
}
// Issue the read.
status = txn_manager_->RunRequests(operations);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to transact read operations: " << zx_status_get_string(status);
return zx::error(status);
}
// Update read metrics
if (info.decompressor == nullptr) {
metrics_->paged_read_metrics().IncrementDiskRead(CompressionAlgorithm::kUncompressed,
block_count * kBlobfsBlockSize, ticker.End());
} else {
metrics_->paged_read_metrics().IncrementDiskRead(info.decompressor->algorithm(),
block_count * kBlobfsBlockSize, ticker.End());
}
return zx::ok();
}
} // namespace blobfs