From 404e660fe1521c081438be4d5df95d7fc27ea8bd Mon Sep 17 00:00:00 2001
From: Todd Eisenberger <teisenbe@google.com>
Date: Tue, 23 Apr 2019 18:23:18 +0000
Subject: [PATCH] [devcoordinator] Store SuspendTask references

This is necessary for supporting composite device suspend.  Without
this, each component would create its own tasks for the composite.

Change-Id: I4f8ae59764dab9883c9fad4d6b085298cfd40dc3
---
 zircon/system/core/devmgr/devcoordinator/device.cpp | 13 +++++++++++++
 zircon/system/core/devmgr/devcoordinator/device.h   |  9 ++++++++-
 .../core/devmgr/devcoordinator/suspend-task.cpp     |  7 +++----
 .../core/devmgr/devcoordinator/suspend-task.h       |  2 ++
 4 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/zircon/system/core/devmgr/devcoordinator/device.cpp b/zircon/system/core/devmgr/devcoordinator/device.cpp
index ffd62ee5335..ac03e06194c 100644
--- a/zircon/system/core/devmgr/devcoordinator/device.cpp
+++ b/zircon/system/core/devmgr/devcoordinator/device.cpp
@@ -11,6 +11,7 @@
 #include "coordinator.h"
 #include "devfs.h"
 #include "fidl.h"
+#include "suspend-task.h"
 
 namespace devmgr {
 
@@ -193,6 +194,17 @@ zx_status_t Device::SignalReadyForBind(zx::duration delay) {
     return publish_task_.PostDelayed(this->coordinator->dispatcher(), delay);
 }
 
+fbl::RefPtr<SuspendTask> Device::RequestSuspendTask(uint32_t suspend_flags) {
+    if (active_suspend_) {
+        // We don't support different types of suspends concurrently, and
+        // shouldn't be able to reach this state.
+        ZX_ASSERT(suspend_flags == active_suspend_->suspend_flags());
+    } else {
+        active_suspend_ = SuspendTask::Create(fbl::WrapRefPtr(this), suspend_flags);
+    }
+    return active_suspend_;
+}
+
 zx_status_t Device::SendSuspend(uint32_t flags, SuspendCompletion completion) {
     if (suspend_completion_) {
         // We already have a pending suspend
@@ -212,6 +224,7 @@ void Device::CompleteSuspend(zx_status_t status) {
         state_ = Device::State::kSuspended;
     }
 
+    active_suspend_ = nullptr;
     SuspendCompletion completion(std::move(suspend_completion_));
     if (completion) {
         completion(status);
diff --git a/zircon/system/core/devmgr/devcoordinator/device.h b/zircon/system/core/devmgr/devcoordinator/device.h
index 9caa7628bc8..6dcfaa60b44 100644
--- a/zircon/system/core/devmgr/devcoordinator/device.h
+++ b/zircon/system/core/devmgr/devcoordinator/device.h
@@ -24,6 +24,7 @@ class Coordinator;
 class Devhost;
 struct Devnode;
 class SuspendContext;
+class SuspendTask;
 
 // clang-format off
 
@@ -252,6 +253,9 @@ struct Device : public fbl::RefCounted<Device>, public AsyncLoopRefCountedRpcHan
 
     State state() const { return state_; }
 
+    // Creates a new suspend task if necessary and returns a reference to it.
+    // If one is already in-progress, a reference to it is returned instead
+    fbl::RefPtr<SuspendTask> RequestSuspendTask(uint32_t suspend_flags);
 private:
     zx_status_t HandleRead();
 
@@ -289,8 +293,11 @@ private:
     // The current state of the device
     State state_ = State::kActive;
 
+    // If a suspend is in-progress, this task represents it.
+    fbl::RefPtr<SuspendTask> active_suspend_;
     // If a suspend is in-progress, this completion will be invoked when it is
-    // completed.
+    // completed.  It will likely mark |active_suspend_| as completed and clear
+    // it.
     SuspendCompletion suspend_completion_;
 };
 
diff --git a/zircon/system/core/devmgr/devcoordinator/suspend-task.cpp b/zircon/system/core/devmgr/devcoordinator/suspend-task.cpp
index 6035380c15c..2821d3d5813 100644
--- a/zircon/system/core/devmgr/devcoordinator/suspend-task.cpp
+++ b/zircon/system/core/devmgr/devcoordinator/suspend-task.cpp
@@ -28,8 +28,8 @@ void SuspendTask::Run() {
         case Device::State::kSuspended: continue;
         case Device::State::kActive: break;
         }
-        auto task = SuspendTask::Create(fbl::WrapRefPtr(&child), flags_);
-        AddDependency(std::move(task));
+
+        AddDependency(child.RequestSuspendTask(flags_));
         found_more_dependencies = true;
     }
     if (found_more_dependencies) {
@@ -42,8 +42,7 @@ void SuspendTask::Run() {
         switch (device_->proxy->state()) {
         case Device::State::kSuspended: break;
         case Device::State::kActive: {
-            auto task = SuspendTask::Create(device_->proxy, flags_);
-            AddDependency(std::move(task));
+            AddDependency(device_->proxy->RequestSuspendTask(flags_));
             return;
         }
         }
diff --git a/zircon/system/core/devmgr/devcoordinator/suspend-task.h b/zircon/system/core/devmgr/devcoordinator/suspend-task.h
index 96f2b676dbf..4d781af29fd 100644
--- a/zircon/system/core/devmgr/devcoordinator/suspend-task.h
+++ b/zircon/system/core/devmgr/devcoordinator/suspend-task.h
@@ -17,6 +17,8 @@ public:
     // Don/t invoke this, use Create
     SuspendTask(fbl::RefPtr<Device> device, uint32_t flags, Completion completion);
 
+    uint32_t suspend_flags() { return flags_; }
+
     ~SuspendTask() final;
 private:
     void Run() final;
-- 
GitLab