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