dist, dev: Fixed the packet ordering in etherswitch
authorMohammad Alian <m.alian1369@gmail.com>
Wed, 8 Jun 2016 14:12:41 +0000 (09:12 -0500)
committerMohammad Alian <m.alian1369@gmail.com>
Wed, 8 Jun 2016 14:12:41 +0000 (09:12 -0500)
This patch fixes the order that packets gets pushed into the output fifo
of etherswitch. If two packets arrive at the same tick to the etherswitch,
we sort and push them based on their source port id.
In dist-gem5 simulations, if there is no ordering inforced while two
packets arrive at the same tick, it can lead to non-deterministic simulations

Committed by Jason Lowe-Power <power.jg@gmail.com>

src/dev/net/etherswitch.cc
src/dev/net/etherswitch.hh
util/cpt_upgraders/etherswitch.py [new file with mode: 0644]

index 02bbff65d1a49bf2606ce3da0a651dc6ccc20fd6..0564ee594265f0a89a054f912ba8bc45de452b7c 100644 (file)
@@ -47,7 +47,7 @@ EtherSwitch::EtherSwitch(const Params *p)
         std::string interfaceName = csprintf("%s.interface%d", name(), i);
         Interface *interface = new Interface(interfaceName, this,
                                         p->output_buffer_size, p->delay,
-                                        p->delay_var, p->fabric_speed);
+                                        p->delay_var, p->fabric_speed, i);
         interfaces.push_back(interface);
     }
 }
@@ -72,13 +72,64 @@ EtherSwitch::getEthPort(const std::string &if_name, int idx)
     return interface;
 }
 
+bool
+EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr, unsigned senderId)
+{
+    assert(ptr->length);
+
+    _size += ptr->length;
+    fifo.emplace_hint(fifo.end(), ptr, curTick(), senderId);
+
+    // Drop the extra pushed packets from end of the fifo
+    while (avail() < 0) {
+        DPRINTF(Ethernet, "Fifo is full. Drop packet: len=%d\n",
+                std::prev(fifo.end())->packet->length);
+
+        _size -= std::prev(fifo.end())->packet->length;
+        fifo.erase(std::prev(fifo.end()));
+    }
+
+    if (empty()) {
+        warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
+             "capacity of port fifo (%d)", ptr->length, _maxsize);
+    }
+
+    // Return true if the newly pushed packet gets inserted
+    // at the head of the queue, otherwise return false
+    // We need this information to deschedule the event that has been
+    // scheduled for the old head of queue packet and schedule a new one
+    if (!empty() && fifo.begin()->packet == ptr) {
+        return true;
+    }
+    return false;
+}
+
+void
+EtherSwitch::Interface::PortFifo::pop()
+{
+    if (empty())
+        return;
+
+    assert(_size >= fifo.begin()->packet->length);
+    // Erase the packet at the head of the queue
+    _size -= fifo.begin()->packet->length;
+    fifo.erase(fifo.begin());
+}
+
+void
+EtherSwitch::Interface::PortFifo::clear()
+{
+    fifo.clear();
+    _size = 0;
+}
+
 EtherSwitch::Interface::Interface(const std::string &name,
                                   EtherSwitch *etherSwitch,
                                   uint64_t outputBufferSize, Tick delay,
-                                  Tick delay_var, double rate)
+                                  Tick delay_var, double rate, unsigned id)
     : EtherInt(name), ticksPerByte(rate), switchDelay(delay),
-      delayVar(delay_var), parent(etherSwitch),
-      outputFifo(outputBufferSize), txEvent(this)
+      delayVar(delay_var), interfaceId(id), parent(etherSwitch),
+      outputFifo(name + ".outputFifo", outputBufferSize), txEvent(this)
 {
 }
 
@@ -94,13 +145,13 @@ EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
     if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
         for (auto it : parent->interfaces)
             if (it != this)
-                it->enqueue(packet);
+                it->enqueue(packet, interfaceId);
     } else {
         DPRINTF(Ethernet, "sending packet from MAC %x on port "
                 "%s to MAC %x on port %s\n", uint64_t(srcMacAddr),
                 this->name(), uint64_t(destMacAddr), receiver->name());
 
-        receiver->enqueue(packet);
+        receiver->enqueue(packet, interfaceId);
     }
     // At the output port, we either have buffer space (no drop) or
     // don't (drop packet); in both cases packet is received on
@@ -110,21 +161,18 @@ EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
 }
 
 void
