| /* |
| * Copyright (c) 2015 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 multi gem5 simulations. |
| * |
| * Multi 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 multi 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 multi-core board with an Ethernet NIC.) |
| * The MultiIface 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 (sendRaw() and recvRaw() |
| * methods are pure virtual). It can work with various low level |
| * send/receive service implementations (e.g. TCP/IP, MPI,...). A TCP |
| * stream socket version is implemented in dev/src/tcp_iface.[hh,cc]. |
| */ |
| #ifndef __DEV_NET_MULTI_IFACE_HH__ |
| #define __DEV_NET_MULTI_IFACE_HH__ |
| |
| #include <array> |
| #include <mutex> |
| #include <queue> |
| #include <thread> |
| #include <utility> |
| |
| #include "dev/net/etherpkt.hh" |
| #include "dev/net/multi_packet.hh" |
| #include "sim/core.hh" |
| #include "sim/drain.hh" |
| #include "sim/global_event.hh" |
| |
| class EventManager; |
| |
| /** |
| * The interface class to talk to peer gem5 processes. |
| */ |
| class MultiIface : public Drainable |
| { |
| public: |
| /*! |
| * The possible reasons a multi sync among gem5 peers is needed for. |
| */ |
| enum |
| class SyncTrigger { |
| periodic, /*!< Regular periodic sync. This can be interrupted by a |
| checkpoint sync request */ |
| ckpt, /*!< sync before taking a checkpoint */ |
| atomic /*!< sync that cannot be interrupted (e.g. sync at startup) */ |
| }; |
| |
| private: |
| typedef MultiHeaderPkt::MsgType MsgType; |
| |
| /** Sync State-Machine |
| \dot |
| digraph Sync { |
| node [shape=box, fontsize=10]; |
| idle -> busy |
| [ label="new trigger\n by run()" fontsize=8 ]; |
| busy -> busy |
| [ label="new message by progress():\n(msg == SyncAck &&\nwaitNum > 1) || \n(msg==CkptSyncReq &&\ntrigger == ckpt)" fontsize=8 ]; |
| busy -> idle |
| [ label="new message by progress():\n(msg == SyncAck &&\nwaitNum == 1)" fontsize=8 ]; |
| busy -> interrupted |
| [ label="new message by progress():\n(msg == CkptSyncReq &&\ntrigger == periodic)" fontsize=8 ]; |
| idle -> asyncCkpt |
| [ label="new message by progress():\nmsg == CkptSyncReq" fontsize=8 ]; |
| asyncCkpt -> asyncCkpt |
| [ label="new message by progress():\nmsg == CkptSyncReq" fontsize=8 ]; |
| asyncCkpt -> busy |
| [ label="new trigger by run():\ntrigger == ckpt" fontsize=8 ]; |
| asyncCkpt -> idle |
| [ label="new trigger by run():\n(trigger == periodic &&\nwaitNum == 0) " fontsize=8 ]; |
| asyncCkpt -> interrupted |
| [ label="new trigger by run():\n(trigger == periodic &&\nwaitNum > 0) " fontsize=8 ]; |
| interrupted -> interrupted |
| [ label="new message by progress():\n(msg == CkptSyncReq &&\nwaitNum > 1)" fontsize=8 ]; |
| interrupted -> idle |
| [ label="new message by progress():\n(msg == CkptSyncReq &&\nwaitNum == 1)" fontsize=8 ]; |
| } |
| \enddot |
| */ |
| /** @class Sync |
| * This class implements global sync operations among gem5 peer processes. |
| * |
| * @note This class is used as a singleton object (shared by all MultiIface |
| * objects). |
| */ |
| class Sync |
| { |
| private: |
| /*! |
| * Internal state of the sync singleton object. |
| */ |
| enum class SyncState { |
| busy, /*!< There is an on-going sync. */ |
| interrupted, /*!< An on-going periodic sync was interrupted. */ |
| asyncCkpt, /*!< A checkpoint (sim_exit) is already scheduled */ |
| idle /*!< There is no active sync. */ |
| }; |
| /** |
| * The lock to protect access to the MultiSync 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; |
| /** |
| * The trigger for the most recent sync. |
| */ |
| SyncTrigger trigger; |
| /** |
| * Map sync triggers to request messages. |
| */ |
| std::array<MsgType, 3> triggerToMsg = {{ |
| MsgType::cmdPeriodicSyncReq, |
| MsgType::cmdCkptSyncReq, |
| MsgType::cmdAtomicSyncReq |
| }}; |
| |
| /** |
| * Current sync state. |
| */ |
| SyncState state; |
| |
| public: |
| /** |
| * Core method to perform a full multi sync. |
| * |
| * @param t Sync trigger. |
| * @param sync_tick The tick the sync was expected to happen at. |
| * @return true if the sync completed, false if it was interrupted. |
| * |
| * @note In case of an interrupted periodic sync, sync_tick can be less |
| * than curTick() when we resume (i.e. re-run) it |
| */ |
| bool run(SyncTrigger t, Tick sync_tick); |
| /** |
| * Callback when the receiver thread gets a sync message. |
| */ |
| void progress(MsgType m); |
| |
| Sync() : waitNum(0), state(SyncState::idle) {} |
| ~Sync() {} |
| }; |
| |
| |
| /** |
| * The global event to schedule peridic multi sync. It is used as a |
| * singleton object. |
| * |
| * The periodic synchronisation works as follows. |
| * 1. A MultisyncEvent is scheduled as a global event when startup() is |
| * called. |
| * 2. The progress() method of the MultisyncEvent initiates a new barrier |
| * for each simulated Ethernet links. |
| * 3. Simulation thread(s) then waits until all receiver threads |
| * completes the ongoing barrier. The global sync event is done. |
| */ |
| class SyncEvent : public GlobalSyncEvent |
| { |
| public: |
| /** |
| * Flag to indicate that the most recent periodic sync was interrupted |
| * (by a checkpoint request). |
| */ |
| bool interrupted; |
| /** |
| * The tick when the most recent periodic synchronisation was scheduled |
| * at. |
| */ |
| Tick scheduledAt; |
| /** |
| * Flag to indicate an on-going drain cycle. |
| */ |
| bool isDraining; |
| |
| public: |
| /** |
| * Only the firstly instanstiated MultiIface object will |
| * call this constructor. |
| */ |
| SyncEvent() : GlobalSyncEvent(Default_Pri, 0), interrupted(false), |
| scheduledAt(0), isDraining(false) {} |
| |
| ~SyncEvent() { assert (scheduled() == false); } |
| /** |
| * Schedule the first periodic sync event. |
| * |
| * @param start Start tick for multi synchronisation |
| * @param repeat Frequency of multi synchronisation |
| * |
| */ |
| void start(Tick start, Tick repeat); |
| /** |
| * Reschedule (if necessary) the periodic sync event. |
| * |
| * @param start Start tick for multi synchronisation |
| * @param repeat Frequency of multi synchronisation |
| * |
| * @note Useful if we have multiple MultiIface objects with |
| * different 'start' and 'repeat' values for global sync. |
| */ |
| void adjust(Tick start, Tick repeat); |
| /** |
| * This is a global event so process() will be called by each |
| * simulation threads. (See further comments in the .cc file.) |
| */ |
| void process() override; |
| /** |
| * Schedule periodic sync when resuming from a checkpoint. |
| */ |
| void resume(); |
| |
| void serialize(const std::string &base, CheckpointOut &cp) const; |
| void unserialize(const std::string &base, CheckpointIn &cp); |
| }; |
| |
| /** |
| * The receive thread needs to store the packet pointer and the computed |
| * receive tick for each incoming data packet. This information is used |
| * by the simulation thread when it processes the corresponding receive |
| * event. (See more comments at the implemetation of the recvThreadFunc() |
| * and RecvPacketIn() methods.) |
| */ |
| typedef std::pair<EthPacketPtr, Tick> RecvInfo; |
| |
| /** |
| * Comparison predicate for RecvInfo, needed by the recvQueue. |
| */ |
| struct RecvInfoCompare { |
| bool operator()(const RecvInfo &lhs, const RecvInfo &rhs) |
| { |
| return lhs.second > rhs.second; |
| } |
| }; |
| |
| /** |
| * Customized priority queue used to store incoming data packets info by |
| * the receiver thread. We need to expose the underlying container to |
| * enable iterator access for serializing. |
| */ |
| class RecvQueue : public std::priority_queue<RecvInfo, |
| std::vector<RecvInfo>, |
| RecvInfoCompare> |
| { |
| public: |
| std::vector<RecvInfo> &impl() { return c; } |
| const std::vector<RecvInfo> &impl() const { return c; } |
| }; |
| |
| /* |
| * The priority queue to store RecvInfo items ordered by receive ticks. |
| */ |
| RecvQueue recvQueue; |
| /** |
| * The singleton Sync object to perform multi synchronisation. |
| */ |
| static Sync *sync; |
| /** |
| * The singleton SyncEvent object to schedule periodic multi sync. |
| */ |
| static SyncEvent *syncEvent; |
| /** |
| * Tick to schedule the first multi sync event. |
| * This is just as optimization : we do not need any multi sync |
| * event until the simulated NIC is brought up by the OS. |
| */ |
| Tick syncStart; |
| /** |
| * Frequency of multi sync events in ticks. |
| */ |
| Tick syncRepeat; |
| /** |
| * Receiver thread pointer. |
| * Each MultiIface object must have exactly one receiver thread. |
| */ |
| std::thread *recvThread; |
| /** |
| * The event manager associated with the MultiIface object. |
| */ |
| EventManager *eventManager; |
| |
| /** |
| * The receive done event for the simulated Ethernet link. |
| * It is scheduled by the receiver thread for each incoming data |
| * packet. |
| */ |
| Event *recvDone; |
| |
| /** |
| * The packet that belongs to the currently scheduled recvDone event. |
| */ |
| EthPacketPtr scheduledRecvPacket; |
| |
| /** |
| * The link delay in ticks for the simulated Ethernet link. |
| */ |
| Tick linkDelay; |
| |
| /** |
| * The rank of this process among the gem5 peers. |
| */ |
| unsigned rank; |
| /** |
| * Total number of receiver threads (in this gem5 process). |
| * During the simulation it should be constant and equal to the |
| * number of MultiIface objects (i.e. simulated Ethernet |
| * links). |
| */ |
| static unsigned recvThreadsNum; |
| /** |
| * The very first MultiIface object created becomes the master. We need |
| * a master to co-ordinate the global synchronisation. |
| */ |
| static MultiIface *master; |
| |
| protected: |
| /** |
| * Low level generic send routine. |
| * @param buf buffer that holds the data to send out |
| * @param length number of bytes to send |
| * @param dest_addr address of the target (simulated NIC). This may be |
| * used by a subclass for optimization (e.g. optimize broadcast) |
| */ |
| virtual void sendRaw(void *buf, |
| unsigned length, |
| const MultiHeaderPkt::AddressType dest_addr) = 0; |
| /** |
| * Low level generic receive routine. |
| * @param buf the buffer to store the incoming message |
| * @param length buffer size (in bytes) |
| */ |
| virtual bool recvRaw(void *buf, unsigned length) = 0; |
| /** |
| * Low level request for synchronisation among gem5 processes. Only one |
| * MultiIface object needs to call this (in each gem5 process) to trigger |
| * a multi sync. |
| * |
| * @param sync_req Sync request command. |
| * @param sync_tick The tick when sync is expected to happen in the sender. |
| */ |
| virtual void syncRaw(MsgType sync_req, Tick sync_tick) = 0; |
| /** |
| * The function executed by a receiver thread. |
| */ |
| void recvThreadFunc(); |
| /** |
| * Receive a multi header packet. Called by the receiver thread. |
| * @param header the structure to store the incoming header packet. |
| * @return false if any error occured during the receive, true otherwise |
| * |
| * A header packet can carry a control command (e.g. 'barrier leave') or |
| * information about a data packet that is following the header packet |
| * back to back. |
| */ |
| bool recvHeader(MultiHeaderPkt::Header &header); |
| /** |
| * Receive a data packet. Called by the receiver thread. |
| * @param data_header The packet descriptor for the expected incoming data |
| * packet. |
| */ |
| void recvData(const MultiHeaderPkt::Header &data_header); |
| |
| public: |
| |
| /** |
| * ctor |
| * @param multi_rank Rank of this gem5 process within the multi run |
| * @param sync_start Start tick for multi synchronisation |
| * @param sync_repeat Frequency for multi synchronisation |
| * @param em The event manager associated with the simulated Ethernet link |
| */ |
| MultiIface(unsigned multi_rank, |
| Tick sync_start, |
| Tick sync_repeat, |
| EventManager *em); |
| |
| virtual ~MultiIface(); |
| /** |
| * 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 next packet from the receive queue. |
| */ |
| EthPacketPtr packetIn(); |
| |
| /** |
| * 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(Event *recv_done, |
| Tick link_delay); |
| /** |
| * Initialize the random number generator with a different seed in each |
| * peer gem5 process. |
| */ |
| void initRandom(); |
| |
| DrainState drain() override; |
| |
| /** |
| * Callback when draining is complete. |
| */ |
| void drainDone(); |
| |
| /** |
| * Initialize the periodic synchronisation among peer gem5 processes. |
| */ |
| void startPeriodicSync(); |
| |
| void serialize(const std::string &base, CheckpointOut &cp) const; |
| void unserialize(const std::string &base, CheckpointIn &cp); |
| |
| }; |
| |
| |
| #endif // __DEV_NET_MULTI_IFACE_HH__ |