From e5b7b6780f9748b6f13ef91e3e22d53ebdf47968 Mon Sep 17 00:00:00 2001 From: Mohammad Alian Date: Wed, 8 Jun 2016 09:12:41 -0500 Subject: [PATCH] dist, dev: Fixed the packet ordering in etherswitch 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 --- src/dev/net/etherswitch.cc | 158 ++++++++++++++++++++++++------ src/dev/net/etherswitch.hh | 86 ++++++++++++++-- util/cpt_upgraders/etherswitch.py | 21 ++++ 3 files changed, 228 insertions(+), 37 deletions(-) create mode 100644 util/cpt_upgraders/etherswitch.py diff --git a/src/dev/net/etherswitch.cc b/src/dev/net/etherswitch.cc index 02bbff65d..0564ee594 100644 --- a/src/dev/net/etherswitch.cc +++ b/src/dev/net/etherswitch.cc @@ -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"); +} - outputFifo.unserialize(base + "outputFifo", cp); +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(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(); + + 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 * diff --git a/src/dev/net/etherswitch.hh b/src/dev/net/etherswitch.hh index 760371777..debe33194 100644 --- a/src/dev/net/etherswitch.hh +++ b/src/dev/net/etherswitch.hh @@ -36,7 +36,8 @@ #ifndef __DEV_ETHERSWITCH_HH__ #define __DEV_ETHERSWITCH_HH__ -#include +#include +#include #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 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 txEvent; }; diff --git a/util/cpt_upgraders/etherswitch.py b/util/cpt_upgraders/etherswitch.py new file mode 100644 index 000000000..e4094f97d --- /dev/null +++ b/util/cpt_upgraders/etherswitch.py @@ -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])