summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/arch/arm/stage2_mmu.cc6
-rwxr-xr-xsrc/arch/arm/stage2_mmu.hh2
-rw-r--r--src/dev/arm/ufs_device.cc14
-rw-r--r--src/dev/copy_engine.cc30
-rw-r--r--src/dev/copy_engine.hh3
-rw-r--r--src/dev/dma_device.cc11
-rw-r--r--src/dev/dma_device.hh4
-rw-r--r--src/dev/i8254xGBe.cc3
-rw-r--r--src/dev/io_device.cc12
-rw-r--r--src/dev/io_device.hh2
-rw-r--r--src/dev/pcidev.cc12
-rw-r--r--src/dev/pcidev.hh2
-rw-r--r--src/mem/cache/base.cc17
-rw-r--r--src/mem/cache/base.hh2
-rw-r--r--src/mem/coherent_xbar.cc14
-rw-r--r--src/mem/coherent_xbar.hh2
-rw-r--r--src/mem/dram_ctrl.cc12
-rw-r--r--src/mem/noncoherent_xbar.cc12
-rw-r--r--src/mem/noncoherent_xbar.hh2
-rw-r--r--src/mem/qport.hh5
-rw-r--r--src/mem/ruby/system/DMASequencer.cc18
-rw-r--r--src/mem/ruby/system/RubyPort.cc35
-rw-r--r--src/mem/ruby/system/RubyPort.hh2
-rw-r--r--src/mem/xbar.hh2
-rw-r--r--src/python/m5/simulate.py83
-rw-r--r--src/python/swig/drain.i18
-rw-r--r--src/sim/cxx_manager.cc11
-rw-r--r--src/sim/cxx_manager.hh2
-rw-r--r--src/sim/drain.cc102
-rw-r--r--src/sim/drain.hh126
-rw-r--r--tests/configs/switcheroo.py5
31 files changed, 241 insertions, 330 deletions
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
@@ -756,15 +737,6 @@ CopyEngine::CopyEngineChannel::restartStateMachine()
}
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 --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
@@ -126,17 +126,6 @@ DmaDevice::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)
{
if (pendingCount == 0)
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
@@ -161,15 +161,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)
{
if (isDeadlockEventScheduled()) {
@@ -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
@@ -400,31 +400,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<PioMasterPort *>::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 @@ 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
+ # 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 @@ 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()
{
}
+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
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":