blob: c49e5f8d5cdb9dcdcaeadf95165712f973053914 [file] [log] [blame]
// Copyright 2022 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.
#ifndef SRC_DEVICES_BLOCK_DRIVERS_CORE_BLOCK_DEVICE_H_
#define SRC_DEVICES_BLOCK_DRIVERS_CORE_BLOCK_DEVICE_H_
#include <assert.h>
#include <fidl/fuchsia.hardware.block.partition/cpp/wire.h>
#include <fidl/fuchsia.hardware.block.volume/cpp/wire.h>
#include <fidl/fuchsia.hardware.block/cpp/wire.h>
#include <fuchsia/hardware/block/driver/cpp/banjo.h>
#include <fuchsia/hardware/block/partition/cpp/banjo.h>
#include <fuchsia/hardware/block/volume/cpp/banjo.h>
#include <inttypes.h>
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/device.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/metadata.h>
#include <lib/operation/block.h>
#include <lib/zbi-format/zbi.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/fifo.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <threads.h>
#include <zircon/process.h>
#include <algorithm>
#include <limits>
#include <list>
#include <mutex>
#include <new>
#include <ddktl/device.h>
#include "src/storage/lib/storage-metrics/block-metrics.h"
// To maintain stats related to time taken by a command or its success/failure, we need to
// intercept command completion with a callback routine. This might introduce memory
// overhead.
// TODO(https://fxbug.dev/42072576): We should be able to turn on/off stats either at compile-time
// or load-time.
struct StatsCookie {
zx::ticks start_tick;
};
class BlockDevice;
using BlockDeviceType = ddk::Device<BlockDevice, ddk::GetProtocolable,
ddk::Messageable<fuchsia_hardware_block_volume::Volume>::Mixin>;
class BlockDevice : public BlockDeviceType,
public ddk::BlockProtocol<BlockDevice, ddk::base_protocol> {
public:
explicit BlockDevice(zx_device_t* parent)
: BlockDeviceType(parent),
parent_protocol_(parent),
parent_partition_protocol_(parent),
parent_volume_protocol_(parent) {
block_protocol_t self{&block_protocol_ops_, this};
self_protocol_ = ddk::BlockProtocolClient(&self);
}
static zx_status_t Bind(void* ctx, zx_device_t* dev);
constexpr size_t OpSize() const {
ZX_DEBUG_ASSERT(parent_op_size_ > 0);
return block::BorrowedOperation<StatsCookie>::OperationSize(parent_op_size_);
}
void DdkRelease();
// ddk::GetProtocolable
zx_status_t DdkGetProtocol(uint32_t proto_id, void* out_protocol);
// ddk::BlockProtocol
void BlockQuery(block_info_t* block_info, size_t* op_size);
void BlockQueue(block_op_t* op, block_impl_queue_callback completion_cb, void* cookie);
// fuchsia_hardware_block_volume::Volume
void GetInfo(GetInfoCompleter::Sync& completer) override;
void GetStats(GetStatsRequestView request, GetStatsCompleter::Sync& completer) override;
void OpenSession(OpenSessionRequestView request, OpenSessionCompleter::Sync& completer) override;
void ReadBlocks(ReadBlocksRequestView request, ReadBlocksCompleter::Sync& completer) override;
void WriteBlocks(WriteBlocksRequestView request, WriteBlocksCompleter::Sync& completer) override;
void GetTypeGuid(GetTypeGuidCompleter::Sync& completer) override;
void GetInstanceGuid(GetInstanceGuidCompleter::Sync& completer) override;
void GetName(GetNameCompleter::Sync& completer) override;
void QuerySlices(QuerySlicesRequestView request, QuerySlicesCompleter::Sync& completer) override;
void GetVolumeInfo(GetVolumeInfoCompleter::Sync& completer) override;
void Extend(ExtendRequestView request, ExtendCompleter::Sync& completer) override;
void Shrink(ShrinkRequestView request, ShrinkCompleter::Sync& completer) override;
void Destroy(DestroyCompleter::Sync& completer) override;
private:
zx_status_t DoIo(zx::vmo& vmo, size_t buf_len, zx_off_t off, zx_off_t vmo_off, bool write);
// Completion callback that expects StatsCookie as |cookie| and calls upper
// layer completion cookie.
static void UpdateStatsAndCallCompletion(void* cookie, zx_status_t status, block_op_t* op);
void UpdateStats(bool success, zx::ticks start_tick, block_op_t* op);
// The block protocol of the device we are binding against.
ddk::BlockImplProtocolClient parent_protocol_;
// An optional partition protocol, if supported by the parent device.
ddk::BlockPartitionProtocolClient parent_partition_protocol_;
// An optional volume protocol, if supported by the parent device.
ddk::BlockVolumeProtocolClient parent_volume_protocol_;
// The block protocol for ourselves, which redirects to the parent protocol,
// but may also collect auxiliary information like statistics.
ddk::BlockProtocolClient self_protocol_;
block_info_t info_ = {};
// parent device's op size
size_t parent_op_size_ = 0;
// True if we have metadata for a ZBI partition map.
bool has_bootpart_ = false;
std::mutex io_lock_;
zx::vmo io_vmo_ TA_GUARDED(io_lock_);
zx_status_t io_status_ = ZX_OK;
sync_completion_t io_signal_;
std::unique_ptr<uint8_t[]> io_op_;
std::mutex stat_lock_;
// TODO(https://fxbug.dev/42072576): We should consider adding the ability to toggle stats, or
// remove this boolean if we aren't going to.
bool enable_stats_ TA_GUARDED(stat_lock_) = true;
storage_metrics::BlockDeviceMetrics stats_ TA_GUARDED(stat_lock_) = {};
// To maintain stats related to time taken by a command or its success/failure, we need to
// intercept command completion with a callback routine. This might introduce cpu
// overhead.
// TODO(https://fxbug.dev/42072576): We should be able to turn on/off stats at run-time.
// Create fidl interface to control how stats are maintained.
bool completion_status_stats_ = true;
};
#endif // SRC_DEVICES_BLOCK_DRIVERS_CORE_BLOCK_DEVICE_H_