blob: 38e2a0ace303cfa048a5c7bb4b7ecca033fd2bd9 [file] [log] [blame]
// Copyright 2019 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_LIB_DEV_OPERATION_INCLUDE_LIB_OPERATION_BLOCK_H_
#define SRC_DEVICES_LIB_DEV_OPERATION_INCLUDE_LIB_OPERATION_BLOCK_H_
#include <lib/operation/operation.h>
#include <memory>
#include <ddk/protocol/block.h>
namespace block {
// Usage notes:
//
// block::Operation is a c++ wrapper around the block_op_t object. It provides
// capabilites to interact with a block_op buffer which is used to traverse the
// block stack. On deletion, it will automatically free itself.
//
// block::BorrowedOperation provides an unowned variant of block::Operation. It adds
// functionality to store and call a complete callback which isn't present in
// block::Operation. In addition, it will call the completion on destruction if it
// wasn't already triggered.
//
// block::OperationPool provides pooling functionality for block::Operation reuse.
//
// block::OperationQueue provides a queue interface for tracking block::Operation and
// block::BorrowedOperation objects.
//
// Available methods for both Operation and BorrowedOperation include:
//
// block_op_t* operation(); // accessor for inner type.
//
// // Takes ownership of inner type. Should only be used when transferring
// // ownership to another driver.
// block_op_t* take();
//
// Available to Operation and BorrowedOperation if they templatize of Storage:
//
// Storage* private_storage(); // accessor for private storage.
//
// Available to Operation:
//
// void Release(); // Frees the inner type.
//
// Available to BorrowedOperation:
//
// void Complete(zx_status_t); // Completes the operation.
//
///////////////////////////////////////////////////////////////////////////////
// Example: Basic allocation with a pool:
//
// block::OperationPool<> pool;
//
// const size_t op_size = block::Operation<>::OperationSize(parent_op_size);
// for (int i = 0; i < kNumRequest; i++) {
// std::optional<block::Operation> request;
// request = block::Operation::Alloc(op_size, parent_op_size);
//
// if (!request) return ZX_ERR_NO_MEMORY;
// pool.add(*std::move(request));
// }
//
///////////////////////////////////////////////////////////////////////////////
// Example: Enqueue incoming operation into a block::OperationQueue:
//
// class Driver {
// public:
// <...>
// private:
// block::BorrowedOperationQueue<> operations_;
// const size_t parent_op_size_;
// };
//
// void Driver::BlockImplQueue(block_op_t* op, block_queue_callback completion_cb, void* cookie) {
// operations_.push(block::BorrowedOperation<>(op, cb, parent_req_size_));
// }
//
///////////////////////////////////////////////////////////////////////////////
// Example: Using private context only visible to your driver:
//
// struct PrivateStorage {
// bool valid;
// size_t count_metric;
// }
//
// using BlockOperation = block::BorrowedOperation<PrivateStorage>;
//
// void Driver::BlockImplQueue(block_op_t* op, block_queue_callback completion_cb, void* cookie) {
// BlockOperation block_op(op, cb, parent_req_size_));
// ZX_DEBUG_ASSERT(block_op.operation()->command == BLOCK_READ);
// block_op.private_storage()->valid = true;
// block_op.private_storage()->count_metric += 1;
// <...>
// }
//
struct OperationTraits {
using OperationType = block_op_t;
static OperationType* Alloc(size_t op_size) {
fbl::AllocChecker ac;
std::unique_ptr<uint8_t[]> raw;
if constexpr (alignof(OperationType) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
raw = std::unique_ptr<uint8_t[]>(
new (static_cast<std::align_val_t>(alignof(OperationType)), &ac) uint8_t[op_size]);
} else {
raw = std::unique_ptr<uint8_t[]>(new (&ac) uint8_t[op_size]);
}
if (!ac.check()) {
return nullptr;
}
return reinterpret_cast<OperationType*>(raw.release());
}
static void Free(OperationType* op) { delete[] reinterpret_cast<uint8_t*>(op); }
};
struct CallbackTraits {
using CallbackType = void(void*, zx_status_t, block_op_t*);
static void Callback(CallbackType* callback, void* cookie, block_op_t* op, zx_status_t status) {
callback(cookie, status, op);
}
};
template <typename Storage = void>
class Operation : public operation::Operation<Operation<Storage>, OperationTraits, Storage> {
public:
using BaseClass = operation::Operation<Operation<Storage>, OperationTraits, Storage>;
using BaseClass::BaseClass;
};
template <typename Storage = void>
class BorrowedOperation
: public operation::BorrowedOperation<BorrowedOperation<Storage>, OperationTraits,
CallbackTraits, Storage> {
public:
using BaseClass = operation::BorrowedOperation<BorrowedOperation<Storage>, OperationTraits,
CallbackTraits, Storage>;
using BaseClass::BaseClass;
};
template <typename Storage = void>
using OperationPool = operation::OperationPool<Operation<Storage>, OperationTraits, Storage>;
template <typename Storage = void>
using OperationQueue = operation::OperationQueue<Operation<Storage>, OperationTraits, Storage>;
template <typename Storage = void>
using BorrowedOperationQueue =
operation::BorrowedOperationQueue<BorrowedOperation<Storage>, OperationTraits, CallbackTraits,
Storage>;
} // namespace block
#endif // SRC_DEVICES_LIB_DEV_OPERATION_INCLUDE_LIB_OPERATION_BLOCK_H_