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 <power.jg@gmail.com>
This commit is contained in:
Mohammad Alian 2016-06-08 09:12:41 -05:00
parent 8d177d128f
commit e5b7b6780f
3 changed files with 228 additions and 37 deletions

View 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");
}
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<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();
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 *

View 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;
};

View file

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