[nand] Use nand::UnownedOperation<>

Tested: paved astro
Change-Id: I920406359b4c63ed3f4a5a6b68fe6ffea2c35b39
diff --git a/system/dev/nand/nand/nand.cpp b/system/dev/nand/nand/nand.cpp
index c98aa02..0c18bd1 100644
--- a/system/dev/nand/nand/nand.cpp
+++ b/system/dev/nand/nand/nand.cpp
@@ -172,24 +172,23 @@
     return status;
 }
 
-void NandDevice::DoIo(Transaction* txn) {
+void NandDevice::DoIo(Transaction txn) {
     zx_status_t status = ZX_OK;
 
-    ZX_DEBUG_ASSERT(txn != nullptr);
-    switch (txn->op.command) {
+    switch (txn.operation()->command) {
     case NAND_OP_READ:
-        status = ReadOp(&txn->op);
+        status = ReadOp(txn.operation());
         break;
     case NAND_OP_WRITE:
-        status = WriteOp(&txn->op);
+        status = WriteOp(txn.operation());
         break;
     case NAND_OP_ERASE:
-        status = EraseOp(&txn->op);
+        status = EraseOp(txn.operation());
         break;
     default:
         ZX_DEBUG_ASSERT(false); // Unexpected.
     }
-    txn->Complete(status);
+    txn.Complete(status);
 }
 
 // Initialization is complete by the time the thread starts.
@@ -199,17 +198,12 @@
     for (;;) {
         // Don't loop until txn_queue_ is empty to check for kNandShutdown
         // between each io.
-        {
-            fbl::AutoLock al(&lock_);
-            Transaction* txn = txn_queue_.pop_front();
-            if (txn) {
-                // Unlock if we execute the transaction.
-                al.release();
-                DoIo(txn);
-            } else {
-                // Clear the "RECEIVED" flag under the lock.
-                worker_event_.signal(kNandTxnReceived, 0);
-            }
+        std::optional<Transaction> txn = txn_queue_.pop();
+        if (txn) {
+            DoIo(std::move(*txn));
+        } else {
+            // Clear the "RECEIVED" flag under the lock.
+            worker_event_.signal(kNandTxnReceived, 0);
         }
 
         zx_signals_t pending;
@@ -230,7 +224,7 @@
 
 void NandDevice::NandQuery(fuchsia_hardware_nand_Info* info_out, size_t* nand_op_size_out) {
     memcpy(info_out, &nand_info_, sizeof(*info_out));
-    *nand_op_size_out = sizeof(Transaction);
+    *nand_op_size_out = Transaction::OperationSize(sizeof(nand_operation_t));
 }
 
 void NandDevice::NandQueue(nand_operation_t* op, nand_queue_callback completion_cb, void* cookie) {
@@ -240,19 +234,19 @@
         return;
     }
 
-    Transaction* txn = Transaction::FromOp(op, completion_cb, cookie);
+    Transaction txn(op, completion_cb, cookie, sizeof(nand_operation_t));
 
-    switch (txn->op.command) {
+    switch (op->command) {
     case NAND_OP_READ:
     case NAND_OP_WRITE: {
         if (op->rw.offset_nand >= num_nand_pages_ || !op->rw.length ||
             (num_nand_pages_ - op->rw.offset_nand) < op->rw.length) {
-            txn->Complete(ZX_ERR_OUT_OF_RANGE);
+            txn.Complete(ZX_ERR_OUT_OF_RANGE);
             return;
         }
         if (op->rw.data_vmo == ZX_HANDLE_INVALID &&
             op->rw.oob_vmo == ZX_HANDLE_INVALID) {
-            txn->Complete(ZX_ERR_BAD_HANDLE);
+            txn.Complete(ZX_ERR_BAD_HANDLE);
             return;
         }
         break;
@@ -261,19 +255,18 @@
         if (!op->erase.num_blocks ||
             op->erase.first_block >= nand_info_.num_blocks ||
             (op->erase.num_blocks > (nand_info_.num_blocks - op->erase.first_block))) {
-            txn->Complete(ZX_ERR_OUT_OF_RANGE);
+            txn.Complete(ZX_ERR_OUT_OF_RANGE);
             return;
         }
         break;
 
     default:
-        txn->Complete(ZX_ERR_NOT_SUPPORTED);
+        txn.Complete(ZX_ERR_NOT_SUPPORTED);
         return;
     }
 
-    fbl::AutoLock al(&lock_);
     // TODO: UPDATE STATS HERE.
-    txn_queue_.push_back(txn);
+    txn_queue_.push(std::move(txn));
     // Wake up the worker thread (while locked, so they don't accidentally
     // clear the event).
     worker_event_.signal(0, kNandTxnReceived);
@@ -294,16 +287,8 @@
     worker_event_.signal(0, kNandShutdown);
     thrd_join(worker_thread_, nullptr);
 
-    {
-        lock_.Acquire();
-        // Error out all pending requests.
-        for (auto& txn : txn_queue_) {
-            lock_.Release();
-            txn.Complete(ZX_ERR_BAD_STATE);
-            lock_.Acquire();
-        }
-        lock_.Release();
-    }
+    // Error out all pending requests.
+    txn_queue_.Release();
 }
 
 // static
diff --git a/system/dev/nand/nand/nand.h b/system/dev/nand/nand/nand.h
index 4d821c2..ff53d43 100644
--- a/system/dev/nand/nand/nand.h
+++ b/system/dev/nand/nand/nand.h
@@ -12,15 +12,16 @@
 #include <ddktl/protocol/rawnand.h>
 #include <fbl/intrusive_double_list.h>
 #include <fbl/mutex.h>
+#include <lib/operation/nand.h>
 #include <lib/fzl/vmo-mapper.h>
 #include <lib/zx/event.h>
 #include <zircon/thread_annotations.h>
 #include <zircon/types.h>
 
-#include "transaction.h"
-
 namespace nand {
 
+using Transaction = nand::UnownedOperation<>;
+
 class NandDevice;
 using DeviceType = ddk::Device<NandDevice, ddk::GetSizable, ddk::Unbindable>;
 
@@ -69,7 +70,7 @@
     zx_status_t ReadOp(nand_operation_t* nand_op);
     zx_status_t WriteOp(nand_operation_t* nand_op);
 
-    void DoIo(Transaction* txn);
+    void DoIo(Transaction txn);
     zx_status_t WorkerThread();
 
     ddk::RawNandProtocolClient raw_nand_;
@@ -77,8 +78,7 @@
     fuchsia_hardware_nand_Info nand_info_;
     uint32_t num_nand_pages_;
 
-    fbl::Mutex lock_;
-    TransactionList txn_queue_ TA_GUARDED(lock_);
+    nand::UnownedOperationQueue<> txn_queue_;
 
     thrd_t worker_thread_;
     zx::event worker_event_;
diff --git a/system/dev/nand/nand/rules.mk b/system/dev/nand/nand/rules.mk
index 8ba2a1c..55a274e 100644
--- a/system/dev/nand/nand/rules.mk
+++ b/system/dev/nand/nand/rules.mk
@@ -15,6 +15,7 @@
     $(LOCAL_DIR)/nand.cpp \
 
 MODULE_STATIC_LIBS := \
+    system/dev/lib/operation \
     system/ulib/ddk \
     system/ulib/ddktl \
     system/ulib/fbl \
@@ -58,6 +59,7 @@
 
 MODULE_STATIC_LIBS := \
     system/dev/lib/fake_ddk \
+    system/dev/lib/operation \
     system/ulib/ddk \
     system/ulib/ddktl \
     system/ulib/fbl \
diff --git a/system/dev/nand/nand/transaction.h b/system/dev/nand/nand/transaction.h
deleted file mode 100644
index aae9f77..0000000
--- a/system/dev/nand/nand/transaction.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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 <memory>
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <ddk/protocol/nand.h>
-#include <fbl/intrusive_double_list.h>
-#include <zircon/types.h>
-
-namespace nand {
-
-template <typename T>
-struct TransactionLinkedListTraits {
-    using PtrTraits = fbl::internal::ContainerPtrTraits<T>;
-    static fbl::DoublyLinkedListNodeState<T>& node_state(typename PtrTraits::RefType obj) {
-        return obj.data.dll_node_state_;
-    }
-};
-
-struct Transaction;
-
-// All data stored in a Transaction other than |nand_operation_t|.
-class TransactionData {
-private:
-    TransactionData(nand_queue_callback completion_cb, void* cookie)
-        : completion_cb_(completion_cb), cookie_(cookie) {}
-
-    friend Transaction;
-    friend TransactionLinkedListTraits<Transaction*>;
-
-    nand_queue_callback completion_cb_;
-    void* cookie_;
-    fbl::DoublyLinkedListNodeState<Transaction*> dll_node_state_;
-};
-
-// A container for both a |nand_operation_t|, but also our arbitrary |TransactionData|.
-//
-// This structure is allocated by the upper level driver, and must be manually initialized
-// for incoming transactions.
-struct Transaction {
-    // Returns a pointer to a Transaction given a nand_operation_t.
-    //
-    // To be used safely, the "nand_operation size" return value from |NandQuery| must
-    // be at least sizeof(Transaction), requesting that enough space is allocated
-    // alongside the |nand_operation_t| for the rest of |TransactionData| to fit.
-    static Transaction* FromOp(nand_operation_t* op, nand_queue_callback completion_cb,
-                               void* cookie) {
-        static_assert(offsetof(Transaction, op) == 0, "Cannot cast from nand op to transaction");
-        auto txn = reinterpret_cast<Transaction*>(op);
-
-        // Transaction was allocated by the upper level driver, but our TransactionData
-        // was not actually constructed. Use placement new to ensure the
-        // object is initialized, with a complementary explicit destructor of TransactionData
-        // within |Complete|.
-        new (&txn->data) TransactionData(completion_cb, cookie);
-        return txn;
-    }
-
-    // Since |TransactionData| is destructed in-place in calls to |Complete|, ensure
-    // the typical destructor of Transaction is never executed.
-    ~Transaction() = delete;
-
-    void Complete(zx_status_t status) {
-        // Since completing a transaction may de-allocate the transaction, save our state
-        // and execute the placment destructor of TransactionData before invoking the
-        // completion callback.
-        nand_queue_callback completion_cb = data.completion_cb_;
-        void* cookie = data.cookie_;
-
-        data.~TransactionData();
-
-        // Transaction should not be referenced after invoking the completion callback.
-        completion_cb(cookie, status, &op);
-    }
-
-    nand_operation_t op;
-    TransactionData data;
-};
-
-static_assert(std::is_standard_layout<Transaction>::value,
-              "Transaction must be standard layout to be convertible from a nand_operation_t");
-
-using TransactionList = fbl::DoublyLinkedList<Transaction*,
-                                              TransactionLinkedListTraits<Transaction*>>;
-} // namespace nand