-EtherSwitch::Interface::enqueue(EthPacketPtr packet)
+EtherSwitch::Interface::enqueue(EthPacketPtr packet, unsigned senderId)
 {
-    if (!outputFifo.push(packet)) {
-        // output buffer full, drop packet
-        DPRINTF(Ethernet, "output buffer full, drop packet\n");
-        return;
-    }
-
     // assuming per-interface transmission events,
-    // if there was nothing in the Fifo before push the
-    // current packet, then we need to schedule an event at
-    // curTick + switchingDelay to send this packet out the external link
+    // if the newly push packet gets inserted at the head of the queue
+    // (either there was nothing in the queue or the priority of the new
+    // packet was higher than the packets already in the fifo)
+    // then we need to schedule an event at
+    // "curTick" + "switchingDelay of the packet at the head of the fifo"
+    // to send this packet out the external link
     // otherwise, there is already a txEvent scheduled
-    if (!txEvent.scheduled()) {
-        parent->schedule(txEvent, curTick() + switchingDelay());
+    if (outputFifo.push(packet, senderId)) {
+        parent->reschedule(txEvent, curTick() + switchingDelay());
     }
 }
 
@@ -211,42 +259,92 @@ void
 EtherSwitch::serialize(CheckpointOut &cp) const
 {
     for (auto it : interfaces)
-        it->serialize(it->name(), cp);
+        it->serializeSection(cp, it->name());
+
 }
 
 void
 EtherSwitch::unserialize(CheckpointIn &cp)
 {
     for (auto it : interfaces)
-        it->unserialize(it->name(), cp);
+        it->unserializeSection(cp, it->name());
+
 }
 
 void
-EtherSwitch::Interface::serialize(const std::string &base, CheckpointOut &cp)
-const
+EtherSwitch::Interface::serialize(CheckpointOut &cp) const
 {
     bool event_scheduled = txEvent.scheduled();
-    paramOut(cp, base + ".event_scheduled", event_scheduled);
+    SERIALIZE_SCALAR(event_scheduled);
+
     if (event_scheduled) {
         Tick event_time = txEvent.when();
-        paramOut(cp, base + ".event_time", event_time);
+        SERIALIZE_SCALAR(event_time);
     }
-
-    outputFifo.serialize(base + "outputFifo", cp);
+    outputFifo.serializeSection(cp, "outputFifo");
 }
 
 void
-EtherSwitch::Interface::unserialize(const std::string &base, CheckpointIn &cp)
+EtherSwitch::Interface::unserialize(CheckpointIn &cp)
 {
     bool event_scheduled;
-    paramIn(cp, base + ".event_scheduled", event_scheduled);
+    UNSERIALIZE_SCALAR(event_scheduled);
+
     if (event_scheduled) {
         Tick event_time;
-        paramIn(cp, base + ".event_time", event_time);
+        UNSERIALIZE_SCALAR(event_time);
         parent->schedule(txEvent, event_time);
     }
+    outputFifo.unserializeSection(cp, "outputFifo");
+}
+
+void
+EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const
+{
+    packet->serialize("packet", cp);
+    SERIALIZE_SCALAR(recvTick);
+    SERIALIZE_SCALAR(srcId);
+}
+
+void
+EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn &cp)
+{
+    packet = make_shared<EthPacketData>(16384);
+    packet->unserialize("packet", cp);
+    UNSERIALIZE_SCALAR(recvTick);
+    UNSERIALIZE_SCALAR(srcId);
+}
+
+void
+EtherSwitch::Interface::PortFifo::serialize(CheckpointOut &cp) const
+{
+    SERIALIZE_SCALAR(_size);
+    int fifosize = fifo.size();
 
-    outputFifo.unserialize(base + "outputFifo", cp);
+    SERIALIZE_SCALAR(fifosize);
+
+    int i = 0;
+    for (const auto &entry : fifo)
+        entry.serializeSection(cp, csprintf("entry%d", i++));
+}
+
+void
+EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn &cp)
+{
+    UNSERIALIZE_SCALAR(_size);
+    int fifosize;
+
+    UNSERIALIZE_SCALAR(fifosize);
+    fifo.clear();
+
+    for (int i = 0; i < fifosize; ++i) {
+        PortFifoEntry entry(nullptr, 0, 0);
+
+        entry.unserializeSection(cp, csprintf("entry%d", i));
+
+        fifo.insert(entry);
+
+    }
 }
 
 EtherSwitch *
index 7603717779d91d032848bdb851a94c601766621c..debe3319499e0bfe80d2eb32f859e13219adde21 100644 (file)
@@ -36,7 +36,8 @@
 #ifndef __DEV_ETHERSWITCH_HH__
 #define __DEV_ETHERSWITCH_HH__
 
-#include <unordered_map>
+#include <map>
+#include <set>
 
 #include "base/inet.hh"
 #include "dev/net/etherint.hh"
@@ -66,12 +67,12 @@ class EtherSwitch : public EtherObject
     /**
      * Model for an Ethernet switch port
      */
