[block][operation] Implement block::Operation and friends.
Specialized operation::Operation and friends for block protocol use cases.
Tested: Tested with ramdisk changes in future CL.
Change-Id: I279efbbf0964f14f1c713a9c1aa1c6918f91e5ee
diff --git a/system/dev/lib/operation/include/lib/operation/block.h b/system/dev/lib/operation/include/lib/operation/block.h
new file mode 100644
index 0000000..75e3424
--- /dev/null
+++ b/system/dev/lib/operation/include/lib/operation/block.h
@@ -0,0 +1,160 @@
+// 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.
+
+#pragma once
+
+#include <lib/operation/operation.h>
+
+#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::UnownedOperation 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::UnownedOperation objects.
+//
+// Available methods for both Operation and UnownedOperation 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 UnownedOperation if they templatize of Storage:
+//
+// Storage* private_storage(); // accessor for private storage.
+//
+// Available to Operation:
+//
+// void Release(); // Frees the inner type.
+//
+// Available to UnownedOperation:
+//
+// 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::UnownedOperationQueue<> operations_;
+// const size_t parent_op_size_;
+// };
+//
+// void Driver::BlockImplQueue(block_op_t* op, block_queue_callback completion_cb, void* cookie) {
+// operations_.push(block::UnownedOperation<>(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::UnownedOperation<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;
+ fbl::unique_ptr<uint8_t[]> raw;
+ if constexpr (alignof(OperationType) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+ raw = fbl::unique_ptr<uint8_t[]>(
+ new (static_cast<std::align_val_t>(alignof(OperationType)), &ac) uint8_t[op_size]);
+ } else {
+ raw = fbl::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 std::tuple<zx_status_t> AutoCompleteArgs() {
+ return std::make_tuple(ZX_ERR_INTERNAL);
+ }
+
+ 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 UnownedOperation : public operation::UnownedOperation<UnownedOperation<Storage>,
+ OperationTraits, CallbackTraits,
+ Storage> {
+public:
+ using BaseClass = operation::UnownedOperation<UnownedOperation<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 UnownedOperationQueue = operation::UnownedOperationQueue<UnownedOperation<Storage>,
+ OperationTraits, CallbackTraits,
+ Storage>;
+
+} // namespace block