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])