| /* |
| * Copyright (c) 2015-2016 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Gabor Dozsa |
| */ |
| |
| /* @file |
| * The interface class for dist gem5 simulations. |
| * |
| * dist-gem5 is an extension to gem5 to enable parallel simulation of a |
| * distributed system (e.g. simulation of a pool of machines |
| * connected by Ethernet links). A dist gem5 run consists of seperate gem5 |
| * processes running in parallel. Each gem5 process executes |
| * the simulation of a component of the simulated distributed system. |
| * (An example component can be a dist-core board with an Ethernet NIC.) |
| * The DistIface class below provides services to transfer data and |
| * control messages among the gem5 processes. The main such services are |
| * as follows. |
| * |
| * 1. Send a data packet coming from a simulated Ethernet link. The packet |
| * will be transferred to (all) the target(s) gem5 processes. The send |
| * operation is always performed by the simulation thread, i.e. the gem5 |
| * thread that is processing the event queue associated with the simulated |
| * Ethernet link. |
| * |
| * 2. Spawn a receiver thread to process messages coming in from the |
| * from other gem5 processes. Each simulated Ethernet link has its own |
| * associated receiver thread. The receiver thread saves the incoming packet |
| * and schedule an appropriate receive event in the event queue. |
| * |
| * 3. Schedule a global barrier event periodically to keep the gem5 |
| * processes in sync. |
| * Periodic barrier event to keep peer gem5 processes in sync. The basic idea |
| * is that no gem5 process can go ahead further than the simulated link |
| * transmission delay to ensure that a corresponding receive event can always |
| * be scheduled for any message coming in from a peer gem5 process. |
| * |
| * |
| * |
| * This interface is an abstract class. It can work with various low level |
| * send/receive service implementations (e.g. TCP/IP, MPI,...). A TCP |
| * stream socket version is implemented in src/dev/net/tcp_iface.[hh,cc]. |
| */ |
| #ifndef __DEV_DIST_IFACE_HH__ |
| #define __DEV_DIST_IFACE_HH__ |
| |
| #include <array> |
| #include <mutex> |
| #include <queue> |
| #include <thread> |
| #include <utility> |
| |
| #include "base/logging.hh" |
| #include "dev/net/dist_packet.hh" |
| #include "dev/net/etherpkt.hh" |
| #include "sim/core.hh" |
| #include "sim/drain.hh" |
| #include "sim/global_event.hh" |
| #include "sim/serialize.hh" |
| |
| class EventManager; |
| class System; |
| class ThreadContext; |
| |
| /** |
| * The interface class to talk to peer gem5 processes. |
| */ |
| class DistIface : public Drainable, public Serializable |
| { |
| public: |
| typedef DistHeaderPkt::Header Header; |
| |
| protected: |
| typedef DistHeaderPkt::MsgType MsgType; |
| typedef DistHeaderPkt::ReqType ReqType; |
| |
| private: |
| class SyncEvent; |
| /** @class Sync |
| * This class implements global sync operations among gem5 peer processes. |
| * |
| * @note This class is used as a singleton object (shared by all DistIface |
| * objects). |
| */ |
| class Sync : public Serializable |
| { |
| protected: |
| /** |
| * The lock to protect access to the Sync object. |
| */ |
| std::mutex lock; |
| /** |
| * Condition variable for the simulation thread to wait on |
| * until all receiver threads completes the current global |
| * synchronisation. |
| */ |
| std::condition_variable cv; |
| /** |
| * Number of receiver threads that not yet completed the current global |
| * synchronisation. |
| */ |
| unsigned waitNum; |
| /** |
| * Flag is set if exit is permitted upon sync completion |
| */ |
| bool doExit; |
| /** |
| * Flag is set if taking a ckpt is permitted upon sync completion |
| */ |
| bool doCkpt; |
| /** |
| * Flag is set if sync is to stop upon sync completion |
| */ |
| bool doStopSync; |
| /** |
| * The repeat value for the next periodic sync |
| */ |
| Tick nextRepeat; |
| /** |
| * Tick for the next periodic sync (if the event is not scheduled yet) |
| */ |
| Tick nextAt; |
| /** |
| * Flag is set if the sync is aborted (e.g. due to connection lost) |
| */ |
| bool isAbort; |
| |
| friend class SyncEvent; |
| |
| public: |
| /** |
| * Initialize periodic sync params. |
| * |
| * @param start Start tick for dist synchronisation |
| * @param repeat Frequency of dist synchronisation |
| * |
| */ |
| void init(Tick start, Tick repeat); |
| /** |
| * Core method to perform a full dist sync. |
| * |
| * @return true if the sync completes, false if it gets aborted |
| */ |
| virtual bool run(bool same_tick) = 0; |
| /** |
| * Callback when the receiver thread gets a sync ack message. |
| * |
| * @return false if the receiver thread needs to stop (e.g. |
| * simulation is to exit) |
| */ |
| virtual bool progress(Tick send_tick, |
| Tick next_repeat, |
| ReqType do_ckpt, |
| ReqType do_exit, |
| ReqType do_stop_sync) = 0; |
| /** |
| * Abort processing an on-going sync event (in case of an error, e.g. |
| * lost connection to a peer gem5) |
| */ |
| void abort(); |
| |
| virtual void requestCkpt(ReqType req) = 0; |
| virtual void requestExit(ReqType req) = 0; |
| virtual void requestStopSync(ReqType req) = 0; |
| |
| void drainComplete(); |
| |
| virtual void serialize(CheckpointOut &cp) const override = 0; |
| virtual void unserialize(CheckpointIn &cp) override = 0; |
| }; |
| |
| class SyncNode: public Sync |
| { |
| private: |
| /** |
| * Exit requested |
| */ |
| ReqType needExit; |
| /** |
| * Ckpt requested |
| */ |
| ReqType needCkpt; |
| /** |
| * Sync stop requested |
| */ |
| ReqType needStopSync; |
| |
| public: |
| |
| SyncNode(); |
| ~SyncNode() {} |
| bool run(bool same_tick) override; |
| bool progress(Tick max_req_tick, |
| Tick next_repeat, |
| ReqType do_ckpt, |
| ReqType do_exit, |
| ReqType do_stop_sync) override; |
| |
| void requestCkpt(ReqType req) override; |
| void requestExit(ReqType req) override; |
| void requestStopSync(ReqType req) override; |
| |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| }; |
| |
| class SyncSwitch: public Sync |
| { |
| private: |
| /** |
| * Counter for recording exit requests |
| */ |
| unsigned numExitReq; |
| /** |
| * Counter for recording ckpt requests |
| */ |
| unsigned numCkptReq; |
| /** |
| * Counter for recording stop sync requests |
| */ |
| unsigned numStopSyncReq; |
| /** |
| * Number of connected simulated nodes |
| */ |
| unsigned numNodes; |
| |
| public: |
| SyncSwitch(int num_nodes); |
| ~SyncSwitch() {} |
| |
| bool run(bool same_tick) override; |
| bool progress(Tick max_req_tick, |
| Tick next_repeat, |
| ReqType do_ckpt, |
| ReqType do_exit, |
| ReqType do_stop_sync) override; |
| |
| void requestCkpt(ReqType) override { |
| panic("Switch requested checkpoint"); |
| } |
| void requestExit(ReqType) override { |
| panic("Switch requested exit"); |
| } |
| void requestStopSync(ReqType) override { |
| panic("Switch requested stop sync"); |
| } |
| |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| }; |
| |
| /** |
| * The global event to schedule periodic dist sync. It is used as a |
| * singleton object. |
| * |
| * The periodic synchronisation works as follows. |
| * 1. A SyncEvent is scheduled as a global event when startup() is |
| * called. |
| * 2. The process() method of the SyncEvent initiates a new barrier |
| * for each simulated Ethernet link. |
| * 3. Simulation thread(s) then waits until all receiver threads |
| * complete the ongoing barrier. The global sync event is done. |
| */ |
| class SyncEvent : public GlobalSyncEvent |
| { |
| private: |
| /** |
| * Flag to set when the system is draining |
| */ |
| bool _draining; |
| public: |
| /** |
| * Only the firstly instantiated DistIface object will |
| * call this constructor. |
| */ |
| SyncEvent() : GlobalSyncEvent(Sim_Exit_Pri, 0), _draining(false) {} |
| |
| ~SyncEvent() {} |
| /** |
| * Schedule the first periodic sync event. |
| */ |
| void start(); |
| /** |
| * This is a global event so process() will only be called by |
| * exactly one simulation thread. (See further comments in the .cc |
| * file.) |
| */ |
| void process() override; |
| |
| bool draining() const { return _draining; } |
| void draining(bool fl) { _draining = fl; } |
| }; |
| /** |
| * Class to encapsulate information about data packets received. |
| |
| * @note The main purpose of the class to take care of scheduling receive |
| * done events for the simulated network link and store incoming packets |
| * until they can be received by the simulated network link. |
| */ |
| class RecvScheduler : public Serializable |
| { |
| private: |
| /** |
| * Received packet descriptor. This information is used by the receive |
| * thread to schedule receive events and by the simulation thread to |
| * process those events. |
| */ |
| struct Desc : public Serializable |
| { |
| EthPacketPtr packet; |
| Tick sendTick; |
| Tick sendDelay; |
| |
| Desc() : sendTick(0), sendDelay(0) {} |
| Desc(EthPacketPtr p, Tick s, Tick d) : |
| packet(p), sendTick(s), sendDelay(d) {} |
| Desc(const Desc &d) : |
| packet(d.packet), sendTick(d.sendTick), sendDelay(d.sendDelay) {} |
| |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| }; |
| /** |
| * The queue to store the receive descriptors. |
| */ |
| std::queue<Desc> descQueue; |
| /** |
| * The tick when the most recent receive event was processed. |
| * |
| * @note This information is necessary to simulate possible receiver |
| * link contention when calculating the receive tick for the next |
| * incoming data packet (see the calcReceiveTick() method) |
| */ |
| Tick prevRecvTick; |
| /** |
| * The receive done event for the simulated Ethernet link. |
| * |
| * @note This object is constructed by the simulated network link. We |
| * schedule this object for each incoming data packet. |
| */ |
| Event *recvDone; |
| /** |
| * The link delay in ticks for the simulated Ethernet link. |
| * |
| * @note This value is used for calculating the receive ticks for |
| * incoming data packets. |
| */ |
| Tick linkDelay; |
| /** |
| * The event manager associated with the simulated Ethernet link. |
| * |
| * @note It is used to access the event queue for scheduling receive |
| * done events for the link. |
| */ |
| EventManager *eventManager; |
| /** |
| * Calculate the tick to schedule the next receive done event. |
| * |
| * @param send_tick The tick the packet was sent. |
| * @param send_delay The simulated delay at the sender side. |
| * @param prev_recv_tick Tick when the last receive event was |
| * processed. |
| * |
| * @note This method tries to take into account possible receiver link |
| * contention and adjust receive tick for the incoming packets |
| * accordingly. |
| */ |
| Tick calcReceiveTick(Tick send_tick, |
| Tick send_delay, |
| Tick prev_recv_tick); |
| |
| /** |
| * Flag to set if receive ticks for pending packets need to be |
| * recalculated due to changed link latencies at a resume |
| */ |
| bool ckptRestore; |
| |
| public: |
| /** |
| * Scheduler for the incoming data packets. |
| * |
| * @param em The event manager associated with the simulated Ethernet |
| * link. |
| */ |
| RecvScheduler(EventManager *em) : |
| prevRecvTick(0), recvDone(nullptr), linkDelay(0), |
| eventManager(em), ckptRestore(false) {} |
| |
| /** |
| * Initialize network link parameters. |
| * |
| * @note This method is called from the receiver thread (see |
| * recvThreadFunc()). |
| */ |
| void init(Event *recv_done, Tick link_delay); |
| /** |
| * Fetch the next packet that is to be received by the simulated network |
| * link. |
| * |
| * @note This method is called from the process() method of the receive |
| * done event associated with the network link. |
| */ |
| EthPacketPtr popPacket(); |
| /** |
| * Push a newly arrived packet into the desc queue. |
| */ |
| void pushPacket(EthPacketPtr new_packet, |
| Tick send_tick, |
| Tick send_delay); |
| |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| /** |
| * Adjust receive ticks for pending packets when restoring from a |
| * checkpoint |
| * |
| * @note Link speed and delay parameters may change at resume. |
| */ |
| void resumeRecvTicks(); |
| }; |
| /** |
| * Tick to schedule the first dist sync event. |
| * This is just as optimization : we do not need any dist sync |
| * event until the simulated NIC is brought up by the OS. |
| */ |
| Tick syncStart; |
| /** |
| * Frequency of dist sync events in ticks. |
| */ |
| Tick syncRepeat; |
| /** |
| * Receiver thread pointer. |
| * Each DistIface object must have exactly one receiver thread. |
| */ |
| std::thread *recvThread; |
| /** |
| * Meta information about data packets received. |
| */ |
| RecvScheduler recvScheduler; |
| /** |
| * Use pseudoOp to start synchronization. |
| */ |
| bool syncStartOnPseudoOp; |
| |
| protected: |
| /** |
| * The rank of this process among the gem5 peers. |
| */ |
| unsigned rank; |
| /** |
| * The number of gem5 processes comprising this dist simulation. |
| */ |
| unsigned size; |
| /** |
| * Number of DistIface objects (i.e. dist links in this gem5 process) |
| */ |
| static unsigned distIfaceNum; |
| /** |
| * Unique id for the dist link |
| */ |
| unsigned distIfaceId; |
| |
| bool isMaster; |
| |
| private: |
| /** |
| * Number of receiver threads (in this gem5 process) |
| */ |
| static unsigned recvThreadsNum; |
| /** |
| * The singleton Sync object to perform dist synchronisation. |
| */ |
| static Sync *sync; |
| /** |
| * The singleton SyncEvent object to schedule periodic dist sync. |
| */ |
| static SyncEvent *syncEvent; |
| /** |
| * The very first DistIface object created becomes the master. We need |
| * a master to co-ordinate the global synchronisation. |
| */ |
| static DistIface *master; |
| /** |
| * System pointer used to wakeup sleeping threads when stopping sync. |
| */ |
| static System *sys; |
| /** |
| * Is this node a switch? |
| */ |
| static bool isSwitch; |
| |
| private: |
| /** |
| * Send out a data packet to the remote end. |
| * @param header Meta info about the packet (which needs to be transferred |
| * to the destination alongside the packet). |
| * @param packet Pointer to the packet to send. |
| */ |
| virtual void sendPacket(const Header &header, const EthPacketPtr &packet) = 0; |
| /** |
| * Send out a control command to the remote end. |
| * @param header Meta info describing the command (e.g. sync request) |
| */ |
| virtual void sendCmd(const Header &header) = 0; |
| /** |
| * Receive a header (i.e. meta info describing a data packet or a control command) |
| * from the remote end. |
| * @param header The meta info structure to store the incoming header. |
| */ |
| virtual bool recvHeader(Header &header) = 0; |
| /** |
| * Receive a packet from the remote end. |
| * @param header Meta info about the incoming packet (obtanied by a previous |
| * call to the recvHedaer() method). |
| * @param Pointer to packet received. |
| */ |
| virtual void recvPacket(const Header &header, EthPacketPtr &packet) = 0; |
| /** |
| * Init hook for the underlaying transport |
| */ |
| virtual void initTransport() = 0; |
| /** |
| * spawn the receiver thread. |
| * @param recv_done The receive done event associated with the simulated |
| * Ethernet link. |
| * @param link_delay The link delay for the simulated Ethernet link. |
| */ |
| void spawnRecvThread(const Event *recv_done, Tick link_delay); |
| /** |
| * The function executed by a receiver thread. |
| */ |
| void recvThreadFunc(Event *recv_done, Tick link_delay); |
| |
| public: |
| |
| /** |
| * ctor |
| * @param dist_rank Rank of this gem5 process within the dist run |
| * @param sync_start Start tick for dist synchronisation |
| * @param sync_repeat Frequency for dist synchronisation |
| * @param em The event manager associated with the simulated Ethernet link |
| */ |
| DistIface(unsigned dist_rank, |
| unsigned dist_size, |
| Tick sync_start, |
| Tick sync_repeat, |
| EventManager *em, |
| bool use_pseudo_op, |
| bool is_switch, |
| int num_nodes); |
| |
| virtual ~DistIface(); |
| /** |
| * Send out an Ethernet packet. |
| * @param pkt The Ethernet packet to send. |
| * @param send_delay The delay in ticks for the send completion event. |
| */ |
| void packetOut(EthPacketPtr pkt, Tick send_delay); |
| /** |
| * Fetch the packet scheduled to be received next by the simulated |
| * network link. |
| * |
| * @note This method is called within the process() method of the link |
| * receive done event. It also schedules the next receive event if the |
| * receive queue is not empty. |
| */ |
| EthPacketPtr packetIn() { return recvScheduler.popPacket(); } |
| |
| DrainState drain() override; |
| void drainResume() override; |
| void init(const Event *e, Tick link_delay); |
| void startup(); |
| |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| /** |
| * Initiate the exit from the simulation. |
| * @param delay Delay param from the m5 exit command. If Delay is zero |
| * then a collaborative exit is requested (i.e. all nodes have to call |
| * this method before the distributed simulation can exit). If Delay is |
| * not zero then exit is requested asap (and it will happen at the next |
| * sync tick). |
| * @return False if we are in distributed mode (i.e. exit can happen only |
| * at sync), True otherwise. |
| */ |
| static bool readyToExit(Tick delay); |
| /** |
| * Initiate taking a checkpoint |
| * @param delay Delay param from the m5 checkpoint command. If Delay is |
| * zero then a collaborative checkpoint is requested (i.e. all nodes have |
| * to call this method before the checkpoint can be taken). If Delay is |
| * not zero then a checkpoint is requested asap (and it will happen at the |
| * next sync tick). |
| * @return False if we are in dist mode (i.e. exit can happen only at |
| * sync), True otherwise. |
| */ |
| static bool readyToCkpt(Tick delay, Tick period); |
| /** |
| * Getter for the dist rank param. |
| */ |
| static uint64_t rankParam(); |
| /** |
| * Getter for the dist size param. |
| */ |
| static uint64_t sizeParam(); |
| /** |
| * Trigger the master to start/stop synchronization. |
| */ |
| static void toggleSync(ThreadContext *tc); |
| }; |
| |
| #endif |