arch,sim,kern,dev,cpu: Create a Workload SimObject.
[gem5.git] / src / sim / drain.cc
index 384374099522d5963e2556891c2a5830a1ba8e52..30067682e03de5e2e37b7be1dc002d87c96bda88 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015 ARM Limited
+ * Copyright (c) 2012, 2015, 2017 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors: Andreas Sandberg
  */
 
 #include "sim/drain.hh"
 
-#include "base/misc.hh"
+#include <algorithm>
+
+#include "base/logging.hh"
 #include "base/trace.hh"
 #include "debug/Drain.hh"
 #include "sim/sim_exit.hh"
+#include "sim/sim_object.hh"
 
 DrainManager DrainManager::_instance;
 
@@ -67,8 +68,15 @@ DrainManager::tryDrain()
 
     DPRINTF(Drain, "Trying to drain %u objects.\n", drainableCount());
     _state = DrainState::Draining;
-    for (auto *obj : _allDrainable)
-        _count += obj->drain(&_instance);
+    for (auto *obj : _allDrainable) {
+        DrainState status = obj->dmDrain();
+        if (DTRACE(Drain) && status != DrainState::Drained) {
+            SimObject *temp = dynamic_cast<SimObject*>(obj);
+            if (temp)
+                DPRINTF(Drain, "Failed to drain %s\n", temp->name());
+        }
+        _count += status == DrainState::Drained ? 0 : 1;
+    }
 
     if (_count == 0) {
         DPRINTF(Drain, "Drain done.\n");
@@ -91,14 +99,33 @@ DrainManager::resume()
             "Resuming a system that isn't fully drained, this is untested and "
             "likely to break\n");
 
+    panic_if(_state == DrainState::Resuming,
+             "Resuming a system that is already trying to resume. This should "
+             "never happen.\n");
+
     panic_if(_count != 0,
              "Resume called in the middle of a drain cycle. %u objects "
              "left to drain.\n", _count);
 
-    DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
+    // At this point in time the DrainManager and all objects will be
+    // in the the Drained state. New objects (i.e., objects created
+    // while resuming) will inherit the Resuming state from the
+    // DrainManager, which means we have to resume objects until all
+    // objects are in the Running state.
+    _state = DrainState::Resuming;
+
+    do {
+        DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
+        for (auto *obj : _allDrainable) {
+            if (obj->drainState() != DrainState::Running) {
+                assert(obj->drainState() == DrainState::Drained ||
+                       obj->drainState() == DrainState::Resuming);
+                obj->dmDrainResume();
+            }
+        }
+    } while (!allInState(DrainState::Running));
+
     _state = DrainState::Running;
-    for (auto *obj : _allDrainable)
-        obj->drainResume();
 }
 
 void
@@ -118,6 +145,7 @@ DrainManager::preCheckpointRestore()
 void
 DrainManager::signalDrainDone()
 {
+    assert(_count > 0);
     if (--_count == 0) {
         DPRINTF(Drain, "All %u objects drained..\n", drainableCount());
         exitSimLoop("Finished drain", 0);
@@ -129,14 +157,29 @@ void
 DrainManager::registerDrainable(Drainable *obj)
 {
     std::lock_guard<std::mutex> lock(globalLock);
-    _allDrainable.insert(obj);
+    assert(std::find(_allDrainable.begin(), _allDrainable.end(), obj) ==
+           _allDrainable.end());
+    _allDrainable.push_back(obj);
 }
 
 void
 DrainManager::unregisterDrainable(Drainable *obj)
 {
     std::lock_guard<std::mutex> lock(globalLock);
-    _allDrainable.erase(obj);
+    auto o = std::find(_allDrainable.begin(), _allDrainable.end(), obj);
+    assert(o != _allDrainable.end());
+    _allDrainable.erase(o);
+}
+
+bool
+DrainManager::allInState(DrainState state) const
+{
+    for (const auto *obj : _allDrainable) {
+        if (obj->drainState() != state)
+            return false;
+    }
+
+    return true;
 }
 
 size_t
@@ -150,7 +193,7 @@ DrainManager::drainableCount() const
 
 Drainable::Drainable()
     : _drainManager(DrainManager::instance()),
-      _drainState(DrainState::Running)
+      _drainState(_drainManager.state())
 {
     _drainManager.registerDrainable(this);
 }
@@ -160,8 +203,24 @@ Drainable::~Drainable()
     _drainManager.unregisterDrainable(this);
 }
 
+DrainState
+Drainable::dmDrain()
+{
+    _drainState = DrainState::Draining;
+    _drainState = drain();
+    assert(_drainState == DrainState::Draining ||
+           _drainState == DrainState::Drained);
+
+    return _drainState;
+}
+
 void
-Drainable::drainResume()
+Drainable::dmDrainResume()
 {
+    panic_if(_drainState != DrainState::Drained &&
+             _drainState != DrainState::Resuming,
+             "Trying to resume an object that hasn't been drained\n");
+
     _drainState = DrainState::Running;
+    drainResume();
 }