blob: a261c07f818ebddbaa7dc0c5aa96f50017bcf9e7 [file] [log] [blame]
// Copyright 2021 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.
#ifdef __Fuchsia__
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <storage/buffer/vmo_buffer.h>
#include <storage/operation/operation.h>
#include "src/lib/storage/block_client/cpp/remote_block_device.h"
#endif // __Fuchsia__
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utility>
#include <fbl/alloc_checker.h>
#include <fbl/ref_ptr.h>
#include <storage/buffer/block_buffer.h>
#include "src/storage/f2fs/f2fs.h"
namespace f2fs {
#ifdef __Fuchsia__
zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool* out_readonly,
std::unique_ptr<f2fs::Bcache>* out) {
fuchsia_hardware_block_BlockInfo info;
if (zx_status_t status = device->BlockGetInfo(&info); status != ZX_OK) {
FX_LOGS(ERROR) << "Coult not access device info: " << status;
return status;
}
uint64_t device_size = info.block_size * info.block_count;
if (device_size == 0) {
FX_LOGS(ERROR) << "Invalid device size";
return ZX_ERR_NO_RESOURCES;
}
uint64_t block_count = device_size / kBlockSize;
// The maximum volume size of f2fs is 16TB
if (block_count >= std::numeric_limits<uint32_t>::max()) {
FX_LOGS(ERROR) << "Block count overflow";
return ZX_ERR_OUT_OF_RANGE;
}
return f2fs::Bcache::Create(std::move(device), block_count, kBlockSize, out);
}
std::unique_ptr<block_client::BlockDevice> Bcache::Destroy(std::unique_ptr<Bcache> bcache) {
// Destroy the VmoBuffer before extracting the underlying device, as it needs
// to de-register itself from the underlying block device to be terminated.
bcache->DestroyVmoBuffer();
return std::move(bcache->owned_device_);
}
Bcache::Bcache(block_client::BlockDevice* device, uint64_t max_blocks, block_t block_size)
: max_blocks_(max_blocks), block_size_(block_size), device_(device) {}
zx_status_t Bcache::BlockAttachVmo(const zx::vmo& vmo, storage::Vmoid* out) {
return GetDevice()->BlockAttachVmo(vmo, out);
}
zx_status_t Bcache::BlockDetachVmo(storage::Vmoid vmoid) {
return GetDevice()->BlockDetachVmo(std::move(vmoid));
}
zx_status_t Bcache::Create(std::unique_ptr<block_client::BlockDevice> device, uint64_t max_blocks,
block_t block_size, std::unique_ptr<Bcache>* out) {
zx_status_t status = Create(device.get(), max_blocks, block_size, out);
if (status == ZX_OK) {
(*out)->owned_device_ = std::move(device);
}
return status;
}
zx_status_t Bcache::Create(block_client::BlockDevice* device, uint64_t max_blocks,
block_t block_size, std::unique_ptr<Bcache>* out) {
std::unique_ptr<Bcache> bcache(new Bcache(device, max_blocks, block_size));
zx_status_t status = bcache->CreateVmoBuffer();
if (status != ZX_OK) {
return status;
}
status = bcache->VerifyDeviceInfo();
if (status != ZX_OK) {
return status;
}
*out = std::move(bcache);
return ZX_OK;
}
#else // __Fuchsia__
Bcache::Bcache(fbl::unique_fd fd, uint64_t max_blocks)
: max_blocks_(max_blocks), fd_(std::move(fd)), buffer_(1, kBlockSize) {}
zx_status_t Bcache::Create(fbl::unique_fd fd, uint64_t max_blocks, std::unique_ptr<Bcache>* out) {
uint64_t max_blocks_converted = max_blocks * kBlockSize / kDefaultSectorSize;
std::unique_ptr<Bcache> bcache(new Bcache(std::move(fd), max_blocks_converted));
*out = std::move(bcache);
return ZX_OK;
}
#endif // __Fuchsia__
zx_status_t Bcache::Readblk(block_t bno, void* data) {
if (bno >= max_blocks_) {
return ZX_ERR_OUT_OF_RANGE;
}
#ifdef __Fuchsia__
TRACE_DURATION("f2fs", "Bcache::Readblk", "blk", bno);
#endif
storage::Operation operation = {};
operation.type = storage::OperationType::kRead;
operation.vmo_offset = 0;
operation.dev_offset = bno;
operation.length = 1;
std::lock_guard lock(buffer_mutex_);
zx_status_t status = RunOperation(operation, &buffer_);
if (status != ZX_OK) {
return status;
}
std::memcpy(data, buffer_.Data(0), BlockSize());
return ZX_OK;
}
zx_status_t Bcache::Writeblk(block_t bno, const void* data) {
if (bno >= max_blocks_) {
return ZX_ERR_OUT_OF_RANGE;
}
#ifdef __Fuchsia__
TRACE_DURATION("f2fs", "Bcache::Writeblk", "blk", bno);
#endif
storage::Operation operation = {};
operation.type = storage::OperationType::kWrite;
operation.vmo_offset = 0;
operation.dev_offset = bno;
operation.length = 1;
std::lock_guard lock(buffer_mutex_);
std::memcpy(buffer_.Data(0), data, BlockSize());
return RunOperation(operation, &buffer_);
}
#ifdef __Fuchsia__
zx_status_t Bcache::RunRequests(const std::vector<storage::BufferedOperation>& operations) {
std::shared_lock lock(mutex_);
return DeviceTransactionHandler::RunRequests(operations);
}
zx_status_t Bcache::VerifyDeviceInfo() {
zx_status_t status = device_->BlockGetInfo(&info_);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "cannot get block device information: " << status;
return status;
}
if (BlockSize() % info_.block_size != 0) {
FX_LOGS(WARNING) << "f2fs block size cannot be multiple of underlying block size: "
<< info_.block_size;
return ZX_ERR_BAD_STATE;
}
return ZX_OK;
}
#else // __Fuchsia__
zx_status_t Bcache::RunOperation(const storage::Operation& operation,
storage::BlockBuffer* buffer) {
return TransactionHandler::RunOperation(operation, buffer);
}
zx_status_t Bcache::RunRequests(const std::vector<storage::BufferedOperation>& operations) {
std::shared_lock lock(mutex_);
for (auto& operation : operations) {
const auto& op = operation.op;
off_t off = static_cast<off_t>(op.dev_offset) * BlockSize();
if (lseek(fd_.get(), off, SEEK_SET) < 0) {
FX_LOGS(ERROR) << "seek failed at " << op.dev_offset << ". " << errno;
return ZX_ERR_IO;
}
size_t length = op.length * BlockSize();
size_t buffer_offset = op.vmo_offset * BlockSize();
switch (op.type) {
case storage::OperationType::kRead:
if (size_t ret =
read(fd_.get(), static_cast<uint8_t*>(operation.data) + buffer_offset, length);
ret != length) {
FX_LOGS(ERROR) << "read failed at " << op.dev_offset;
return ZX_ERR_IO;
}
break;
case storage::OperationType::kWrite:
if (size_t ret =
write(fd_.get(), static_cast<uint8_t*>(operation.data) + buffer_offset, length);
ret != length) {
FX_LOGS(ERROR) << "write failed at " << op.dev_offset << " (" << ret << ")";
return ZX_ERR_IO;
}
break;
case storage::OperationType::kTrim:
// TODO : zeroing
break;
default:
ZX_DEBUG_ASSERT_MSG(false, "Unsupported operation");
}
}
return ZX_OK;
}
#endif // __Fuchsia__
zx_status_t Bcache::Trim(block_t start, block_t num) {
#ifdef __Fuchsia__
if (!(info_.flags & fuchsia_hardware_block_FLAG_TRIM_SUPPORT)) {
return ZX_ERR_NOT_SUPPORTED;
}
block_fifo_request_t request = {
.opcode = BLOCKIO_TRIM,
.vmoid = BLOCK_VMOID_INVALID,
.length = static_cast<uint32_t>(BlockNumberToDevice(num)),
.vmo_offset = 0,
.dev_offset = BlockNumberToDevice(start),
};
return GetDevice()->FifoTransaction(&request, 1);
#else // __Fuchsia__
return ZX_OK;
#endif // __Fuchsia__
}
} // namespace f2fs