/* * 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 #include #include #include #include #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 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