diff --git a/src/arch/arm/stage2_mmu.cc b/src/arch/arm/stage2_mmu.cc index 3525768e0..a2ae8cc73 100755 --- a/src/arch/arm/stage2_mmu.cc +++ b/src/arch/arm/stage2_mmu.cc @@ -141,12 +141,6 @@ Stage2MMU::Stage2Translation::finish(const Fault &_fault, RequestPtr req, } } -unsigned int -Stage2MMU::drain(DrainManager *dm) -{ - return port.drain(dm); -} - ArmISA::Stage2MMU * ArmStage2MMUParams::create() { diff --git a/src/arch/arm/stage2_mmu.hh b/src/arch/arm/stage2_mmu.hh index b42f213e8..9543c7471 100755 --- a/src/arch/arm/stage2_mmu.hh +++ b/src/arch/arm/stage2_mmu.hh @@ -112,8 +112,6 @@ class Stage2MMU : public SimObject */ DmaPort& getPort() { return port; } - unsigned int drain(DrainManager *dm); - Fault readDataUntimed(ThreadContext *tc, Addr oVAddr, Addr descAddr, uint8_t *data, int numBytes, Request::Flags flags, bool isFunctional); Fault readDataTimed(ThreadContext *tc, Addr descAddr, diff --git a/src/dev/arm/ufs_device.cc b/src/dev/arm/ufs_device.cc index ef6398216..696aeba6f 100644 --- a/src/dev/arm/ufs_device.cc +++ b/src/dev/arm/ufs_device.cc @@ -2319,26 +2319,16 @@ UFSHostDevice::unserialize(CheckpointIn &cp) unsigned int UFSHostDevice::drain(DrainManager *dm) { - unsigned int count = 0; - - // check pio, dma port, and doorbells - count = pioPort.drain(dm) + dmaPort.drain(dm); - if (UFSHCIMem.TRUTRLDBR) { - count += 1; drainManager = dm; - } else { - DPRINTF(UFSHostDevice, "UFSHostDevice in drained state\n"); - } - - if (count) { DPRINTF(UFSHostDevice, "UFSDevice is draining...\n"); setDrainState(DrainState::Draining); + return 1; } else { DPRINTF(UFSHostDevice, "UFSDevice drained\n"); setDrainState(DrainState::Drained); + return 0; } - return count; } /** diff --git a/src/dev/copy_engine.cc b/src/dev/copy_engine.cc index 035f824fa..ed177cf7f 100644 --- a/src/dev/copy_engine.cc +++ b/src/dev/copy_engine.cc @@ -650,29 +650,10 @@ CopyEngine::CopyEngineChannel::drain(DrainManager *dm) { if (nextState == Idle || ce->getDrainState() != DrainState::Running) return 0; - unsigned int count = 1; - count += cePort.drain(dm); DPRINTF(Drain, "CopyEngineChannel not drained\n"); this->drainManager = dm; - return count; -} - -unsigned int -CopyEngine::drain(DrainManager *dm) -{ - unsigned int count; - count = pioPort.drain(dm) + dmaPort.drain(dm) + configPort.drain(dm); - for (int x = 0;x < chan.size(); x++) - count += chan[x]->drain(dm); - - if (count) - setDrainState(DrainState::Draining); - else - setDrainState(DrainState::Drained); - - DPRINTF(Drain, "CopyEngine not drained\n"); - return count; + return 1; } void @@ -755,15 +736,6 @@ CopyEngine::CopyEngineChannel::restartStateMachine() } } -void -CopyEngine::drainResume() -{ - Drainable::drainResume(); - for (int x = 0;x < chan.size(); x++) - chan[x]->drainResume(); -} - - void CopyEngine::CopyEngineChannel::drainResume() { diff --git a/src/dev/copy_engine.hh b/src/dev/copy_engine.hh index 80c9798ee..d09a18dbd 100644 --- a/src/dev/copy_engine.hh +++ b/src/dev/copy_engine.hh @@ -207,9 +207,6 @@ class CopyEngine : public PciDevice void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE; void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; - - unsigned int drain(DrainManager *drainManger); - void drainResume(); }; #endif //__DEV_COPY_ENGINE_HH__ diff --git a/src/dev/dma_device.cc b/src/dev/dma_device.cc index a778833ff..97b31e976 100644 --- a/src/dev/dma_device.cc +++ b/src/dev/dma_device.cc @@ -125,17 +125,6 @@ DmaDevice::init() PioDevice::init(); } -unsigned int -DmaDevice::drain(DrainManager *dm) -{ - unsigned int count = pioPort.drain(dm) + dmaPort.drain(dm); - if (count) - setDrainState(DrainState::Draining); - else - setDrainState(DrainState::Drained); - return count; -} - unsigned int DmaPort::drain(DrainManager *dm) { diff --git a/src/dev/dma_device.hh b/src/dev/dma_device.hh index 933cbeb00..92bf8f72c 100644 --- a/src/dev/dma_device.hh +++ b/src/dev/dma_device.hh @@ -51,7 +51,7 @@ #include "sim/drain.hh" #include "sim/system.hh" -class DmaPort : public MasterPort +class DmaPort : public MasterPort, public Drainable { private: @@ -176,8 +176,6 @@ class DmaDevice : public PioDevice virtual void init(); - unsigned int drain(DrainManager *drainManger); - unsigned int cacheBlockSize() const { return sys->cacheLineSize(); } virtual BaseMasterPort &getMasterPort(const std::string &if_name, diff --git a/src/dev/i8254xGBe.cc b/src/dev/i8254xGBe.cc index 3b49c8b5d..cd96c9eca 100644 --- a/src/dev/i8254xGBe.cc +++ b/src/dev/i8254xGBe.cc @@ -2058,8 +2058,7 @@ IGbE::restartClock() unsigned int IGbE::drain(DrainManager *dm) { - unsigned int count; - count = pioPort.drain(dm) + dmaPort.drain(dm); + unsigned int count(0); if (rxDescCache.hasOutstandingEvents() || txDescCache.hasOutstandingEvents()) { count++; diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc index 9e5855a0b..74ab8c3b3 100644 --- a/src/dev/io_device.cc +++ b/src/dev/io_device.cc @@ -93,18 +93,6 @@ PioDevice::getSlavePort(const std::string &if_name, PortID idx) return MemObject::getSlavePort(if_name, idx); } -unsigned int -PioDevice::drain(DrainManager *dm) -{ - unsigned int count; - count = pioPort.drain(dm); - if (count) - setDrainState(DrainState::Draining); - else - setDrainState(DrainState::Drained); - return count; -} - BasicPioDevice::BasicPioDevice(const Params *p, Addr size) : PioDevice(p), pioAddr(p->pio_addr), pioSize(size), pioDelay(p->pio_latency) diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh index a528b250e..7e323b3bc 100644 --- a/src/dev/io_device.hh +++ b/src/dev/io_device.hh @@ -125,8 +125,6 @@ class PioDevice : public MemObject virtual void init(); - unsigned int drain(DrainManager *drainManger); - virtual BaseSlavePort &getSlavePort(const std::string &if_name, PortID idx = InvalidPortID); diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc index 5788c94d2..4126141b9 100644 --- a/src/dev/pcidev.cc +++ b/src/dev/pcidev.cc @@ -255,18 +255,6 @@ PciDevice::init() DmaDevice::init(); } -unsigned int -PciDevice::drain(DrainManager *dm) -{ - unsigned int count; - count = pioPort.drain(dm) + dmaPort.drain(dm) + configPort.drain(dm); - if (count) - setDrainState(DrainState::Draining); - else - setDrainState(DrainState::Drained); - return count; -} - Tick PciDevice::readConfig(PacketPtr pkt) { diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh index 4f997932b..903d83c77 100644 --- a/src/dev/pcidev.hh +++ b/src/dev/pcidev.hh @@ -259,8 +259,6 @@ class PciDevice : public DmaDevice void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; - virtual unsigned int drain(DrainManager *dm); - virtual BaseSlavePort &getSlavePort(const std::string &if_name, PortID idx = InvalidPortID) { diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc index e58d5f6b9..c270d5b65 100644 --- a/src/mem/cache/base.cc +++ b/src/mem/cache/base.cc @@ -775,23 +775,6 @@ BaseCache::regStats() } -unsigned int -BaseCache::drain(DrainManager *dm) -{ - int count = memSidePort->drain(dm) + cpuSidePort->drain(dm) + - mshrQueue.drain(dm) + writeBuffer.drain(dm); - - // Set status - if (count != 0) { - setDrainState(DrainState::Draining); - DPRINTF(Drain, "Cache not drained\n"); - return count; - } - - setDrainState(DrainState::Drained); - return 0; -} - BaseCache * BaseCacheParams::create() { diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index 6c87fad12..041b1f6a5 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -593,8 +593,6 @@ class BaseCache : public MemObject // interesting again. } - virtual unsigned int drain(DrainManager *dm); - virtual bool inCache(Addr addr, bool is_secure) const = 0; virtual bool inMissQueue(Addr addr, bool is_secure) const = 0; diff --git a/src/mem/coherent_xbar.cc b/src/mem/coherent_xbar.cc index 7f1639e44..9e5407981 100644 --- a/src/mem/coherent_xbar.cc +++ b/src/mem/coherent_xbar.cc @@ -834,20 +834,6 @@ CoherentXBar::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id) } } -unsigned int -CoherentXBar::drain(DrainManager *dm) -{ - // sum up the individual layers - unsigned int total = 0; - for (auto l: reqLayers) - total += l->drain(dm); - for (auto l: respLayers) - total += l->drain(dm); - for (auto l: snoopLayers) - total += l->drain(dm); - return total; -} - void CoherentXBar::regStats() { diff --git a/src/mem/coherent_xbar.hh b/src/mem/coherent_xbar.hh index 4b495ac2c..d431a1d24 100644 --- a/src/mem/coherent_xbar.hh +++ b/src/mem/coherent_xbar.hh @@ -396,8 +396,6 @@ class CoherentXBar : public BaseXBar virtual ~CoherentXBar(); - unsigned int drain(DrainManager *dm); - virtual void regStats(); }; diff --git a/src/mem/dram_ctrl.cc b/src/mem/dram_ctrl.cc index 9d264d971..0b1509e2f 100644 --- a/src/mem/dram_ctrl.cc +++ b/src/mem/dram_ctrl.cc @@ -2169,8 +2169,6 @@ DRAMCtrl::getSlavePort(const string &if_name, PortID idx) unsigned int DRAMCtrl::drain(DrainManager *dm) { - unsigned int count = port.drain(dm); - // if there is anything in any of our internal queues, keep track // of that as well if (!(writeQueue.empty() && readQueue.empty() && @@ -2178,7 +2176,6 @@ DRAMCtrl::drain(DrainManager *dm) DPRINTF(Drain, "DRAM controller not drained, write: %d, read: %d," " resp: %d\n", writeQueue.size(), readQueue.size(), respQueue.size()); - ++count; drainManager = dm; // the only part that is not drained automatically over time @@ -2186,13 +2183,12 @@ DRAMCtrl::drain(DrainManager *dm) if (!writeQueue.empty() && !nextReqEvent.scheduled()) { schedule(nextReqEvent, curTick()); } - } - - if (count) setDrainState(DrainState::Draining); - else + return 1; + } else { setDrainState(DrainState::Drained); - return count; + return 0; + } } void diff --git a/src/mem/noncoherent_xbar.cc b/src/mem/noncoherent_xbar.cc index 330a91f1f..409937826 100644 --- a/src/mem/noncoherent_xbar.cc +++ b/src/mem/noncoherent_xbar.cc @@ -313,18 +313,6 @@ NoncoherentXBar::recvFunctional(PacketPtr pkt, PortID slave_port_id) masterPorts[dest_id]->sendFunctional(pkt); } -unsigned int -NoncoherentXBar::drain(DrainManager *dm) -{ - // sum up the individual layers - unsigned int total = 0; - for (auto l: reqLayers) - total += l->drain(dm); - for (auto l: respLayers) - total += l->drain(dm); - return total; -} - NoncoherentXBar* NoncoherentXBarParams::create() { diff --git a/src/mem/noncoherent_xbar.hh b/src/mem/noncoherent_xbar.hh index ffcf9797f..4ff1ec4fc 100644 --- a/src/mem/noncoherent_xbar.hh +++ b/src/mem/noncoherent_xbar.hh @@ -195,8 +195,6 @@ class NoncoherentXBar : public BaseXBar virtual ~NoncoherentXBar(); - unsigned int drain(DrainManager *dm); - /** * stats */ diff --git a/src/mem/qport.hh b/src/mem/qport.hh index 8009454ba..94bcc53fd 100644 --- a/src/mem/qport.hh +++ b/src/mem/qport.hh @@ -95,8 +95,6 @@ class QueuedSlavePort : public SlavePort * functional request. */ bool checkFunctional(PacketPtr pkt) { return respQueue.checkFunctional(pkt); } - - unsigned int drain(DrainManager *dm) { return respQueue.drain(dm); } }; /** @@ -166,9 +164,6 @@ class QueuedMasterPort : public MasterPort return reqQueue.checkFunctional(pkt) || snoopRespQueue.checkFunctional(pkt); } - - unsigned int drain(DrainManager *dm) - { return reqQueue.drain(dm) + snoopRespQueue.drain(dm); } }; #endif // __MEM_QPORT_HH__ diff --git a/src/mem/ruby/system/DMASequencer.cc b/src/mem/ruby/system/DMASequencer.cc index b10538024..0dd28d91c 100644 --- a/src/mem/ruby/system/DMASequencer.cc +++ b/src/mem/ruby/system/DMASequencer.cc @@ -160,15 +160,6 @@ DMASequencer::testDrainComplete() } } -unsigned int -DMASequencer::getChildDrainCount(DrainManager *dm) -{ - int count = 0; - count += slave_port.drain(dm); - DPRINTF(Config, "count after slave port check %d\n", count); - return count; -} - unsigned int DMASequencer::drain(DrainManager *dm) { @@ -181,11 +172,6 @@ DMASequencer::drain(DrainManager *dm) DPRINTF(Config, "outstanding count %d\n", outstandingCount()); bool need_drain = outstandingCount() > 0; - // - // Also, get the number of child ports that will also need to clear - // their buffered requests before they call drainManager->signalDrainDone() - // - unsigned int child_drain_count = getChildDrainCount(dm); // Set status if (need_drain) { @@ -193,12 +179,12 @@ DMASequencer::drain(DrainManager *dm) DPRINTF(Drain, "DMASequencer not drained\n"); setDrainState(DrainState::Draining); - return child_drain_count + 1; + return 1; } drainManager = NULL; setDrainState(DrainState::Drained); - return child_drain_count; + return 0; } void diff --git a/src/mem/ruby/system/RubyPort.cc b/src/mem/ruby/system/RubyPort.cc index b6aa871b3..5818056e9 100644 --- a/src/mem/ruby/system/RubyPort.cc +++ b/src/mem/ruby/system/RubyPort.cc @@ -399,31 +399,6 @@ RubyPort::testDrainComplete() } } -unsigned int -RubyPort::getChildDrainCount(DrainManager *dm) -{ - int count = 0; - - if (memMasterPort.isConnected()) { - count += memMasterPort.drain(dm); - DPRINTF(Config, "count after pio check %d\n", count); - } - - for (CpuPortIter p = slave_ports.begin(); p != slave_ports.end(); ++p) { - count += (*p)->drain(dm); - DPRINTF(Config, "count after slave port check %d\n", count); - } - - for (std::vector::iterator p = master_ports.begin(); - p != master_ports.end(); ++p) { - count += (*p)->drain(dm); - DPRINTF(Config, "count after master port check %d\n", count); - } - - DPRINTF(Config, "final count %d\n", count); - return count; -} - unsigned int RubyPort::drain(DrainManager *dm) { @@ -438,24 +413,18 @@ RubyPort::drain(DrainManager *dm) DPRINTF(Config, "outstanding count %d\n", outstandingCount()); bool need_drain = outstandingCount() > 0; - // - // Also, get the number of child ports that will also need to clear - // their buffered requests before they call drainManager->signalDrainDone() - // - unsigned int child_drain_count = getChildDrainCount(dm); - // Set status if (need_drain) { drainManager = dm; DPRINTF(Drain, "RubyPort not drained\n"); setDrainState(DrainState::Draining); - return child_drain_count + 1; + return 1; } drainManager = NULL; setDrainState(DrainState::Drained); - return child_drain_count; + return 0; } void diff --git a/src/mem/ruby/system/RubyPort.hh b/src/mem/ruby/system/RubyPort.hh index e68af6dab..ff1b2af04 100644 --- a/src/mem/ruby/system/RubyPort.hh +++ b/src/mem/ruby/system/RubyPort.hh @@ -193,8 +193,6 @@ class RubyPort : public MemObject retryList.push_back(port); } - unsigned int getChildDrainCount(DrainManager *dm); - PioMasterPort pioMasterPort; PioSlavePort pioSlavePort; MemMasterPort memMasterPort; diff --git a/src/mem/xbar.hh b/src/mem/xbar.hh index ede60e666..547e138e9 100644 --- a/src/mem/xbar.hh +++ b/src/mem/xbar.hh @@ -467,8 +467,6 @@ class BaseXBar : public MemObject BaseSlavePort& getSlavePort(const std::string& if_name, PortID idx = InvalidPortID); - virtual unsigned int drain(DrainManager *dm) = 0; - virtual void regStats(); }; diff --git a/src/python/m5/simulate.py b/src/python/m5/simulate.py index 2aee677bd..66344bc79 100644 --- a/src/python/m5/simulate.py +++ b/src/python/m5/simulate.py @@ -66,6 +66,8 @@ _memory_modes = { "atomic_noncaching" : objects.params.atomic_noncaching, } +_drain_manager = internal.drain.DrainManager.instance() + # The final hook to generate .ini files. Called from the user script # once the config is built. def instantiate(ckpt_dir=None): @@ -129,10 +131,10 @@ def instantiate(ckpt_dir=None): # Restore checkpoint (if any) if ckpt_dir: + _drain_manager.preCheckpointRestore() ckpt = internal.core.getCheckpoint(ckpt_dir) internal.core.unserializeGlobals(ckpt); for obj in root.descendants(): obj.loadState(ckpt) - need_resume.append(root) else: for obj in root.descendants(): obj.initState() @@ -140,10 +142,9 @@ def instantiate(ckpt_dir=None): # a checkpoint, If so, this call will shift them to be at a valid time. updateStatEvents() -need_resume = [] need_startup = True def simulate(*args, **kwargs): - global need_resume, need_startup + global need_startup if need_startup: root = objects.Root.getInstance() @@ -160,9 +161,8 @@ def simulate(*args, **kwargs): # Reset to put the stats in a consistent state. stats.reset() - for root in need_resume: - resume(root) - need_resume = [] + if _drain_manager.isDrained(): + _drain_manager.resume() return internal.event.simulate(*args, **kwargs) @@ -170,33 +170,40 @@ def simulate(*args, **kwargs): def curTick(): return internal.core.curTick() -# Drain the system in preparation of a checkpoint or memory mode -# switch. -def drain(root): +def drain(): + """Drain the simulator in preparation of a checkpoint or memory mode + switch. + + This operation is a no-op if the simulator is already in the + Drained state. + + """ + # Try to drain all objects. Draining might not be completed unless # all objects return that they are drained on the first call. This # is because as objects drain they may cause other objects to no # longer be drained. def _drain(): - all_drained = False - dm = internal.drain.createDrainManager() - unready_objs = sum(obj.drain(dm) for obj in root.descendants()) - # If we've got some objects that can't drain immediately, then simulate - if unready_objs > 0: - dm.setCount(unready_objs) - #WARNING: if a valid exit event occurs while draining, it will not - # get returned to the user script - exit_event = simulate() - while exit_event.getCause() != 'Finished drain': - exit_event = simulate() - else: - all_drained = True - internal.drain.cleanupDrainManager(dm) - return all_drained + # Try to drain the system. The drain is successful if all + # objects are done without simulation. We need to simulate + # more if not. + if _drain_manager.tryDrain(): + return True - all_drained = _drain() - while (not all_drained): - all_drained = _drain() + # WARNING: if a valid exit event occurs while draining, it + # will not get returned to the user script + exit_event = internal.event.simulate() + while exit_event.getCause() != 'Finished drain': + exit_event = simulate() + + return False + + # Don't try to drain a system that is already drained + is_drained = _drain_manager.isDrained() + while not is_drained: + is_drained = _drain() + + assert _drain_manager.isDrained(), "Drain state inconsistent" def memWriteback(root): for obj in root.descendants(): @@ -206,18 +213,15 @@ def memInvalidate(root): for obj in root.descendants(): obj.memInvalidate() -def resume(root): - for obj in root.descendants(): obj.drainResume() - def checkpoint(dir): root = objects.Root.getInstance() if not isinstance(root, objects.Root): raise TypeError, "Checkpoint must be called on a root object." - drain(root) + + drain() memWriteback(root) print "Writing checkpoint" internal.core.serializeAll(dir) - resume(root) def _changeMemoryMode(system, mode): if not isinstance(system, (objects.Root, objects.System)): @@ -228,15 +232,9 @@ def _changeMemoryMode(system, mode): else: print "System already in target mode. Memory mode unchanged." -def switchCpus(system, cpuList, do_drain=True, verbose=True): +def switchCpus(system, cpuList, verbose=True): """Switch CPUs in a system. - By default, this method drains and resumes the system. This - behavior can be disabled by setting the keyword argument - 'do_drain' to false, which might be desirable if multiple - operations requiring a drained system are going to be performed in - sequence. - Note: This method may switch the memory mode of the system if that is required by the CPUs. It may also flush all caches in the system. @@ -244,9 +242,6 @@ def switchCpus(system, cpuList, do_drain=True, verbose=True): Arguments: system -- Simulated system. cpuList -- (old_cpu, new_cpu) tuples - - Keyword Arguments: - do_drain -- Perform a drain/resume of the system when switching. """ if verbose: @@ -292,8 +287,7 @@ def switchCpus(system, cpuList, do_drain=True, verbose=True): except KeyError: raise RuntimeError, "Invalid memory mode (%s)" % memory_mode_name - if do_drain: - drain(system) + drain() # Now all of the CPUs are ready to be switched out for old_cpu, new_cpu in cpuList: @@ -314,7 +308,4 @@ def switchCpus(system, cpuList, do_drain=True, verbose=True): for old_cpu, new_cpu in cpuList: new_cpu.takeOverFrom(old_cpu) - if do_drain: - resume(system) - from internal.core import disableAllListeners diff --git a/src/python/swig/drain.i b/src/python/swig/drain.i index 4442db207..59474f190 100644 --- a/src/python/swig/drain.i +++ b/src/python/swig/drain.i @@ -46,21 +46,3 @@ %nodefaultctor Drainable; %include "sim/drain.hh" - -%inline %{ - -DrainManager * -createDrainManager() -{ - return new DrainManager(); -} - -void -cleanupDrainManager(DrainManager *drain_manager) -{ - assert(drain_manager); - assert(drain_manager->getCount() == 0); - delete drain_manager; -} - -%} 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() { } -void -DrainManager::drainCycleDone() +bool +DrainManager::tryDrain() { - exitSimLoop("Finished drain", 0); + 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 lock(globalLock); + _allDrainable.insert(obj); +} + +void +DrainManager::unregisterDrainable(Drainable *obj) +{ + std::lock_guard lock(globalLock); + _allDrainable.erase(obj); +} + +size_t +DrainManager::drainableCount() const +{ + std::lock_guard 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 -#include +#include +#include +#include #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; } /** - * Get the number of objects registered with this DrainManager - * that are currently draining their state. + * Try to drain the system. * - * @return Number of objects currently draining. + * 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. */ - unsigned int getCount() const { return _count; } + bool tryDrain(); - void setCount(int count) { _count = count; } + /** + * Resume normal simulation in a Drained system. + */ + void resume(); + + /** + * Run state fixups before a checkpoint restore operation + * + * 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. + */ + void preCheckpointRestore(); + + /** Check if the system is drained */ + bool isDrained() { return _state == DrainState::Drained; } + + /** 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 _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 - * all objects in the system, the objects are serialized to + * all 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): * *
    - *
  1. 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. - * *
  2. 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. * *
  3. 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. * *
  4. 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 diff --git a/tests/configs/switcheroo.py b/tests/configs/switcheroo.py index 05d3af2da..61145bbf6 100644 --- a/tests/configs/switcheroo.py +++ b/tests/configs/switcheroo.py @@ -122,13 +122,12 @@ def run_test(root, switcher=None, freq=1000, verbose=False): if verbose: print "Switching CPUs..." print "Next CPU: %s" % type(next_cpu) - m5.drain(system) + m5.drain() if current_cpu != next_cpu: m5.switchCpus(system, [ (current_cpu, next_cpu) ], - do_drain=False, verbose=verbose) + verbose=verbose) else: print "Source CPU and destination CPU are the same, skipping..." - m5.resume(system) current_cpu = next_cpu elif exit_cause == "target called exit()" or \ exit_cause == "m5_exit instruction encountered":