110923SN/A/* 211757Sgabor.dozsa@arm.com * Copyright (c) 2015-2016 ARM Limited 310923SN/A * All rights reserved 410923SN/A * 510923SN/A * The license below extends only to copyright in the software and shall 610923SN/A * not be construed as granting a license to any other intellectual 710923SN/A * property including but not limited to intellectual property relating 810923SN/A * to a hardware implementation of the functionality of the software 910923SN/A * licensed hereunder. You may use the software subject to the license 1010923SN/A * terms below provided that you ensure that this notice is replicated 1110923SN/A * unmodified and in its entirety in all distributions of the software, 1210923SN/A * modified or unmodified, in source code or in binary form. 1310923SN/A * 1410923SN/A * Redistribution and use in source and binary forms, with or without 1510923SN/A * modification, are permitted provided that the following conditions are 1610923SN/A * met: redistributions of source code must retain the above copyright 1710923SN/A * notice, this list of conditions and the following disclaimer; 1810923SN/A * redistributions in binary form must reproduce the above copyright 1910923SN/A * notice, this list of conditions and the following disclaimer in the 2010923SN/A * documentation and/or other materials provided with the distribution; 2110923SN/A * neither the name of the copyright holders nor the names of its 2210923SN/A * contributors may be used to endorse or promote products derived from 2310923SN/A * this software without specific prior written permission. 2410923SN/A * 2510923SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2610923SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2710923SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2810923SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2910923SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3010923SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3110923SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3210923SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3310923SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3410923SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3510923SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3610923SN/A * 3710923SN/A * Authors: Gabor Dozsa 3810923SN/A */ 3910923SN/A 4010923SN/A/* @file 4111290Sgabor.dozsa@arm.com * The interface class for dist-gem5 simulations. 4210923SN/A */ 4310923SN/A 4411290Sgabor.dozsa@arm.com#include "dev/net/dist_iface.hh" 4510923SN/A 4610923SN/A#include <queue> 4710923SN/A#include <thread> 4810923SN/A 4910923SN/A#include "base/random.hh" 5010923SN/A#include "base/trace.hh" 5111703Smichael.lebeane@amd.com#include "cpu/thread_context.hh" 5211290Sgabor.dozsa@arm.com#include "debug/DistEthernet.hh" 5311290Sgabor.dozsa@arm.com#include "debug/DistEthernetPkt.hh" 5411263SN/A#include "dev/net/etherpkt.hh" 5510923SN/A#include "sim/sim_exit.hh" 5610923SN/A#include "sim/sim_object.hh" 5711703Smichael.lebeane@amd.com#include "sim/system.hh" 5810923SN/A 5911290Sgabor.dozsa@arm.comusing namespace std; 6011290Sgabor.dozsa@arm.comDistIface::Sync *DistIface::sync = nullptr; 6111703Smichael.lebeane@amd.comSystem *DistIface::sys = nullptr; 6211290Sgabor.dozsa@arm.comDistIface::SyncEvent *DistIface::syncEvent = nullptr; 6311290Sgabor.dozsa@arm.comunsigned DistIface::distIfaceNum = 0; 6411290Sgabor.dozsa@arm.comunsigned DistIface::recvThreadsNum = 0; 6511290Sgabor.dozsa@arm.comDistIface *DistIface::master = nullptr; 6611703Smichael.lebeane@amd.combool DistIface::isSwitch = false; 6710923SN/A 6811290Sgabor.dozsa@arm.comvoid 6911290Sgabor.dozsa@arm.comDistIface::Sync::init(Tick start_tick, Tick repeat_tick) 7011290Sgabor.dozsa@arm.com{ 7111703Smichael.lebeane@amd.com if (start_tick < nextAt) { 7211703Smichael.lebeane@amd.com nextAt = start_tick; 7311290Sgabor.dozsa@arm.com inform("Next dist synchronisation tick is changed to %lu.\n", nextAt); 7411290Sgabor.dozsa@arm.com } 7510923SN/A 7611290Sgabor.dozsa@arm.com if (repeat_tick == 0) 7711290Sgabor.dozsa@arm.com panic("Dist synchronisation interval must be greater than zero"); 7811290Sgabor.dozsa@arm.com 7911290Sgabor.dozsa@arm.com if (repeat_tick < nextRepeat) { 8011290Sgabor.dozsa@arm.com nextRepeat = repeat_tick; 8111290Sgabor.dozsa@arm.com inform("Dist synchronisation interval is changed to %lu.\n", 8211290Sgabor.dozsa@arm.com nextRepeat); 8311290Sgabor.dozsa@arm.com } 8411290Sgabor.dozsa@arm.com} 8511290Sgabor.dozsa@arm.com 8611757Sgabor.dozsa@arm.comvoid 8711757Sgabor.dozsa@arm.comDistIface::Sync::abort() 8811757Sgabor.dozsa@arm.com{ 8911757Sgabor.dozsa@arm.com std::unique_lock<std::mutex> sync_lock(lock); 9011757Sgabor.dozsa@arm.com waitNum = 0; 9111757Sgabor.dozsa@arm.com isAbort = true; 9211757Sgabor.dozsa@arm.com sync_lock.unlock(); 9311757Sgabor.dozsa@arm.com cv.notify_one(); 9411757Sgabor.dozsa@arm.com} 9511757Sgabor.dozsa@arm.com 9611290Sgabor.dozsa@arm.comDistIface::SyncSwitch::SyncSwitch(int num_nodes) 9711290Sgabor.dozsa@arm.com{ 9811290Sgabor.dozsa@arm.com numNodes = num_nodes; 9911290Sgabor.dozsa@arm.com waitNum = num_nodes; 10011290Sgabor.dozsa@arm.com numExitReq = 0; 10111290Sgabor.dozsa@arm.com numCkptReq = 0; 10211703Smichael.lebeane@amd.com numStopSyncReq = 0; 10311290Sgabor.dozsa@arm.com doExit = false; 10411290Sgabor.dozsa@arm.com doCkpt = false; 10511703Smichael.lebeane@amd.com doStopSync = false; 10611703Smichael.lebeane@amd.com nextAt = std::numeric_limits<Tick>::max(); 10711290Sgabor.dozsa@arm.com nextRepeat = std::numeric_limits<Tick>::max(); 10811757Sgabor.dozsa@arm.com isAbort = false; 10911290Sgabor.dozsa@arm.com} 11011290Sgabor.dozsa@arm.com 11111290Sgabor.dozsa@arm.comDistIface::SyncNode::SyncNode() 11211290Sgabor.dozsa@arm.com{ 11311290Sgabor.dozsa@arm.com waitNum = 0; 11411290Sgabor.dozsa@arm.com needExit = ReqType::none; 11511290Sgabor.dozsa@arm.com needCkpt = ReqType::none; 11611703Smichael.lebeane@amd.com needStopSync = ReqType::none; 11711290Sgabor.dozsa@arm.com doExit = false; 11811290Sgabor.dozsa@arm.com doCkpt = false; 11911703Smichael.lebeane@amd.com doStopSync = false; 12011703Smichael.lebeane@amd.com nextAt = std::numeric_limits<Tick>::max(); 12111290Sgabor.dozsa@arm.com nextRepeat = std::numeric_limits<Tick>::max(); 12211757Sgabor.dozsa@arm.com isAbort = false; 12311290Sgabor.dozsa@arm.com} 12411290Sgabor.dozsa@arm.com 12511757Sgabor.dozsa@arm.combool 12611290Sgabor.dozsa@arm.comDistIface::SyncNode::run(bool same_tick) 12710923SN/A{ 12810923SN/A std::unique_lock<std::mutex> sync_lock(lock); 12911290Sgabor.dozsa@arm.com Header header; 13010923SN/A 13111290Sgabor.dozsa@arm.com assert(waitNum == 0); 13211757Sgabor.dozsa@arm.com assert(!isAbort); 13311290Sgabor.dozsa@arm.com waitNum = DistIface::recvThreadsNum; 13411290Sgabor.dozsa@arm.com // initiate the global synchronisation 13511290Sgabor.dozsa@arm.com header.msgType = MsgType::cmdSyncReq; 13611290Sgabor.dozsa@arm.com header.sendTick = curTick(); 13711290Sgabor.dozsa@arm.com header.syncRepeat = nextRepeat; 13811290Sgabor.dozsa@arm.com header.needCkpt = needCkpt; 13911703Smichael.lebeane@amd.com header.needStopSync = needStopSync; 14011290Sgabor.dozsa@arm.com if (needCkpt != ReqType::none) 14111290Sgabor.dozsa@arm.com needCkpt = ReqType::pending; 14211290Sgabor.dozsa@arm.com header.needExit = needExit; 14311290Sgabor.dozsa@arm.com if (needExit != ReqType::none) 14411290Sgabor.dozsa@arm.com needExit = ReqType::pending; 14511703Smichael.lebeane@amd.com if (needStopSync != ReqType::none) 14611703Smichael.lebeane@amd.com needStopSync = ReqType::pending; 14711290Sgabor.dozsa@arm.com DistIface::master->sendCmd(header); 14810923SN/A // now wait until all receiver threads complete the synchronisation 14910923SN/A auto lf = [this]{ return waitNum == 0; }; 15010923SN/A cv.wait(sync_lock, lf); 15111757Sgabor.dozsa@arm.com // global synchronisation is done. 15211757Sgabor.dozsa@arm.com assert(isAbort || !same_tick || (nextAt == curTick())); 15311757Sgabor.dozsa@arm.com return !isAbort; 15411290Sgabor.dozsa@arm.com} 15510923SN/A 15611290Sgabor.dozsa@arm.com 15711757Sgabor.dozsa@arm.combool 15811290Sgabor.dozsa@arm.comDistIface::SyncSwitch::run(bool same_tick) 15911290Sgabor.dozsa@arm.com{ 16011290Sgabor.dozsa@arm.com std::unique_lock<std::mutex> sync_lock(lock); 16111290Sgabor.dozsa@arm.com Header header; 16211290Sgabor.dozsa@arm.com // Wait for the sync requests from the nodes 16311290Sgabor.dozsa@arm.com if (waitNum > 0) { 16411290Sgabor.dozsa@arm.com auto lf = [this]{ return waitNum == 0; }; 16511290Sgabor.dozsa@arm.com cv.wait(sync_lock, lf); 16611290Sgabor.dozsa@arm.com } 16711290Sgabor.dozsa@arm.com assert(waitNum == 0); 16811757Sgabor.dozsa@arm.com if (isAbort) // sync aborted 16911757Sgabor.dozsa@arm.com return false; 17011290Sgabor.dozsa@arm.com assert(!same_tick || (nextAt == curTick())); 17111290Sgabor.dozsa@arm.com waitNum = numNodes; 17211290Sgabor.dozsa@arm.com // Complete the global synchronisation 17311290Sgabor.dozsa@arm.com header.msgType = MsgType::cmdSyncAck; 17411290Sgabor.dozsa@arm.com header.sendTick = nextAt; 17511290Sgabor.dozsa@arm.com header.syncRepeat = nextRepeat; 17611290Sgabor.dozsa@arm.com if (doCkpt || numCkptReq == numNodes) { 17711290Sgabor.dozsa@arm.com doCkpt = true; 17811290Sgabor.dozsa@arm.com header.needCkpt = ReqType::immediate; 17911290Sgabor.dozsa@arm.com numCkptReq = 0; 18011290Sgabor.dozsa@arm.com } else { 18111290Sgabor.dozsa@arm.com header.needCkpt = ReqType::none; 18211290Sgabor.dozsa@arm.com } 18311290Sgabor.dozsa@arm.com if (doExit || numExitReq == numNodes) { 18411290Sgabor.dozsa@arm.com doExit = true; 18511290Sgabor.dozsa@arm.com header.needExit = ReqType::immediate; 18611290Sgabor.dozsa@arm.com } else { 18711290Sgabor.dozsa@arm.com header.needExit = ReqType::none; 18811290Sgabor.dozsa@arm.com } 18911703Smichael.lebeane@amd.com if (doStopSync || numStopSyncReq == numNodes) { 19011703Smichael.lebeane@amd.com doStopSync = true; 19111703Smichael.lebeane@amd.com numStopSyncReq = 0; 19211703Smichael.lebeane@amd.com header.needStopSync = ReqType::immediate; 19311703Smichael.lebeane@amd.com } else { 19411703Smichael.lebeane@amd.com header.needStopSync = ReqType::none; 19511703Smichael.lebeane@amd.com } 19611290Sgabor.dozsa@arm.com DistIface::master->sendCmd(header); 19711757Sgabor.dozsa@arm.com return true; 19810923SN/A} 19910923SN/A 20011757Sgabor.dozsa@arm.combool 20111290Sgabor.dozsa@arm.comDistIface::SyncSwitch::progress(Tick send_tick, 20211290Sgabor.dozsa@arm.com Tick sync_repeat, 20311290Sgabor.dozsa@arm.com ReqType need_ckpt, 20411703Smichael.lebeane@amd.com ReqType need_exit, 20511703Smichael.lebeane@amd.com ReqType need_stop_sync) 20610923SN/A{ 20710923SN/A std::unique_lock<std::mutex> sync_lock(lock); 20811757Sgabor.dozsa@arm.com if (isAbort) // sync aborted 20911757Sgabor.dozsa@arm.com return false; 21011290Sgabor.dozsa@arm.com assert(waitNum > 0); 21110923SN/A 21211290Sgabor.dozsa@arm.com if (send_tick > nextAt) 21311290Sgabor.dozsa@arm.com nextAt = send_tick; 21411290Sgabor.dozsa@arm.com if (nextRepeat > sync_repeat) 21511290Sgabor.dozsa@arm.com nextRepeat = sync_repeat; 21611290Sgabor.dozsa@arm.com 21711290Sgabor.dozsa@arm.com if (need_ckpt == ReqType::collective) 21811290Sgabor.dozsa@arm.com numCkptReq++; 21911290Sgabor.dozsa@arm.com else if (need_ckpt == ReqType::immediate) 22011290Sgabor.dozsa@arm.com doCkpt = true; 22111290Sgabor.dozsa@arm.com if (need_exit == ReqType::collective) 22211290Sgabor.dozsa@arm.com numExitReq++; 22311290Sgabor.dozsa@arm.com else if (need_exit == ReqType::immediate) 22411290Sgabor.dozsa@arm.com doExit = true; 22511703Smichael.lebeane@amd.com if (need_stop_sync == ReqType::collective) 22611703Smichael.lebeane@amd.com numStopSyncReq++; 22711703Smichael.lebeane@amd.com else if (need_stop_sync == ReqType::immediate) 22811703Smichael.lebeane@amd.com doStopSync = true; 22911290Sgabor.dozsa@arm.com 23010923SN/A waitNum--; 23111290Sgabor.dozsa@arm.com // Notify the simulation thread if the on-going sync is complete 23211290Sgabor.dozsa@arm.com if (waitNum == 0) { 23310923SN/A sync_lock.unlock(); 23410923SN/A cv.notify_one(); 23510923SN/A } 23611757Sgabor.dozsa@arm.com // The receive thread must keep alive in the switch until the node 23711757Sgabor.dozsa@arm.com // closes the connection. Thus, we always return true here. 23811757Sgabor.dozsa@arm.com return true; 23910923SN/A} 24010923SN/A 24111757Sgabor.dozsa@arm.combool 24211290Sgabor.dozsa@arm.comDistIface::SyncNode::progress(Tick max_send_tick, 24311290Sgabor.dozsa@arm.com Tick next_repeat, 24411290Sgabor.dozsa@arm.com ReqType do_ckpt, 24511703Smichael.lebeane@amd.com ReqType do_exit, 24611703Smichael.lebeane@amd.com ReqType do_stop_sync) 24710923SN/A{ 24811290Sgabor.dozsa@arm.com std::unique_lock<std::mutex> sync_lock(lock); 24911757Sgabor.dozsa@arm.com if (isAbort) // sync aborted 25011757Sgabor.dozsa@arm.com return false; 25111290Sgabor.dozsa@arm.com assert(waitNum > 0); 25211290Sgabor.dozsa@arm.com 25311290Sgabor.dozsa@arm.com nextAt = max_send_tick; 25411290Sgabor.dozsa@arm.com nextRepeat = next_repeat; 25511290Sgabor.dozsa@arm.com doCkpt = (do_ckpt != ReqType::none); 25611290Sgabor.dozsa@arm.com doExit = (do_exit != ReqType::none); 25711703Smichael.lebeane@amd.com doStopSync = (do_stop_sync != ReqType::none); 25811290Sgabor.dozsa@arm.com 25911290Sgabor.dozsa@arm.com waitNum--; 26011290Sgabor.dozsa@arm.com // Notify the simulation thread if the on-going sync is complete 26111290Sgabor.dozsa@arm.com if (waitNum == 0) { 26211290Sgabor.dozsa@arm.com sync_lock.unlock(); 26311290Sgabor.dozsa@arm.com cv.notify_one(); 26411290Sgabor.dozsa@arm.com } 26511757Sgabor.dozsa@arm.com // The receive thread must finish when simulation is about to exit 26611757Sgabor.dozsa@arm.com return !doExit; 26710923SN/A} 26810923SN/A 26910923SN/Avoid 27011290Sgabor.dozsa@arm.comDistIface::SyncNode::requestCkpt(ReqType req) 27110923SN/A{ 27211290Sgabor.dozsa@arm.com std::lock_guard<std::mutex> sync_lock(lock); 27311290Sgabor.dozsa@arm.com assert(req != ReqType::none); 27411290Sgabor.dozsa@arm.com if (needCkpt != ReqType::none) 27511290Sgabor.dozsa@arm.com warn("Ckpt requested multiple times (req:%d)\n", static_cast<int>(req)); 27611290Sgabor.dozsa@arm.com if (needCkpt == ReqType::none || req == ReqType::immediate) 27711290Sgabor.dozsa@arm.com needCkpt = req; 27810923SN/A} 27910923SN/A 28010923SN/Avoid 28111290Sgabor.dozsa@arm.comDistIface::SyncNode::requestExit(ReqType req) 28210923SN/A{ 28311290Sgabor.dozsa@arm.com std::lock_guard<std::mutex> sync_lock(lock); 28411290Sgabor.dozsa@arm.com assert(req != ReqType::none); 28511290Sgabor.dozsa@arm.com if (needExit != ReqType::none) 28611290Sgabor.dozsa@arm.com warn("Exit requested multiple times (req:%d)\n", static_cast<int>(req)); 28711290Sgabor.dozsa@arm.com if (needExit == ReqType::none || req == ReqType::immediate) 28811290Sgabor.dozsa@arm.com needExit = req; 28911290Sgabor.dozsa@arm.com} 29011290Sgabor.dozsa@arm.com 29111290Sgabor.dozsa@arm.comvoid 29211290Sgabor.dozsa@arm.comDistIface::Sync::drainComplete() 29311290Sgabor.dozsa@arm.com{ 29411290Sgabor.dozsa@arm.com if (doCkpt) { 29511290Sgabor.dozsa@arm.com // The first DistIface object called this right before writing the 29611290Sgabor.dozsa@arm.com // checkpoint. We need to drain the underlying physical network here. 29711290Sgabor.dozsa@arm.com // Note that other gem5 peers may enter this barrier at different 29811290Sgabor.dozsa@arm.com // ticks due to draining. 29911290Sgabor.dozsa@arm.com run(false); 30011290Sgabor.dozsa@arm.com // Only the "first" DistIface object has to perform the sync 30111290Sgabor.dozsa@arm.com doCkpt = false; 30211290Sgabor.dozsa@arm.com } 30311290Sgabor.dozsa@arm.com} 30411290Sgabor.dozsa@arm.com 30511290Sgabor.dozsa@arm.comvoid 30611290Sgabor.dozsa@arm.comDistIface::SyncNode::serialize(CheckpointOut &cp) const 30711290Sgabor.dozsa@arm.com{ 30811290Sgabor.dozsa@arm.com int need_exit = static_cast<int>(needExit); 30911290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(need_exit); 31011290Sgabor.dozsa@arm.com} 31111290Sgabor.dozsa@arm.com 31211290Sgabor.dozsa@arm.comvoid 31311290Sgabor.dozsa@arm.comDistIface::SyncNode::unserialize(CheckpointIn &cp) 31411290Sgabor.dozsa@arm.com{ 31511290Sgabor.dozsa@arm.com int need_exit; 31611290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(need_exit); 31711290Sgabor.dozsa@arm.com needExit = static_cast<ReqType>(need_exit); 31811290Sgabor.dozsa@arm.com} 31911290Sgabor.dozsa@arm.com 32011290Sgabor.dozsa@arm.comvoid 32111290Sgabor.dozsa@arm.comDistIface::SyncSwitch::serialize(CheckpointOut &cp) const 32211290Sgabor.dozsa@arm.com{ 32311290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(numExitReq); 32411290Sgabor.dozsa@arm.com} 32511290Sgabor.dozsa@arm.com 32611290Sgabor.dozsa@arm.comvoid 32711290Sgabor.dozsa@arm.comDistIface::SyncSwitch::unserialize(CheckpointIn &cp) 32811290Sgabor.dozsa@arm.com{ 32911290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(numExitReq); 33011290Sgabor.dozsa@arm.com} 33111290Sgabor.dozsa@arm.com 33211290Sgabor.dozsa@arm.comvoid 33311290Sgabor.dozsa@arm.comDistIface::SyncEvent::start() 33411290Sgabor.dozsa@arm.com{ 33511290Sgabor.dozsa@arm.com // Note that this may be called either from startup() or drainResume() 33611290Sgabor.dozsa@arm.com 33711290Sgabor.dozsa@arm.com // At this point, all DistIface objects has already called Sync::init() so 33811290Sgabor.dozsa@arm.com // we have a local minimum of the start tick and repeat for the periodic 33911290Sgabor.dozsa@arm.com // sync. 34011290Sgabor.dozsa@arm.com repeat = DistIface::sync->nextRepeat; 34111290Sgabor.dozsa@arm.com // Do a global barrier to agree on a common repeat value (the smallest 34211703Smichael.lebeane@amd.com // one from all participating nodes. 34311757Sgabor.dozsa@arm.com if (!DistIface::sync->run(false)) 34411757Sgabor.dozsa@arm.com panic("DistIface::SyncEvent::start() aborted\n"); 34511290Sgabor.dozsa@arm.com 34611290Sgabor.dozsa@arm.com assert(!DistIface::sync->doCkpt); 34711290Sgabor.dozsa@arm.com assert(!DistIface::sync->doExit); 34811703Smichael.lebeane@amd.com assert(!DistIface::sync->doStopSync); 34911290Sgabor.dozsa@arm.com assert(DistIface::sync->nextAt >= curTick()); 35011290Sgabor.dozsa@arm.com assert(DistIface::sync->nextRepeat <= repeat); 35111290Sgabor.dozsa@arm.com 35211703Smichael.lebeane@amd.com if (curTick() == 0) 35311290Sgabor.dozsa@arm.com assert(!scheduled()); 35411703Smichael.lebeane@amd.com 35511703Smichael.lebeane@amd.com // Use the maximum of the current tick for all participating nodes or a 35611703Smichael.lebeane@amd.com // user provided starting tick. 35711703Smichael.lebeane@amd.com if (scheduled()) 35811703Smichael.lebeane@amd.com reschedule(DistIface::sync->nextAt); 35911703Smichael.lebeane@amd.com else 36011703Smichael.lebeane@amd.com schedule(DistIface::sync->nextAt); 36111703Smichael.lebeane@amd.com 36211290Sgabor.dozsa@arm.com inform("Dist sync scheduled at %lu and repeats %lu\n", when(), 36311290Sgabor.dozsa@arm.com DistIface::sync->nextRepeat); 36411290Sgabor.dozsa@arm.com} 36511290Sgabor.dozsa@arm.com 36611290Sgabor.dozsa@arm.comvoid 36711290Sgabor.dozsa@arm.comDistIface::SyncEvent::process() 36811290Sgabor.dozsa@arm.com{ 36911290Sgabor.dozsa@arm.com // We may not start a global periodic sync while draining before taking a 37011290Sgabor.dozsa@arm.com // checkpoint. This is due to the possibility that peer gem5 processes 37111290Sgabor.dozsa@arm.com // may not hit the same periodic sync before they complete draining and 37211290Sgabor.dozsa@arm.com // that would make this periodic sync clash with sync called from 37311290Sgabor.dozsa@arm.com // DistIface::serialize() by other gem5 processes. 37411290Sgabor.dozsa@arm.com // We would need a 'distributed drain' solution to eliminate this 37511290Sgabor.dozsa@arm.com // restriction. 37611290Sgabor.dozsa@arm.com // Note that if draining was not triggered by checkpointing then we are 37711290Sgabor.dozsa@arm.com // fine since no extra global sync will happen (i.e. all peer gem5 will 37811290Sgabor.dozsa@arm.com // hit this periodic sync eventually). 37911290Sgabor.dozsa@arm.com panic_if(_draining && DistIface::sync->doCkpt, 38011290Sgabor.dozsa@arm.com "Distributed sync is hit while draining"); 38110923SN/A /* 38210923SN/A * Note that this is a global event so this process method will be called 38310923SN/A * by only exactly one thread. 38410923SN/A */ 38510923SN/A /* 38610923SN/A * We hold the eventq lock at this point but the receiver thread may 38710923SN/A * need the lock to schedule new recv events while waiting for the 38811290Sgabor.dozsa@arm.com * dist sync to complete. 38910923SN/A * Note that the other simulation threads also release their eventq 39010923SN/A * locks while waiting for us due to the global event semantics. 39110923SN/A */ 39211290Sgabor.dozsa@arm.com { 39311290Sgabor.dozsa@arm.com EventQueue::ScopedRelease sr(curEventQueue()); 39411290Sgabor.dozsa@arm.com // we do a global sync here that is supposed to happen at the same 39511290Sgabor.dozsa@arm.com // tick in all gem5 peers 39611757Sgabor.dozsa@arm.com if (!DistIface::sync->run(true)) 39711757Sgabor.dozsa@arm.com return; // global sync aborted 39811290Sgabor.dozsa@arm.com // global sync completed 39911290Sgabor.dozsa@arm.com } 40011290Sgabor.dozsa@arm.com if (DistIface::sync->doCkpt) 40111290Sgabor.dozsa@arm.com exitSimLoop("checkpoint"); 40211757Sgabor.dozsa@arm.com if (DistIface::sync->doExit) { 40311290Sgabor.dozsa@arm.com exitSimLoop("exit request from gem5 peers"); 40411757Sgabor.dozsa@arm.com return; 40511757Sgabor.dozsa@arm.com } 40611703Smichael.lebeane@amd.com if (DistIface::sync->doStopSync) { 40711703Smichael.lebeane@amd.com DistIface::sync->doStopSync = false; 40811703Smichael.lebeane@amd.com inform("synchronization disabled at %lu\n", curTick()); 40911290Sgabor.dozsa@arm.com 41011703Smichael.lebeane@amd.com // The switch node needs to wait for the next sync immediately. 41111703Smichael.lebeane@amd.com if (DistIface::isSwitch) { 41211703Smichael.lebeane@amd.com start(); 41311703Smichael.lebeane@amd.com } else { 41411703Smichael.lebeane@amd.com // Wake up thread contexts on non-switch nodes. 41511703Smichael.lebeane@amd.com for (int i = 0; i < DistIface::master->sys->numContexts(); i++) { 41611703Smichael.lebeane@amd.com ThreadContext *tc = 41711703Smichael.lebeane@amd.com DistIface::master->sys->getThreadContext(i); 41811703Smichael.lebeane@amd.com if (tc->status() == ThreadContext::Suspended) 41911703Smichael.lebeane@amd.com tc->activate(); 42011703Smichael.lebeane@amd.com else 42111703Smichael.lebeane@amd.com warn_once("Tried to wake up thread in dist-gem5, but it " 42211703Smichael.lebeane@amd.com "was already awake!\n"); 42311703Smichael.lebeane@amd.com } 42411703Smichael.lebeane@amd.com } 42511703Smichael.lebeane@amd.com return; 42611703Smichael.lebeane@amd.com } 42711290Sgabor.dozsa@arm.com // schedule the next periodic sync 42811290Sgabor.dozsa@arm.com repeat = DistIface::sync->nextRepeat; 42911290Sgabor.dozsa@arm.com schedule(curTick() + repeat); 43010923SN/A} 43110923SN/A 43211290Sgabor.dozsa@arm.comvoid 43311290Sgabor.dozsa@arm.comDistIface::RecvScheduler::init(Event *recv_done, Tick link_delay) 43410923SN/A{ 43511290Sgabor.dozsa@arm.com // This is called from the receiver thread when it starts running. The new 43611290Sgabor.dozsa@arm.com // receiver thread shares the event queue with the simulation thread 43711290Sgabor.dozsa@arm.com // (associated with the simulated Ethernet link). 43811290Sgabor.dozsa@arm.com curEventQueue(eventManager->eventQueue()); 43911290Sgabor.dozsa@arm.com 44011290Sgabor.dozsa@arm.com recvDone = recv_done; 44111290Sgabor.dozsa@arm.com linkDelay = link_delay; 44210923SN/A} 44310923SN/A 44411290Sgabor.dozsa@arm.comTick 44511290Sgabor.dozsa@arm.comDistIface::RecvScheduler::calcReceiveTick(Tick send_tick, 44611290Sgabor.dozsa@arm.com Tick send_delay, 44711290Sgabor.dozsa@arm.com Tick prev_recv_tick) 44810923SN/A{ 44911290Sgabor.dozsa@arm.com Tick recv_tick = send_tick + send_delay + linkDelay; 45011290Sgabor.dozsa@arm.com // sanity check (we need atleast a send delay long window) 45111290Sgabor.dozsa@arm.com assert(recv_tick >= prev_recv_tick + send_delay); 45211290Sgabor.dozsa@arm.com panic_if(prev_recv_tick + send_delay > recv_tick, 45311290Sgabor.dozsa@arm.com "Receive window is smaller than send delay"); 45411290Sgabor.dozsa@arm.com panic_if(recv_tick <= curTick(), 45511290Sgabor.dozsa@arm.com "Simulators out of sync - missed packet receive by %llu ticks" 45611290Sgabor.dozsa@arm.com "(rev_recv_tick: %lu send_tick: %lu send_delay: %lu " 45711290Sgabor.dozsa@arm.com "linkDelay: %lu )", 45811290Sgabor.dozsa@arm.com curTick() - recv_tick, prev_recv_tick, send_tick, send_delay, 45911290Sgabor.dozsa@arm.com linkDelay); 46011290Sgabor.dozsa@arm.com 46111290Sgabor.dozsa@arm.com return recv_tick; 46210923SN/A} 46310923SN/A 46411290Sgabor.dozsa@arm.comvoid 46511290Sgabor.dozsa@arm.comDistIface::RecvScheduler::resumeRecvTicks() 46610923SN/A{ 46711290Sgabor.dozsa@arm.com // Schedule pending packets asap in case link speed/delay changed when 46811290Sgabor.dozsa@arm.com // restoring from the checkpoint. 46911290Sgabor.dozsa@arm.com // This may be done during unserialize except that curTick() is unknown 47011290Sgabor.dozsa@arm.com // so we call this during drainResume(). 47111290Sgabor.dozsa@arm.com // If we are not restoring from a checkppint then link latency could not 47211290Sgabor.dozsa@arm.com // change so we just return. 47311290Sgabor.dozsa@arm.com if (!ckptRestore) 47411290Sgabor.dozsa@arm.com return; 47511290Sgabor.dozsa@arm.com 47611290Sgabor.dozsa@arm.com std::vector<Desc> v; 47711290Sgabor.dozsa@arm.com while (!descQueue.empty()) { 47811290Sgabor.dozsa@arm.com Desc d = descQueue.front(); 47911290Sgabor.dozsa@arm.com descQueue.pop(); 48011290Sgabor.dozsa@arm.com d.sendTick = curTick(); 48111701Smichael.lebeane@amd.com d.sendDelay = d.packet->simLength; // assume 1 tick/byte max link speed 48211290Sgabor.dozsa@arm.com v.push_back(d); 48311290Sgabor.dozsa@arm.com } 48411290Sgabor.dozsa@arm.com 48511290Sgabor.dozsa@arm.com for (auto &d : v) 48611290Sgabor.dozsa@arm.com descQueue.push(d); 48711290Sgabor.dozsa@arm.com 48811290Sgabor.dozsa@arm.com if (recvDone->scheduled()) { 48911290Sgabor.dozsa@arm.com assert(!descQueue.empty()); 49011290Sgabor.dozsa@arm.com eventManager->reschedule(recvDone, curTick()); 49111290Sgabor.dozsa@arm.com } else { 49211290Sgabor.dozsa@arm.com assert(descQueue.empty() && v.empty()); 49311290Sgabor.dozsa@arm.com } 49411290Sgabor.dozsa@arm.com ckptRestore = false; 49510923SN/A} 49610923SN/A 49711290Sgabor.dozsa@arm.comvoid 49811290Sgabor.dozsa@arm.comDistIface::RecvScheduler::pushPacket(EthPacketPtr new_packet, 49911290Sgabor.dozsa@arm.com Tick send_tick, 50011290Sgabor.dozsa@arm.com Tick send_delay) 50111290Sgabor.dozsa@arm.com{ 50211290Sgabor.dozsa@arm.com // Note : this is called from the receiver thread 50311290Sgabor.dozsa@arm.com curEventQueue()->lock(); 50411290Sgabor.dozsa@arm.com Tick recv_tick = calcReceiveTick(send_tick, send_delay, prevRecvTick); 50511290Sgabor.dozsa@arm.com 50611290Sgabor.dozsa@arm.com DPRINTF(DistEthernetPkt, "DistIface::recvScheduler::pushPacket " 50711290Sgabor.dozsa@arm.com "send_tick:%llu send_delay:%llu link_delay:%llu recv_tick:%llu\n", 50811290Sgabor.dozsa@arm.com send_tick, send_delay, linkDelay, recv_tick); 50911290Sgabor.dozsa@arm.com // Every packet must be sent and arrive in the same quantum 51011290Sgabor.dozsa@arm.com assert(send_tick > master->syncEvent->when() - 51111290Sgabor.dozsa@arm.com master->syncEvent->repeat); 51211290Sgabor.dozsa@arm.com // No packet may be scheduled for receive in the arrival quantum 51311290Sgabor.dozsa@arm.com assert(send_tick + send_delay + linkDelay > master->syncEvent->when()); 51411290Sgabor.dozsa@arm.com 51511290Sgabor.dozsa@arm.com // Now we are about to schedule a recvDone event for the new data packet. 51611290Sgabor.dozsa@arm.com // We use the same recvDone object for all incoming data packets. Packet 51711290Sgabor.dozsa@arm.com // descriptors are saved in the ordered queue. The currently scheduled 51811290Sgabor.dozsa@arm.com // packet is always on the top of the queue. 51911290Sgabor.dozsa@arm.com // NOTE: we use the event queue lock to protect the receive desc queue, 52011290Sgabor.dozsa@arm.com // too, which is accessed both by the receiver thread and the simulation 52111290Sgabor.dozsa@arm.com // thread. 52211290Sgabor.dozsa@arm.com descQueue.emplace(new_packet, send_tick, send_delay); 52311290Sgabor.dozsa@arm.com if (descQueue.size() == 1) { 52411290Sgabor.dozsa@arm.com assert(!recvDone->scheduled()); 52511290Sgabor.dozsa@arm.com eventManager->schedule(recvDone, recv_tick); 52611290Sgabor.dozsa@arm.com } else { 52711290Sgabor.dozsa@arm.com assert(recvDone->scheduled()); 52811290Sgabor.dozsa@arm.com panic_if(descQueue.front().sendTick + descQueue.front().sendDelay > recv_tick, 52911290Sgabor.dozsa@arm.com "Out of order packet received (recv_tick: %lu top(): %lu\n", 53011290Sgabor.dozsa@arm.com recv_tick, descQueue.front().sendTick + descQueue.front().sendDelay); 53111290Sgabor.dozsa@arm.com } 53211290Sgabor.dozsa@arm.com curEventQueue()->unlock(); 53311290Sgabor.dozsa@arm.com} 53411290Sgabor.dozsa@arm.com 53511290Sgabor.dozsa@arm.comEthPacketPtr 53611290Sgabor.dozsa@arm.comDistIface::RecvScheduler::popPacket() 53711290Sgabor.dozsa@arm.com{ 53811290Sgabor.dozsa@arm.com // Note : this is called from the simulation thread when a receive done 53911290Sgabor.dozsa@arm.com // event is being processed for the link. We assume that the thread holds 54011290Sgabor.dozsa@arm.com // the event queue queue lock when this is called! 54111290Sgabor.dozsa@arm.com EthPacketPtr next_packet = descQueue.front().packet; 54211290Sgabor.dozsa@arm.com descQueue.pop(); 54311290Sgabor.dozsa@arm.com 54411290Sgabor.dozsa@arm.com if (descQueue.size() > 0) { 54511290Sgabor.dozsa@arm.com Tick recv_tick = calcReceiveTick(descQueue.front().sendTick, 54611290Sgabor.dozsa@arm.com descQueue.front().sendDelay, 54711290Sgabor.dozsa@arm.com curTick()); 54811290Sgabor.dozsa@arm.com eventManager->schedule(recvDone, recv_tick); 54911290Sgabor.dozsa@arm.com } 55011290Sgabor.dozsa@arm.com prevRecvTick = curTick(); 55111290Sgabor.dozsa@arm.com return next_packet; 55211290Sgabor.dozsa@arm.com} 55311290Sgabor.dozsa@arm.com 55411290Sgabor.dozsa@arm.comvoid 55511290Sgabor.dozsa@arm.comDistIface::RecvScheduler::Desc::serialize(CheckpointOut &cp) const 55611290Sgabor.dozsa@arm.com{ 55711290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(sendTick); 55811290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(sendDelay); 55911290Sgabor.dozsa@arm.com packet->serialize("rxPacket", cp); 56011290Sgabor.dozsa@arm.com} 56111290Sgabor.dozsa@arm.com 56211290Sgabor.dozsa@arm.comvoid 56311290Sgabor.dozsa@arm.comDistIface::RecvScheduler::Desc::unserialize(CheckpointIn &cp) 56411290Sgabor.dozsa@arm.com{ 56511290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(sendTick); 56611290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(sendDelay); 56711701Smichael.lebeane@amd.com packet = std::make_shared<EthPacketData>(); 56811290Sgabor.dozsa@arm.com packet->unserialize("rxPacket", cp); 56911290Sgabor.dozsa@arm.com} 57011290Sgabor.dozsa@arm.com 57111290Sgabor.dozsa@arm.comvoid 57211290Sgabor.dozsa@arm.comDistIface::RecvScheduler::serialize(CheckpointOut &cp) const 57311290Sgabor.dozsa@arm.com{ 57411290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(prevRecvTick); 57511290Sgabor.dozsa@arm.com // serialize the receive desc queue 57611290Sgabor.dozsa@arm.com std::queue<Desc> tmp_queue(descQueue); 57711290Sgabor.dozsa@arm.com unsigned n_desc_queue = descQueue.size(); 57811290Sgabor.dozsa@arm.com assert(tmp_queue.size() == descQueue.size()); 57911290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(n_desc_queue); 58011290Sgabor.dozsa@arm.com for (int i = 0; i < n_desc_queue; i++) { 58111290Sgabor.dozsa@arm.com tmp_queue.front().serializeSection(cp, csprintf("rxDesc_%d", i)); 58211290Sgabor.dozsa@arm.com tmp_queue.pop(); 58311290Sgabor.dozsa@arm.com } 58411290Sgabor.dozsa@arm.com assert(tmp_queue.empty()); 58511290Sgabor.dozsa@arm.com} 58611290Sgabor.dozsa@arm.com 58711290Sgabor.dozsa@arm.comvoid 58811290Sgabor.dozsa@arm.comDistIface::RecvScheduler::unserialize(CheckpointIn &cp) 58911290Sgabor.dozsa@arm.com{ 59011290Sgabor.dozsa@arm.com assert(descQueue.size() == 0); 59111325Ssteve.reinhardt@amd.com assert(!recvDone->scheduled()); 59211325Ssteve.reinhardt@amd.com assert(!ckptRestore); 59311290Sgabor.dozsa@arm.com 59411290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(prevRecvTick); 59511290Sgabor.dozsa@arm.com // unserialize the receive desc queue 59611290Sgabor.dozsa@arm.com unsigned n_desc_queue; 59711290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(n_desc_queue); 59811290Sgabor.dozsa@arm.com for (int i = 0; i < n_desc_queue; i++) { 59911290Sgabor.dozsa@arm.com Desc recv_desc; 60011290Sgabor.dozsa@arm.com recv_desc.unserializeSection(cp, csprintf("rxDesc_%d", i)); 60111290Sgabor.dozsa@arm.com descQueue.push(recv_desc); 60211290Sgabor.dozsa@arm.com } 60311290Sgabor.dozsa@arm.com ckptRestore = true; 60411290Sgabor.dozsa@arm.com} 60511290Sgabor.dozsa@arm.com 60611290Sgabor.dozsa@arm.comDistIface::DistIface(unsigned dist_rank, 60711290Sgabor.dozsa@arm.com unsigned dist_size, 60811290Sgabor.dozsa@arm.com Tick sync_start, 60911290Sgabor.dozsa@arm.com Tick sync_repeat, 61011290Sgabor.dozsa@arm.com EventManager *em, 61111703Smichael.lebeane@amd.com bool use_pseudo_op, 61211290Sgabor.dozsa@arm.com bool is_switch, int num_nodes) : 61310923SN/A syncStart(sync_start), syncRepeat(sync_repeat), 61411703Smichael.lebeane@amd.com recvThread(nullptr), recvScheduler(em), syncStartOnPseudoOp(use_pseudo_op), 61511290Sgabor.dozsa@arm.com rank(dist_rank), size(dist_size) 61610923SN/A{ 61711290Sgabor.dozsa@arm.com DPRINTF(DistEthernet, "DistIface() ctor rank:%d\n",dist_rank); 61811290Sgabor.dozsa@arm.com isMaster = false; 61910923SN/A if (master == nullptr) { 62010923SN/A assert(sync == nullptr); 62110923SN/A assert(syncEvent == nullptr); 62211703Smichael.lebeane@amd.com isSwitch = is_switch; 62311290Sgabor.dozsa@arm.com if (is_switch) 62411290Sgabor.dozsa@arm.com sync = new SyncSwitch(num_nodes); 62511290Sgabor.dozsa@arm.com else 62611290Sgabor.dozsa@arm.com sync = new SyncNode(); 62710923SN/A syncEvent = new SyncEvent(); 62810923SN/A master = this; 62911290Sgabor.dozsa@arm.com isMaster = true; 63010923SN/A } 63111290Sgabor.dozsa@arm.com distIfaceId = distIfaceNum; 63211290Sgabor.dozsa@arm.com distIfaceNum++; 63310923SN/A} 63410923SN/A 63511290Sgabor.dozsa@arm.comDistIface::~DistIface() 63610923SN/A{ 63710923SN/A assert(recvThread); 63811757Sgabor.dozsa@arm.com recvThread->join(); 63910923SN/A delete recvThread; 64011757Sgabor.dozsa@arm.com if (distIfaceNum-- == 0) { 64110923SN/A assert(syncEvent); 64210923SN/A delete syncEvent; 64310923SN/A assert(sync); 64410923SN/A delete sync; 64511757Sgabor.dozsa@arm.com } 64611757Sgabor.dozsa@arm.com if (this == master) 64711290Sgabor.dozsa@arm.com master = nullptr; 64810923SN/A} 64910923SN/A 65010923SN/Avoid 65111290Sgabor.dozsa@arm.comDistIface::packetOut(EthPacketPtr pkt, Tick send_delay) 65210923SN/A{ 65311290Sgabor.dozsa@arm.com Header header; 65410923SN/A 65511290Sgabor.dozsa@arm.com // Prepare a dist header packet for the Ethernet packet we want to 65610923SN/A // send out. 65711290Sgabor.dozsa@arm.com header.msgType = MsgType::dataDescriptor; 65811290Sgabor.dozsa@arm.com header.sendTick = curTick(); 65911290Sgabor.dozsa@arm.com header.sendDelay = send_delay; 66010923SN/A 66111701Smichael.lebeane@amd.com header.dataPacketLength = pkt->length; 66211701Smichael.lebeane@amd.com header.simLength = pkt->simLength; 66310923SN/A 66411290Sgabor.dozsa@arm.com // Send out the packet and the meta info. 66511290Sgabor.dozsa@arm.com sendPacket(header, pkt); 66610923SN/A 66711290Sgabor.dozsa@arm.com DPRINTF(DistEthernetPkt, 66811290Sgabor.dozsa@arm.com "DistIface::sendDataPacket() done size:%d send_delay:%llu\n", 66911701Smichael.lebeane@amd.com pkt->length, send_delay); 67010923SN/A} 67110923SN/A 67210923SN/Avoid 67311290Sgabor.dozsa@arm.comDistIface::recvThreadFunc(Event *recv_done, Tick link_delay) 67410923SN/A{ 67510923SN/A EthPacketPtr new_packet; 67611290Sgabor.dozsa@arm.com DistHeaderPkt::Header header; 67710923SN/A 67811290Sgabor.dozsa@arm.com // Initialize receive scheduler parameters 67911290Sgabor.dozsa@arm.com recvScheduler.init(recv_done, link_delay); 68011290Sgabor.dozsa@arm.com 68110923SN/A // Main loop to wait for and process any incoming message. 68210923SN/A for (;;) { 68311290Sgabor.dozsa@arm.com // recvHeader() blocks until the next dist header packet comes in. 68410923SN/A if (!recvHeader(header)) { 68510923SN/A // We lost connection to the peer gem5 processes most likely 68610923SN/A // because one of them called m5 exit. So we stop here. 68711290Sgabor.dozsa@arm.com // Grab the eventq lock to stop the simulation thread 68811290Sgabor.dozsa@arm.com curEventQueue()->lock(); 68911757Sgabor.dozsa@arm.com exitSimLoop("connection to gem5 peer got closed"); 69011622Smichael.lebeane@amd.com curEventQueue()->unlock(); 69111757Sgabor.dozsa@arm.com // The simulation thread may be blocked in processing an on-going 69211757Sgabor.dozsa@arm.com // global synchronisation. Abort the sync to give the simulation 69311757Sgabor.dozsa@arm.com // thread a chance to make progress and process the exit event. 69411757Sgabor.dozsa@arm.com sync->abort(); 69511757Sgabor.dozsa@arm.com // Finish receiver thread 69611622Smichael.lebeane@amd.com break; 69710923SN/A } 69811290Sgabor.dozsa@arm.com 69911290Sgabor.dozsa@arm.com // We got a valid dist header packet, let's process it 70010923SN/A if (header.msgType == MsgType::dataDescriptor) { 70111290Sgabor.dozsa@arm.com recvPacket(header, new_packet); 70211290Sgabor.dozsa@arm.com recvScheduler.pushPacket(new_packet, 70311290Sgabor.dozsa@arm.com header.sendTick, 70411290Sgabor.dozsa@arm.com header.sendDelay); 70510923SN/A } else { 70610923SN/A // everything else must be synchronisation related command 70711757Sgabor.dozsa@arm.com if (!sync->progress(header.sendTick, 70811757Sgabor.dozsa@arm.com header.syncRepeat, 70911757Sgabor.dozsa@arm.com header.needCkpt, 71011757Sgabor.dozsa@arm.com header.needExit, 71111757Sgabor.dozsa@arm.com header.needStopSync)) 71211757Sgabor.dozsa@arm.com // Finish receiver thread if simulation is about to exit 71311757Sgabor.dozsa@arm.com break; 71410923SN/A } 71510923SN/A } 71610923SN/A} 71710923SN/A 71810923SN/Avoid 71911290Sgabor.dozsa@arm.comDistIface::spawnRecvThread(const Event *recv_done, Tick link_delay) 72010923SN/A{ 72110923SN/A assert(recvThread == nullptr); 72210923SN/A 72311290Sgabor.dozsa@arm.com recvThread = new std::thread(&DistIface::recvThreadFunc, 72411290Sgabor.dozsa@arm.com this, 72511290Sgabor.dozsa@arm.com const_cast<Event *>(recv_done), 72611290Sgabor.dozsa@arm.com link_delay); 72710923SN/A recvThreadsNum++; 72810923SN/A} 72910923SN/A 73010923SN/ADrainState 73111290Sgabor.dozsa@arm.comDistIface::drain() 73210923SN/A{ 73311290Sgabor.dozsa@arm.com DPRINTF(DistEthernet,"DistIFace::drain() called\n"); 73410923SN/A // This can be called multiple times in the same drain cycle. 73511290Sgabor.dozsa@arm.com if (this == master) 73611290Sgabor.dozsa@arm.com syncEvent->draining(true); 73710923SN/A return DrainState::Drained; 73810923SN/A} 73910923SN/A 74011290Sgabor.dozsa@arm.comvoid 74111290Sgabor.dozsa@arm.comDistIface::drainResume() { 74211290Sgabor.dozsa@arm.com DPRINTF(DistEthernet,"DistIFace::drainResume() called\n"); 74311290Sgabor.dozsa@arm.com if (this == master) 74411290Sgabor.dozsa@arm.com syncEvent->draining(false); 74511290Sgabor.dozsa@arm.com recvScheduler.resumeRecvTicks(); 74611290Sgabor.dozsa@arm.com} 74711290Sgabor.dozsa@arm.com 74811290Sgabor.dozsa@arm.comvoid 74911290Sgabor.dozsa@arm.comDistIface::serialize(CheckpointOut &cp) const 75011290Sgabor.dozsa@arm.com{ 75111290Sgabor.dozsa@arm.com // Drain the dist interface before the checkpoint is taken. We cannot call 75211290Sgabor.dozsa@arm.com // this as part of the normal drain cycle because this dist sync has to be 75311290Sgabor.dozsa@arm.com // called exactly once after the system is fully drained. 75411290Sgabor.dozsa@arm.com sync->drainComplete(); 75511290Sgabor.dozsa@arm.com 75611290Sgabor.dozsa@arm.com unsigned rank_orig = rank, dist_iface_id_orig = distIfaceId; 75711290Sgabor.dozsa@arm.com 75811290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(rank_orig); 75911290Sgabor.dozsa@arm.com SERIALIZE_SCALAR(dist_iface_id_orig); 76011290Sgabor.dozsa@arm.com 76111290Sgabor.dozsa@arm.com recvScheduler.serializeSection(cp, "recvScheduler"); 76211290Sgabor.dozsa@arm.com if (this == master) { 76311290Sgabor.dozsa@arm.com sync->serializeSection(cp, "Sync"); 76410923SN/A } 76510923SN/A} 76610923SN/A 76711290Sgabor.dozsa@arm.comvoid 76811290Sgabor.dozsa@arm.comDistIface::unserialize(CheckpointIn &cp) 76910923SN/A{ 77011290Sgabor.dozsa@arm.com unsigned rank_orig, dist_iface_id_orig; 77111290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(rank_orig); 77211290Sgabor.dozsa@arm.com UNSERIALIZE_SCALAR(dist_iface_id_orig); 77310923SN/A 77411290Sgabor.dozsa@arm.com panic_if(rank != rank_orig, "Rank mismatch at resume (rank=%d, orig=%d)", 77511290Sgabor.dozsa@arm.com rank, rank_orig); 77611290Sgabor.dozsa@arm.com panic_if(distIfaceId != dist_iface_id_orig, "Dist iface ID mismatch " 77711290Sgabor.dozsa@arm.com "at resume (distIfaceId=%d, orig=%d)", distIfaceId, 77811290Sgabor.dozsa@arm.com dist_iface_id_orig); 77910923SN/A 78011290Sgabor.dozsa@arm.com recvScheduler.unserializeSection(cp, "recvScheduler"); 78111290Sgabor.dozsa@arm.com if (this == master) { 78211290Sgabor.dozsa@arm.com sync->unserializeSection(cp, "Sync"); 78310923SN/A } 78410923SN/A} 78510923SN/A 78611290Sgabor.dozsa@arm.comvoid 78711290Sgabor.dozsa@arm.comDistIface::init(const Event *done_event, Tick link_delay) 78810923SN/A{ 78911290Sgabor.dozsa@arm.com // Init hook for the underlaying message transport to setup/finalize 79011290Sgabor.dozsa@arm.com // communication channels 79111290Sgabor.dozsa@arm.com initTransport(); 79210923SN/A 79311290Sgabor.dozsa@arm.com // Spawn a new receiver thread that will process messages 79411290Sgabor.dozsa@arm.com // coming in from peer gem5 processes. 79511290Sgabor.dozsa@arm.com // The receive thread will also schedule a (receive) doneEvent 79611290Sgabor.dozsa@arm.com // for each incoming data packet. 79711290Sgabor.dozsa@arm.com spawnRecvThread(done_event, link_delay); 79810923SN/A 79910923SN/A 80011290Sgabor.dozsa@arm.com // Adjust the periodic sync start and interval. Different DistIface 80111290Sgabor.dozsa@arm.com // might have different requirements. The singleton sync object 80211290Sgabor.dozsa@arm.com // will select the minimum values for both params. 80311290Sgabor.dozsa@arm.com assert(sync != nullptr); 80411290Sgabor.dozsa@arm.com sync->init(syncStart, syncRepeat); 80510923SN/A 80610923SN/A // Initialize the seed for random generator to avoid the same sequence 80710923SN/A // in all gem5 peer processes 80810923SN/A assert(master != nullptr); 80910923SN/A if (this == master) 81010923SN/A random_mt.init(5489 * (rank+1) + 257); 81110923SN/A} 81210923SN/A 81311290Sgabor.dozsa@arm.comvoid 81411290Sgabor.dozsa@arm.comDistIface::startup() 81510923SN/A{ 81611290Sgabor.dozsa@arm.com DPRINTF(DistEthernet, "DistIface::startup() started\n"); 81711703Smichael.lebeane@amd.com // Schedule synchronization unless we are not a switch in pseudo_op mode. 81811703Smichael.lebeane@amd.com if (this == master && (!syncStartOnPseudoOp || isSwitch)) 81911290Sgabor.dozsa@arm.com syncEvent->start(); 82011290Sgabor.dozsa@arm.com DPRINTF(DistEthernet, "DistIface::startup() done\n"); 82111290Sgabor.dozsa@arm.com} 82210923SN/A 82311290Sgabor.dozsa@arm.combool 82411290Sgabor.dozsa@arm.comDistIface::readyToCkpt(Tick delay, Tick period) 82511290Sgabor.dozsa@arm.com{ 82611290Sgabor.dozsa@arm.com bool ret = true; 82711290Sgabor.dozsa@arm.com DPRINTF(DistEthernet, "DistIface::readyToCkpt() called, delay:%lu " 82811290Sgabor.dozsa@arm.com "period:%lu\n", delay, period); 82911290Sgabor.dozsa@arm.com if (master) { 83011290Sgabor.dozsa@arm.com if (delay == 0) { 83111290Sgabor.dozsa@arm.com inform("m5 checkpoint called with zero delay => triggering collaborative " 83211290Sgabor.dozsa@arm.com "checkpoint\n"); 83311290Sgabor.dozsa@arm.com sync->requestCkpt(ReqType::collective); 83410923SN/A } else { 83511290Sgabor.dozsa@arm.com inform("m5 checkpoint called with non-zero delay => triggering immediate " 83611290Sgabor.dozsa@arm.com "checkpoint (at the next sync)\n"); 83711290Sgabor.dozsa@arm.com sync->requestCkpt(ReqType::immediate); 83810923SN/A } 83911290Sgabor.dozsa@arm.com if (period != 0) 84011290Sgabor.dozsa@arm.com inform("Non-zero period for m5_ckpt is ignored in " 84111290Sgabor.dozsa@arm.com "distributed gem5 runs\n"); 84211290Sgabor.dozsa@arm.com ret = false; 84311290Sgabor.dozsa@arm.com } 84411290Sgabor.dozsa@arm.com return ret; 84511290Sgabor.dozsa@arm.com} 84611290Sgabor.dozsa@arm.com 84711703Smichael.lebeane@amd.comvoid 84811703Smichael.lebeane@amd.comDistIface::SyncNode::requestStopSync(ReqType req) 84911703Smichael.lebeane@amd.com{ 85011703Smichael.lebeane@amd.com std::lock_guard<std::mutex> sync_lock(lock); 85111703Smichael.lebeane@amd.com needStopSync = req; 85211703Smichael.lebeane@amd.com} 85311703Smichael.lebeane@amd.com 85411703Smichael.lebeane@amd.comvoid 85511703Smichael.lebeane@amd.comDistIface::toggleSync(ThreadContext *tc) 85611703Smichael.lebeane@amd.com{ 85711703Smichael.lebeane@amd.com // Unforunate that we have to populate the system pointer member this way. 85811703Smichael.lebeane@amd.com master->sys = tc->getSystemPtr(); 85911703Smichael.lebeane@amd.com 86011703Smichael.lebeane@amd.com // The invariant for both syncing and "unsyncing" is that all threads will 86111703Smichael.lebeane@amd.com // stop executing intructions until the desired sync state has been reached 86211703Smichael.lebeane@amd.com // for all nodes. This is the easiest way to prevent deadlock (in the case 86311703Smichael.lebeane@amd.com // of "unsyncing") and causality errors (in the case of syncing). 86411703Smichael.lebeane@amd.com if (master->syncEvent->scheduled()) { 86511703Smichael.lebeane@amd.com inform("Request toggling syncronization off\n"); 86611703Smichael.lebeane@amd.com master->sync->requestStopSync(ReqType::collective); 86711703Smichael.lebeane@amd.com 86811703Smichael.lebeane@amd.com // At this point, we have no clue when everyone will reach the sync 86911703Smichael.lebeane@amd.com // stop point. Suspend execution of all local thread contexts. 87011703Smichael.lebeane@amd.com // Dist-gem5 will reactivate all thread contexts when everyone has 87111703Smichael.lebeane@amd.com // reached the sync stop point. 87213340Sgabeblack@google.com#if THE_ISA != NULL_ISA 87311703Smichael.lebeane@amd.com for (int i = 0; i < master->sys->numContexts(); i++) { 87411703Smichael.lebeane@amd.com ThreadContext *tc = master->sys->getThreadContext(i); 87511703Smichael.lebeane@amd.com if (tc->status() == ThreadContext::Active) 87611703Smichael.lebeane@amd.com tc->quiesce(); 87711703Smichael.lebeane@amd.com } 87813340Sgabeblack@google.com#endif 87911703Smichael.lebeane@amd.com } else { 88011703Smichael.lebeane@amd.com inform("Request toggling syncronization on\n"); 88111703Smichael.lebeane@amd.com master->syncEvent->start(); 88211703Smichael.lebeane@amd.com 88311703Smichael.lebeane@amd.com // We need to suspend all CPUs until the sync point is reached by all 88411703Smichael.lebeane@amd.com // nodes to prevent causality errors. We can also schedule CPU 88511703Smichael.lebeane@amd.com // activation here, since we know exactly when the next sync will 88611703Smichael.lebeane@amd.com // occur. 88713340Sgabeblack@google.com#if THE_ISA != NULL_ISA 88811703Smichael.lebeane@amd.com for (int i = 0; i < master->sys->numContexts(); i++) { 88911703Smichael.lebeane@amd.com ThreadContext *tc = master->sys->getThreadContext(i); 89011703Smichael.lebeane@amd.com if (tc->status() == ThreadContext::Active) 89111703Smichael.lebeane@amd.com tc->quiesceTick(master->syncEvent->when() + 1); 89211703Smichael.lebeane@amd.com } 89313340Sgabeblack@google.com#endif 89411703Smichael.lebeane@amd.com } 89511703Smichael.lebeane@amd.com} 89611703Smichael.lebeane@amd.com 89711290Sgabor.dozsa@arm.combool 89811290Sgabor.dozsa@arm.comDistIface::readyToExit(Tick delay) 89911290Sgabor.dozsa@arm.com{ 90011290Sgabor.dozsa@arm.com bool ret = true; 90111290Sgabor.dozsa@arm.com DPRINTF(DistEthernet, "DistIface::readyToExit() called, delay:%lu\n", 90211290Sgabor.dozsa@arm.com delay); 90311290Sgabor.dozsa@arm.com if (master) { 90411703Smichael.lebeane@amd.com // To successfully coordinate an exit, all nodes must be synchronising 90511703Smichael.lebeane@amd.com if (!master->syncEvent->scheduled()) 90611703Smichael.lebeane@amd.com master->syncEvent->start(); 90711703Smichael.lebeane@amd.com 90811290Sgabor.dozsa@arm.com if (delay == 0) { 90911290Sgabor.dozsa@arm.com inform("m5 exit called with zero delay => triggering collaborative " 91011290Sgabor.dozsa@arm.com "exit\n"); 91111290Sgabor.dozsa@arm.com sync->requestExit(ReqType::collective); 91211290Sgabor.dozsa@arm.com } else { 91311290Sgabor.dozsa@arm.com inform("m5 exit called with non-zero delay => triggering immediate " 91411290Sgabor.dozsa@arm.com "exit (at the next sync)\n"); 91511290Sgabor.dozsa@arm.com sync->requestExit(ReqType::immediate); 91611290Sgabor.dozsa@arm.com } 91711290Sgabor.dozsa@arm.com ret = false; 91811290Sgabor.dozsa@arm.com } 91911290Sgabor.dozsa@arm.com return ret; 92011290Sgabor.dozsa@arm.com} 92111290Sgabor.dozsa@arm.com 92211290Sgabor.dozsa@arm.comuint64_t 92311290Sgabor.dozsa@arm.comDistIface::rankParam() 92411290Sgabor.dozsa@arm.com{ 92511290Sgabor.dozsa@arm.com uint64_t val; 92611290Sgabor.dozsa@arm.com if (master) { 92711290Sgabor.dozsa@arm.com val = master->rank; 92810923SN/A } else { 92911290Sgabor.dozsa@arm.com warn("Dist-rank parameter is queried in single gem5 simulation."); 93011290Sgabor.dozsa@arm.com val = 0; 93110923SN/A } 93211290Sgabor.dozsa@arm.com return val; 93310923SN/A} 93411290Sgabor.dozsa@arm.com 93511290Sgabor.dozsa@arm.comuint64_t 93611290Sgabor.dozsa@arm.comDistIface::sizeParam() 93711290Sgabor.dozsa@arm.com{ 93811290Sgabor.dozsa@arm.com uint64_t val; 93911290Sgabor.dozsa@arm.com if (master) { 94011290Sgabor.dozsa@arm.com val = master->size; 94111290Sgabor.dozsa@arm.com } else { 94211290Sgabor.dozsa@arm.com warn("Dist-size parameter is queried in single gem5 simulation."); 94311290Sgabor.dozsa@arm.com val = 1; 94411290Sgabor.dozsa@arm.com } 94511290Sgabor.dozsa@arm.com return val; 94611290Sgabor.dozsa@arm.com} 947