summaryrefslogtreecommitdiff
path: root/src/dev/multi_iface.hh
blob: 5c7834d51ae04cf4c262c5ae26106a2419964cfb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
/*
 * 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_MULTI_IFACE_HH__
#define __DEV_MULTI_IFACE_HH__

#include <array>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>

#include "dev/etherpkt.hh"
#include "dev/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