diff -r d9abf8deaa4c -r 1505317a33a8 src/arch/arm/stage2_mmu.hh --- a/src/arch/arm/stage2_mmu.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/arch/arm/stage2_mmu.hh Mon Jul 06 11:41:39 2015 +0100 @@ -112,8 +112,6 @@ */ 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 -r d9abf8deaa4c -r 1505317a33a8 src/arch/arm/stage2_mmu.cc --- a/src/arch/arm/stage2_mmu.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/arch/arm/stage2_mmu.cc Mon Jul 06 11:41:39 2015 +0100 @@ -141,12 +141,6 @@ } } -unsigned int -Stage2MMU::drain(DrainManager *dm) -{ - return port.drain(dm); -} - ArmISA::Stage2MMU * ArmStage2MMUParams::create() { diff -r d9abf8deaa4c -r 1505317a33a8 src/dev/arm/ufs_device.cc --- a/src/dev/arm/ufs_device.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/arm/ufs_device.cc Mon Jul 06 11:41:39 2015 +0100 @@ -2319,26 +2319,16 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/dev/copy_engine.hh --- a/src/dev/copy_engine.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/copy_engine.hh Mon Jul 06 11:41:39 2015 +0100 @@ -207,9 +207,6 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/dev/copy_engine.cc --- a/src/dev/copy_engine.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/copy_engine.cc Mon Jul 06 11:41:39 2015 +0100 @@ -650,29 +650,10 @@ { 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 @@ -756,15 +737,6 @@ } void -CopyEngine::drainResume() -{ - Drainable::drainResume(); - for (int x = 0;x < chan.size(); x++) - chan[x]->drainResume(); -} - - -void CopyEngine::CopyEngineChannel::drainResume() { DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState); diff -r d9abf8deaa4c -r 1505317a33a8 src/dev/dma_device.hh --- a/src/dev/dma_device.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/dma_device.hh Mon Jul 06 11:41:39 2015 +0100 @@ -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 @@ virtual void init(); - unsigned int drain(DrainManager *drainManger); - unsigned int cacheBlockSize() const { return sys->cacheLineSize(); } virtual BaseMasterPort &getMasterPort(const std::string &if_name, diff -r d9abf8deaa4c -r 1505317a33a8 src/dev/dma_device.cc --- a/src/dev/dma_device.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/dma_device.cc Mon Jul 06 11:41:39 2015 +0100 @@ -126,17 +126,6 @@ } 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) { if (pendingCount == 0) diff -r d9abf8deaa4c -r 1505317a33a8 src/dev/i8254xGBe.cc --- a/src/dev/i8254xGBe.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/i8254xGBe.cc Mon Jul 06 11:41:39 2015 +0100 @@ -2058,8 +2058,7 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/dev/io_device.hh --- a/src/dev/io_device.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/io_device.hh Mon Jul 06 11:41:39 2015 +0100 @@ -125,8 +125,6 @@ virtual void init(); - unsigned int drain(DrainManager *drainManger); - virtual BaseSlavePort &getSlavePort(const std::string &if_name, PortID idx = InvalidPortID); diff -r d9abf8deaa4c -r 1505317a33a8 src/dev/io_device.cc --- a/src/dev/io_device.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/io_device.cc Mon Jul 06 11:41:39 2015 +0100 @@ -93,18 +93,6 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/dev/pcidev.hh --- a/src/dev/pcidev.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/pcidev.hh Mon Jul 06 11:41:39 2015 +0100 @@ -259,8 +259,6 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/dev/pcidev.cc --- a/src/dev/pcidev.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/dev/pcidev.cc Mon Jul 06 11:41:39 2015 +0100 @@ -255,18 +255,6 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/cache/base.hh --- a/src/mem/cache/base.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/cache/base.hh Mon Jul 06 11:41:39 2015 +0100 @@ -588,8 +588,6 @@ // 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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/cache/base.cc --- a/src/mem/cache/base.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/cache/base.cc Mon Jul 06 11:41:39 2015 +0100 @@ -774,23 +774,6 @@ } -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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/coherent_xbar.hh --- a/src/mem/coherent_xbar.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/coherent_xbar.hh Mon Jul 06 11:41:39 2015 +0100 @@ -389,8 +389,6 @@ virtual ~CoherentXBar(); - unsigned int drain(DrainManager *dm); - virtual void regStats(); }; diff -r d9abf8deaa4c -r 1505317a33a8 src/mem/coherent_xbar.cc --- a/src/mem/coherent_xbar.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/coherent_xbar.cc Mon Jul 06 11:41:39 2015 +0100 @@ -811,20 +811,6 @@ } } -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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/dram_ctrl.cc --- a/src/mem/dram_ctrl.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/dram_ctrl.cc Mon Jul 06 11:41:39 2015 +0100 @@ -2194,8 +2194,6 @@ 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() && @@ -2203,7 +2201,6 @@ 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 @@ -2211,13 +2208,12 @@ if (!writeQueue.empty() && !nextReqEvent.scheduled()) { schedule(nextReqEvent, curTick()); } + setDrainState(DrainState::Draining); + return 1; + } else { + setDrainState(DrainState::Drained); + return 0; } - - if (count) - setDrainState(DrainState::Draining); - else - setDrainState(DrainState::Drained); - return count; } void diff -r d9abf8deaa4c -r 1505317a33a8 src/mem/noncoherent_xbar.hh --- a/src/mem/noncoherent_xbar.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/noncoherent_xbar.hh Mon Jul 06 11:41:39 2015 +0100 @@ -197,8 +197,6 @@ virtual ~NoncoherentXBar(); - unsigned int drain(DrainManager *dm); - /** * stats */ diff -r d9abf8deaa4c -r 1505317a33a8 src/mem/noncoherent_xbar.cc --- a/src/mem/noncoherent_xbar.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/noncoherent_xbar.cc Mon Jul 06 11:41:39 2015 +0100 @@ -302,18 +302,6 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/qport.hh --- a/src/mem/qport.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/qport.hh Mon Jul 06 11:41:39 2015 +0100 @@ -95,8 +95,6 @@ * functional request. */ bool checkFunctional(PacketPtr pkt) { return respQueue.checkFunctional(pkt); } - - unsigned int drain(DrainManager *dm) { return respQueue.drain(dm); } }; /** @@ -166,9 +164,6 @@ return reqQueue.checkFunctional(pkt) || snoopRespQueue.checkFunctional(pkt); } - - unsigned int drain(DrainManager *dm) - { return reqQueue.drain(dm) + snoopRespQueue.drain(dm); } }; #endif // __MEM_QPORT_HH__ diff -r d9abf8deaa4c -r 1505317a33a8 src/mem/ruby/system/DMASequencer.cc --- a/src/mem/ruby/system/DMASequencer.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/ruby/system/DMASequencer.cc Mon Jul 06 11:41:39 2015 +0100 @@ -161,15 +161,6 @@ } 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) { if (isDeadlockEventScheduled()) { @@ -181,11 +172,6 @@ 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 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/ruby/system/RubyPort.hh --- a/src/mem/ruby/system/RubyPort.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/ruby/system/RubyPort.hh Mon Jul 06 11:41:39 2015 +0100 @@ -193,8 +193,6 @@ retryList.push_back(port); } - unsigned int getChildDrainCount(DrainManager *dm); - PioMasterPort pioMasterPort; PioSlavePort pioSlavePort; MemMasterPort memMasterPort; diff -r d9abf8deaa4c -r 1505317a33a8 src/mem/ruby/system/RubyPort.cc --- a/src/mem/ruby/system/RubyPort.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/ruby/system/RubyPort.cc Mon Jul 06 11:41:39 2015 +0100 @@ -400,31 +400,6 @@ } 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) { if (isDeadlockEventScheduled()) { @@ -438,24 +413,18 @@ 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 -r d9abf8deaa4c -r 1505317a33a8 src/mem/xbar.hh --- a/src/mem/xbar.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/mem/xbar.hh Mon Jul 06 11:41:39 2015 +0100 @@ -466,8 +466,6 @@ BaseSlavePort& getSlavePort(const std::string& if_name, PortID idx = InvalidPortID); - virtual unsigned int drain(DrainManager *dm) = 0; - virtual void regStats(); }; diff -r d9abf8deaa4c -r 1505317a33a8 src/python/m5/simulate.py --- a/src/python/m5/simulate.py Mon Jul 06 10:41:05 2015 +0100 +++ b/src/python/m5/simulate.py Mon Jul 06 11:41:39 2015 +0100 @@ -66,6 +66,8 @@ "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 @@ # 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 @@ # 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 @@ # 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 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 + # 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 + + # 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() - while exit_event.getCause() != 'Finished drain': - exit_event = simulate() - else: - all_drained = True - internal.drain.cleanupDrainManager(dm) - return all_drained - all_drained = _drain() - while (not all_drained): - all_drained = _drain() + 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ for old_cpu, new_cpu in cpuList: new_cpu.takeOverFrom(old_cpu) - if do_drain: - resume(system) - from internal.core import disableAllListeners diff -r d9abf8deaa4c -r 1505317a33a8 src/python/swig/drain.i --- a/src/python/swig/drain.i Mon Jul 06 10:41:05 2015 +0100 +++ b/src/python/swig/drain.i Mon Jul 06 11:41:39 2015 +0100 @@ -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 -r d9abf8deaa4c -r 1505317a33a8 src/sim/cxx_manager.hh --- a/src/sim/cxx_manager.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/sim/cxx_manager.hh Mon Jul 06 11:41:39 2015 +0100 @@ -283,7 +283,7 @@ void startup(); /** Drain all objects */ - unsigned int drain(DrainManager *drain_manager); + unsigned int drain(); /** Resume from drain */ void drainResume(); diff -r d9abf8deaa4c -r 1505317a33a8 src/sim/cxx_manager.cc --- a/src/sim/cxx_manager.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/sim/cxx_manager.cc Mon Jul 06 11:41:39 2015 +0100 @@ -644,20 +644,15 @@ } 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 -r d9abf8deaa4c -r 1505317a33a8 src/sim/drain.hh --- a/src/sim/drain.hh Mon Jul 06 10:41:05 2015 +0100 +++ b/src/sim/drain.hh Mon Jul 06 11:41:39 2015 +0100 @@ -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 @@ /** * 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,97 @@ */ class DrainManager { + private: + DrainManager(); +#ifndef SWIG + DrainManager(DrainManager &) = delete; +#endif + ~DrainManager(); + public: - DrainManager(); - virtual ~DrainManager(); + /** 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(); + + public: + void registerDrainable(Drainable *obj); + void unregisterDrainable(Drainable *obj); + + /** Get a set of all drainable objects */ + const std::unordered_set &allDrainable() const { + return _allDrainable; } - protected: + 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 +192,11 @@ * 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 +204,9 @@ * 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 +219,8 @@ */ class Drainable { + friend class DrainManager; + public: Drainable(); virtual ~Drainable(); @@ -210,10 +265,8 @@ void setDrainState(DrainState new_state) { _drainState = new_state; } private: + DrainManager &_drainManager; DrainState _drainState; }; -DrainManager *createDrainManager(); -void cleanupDrainManager(DrainManager *drain_manager); - #endif diff -r d9abf8deaa4c -r 1505317a33a8 src/sim/drain.cc --- a/src/sim/drain.cc Mon Jul 06 10:41:05 2015 +0100 +++ b/src/sim/drain.cc Mon Jul 06 11:41:39 2015 +0100 @@ -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 @@ { } -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 -r d9abf8deaa4c -r 1505317a33a8 tests/configs/switcheroo.py --- a/tests/configs/switcheroo.py Mon Jul 06 10:41:05 2015 +0100 +++ b/tests/configs/switcheroo.py Mon Jul 06 11:41:39 2015 +0100 @@ -122,13 +122,12 @@ 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":