-    class Interface : public EtherInt
+    class Interface : public EtherInt, public Serializable
     {
       public:
         Interface(const std::string &name, EtherSwitch *_etherSwitch,
                   uint64_t outputBufferSize, Tick delay, Tick delay_var,
-                  double rate);
+                  double rate, unsigned id);
         /**
          * When a packet is received from a device, route it
          * through an (several) output queue(s)
@@ -80,25 +81,96 @@ class EtherSwitch : public EtherObject
         /**
          * enqueue packet to the outputFifo
          */
-        void enqueue(EthPacketPtr packet);
+        void enqueue(EthPacketPtr packet, unsigned senderId);
         void sendDone() {}
         Tick switchingDelay();
 
         Interface* lookupDestPort(Net::EthAddr destAddr);
         void learnSenderAddr(Net::EthAddr srcMacAddr, Interface *sender);
 
-        void serialize(const std::string &base, CheckpointOut &cp) const;
-        void unserialize(const std::string &base, CheckpointIn &cp);
+        void serialize(CheckpointOut &cp) const;
+        void unserialize(CheckpointIn &cp);
 
       private:
         const double ticksPerByte;
         const Tick switchDelay;
         const Tick delayVar;
+        const unsigned interfaceId;
+
         EtherSwitch *parent;
+      protected:
+        struct PortFifoEntry : public Serializable
+        {
+            PortFifoEntry(EthPacketPtr pkt, Tick recv_tick, unsigned id)
+                : packet(pkt), recvTick(recv_tick), srcId(id) {}
+
+            EthPacketPtr packet;
+            Tick recvTick;
+            // id of the port that the packet has been received from
+            unsigned srcId;
+            ~PortFifoEntry()
+            {
+                packet = nullptr;
+                recvTick = 0;
+                srcId = 0;
+            }
+            void serialize(CheckpointOut &cp) const;
+            void unserialize(CheckpointIn &cp);
+        };
+
+        class PortFifo : public Serializable
+        {
+          protected:
+            struct EntryOrder {
+                bool operator() (const PortFifoEntry& lhs,
+                                 const PortFifoEntry& rhs) const
+                {
+                    if (lhs.recvTick == rhs.recvTick)
+                        return lhs.srcId < rhs.srcId;
+                    else
+                        return lhs.recvTick < rhs.recvTick;
+                }
+            };
+            std::set<PortFifoEntry, EntryOrder> fifo;
+
+            const std::string objName;
+            const unsigned _maxsize;
+            unsigned _size;
+
+          public:
+            PortFifo(const std::string &name, int max)
+                :objName(name), _maxsize(max), _size(0) {}
+            ~PortFifo() {}
+
+            const std::string name() { return objName; }
+            // Returns the available capacity of the fifo.
+            // It can return a negative value because in "push" function
+            // we first push the received packet into the fifo and then
+            // check if we exceed the available capacity (if avail() < 0)
+            // and remove packets from the end of fifo
+            int avail() const { return _maxsize - _size; }
+
+            EthPacketPtr front() { return fifo.begin()->packet; }
+            bool empty() const { return _size == 0; }
+            unsigned size() const { return _size; }
+
+            /**
+             * Push a packet into the fifo
+             * and sort the packets with same recv tick by port id
+             */
+            bool push(EthPacketPtr ptr, unsigned senderId);
+            void pop();
+            void clear();
+            /**
+             * Serialization stuff
+             */
+            void serialize(CheckpointOut &cp) const;
+            void unserialize(CheckpointIn &cp);
+        };
         /**
          * output fifo at each interface
          */
-        PacketFifo outputFifo;
+        PortFifo outputFifo;
         void transmit();
         EventWrapper<Interface, &Interface::transmit> txEvent;
     };
diff --git a/util/cpt_upgraders/etherswitch.py b/util/cpt_upgraders/etherswitch.py
new file mode 100644 (file)
index 0000000..e4094f9
--- /dev/null
@@ -0,0 +1,21 @@
+def upgrader(cpt):
+    for sec in cpt.sections():
+        if sec == "system":
+            options = cpt.items(sec)
+            for it in options:
+                opt_split = it[0].split('.')
+                new_sec_name = opt_split[1]
+                old_opt_name = opt_split[len(opt_split) - 1]
+                if "outputFifo" in new_sec_name:
+                    new_sec_name = new_sec_name.rstrip("outputFifo")
+                    new_sec_name += ".outputFifo"
+                new_sec_name = "system.system.%s" %(new_sec_name)
+                if not cpt.has_section(new_sec_name):
+                    cpt.add_section(new_sec_name)
+                if old_opt_name == "size":
+                    cpt.set(new_sec_name, "_size", it[1])
+                elif old_opt_name == "packets":
+                    cpt.set(new_sec_name, "fifosize", it[1])
+                else:
+                    cpt.set(new_sec_name, old_opt_name, it[1])
+                cpt.remove_option(sec, it[0])