dist, dev: add an ethernet switch model
This commit is contained in:
parent
ce35c06c6e
commit
24da47cf96
4 changed files with 392 additions and 0 deletions
|
@ -82,6 +82,18 @@ class EtherBus(EtherObject):
|
|||
dump = Param.EtherDump(NULL, "dump object")
|
||||
speed = Param.NetworkBandwidth('100Mbps', "bus speed in bits per second")
|
||||
|
||||
class EtherSwitch(EtherObject):
|
||||
type = 'EtherSwitch'
|
||||
cxx_header = "dev/net/etherswitch.hh"
|
||||
dump = Param.EtherDump(NULL, "dump object")
|
||||
fabric_speed = Param.NetworkBandwidth('10Gbps', "switch fabric speed in bits "
|
||||
"per second")
|
||||
interface = VectorMasterPort("Ethernet Interface")
|
||||
output_buffer_size = Param.MemorySize('1MB', "size of output port buffers")
|
||||
delay = Param.Latency('0us', "packet transmit delay")
|
||||
delay_var = Param.Latency('0ns', "packet transmit delay variability")
|
||||
time_to_live = Param.Latency('10ms', "time to live of MAC address maping")
|
||||
|
||||
class EtherTap(EtherObject):
|
||||
type = 'EtherTap'
|
||||
cxx_header = "dev/net/ethertap.hh"
|
||||
|
|
|
@ -51,6 +51,7 @@ SimObject('Ethernet.py')
|
|||
|
||||
# Basic Ethernet infrastructure
|
||||
Source('etherbus.cc')
|
||||
Source('etherswitch.cc')
|
||||
Source('etherdevice.cc')
|
||||
Source('etherdump.cc')
|
||||
Source('etherint.cc')
|
||||
|
|
256
src/dev/net/etherswitch.cc
Normal file
256
src/dev/net/etherswitch.cc
Normal file
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The Regents of The University of Michigan
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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: Anthony Gutierrez
|
||||
* Mohammad Alian
|
||||
*/
|
||||
|
||||
/* @file
|
||||
* Device model for an ethernet switch
|
||||
*/
|
||||
|
||||
#include "dev/net/etherswitch.hh"
|
||||
|
||||
#include "base/random.hh"
|
||||
#include "debug/EthernetAll.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
EtherSwitch::EtherSwitch(const Params *p)
|
||||
: EtherObject(p), ttl(p->time_to_live)
|
||||
{
|
||||
for (int i = 0; i < p->port_interface_connection_count; ++i) {
|
||||
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);
|
||||
interfaces.push_back(interface);
|
||||
}
|
||||
}
|
||||
|
||||
EtherSwitch::~EtherSwitch()
|
||||
{
|
||||
for (auto it : interfaces)
|
||||
delete it;
|
||||
|
||||
interfaces.clear();
|
||||
}
|
||||
|
||||
EtherInt*
|
||||
EtherSwitch::getEthPort(const std::string &if_name, int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= interfaces.size())
|
||||
return nullptr;
|
||||
|
||||
Interface *interface = interfaces.at(idx);
|
||||
panic_if(interface->getPeer(), "interface already connected\n");
|
||||
|
||||
return interface;
|
||||
}
|
||||
|
||||
EtherSwitch::Interface::Interface(const std::string &name,
|
||||
EtherSwitch *etherSwitch,
|
||||
uint64_t outputBufferSize, Tick delay,
|
||||
Tick delay_var, double rate)
|
||||
: EtherInt(name), ticksPerByte(rate), switchDelay(delay),
|
||||
delayVar(delay_var), parent(etherSwitch),
|
||||
outputFifo(outputBufferSize), txEvent(this)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
|
||||
{
|
||||
Net::EthAddr destMacAddr(packet->data);
|
||||
Net::EthAddr srcMacAddr(&packet->data[6]);
|
||||
|
||||
learnSenderAddr(srcMacAddr, this);
|
||||
Interface *receiver = lookupDestPort(destMacAddr);
|
||||
|
||||
if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
|
||||
for (auto it : parent->interfaces)
|
||||
if (it != this)
|
||||
it->enqueue(packet);
|
||||
} 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);
|
||||
}
|
||||
// At the output port, we either have buffer space (no drop) or
|
||||
// don't (drop packet); in both cases packet is received on
|
||||
// the interface successfully and there is no notion of busy
|
||||
// interface here (as we don't have inputFifo)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::Interface::enqueue(EthPacketPtr packet)
|
||||
{
|
||||
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
|
||||
// otherwise, there is already a txEvent scheduled
|
||||
if (!txEvent.scheduled()) {
|
||||
parent->schedule(txEvent, curTick() + switchingDelay());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::Interface::transmit()
|
||||
{
|
||||
// there should be something in the output queue
|
||||
assert(!outputFifo.empty());
|
||||
|
||||
if (!sendPacket(outputFifo.front())) {
|
||||
DPRINTF(Ethernet, "output port busy...retry later\n");
|
||||
if (!txEvent.scheduled())
|
||||
parent->schedule(txEvent, curTick() + retryTime);
|
||||
} else {
|
||||
DPRINTF(Ethernet, "packet sent: len=%d\n", outputFifo.front()->length);
|
||||
outputFifo.pop();
|
||||
// schedule an event to send the pkt at
|
||||
// the head of queue, if there is any
|
||||
if (!outputFifo.empty()) {
|
||||
parent->schedule(txEvent, curTick() + switchingDelay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tick
|
||||
EtherSwitch::Interface::switchingDelay()
|
||||
{
|
||||
Tick delay = (Tick)ceil(((double)outputFifo.front()->length
|
||||
* ticksPerByte) + 1.0);
|
||||
if (delayVar != 0)
|
||||
delay += random_mt.random<Tick>(0, delayVar);
|
||||
delay += switchDelay;
|
||||
return delay;
|
||||
}
|
||||
|
||||
EtherSwitch::Interface*
|
||||
EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr)
|
||||
{
|
||||
auto it = parent->forwardingTable.find(uint64_t(destMacAddr));
|
||||
|
||||
if (it == parent->forwardingTable.end()) {
|
||||
DPRINTF(Ethernet, "no entry in forwaring table for MAC: "
|
||||
"%x\n", uint64_t(destMacAddr));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// check if this entry is valid based on TTL and lastUseTime
|
||||
if ((curTick() - it->second.lastUseTime) > parent->ttl) {
|
||||
// TTL for this mapping has been expired, so this item is not
|
||||
// valide anymore, let's remove it from the map
|
||||
parent->forwardingTable.erase(it);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DPRINTF(Ethernet, "found entry for MAC address %x on port %s\n",
|
||||
uint64_t(destMacAddr), it->second.interface->name());
|
||||
return it->second.interface;
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr,
|
||||
Interface *sender)
|
||||
{
|
||||
// learn the port for the sending MAC address
|
||||
auto it = parent->forwardingTable.find(uint64_t(srcMacAddr));
|
||||
|
||||
// if the port for sender's MAC address is not cached,
|
||||
// cache it now, otherwise just update lastUseTime time
|
||||
if (it == parent->forwardingTable.end()) {
|
||||
DPRINTF(Ethernet, "adding forwarding table entry for MAC "
|
||||
" address %x on port %s\n", uint64_t(srcMacAddr),
|
||||
sender->name());
|
||||
EtherSwitch::SwitchTableEntry forwardingTableEntry;
|
||||
forwardingTableEntry.interface = sender;
|
||||
forwardingTableEntry.lastUseTime = curTick();
|
||||
parent->forwardingTable.insert(std::make_pair(uint64_t(srcMacAddr),
|
||||
forwardingTableEntry));
|
||||
} else {
|
||||
it->second.lastUseTime = curTick();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::serialize(CheckpointOut &cp) const
|
||||
{
|
||||
for (auto it : interfaces)
|
||||
it->serialize(it->name(), cp);
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::unserialize(CheckpointIn &cp)
|
||||
{
|
||||
for (auto it : interfaces)
|
||||
it->unserialize(it->name(), cp);
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::Interface::serialize(const std::string &base, CheckpointOut &cp)
|
||||
const
|
||||
{
|
||||
bool event_scheduled = txEvent.scheduled();
|
||||
paramOut(cp, base + ".event_scheduled", event_scheduled);
|
||||
if (event_scheduled) {
|
||||
Tick event_time = txEvent.when();
|
||||
paramOut(cp, base + ".event_time", event_time);
|
||||
}
|
||||
|
||||
outputFifo.serialize(base + "outputFifo", cp);
|
||||
}
|
||||
|
||||
void
|
||||
EtherSwitch::Interface::unserialize(const std::string &base, CheckpointIn &cp)
|
||||
{
|
||||
bool event_scheduled;
|
||||
paramIn(cp, base + ".event_scheduled", event_scheduled);
|
||||
if (event_scheduled) {
|
||||
Tick event_time;
|
||||
paramIn(cp, base + ".event_time", event_time);
|
||||
parent->schedule(txEvent, event_time);
|
||||
}
|
||||
|
||||
outputFifo.unserialize(base + "outputFifo", cp);
|
||||
}
|
||||
|
||||
EtherSwitch *
|
||||
EtherSwitchParams::create()
|
||||
{
|
||||
return new EtherSwitch(this);
|
||||
}
|
123
src/dev/net/etherswitch.hh
Normal file
123
src/dev/net/etherswitch.hh
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The Regents of The University of Michigan
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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: Anthony Gutierrez
|
||||
* Mohammad Alian
|
||||
*/
|
||||
|
||||
/* @file
|
||||
* Device model for an ethernet switch
|
||||
*/
|
||||
|
||||
#ifndef __DEV_ETHERSWITCH_HH__
|
||||
#define __DEV_ETHERSWITCH_HH__
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/inet.hh"
|
||||
#include "dev/net/etherint.hh"
|
||||
#include "dev/net/etherlink.hh"
|
||||
#include "dev/net/etherobject.hh"
|
||||
#include "dev/net/etherpkt.hh"
|
||||
#include "dev/net/pktfifo.hh"
|
||||
#include "params/EtherSwitch.hh"
|
||||
#include "sim/eventq.hh"
|
||||
|
||||
class EtherSwitch : public EtherObject
|
||||
{
|
||||
public:
|
||||
typedef EtherSwitchParams Params;
|
||||
|
||||
EtherSwitch(const Params *p);
|
||||
~EtherSwitch();
|
||||
|
||||
const Params * params() const
|
||||
{
|
||||
return dynamic_cast<const Params*>(_params);
|
||||
}
|
||||
|
||||
EtherInt *getEthPort(const std::string &if_name, int idx);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Model for an Ethernet switch port
|
||||
*/
|
||||
class Interface : public EtherInt
|
||||
{
|
||||
public:
|
||||
Interface(const std::string &name, EtherSwitch *_etherSwitch,
|
||||
uint64_t outputBufferSize, Tick delay, Tick delay_var,
|
||||
double rate);
|
||||
/**
|
||||
* When a packet is received from a device, route it
|
||||
* through an (several) output queue(s)
|
||||
*/
|
||||
bool recvPacket(EthPacketPtr packet);
|
||||
/**
|
||||
* enqueue packet to the outputFifo
|
||||
*/
|
||||
void enqueue(EthPacketPtr packet);
|
||||
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);
|
||||
|
||||
private:
|
||||
const double ticksPerByte;
|
||||
const Tick switchDelay;
|
||||
const Tick delayVar;
|
||||
EtherSwitch *parent;
|
||||
/**
|
||||
* output fifo at each interface
|
||||
*/
|
||||
PacketFifo outputFifo;
|
||||
void transmit();
|
||||
EventWrapper<Interface, &Interface::transmit> txEvent;
|
||||
};
|
||||
|
||||
struct SwitchTableEntry {
|
||||
Interface *interface;
|
||||
Tick lastUseTime;
|
||||
};
|
||||
|
||||
private:
|
||||
// time to live for MAC address mappings
|
||||
const double ttl;
|
||||
// all interfaces of the switch
|
||||
std::vector<Interface*> interfaces;
|
||||
// table that maps MAC address to interfaces
|
||||
std::map<uint64_t, SwitchTableEntry> forwardingTable;
|
||||
|
||||
void serialize(CheckpointOut &cp) const override;
|
||||
void unserialize(CheckpointIn &cp) override;
|
||||
};
|
||||
|
||||
#endif // __DEV_ETHERSWITCH_HH__
|
Loading…
Reference in a new issue