blob: 6a0303b4c452e7e667d0b29b4462e8b01615b222 [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 "utils.h"
#include <memory>
#include <fbl/auto_call.h>
#include <safemath/checked_math.h>
#include <storage/buffer/owned_vmoid.h>
#include <zxtest/zxtest.h>
using id_allocator::IdAllocator;
namespace blobfs {
namespace {
storage::OwnedVmoid AttachVmo(BlockDevice* device, zx::vmo* vmo) {
storage::Vmoid vmoid;
EXPECT_OK(device->BlockAttachVmo(*vmo, &vmoid));
return storage::OwnedVmoid(std::move(vmoid), device);
}
// Verify that the |size| and |offset| are |device| block size aligned.
// Returns block_size in |out_block_size|.
void VerifySizeBlockAligned(BlockDevice* device, size_t size, uint64_t offset,
uint32_t* out_block_size) {
fuchsia_hardware_block_BlockInfo info = {};
ASSERT_OK(device->BlockGetInfo(&info));
ASSERT_EQ(size % info.block_size, 0);
ASSERT_EQ(offset % info.block_size, 0);
*out_block_size = info.block_size;
}
} // namespace
zx_status_t MockTransactionManager::Transaction(block_fifo_request_t* requests, size_t count) {
fbl::AutoLock lock(&lock_);
if (transaction_callback_) {
for (size_t i = 0; i < count; i++) {
if (attached_vmos_.size() < requests[i].vmoid) {
return ZX_ERR_INVALID_ARGS;
}
std::optional<zx::vmo>* optional_vmo = &attached_vmos_[requests[i].vmoid - 1];
if (!optional_vmo->has_value()) {
return ZX_ERR_BAD_STATE;
}
const zx::vmo& dest_vmo = optional_vmo->value();
if (dest_vmo.get() == ZX_HANDLE_INVALID) {
return ZX_ERR_INVALID_ARGS;
}
zx_status_t status = transaction_callback_(requests[i], dest_vmo);
if (status != ZX_OK) {
return status;
}
}
}
return ZX_OK;
}
zx_status_t MockTransactionManager::BlockAttachVmo(const zx::vmo& vmo, storage::Vmoid* out) {
fbl::AutoLock lock(&lock_);
zx::vmo duplicate_vmo;
zx_status_t status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &duplicate_vmo);
if (status != ZX_OK) {
return status;
}
attached_vmos_.push_back(std::move(duplicate_vmo));
*out = storage::Vmoid(static_cast<uint16_t>(attached_vmos_.size()));
if (out->get() == 0) {
return ZX_ERR_OUT_OF_RANGE;
}
return ZX_OK;
}
zx_status_t MockTransactionManager::BlockDetachVmo(storage::Vmoid vmoid) {
fbl::AutoLock lock(&lock_);
vmoid_t id = vmoid.TakeId();
if (attached_vmos_.size() < id) {
return ZX_ERR_INVALID_ARGS;
}
attached_vmos_[id - 1].reset();
return ZX_OK;
}
// Create a block and node map of the requested size, update the superblock of
// the |space_manager|, and create an allocator from this provided info.
void InitializeAllocator(size_t blocks, size_t nodes, MockSpaceManager* space_manager,
std::unique_ptr<Allocator>* out) {
RawBitmap block_map;
ASSERT_OK(block_map.Reset(blocks));
fzl::ResizeableVmoMapper node_map;
ASSERT_OK(node_map.CreateAndMap(nodes * kBlobfsBlockSize, "node map"));
space_manager->MutableInfo().inode_count = nodes;
space_manager->MutableInfo().data_block_count = blocks;
std::unique_ptr<IdAllocator> nodes_bitmap = {};
ASSERT_OK(IdAllocator::Create(nodes, &nodes_bitmap), "nodes bitmap");
*out = std::make_unique<Allocator>(space_manager, std::move(block_map), std::move(node_map),
std::move(nodes_bitmap));
(*out)->SetLogging(false);
}
// Force the allocator to become maximally fragmented by allocating
// every-other block within up to |blocks|.
void ForceFragmentation(Allocator* allocator, size_t blocks) {
fbl::Vector<ReservedExtent> extents[blocks];
for (size_t i = 0; i < blocks; i++) {
ASSERT_OK(allocator->ReserveBlocks(1, &extents[i]));
ASSERT_EQ(1, extents[i].size());
}
for (size_t i = 0; i < blocks; i += 2) {
allocator->MarkBlocksAllocated(extents[i][0]);
}
}
// Save the extents within |in| in a non-reserved vector |out|.
void CopyExtents(const fbl::Vector<ReservedExtent>& in, fbl::Vector<Extent>* out) {
out->reserve(in.size());
for (size_t i = 0; i < in.size(); i++) {
out->push_back(in[i].extent());
}
}
// Save the nodes within |in| in a non-reserved vector |out|.
void CopyNodes(const fbl::Vector<ReservedNode>& in, fbl::Vector<uint32_t>* out) {
out->reserve(in.size());
for (size_t i = 0; i < in.size(); i++) {
out->push_back(in[i].index());
}
}
void DeviceBlockRead(BlockDevice* device, void* buf, size_t size, uint64_t dev_offset) {
uint32_t block_size;
VerifySizeBlockAligned(device, size, dev_offset, &block_size);
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(size, 0, &vmo));
storage::OwnedVmoid vmoid = AttachVmo(device, &vmo);
block_fifo_request_t request;
request.opcode = BLOCKIO_READ;
request.vmoid = vmoid.get();
request.length = safemath::checked_cast<uint32_t>(size / block_size);
request.vmo_offset = 0;
request.dev_offset = safemath::checked_cast<uint32_t>(dev_offset / block_size);
ASSERT_OK(device->FifoTransaction(&request, 1));
ASSERT_OK(vmo.read(buf, 0, size));
}
void DeviceBlockWrite(BlockDevice* device, const void* buf, size_t size, uint64_t dev_offset) {
uint32_t block_size;
VerifySizeBlockAligned(device, size, dev_offset, &block_size);
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(size, 0, &vmo));
ASSERT_OK(vmo.write(buf, 0, size));
storage::OwnedVmoid vmoid = AttachVmo(device, &vmo);
block_fifo_request_t request;
request.opcode = BLOCKIO_WRITE;
request.vmoid = vmoid.get();
request.length = safemath::checked_cast<uint32_t>(size / block_size);
request.vmo_offset = 0;
request.dev_offset = safemath::checked_cast<uint32_t>(dev_offset / block_size);
ASSERT_OK(device->FifoTransaction(&request, 1));
}
} // namespace blobfs