diff options
Diffstat (limited to 'src/sim')
-rw-r--r-- | src/sim/cxx_manager.cc | 11 | ||||
-rw-r--r-- | src/sim/cxx_manager.hh | 2 | ||||
-rw-r--r-- | src/sim/drain.cc | 102 | ||||
-rw-r--r-- | src/sim/drain.hh | 126 |
4 files changed, 189 insertions, 52 deletions
diff --git a/src/sim/cxx_manager.cc b/src/sim/cxx_manager.cc index 6d4565dbc..e0b209aa4 100644 --- a/src/sim/cxx_manager.cc +++ b/src/sim/cxx_manager.cc @@ -644,20 +644,15 @@ CxxConfigManager::startup() } unsigned int -CxxConfigManager::drain(DrainManager *drain_manager) +CxxConfigManager::drain() { - unsigned int ret = 0; - - for (auto i = objectsInOrder.begin(); i != objectsInOrder.end(); ++ i) - ret += (*i)->drain(drain_manager); - - return ret; + return DrainManager::instance().tryDrain() ? 0 : 1; } void CxxConfigManager::drainResume() { - forEachObject(&SimObject::drainResume); + DrainManager::instance().resume(); } void diff --git a/src/sim/cxx_manager.hh b/src/sim/cxx_manager.hh index b2ba31214..458dfcd0c 100644 --- a/src/sim/cxx_manager.hh +++ b/src/sim/cxx_manager.hh @@ -283,7 +283,7 @@ class CxxConfigManager void startup(); /** Drain all objects */ - unsigned int drain(DrainManager *drain_manager); + unsigned int drain(); /** Resume from drain */ void drainResume(); diff --git a/src/sim/drain.cc b/src/sim/drain.cc index 90fb0c18d..384374099 100644 --- a/src/sim/drain.cc +++ b/src/sim/drain.cc @@ -38,10 +38,17 @@ */ #include "sim/drain.hh" + +#include "base/misc.hh" +#include "base/trace.hh" +#include "debug/Drain.hh" #include "sim/sim_exit.hh" +DrainManager DrainManager::_instance; + DrainManager::DrainManager() - : _count(0) + : _count(0), + _state(DrainState::Running) { } @@ -49,21 +56,108 @@ DrainManager::~DrainManager() { } +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) + _count += obj->drain(&_instance); + + 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::resume() +{ + 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(_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()); + _state = DrainState::Running; + for (auto *obj : _allDrainable) + obj->drainResume(); +} + +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() +{ + 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); + _allDrainable.insert(obj); +} + void -DrainManager::drainCycleDone() +DrainManager::unregisterDrainable(Drainable *obj) +{ + std::lock_guard<std::mutex> lock(globalLock); + _allDrainable.erase(obj); +} + +size_t +DrainManager::drainableCount() const { - exitSimLoop("Finished drain", 0); + std::lock_guard<std::mutex> lock(globalLock); + return _allDrainable.size(); } Drainable::Drainable() - : _drainState(DrainState::Running) + : _drainManager(DrainManager::instance()), + _drainState(DrainState::Running) { + _drainManager.registerDrainable(this); } Drainable::~Drainable() { + _drainManager.unregisterDrainable(this); } void diff --git a/src/sim/drain.hh b/src/sim/drain.hh index 4ed6074dd..a045bf169 100644 --- a/src/sim/drain.hh +++ b/src/sim/drain.hh @@ -40,8 +40,9 @@ #ifndef __SIM_DRAIN_HH__ #define __SIM_DRAIN_HH__ -#include <cassert> -#include <vector> +#include <atomic> +#include <mutex> +#include <unordered_set> #include "base/flags.hh" @@ -76,12 +77,12 @@ enum class DrainState { /** * This class coordinates draining of a System. * - * When draining a System, we need to make sure that all SimObjects in - * that system have drained their state before declaring the operation - * to be successful. This class keeps track of how many objects are - * still in the process of draining their state. Once it determines - * that all objects have drained their state, it exits the simulation - * loop. + * When draining the simulator, we need to make sure that all + * Drainable objects within the system have ended up in the drained + * state before declaring the operation to be successful. This class + * keeps track of how many objects are still in the process of + * draining. Once it determines that all objects have drained their + * state, it exits the simulation loop. * * @note A System might not be completely drained even though the * DrainManager has caused the simulation loop to exit. Draining needs @@ -91,39 +92,92 @@ enum class DrainState { */ class DrainManager { - public: + private: DrainManager(); - virtual ~DrainManager(); +#ifndef SWIG + DrainManager(DrainManager &) = delete; +#endif + ~DrainManager(); + + public: + /** Get the singleton DrainManager instance */ + static DrainManager &instance() { return _instance; } + + /** + * Try to drain the system. + * + * Try to drain the system and return true if all objects are in a + * the Drained state at which point the whole simulator is in a + * consistent state and ready for checkpointing or CPU + * handover. The simulation script must continue simulating until + * the simulation loop returns "Finished drain", at which point + * this method should be called again. This cycle should continue + * until this method returns true. + * + * @return true if all objects were drained successfully, false if + * more simulation is needed. + */ + bool tryDrain(); /** - * Get the number of objects registered with this DrainManager - * that are currently draining their state. + * Resume normal simulation in a Drained system. + */ + void resume(); + + /** + * Run state fixups before a checkpoint restore operation * - * @return Number of objects currently draining. + * The drain state of an object isn't stored in a checkpoint since + * the whole system is always going to be in the Drained state + * when the checkpoint is created. When the checkpoint is restored + * at a later stage, recreated objects will be in the Running + * state since the state isn't stored in checkpoints. This method + * performs state fixups on all Drainable objects and the + * DrainManager itself. */ - unsigned int getCount() const { return _count; } + void preCheckpointRestore(); + + /** Check if the system is drained */ + bool isDrained() { return _state == DrainState::Drained; } - void setCount(int count) { _count = count; } + /** Get the simulators global drain state */ + DrainState state() { return _state; } /** * Notify the DrainManager that a Drainable object has finished * draining. */ - void signalDrainDone() { - assert(_count > 0); - if (--_count == 0) - drainCycleDone(); - } + void signalDrainDone(); - protected: + public: + void registerDrainable(Drainable *obj); + void unregisterDrainable(Drainable *obj); + + private: /** - * Callback when all registered Drainable objects have completed a - * drain cycle. + * Thread-safe helper function to get the number of Drainable + * objects in a system. */ - virtual void drainCycleDone(); + size_t drainableCount() const; - /** Number of objects still draining. */ - unsigned int _count; + /** Lock protecting the set of drainable objects */ + mutable std::mutex globalLock; + + /** Set of all drainable objects */ + std::unordered_set<Drainable *> _allDrainable; + + /** + * Number of objects still draining. This is flagged atomic since + * it can be manipulated by SimObjects living in different + * threads. + */ + std::atomic_uint _count; + + /** Global simulator drain state */ + DrainState _state; + + /** Singleton instance of the drain manager */ + static DrainManager _instance; }; /** @@ -133,17 +187,11 @@ class DrainManager * An object's internal state needs to be drained when creating a * checkpoint, switching between CPU models, or switching between * timing models. Once the internal state has been drained from - * <i>all</i> objects in the system, the objects are serialized to + * <i>all</i> objects in the simulator, the objects are serialized to * disc or the configuration change takes place. The process works as * follows (see simulate.py for details): * * <ol> - * <li>An instance of a DrainManager is created to keep track of how - * many objects need to be drained. The object maintains an - * internal counter that is decreased every time its - * CountedDrainEvent::signalDrainDone() method is called. When the - * counter reaches zero, the simulation is stopped. - * * <li>Call Drainable::drain() for every object in the * system. Draining has completed if all of them return * zero. Otherwise, the sum of the return values is loaded into @@ -151,9 +199,9 @@ class DrainManager * manager is passed as an argument to the drain() method. * * <li>Continue simulation. When an object has finished draining its - * internal state, it calls CountedDrainEvent::signalDrainDone() - * on the manager. When the counter in the manager reaches zero, - * the simulation stops. + * internal state, it calls DrainManager::signalDrainDone() on the + * manager. When the counter in the manager reaches zero, the + * simulation stops. * * <li>Check if any object still needs draining, if so repeat the * process above. @@ -166,6 +214,8 @@ class DrainManager */ class Drainable { + friend class DrainManager; + public: Drainable(); virtual ~Drainable(); @@ -210,10 +260,8 @@ class Drainable void setDrainState(DrainState new_state) { _drainState = new_state; } private: + DrainManager &_drainManager; DrainState _drainState; }; -DrainManager *createDrainManager(); -void cleanupDrainManager(DrainManager *drain_manager); - #endif |