/*
- * 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
*/
#include "sim/drain.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;
DrainManager::DrainManager()
- : _count(0)
+ : _count(0),
+ _state(DrainState::Running)
{
}
{
}
+bool
+DrainManager::tryDrain()
+{
+ panic_if(_state == DrainState::Drained,
+ "Trying to drain a drained system\n");
+
+ panic_if(_count != 0,
+ "Drain counter must be zero at the start of a drain cycle\n");
+
+ DPRINTF(Drain, "Trying to drain %u objects.\n", drainableCount());
+ _state = DrainState::Draining;
+ 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");
+ _state = DrainState::Drained;
+ return true;
+ } else {
+ DPRINTF(Drain, "Need another drain cycle. %u/%u objects not ready.\n",
+ _count, drainableCount());
+ return false;
+ }
+}
+
void
-DrainManager::drainCycleDone()
+DrainManager::resume()
{
- exitSimLoop("Finished drain", 0);
+ panic_if(_state == DrainState::Running,
+ "Trying to resume a system that is already running\n");
+
+ warn_if(_state == DrainState::Draining,
+ "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);
+
+ // 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;
+}
+
+void
+DrainManager::preCheckpointRestore()
+{
+ panic_if(_state != DrainState::Running,
+ "preCheckpointRestore() called on a system that isn't in the "
+ "Running state.\n");
+
+ DPRINTF(Drain, "Applying pre-restore fixes to %u objects.\n",
+ drainableCount());
+ _state = DrainState::Drained;
+ for (auto *obj : _allDrainable)
+ obj->_drainState = DrainState::Drained;
+}
+
+void
+DrainManager::signalDrainDone()
+{
+ assert(_count > 0);
+ if (--_count == 0) {
+ DPRINTF(Drain, "All %u objects drained..\n", drainableCount());
+ exitSimLoop("Finished drain", 0);
+ }
+}
+
+
+void
+DrainManager::registerDrainable(Drainable *obj)
+{
+ std::lock_guard<std::mutex> lock(globalLock);
+ 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);
+ 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
+DrainManager::drainableCount() const
+{
+ std::lock_guard<std::mutex> lock(globalLock);
+ return _allDrainable.size();
}
Drainable::Drainable()
- : _drainState(DrainState::Running)
+ : _drainManager(DrainManager::instance()),
+ _drainState(_drainManager.state())
{
+ _drainManager.registerDrainable(this);
}
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();
}