[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