diff -r 9d2364203316 -r ed7527fcc338 src/dev/net/Ethernet.py --- a/src/dev/net/Ethernet.py Thu Jan 07 16:33:47 2016 -0600 +++ b/src/dev/net/Ethernet.py Sun Jan 10 23:57:37 2016 -0600 @@ -82,6 +82,19 @@ 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', "fabric links speed in bits " + "per second") + interface = VectorMasterPort("Ethernet Interface") + input_buffer_size = Param.MemorySize('1MB', "size of input port buffers") + 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") + clock_period = Param.Latency('2ns', "switch clock period") + class EtherTap(EtherObject): type = 'EtherTap' cxx_header = "dev/net/ethertap.hh" diff -r 9d2364203316 -r ed7527fcc338 src/dev/net/SConscript --- a/src/dev/net/SConscript Thu Jan 07 16:33:47 2016 -0600 +++ b/src/dev/net/SConscript Sun Jan 10 23:57:37 2016 -0600 @@ -51,6 +51,7 @@ # Basic Ethernet infrastructure Source('etherbus.cc') +Source('etherswitch.cc') Source('etherdevice.cc') Source('etherdump.cc') Source('etherint.cc') diff -r 9d2364203316 -r ed7527fcc338 src/dev/net/etherswitch.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dev/net/etherswitch.hh Sun Jan 10 23:57:37 2016 -0600 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2014 The Regents of The University of Michigan + * All rights reserved. + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Copyright (c) 2015 University of Illinois Urbana Champaign + * 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 + +#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(_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, + int inputBufferSize, int outputBufferSize); + /** + * Once a packet makes it through a fabric link, + * put it in an output queue + */ + bool deliverPacket(EthPacketPtr packet); + /** + * When a packet is received from a device just store + * it in an input queue until it can be forwarded + */ + bool recvPacket(EthPacketPtr packet); + /** + * This is where we actually send a packet to the receiving device + */ + void sendQueuedPacket(); + EthPacketPtr getInputPacket(); + void popInputPacket() { inputFifo.pop(); } + /** + * Gets called when a packet was successfully sent to output port + */ + void sendDone(); + bool isBusy() { return inputFifo.full(); } + bool inputFifoEmpty() const { return inputFifo.empty(); } + bool outputFifoEmpty() const { return outputFifo.empty(); } + bool broadcasting() const { return numBroadcasting; } + std::vector& getBroadcastPeers() { return broadcastPeers; } + void incNumBroadcasting() { ++numBroadcasting; } + void serialize(const std::string &base, CheckpointOut &cp) const; + void unserialize(const std::string &base, CheckpointIn &cp); + + private: + // how many ports need to receive a packet on a broadcast + int numBroadcasting; + // vector of interfaces which need to receive a packet on a broadcast + std::vector broadcastPeers; + EtherSwitch ðerSwitch; + PacketFifo inputFifo; + PacketFifo outputFifo; + }; + + class FabricLink + { + protected: + const std::string objName; + + EtherSwitch *parent; + const int number; + + EtherSwitch::Interface *txint; + EtherSwitch::Interface *rxint; + + const double ticksPerByte; + const Tick linkDelay; + const Tick delayVar; + + protected: + EthPacketPtr packet; + void txDone(); + typedef EventWrapper DoneEvent; + friend void DoneEvent::process(); + DoneEvent doneEvent; + + /** + * Maintain a queue of in-flight packets. Assume that the + * delay is non-zero and constant (i.e., at most one packet + * per tick). + */ + std::deque> txQueue; + + void processTxQueue(); + typedef EventWrapper TxQueueEvent; + friend void TxQueueEvent::process(); + TxQueueEvent txQueueEvent; + + void txComplete(EthPacketPtr packet); + + public: + FabricLink(const std::string &name, EtherSwitch *p, int num, + double rate, Tick delay, Tick delay_var); + ~FabricLink() {} + + const std::string &name() const { return objName; } + + bool busy() const { return packet != nullptr; } + bool transmit(EthPacketPtr packet); + + void setTxInt(EtherSwitch::Interface *i) { assert(!txint); txint = i; } + void setRxInt(EtherSwitch::Interface *i) { assert(!rxint); rxint = i; } + + void serialize(const std::string &base, CheckpointOut &cp) const; + void unserialize(const std::string &base, CheckpointIn &cp); + }; + /** + * Model for an Ethernet switch Fabric + */ + class EtherFabric + { + public: + EtherFabric(const std::string &name, EtherSwitch &_etherSwitch, + double rate); + ~EtherFabric(); + const std::string &name() const { return fabricName; } + /** + * Connect rx and tx links in a point-to-point fashion for each port + * and create the switch fabric. There will always be n*(n-1) links. + */ + void connectAllLinks(std::vector _interfaces); + /** + * For each input queue forward along or broadcast any + * packets they may have. + */ + void forwardingEngine(); + /** + * Send any outgoing packets along to their receiving device + */ + void transmissionEngine(); + /** + * Try to find which port a device (MAC) is associated with + */ + Interface* lookupDestPort(Net::EthAddr destAddr); + /** + * Cache the MAC address for the device connected to a given port + */ + void learnSenderAddr(Net::EthAddr srcAddr, Interface* sender); + /** + * This provides for a simple arbitration policy. to avoid starvation + * the ports are allowed to send packets in least-recenty-granted + * order (LRG). when a port is allowed to send a packet, it is set to + * the most-recently-granted position. On arbitration the port in the + * LRG position is chosen to send a packet first + */ + void updateLRG(Interface *interface); + /** + * Broadcast a packet to all ports without allowing any loopback + */ + void broadcast(EthPacketPtr packet, Interface *sender); + void serialize(CheckpointOut &cp) const; + void unserialize(CheckpointIn &cp); + + protected: + struct SwitchTableEntry { + Interface *interface; + Tick arrivalTime; + }; + + private: + const std::string fabricName; + EtherSwitch ðerSwitch; + // fabric speed + double ticksPerByte; + // table that maps MAC address to interfaces + std::map forwardingTable; + struct pairhash { + public: + std::size_t operator()( + const std::pair &x) const + { + return std::hash()(x.first->name()) ^ + std::hash()(x.second->name()); + } + }; + // the actual fabric, i.e., this holds all the + // point-to-point links + std::unordered_map, + EtherSwitch::FabricLink*, pairhash> fabricLinks; + // maintain a least recently granted (LRG) ordering + // of the ports for arbitration + std::vector interfaces; + }; + + private: + // actual switching fabric + EtherFabric switchFabric; + // switch clock period + const Tick clockPeriod; + bool txTick, rxTick; + void tick(); + void restartClock(); + // tick event + EventWrapper tickEvent; + // all interfaces of the switch + std::vector interfaces; + void serialize(CheckpointOut &cp) const override; + void unserialize(CheckpointIn &cp) override; +}; + +#endif // __DEV_ETHERSWITCH_HH__ diff -r 9d2364203316 -r ed7527fcc338 src/dev/net/etherswitch.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dev/net/etherswitch.cc Sun Jan 10 23:57:37 2016 -0600 @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2014 The Regents of The University of Michigan + * All rights reserved. + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Copyright (c) 2015 University of Illinois Urbana Champaign + * 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), switchFabric(name() + ".switchFabric", + *this, p->fabric_speed), clockPeriod(p->clock_period), txTick(false), + rxTick(false), tickEvent(this) +{ + 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->input_buffer_size, + p->output_buffer_size); + interfaces.push_back(interface); + } + switchFabric.connectAllLinks(interfaces); +} + +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, + int inputBufferSize, int outputBufferSize) + : EtherInt(name), numBroadcasting(0), etherSwitch(_etherSwitch), + inputFifo(inputBufferSize), outputFifo(outputBufferSize) +{ +} + +// when a packet is received from a device just store +// it in an input queue until it can be forwarded +bool +EtherSwitch::Interface::recvPacket(EthPacketPtr packet) +{ + DPRINTF(Ethernet, "received packet\n"); + if (inputFifo.push(packet)) { + DPRINTF(Ethernet, "adding packet to input queue\n"); + etherSwitch.rxTick = true; + etherSwitch.restartClock(); + return true; + } else { + DPRINTF(Ethernet, "input fifo for interface %s full, " + "dropping packet.\n", name()); + return false; + } +} + +// don't actually send a packet here. once a packet +// makes it through a fabric link, put it in +// an output queue +bool +EtherSwitch::Interface::deliverPacket(EthPacketPtr packet) +{ + DPRINTF(Ethernet, "sending packet\n"); + if (outputFifo.push(packet)) { + DPRINTF(Ethernet, "pushing packet into output queue\n"); + etherSwitch.txTick = true; + etherSwitch.restartClock(); + return true; + } else { + DPRINTF(Ethernet, "output fifo full; dropping packet.\n"); + return false; + } +} + +// this is where we actually send a packet to the +// receiving device +void +EtherSwitch::Interface::sendQueuedPacket() +{ + if (outputFifo.empty()) + return; + + if (peer->recvPacket(outputFifo.front())) + outputFifo.pop(); + else + etherSwitch.txTick = true; +} + +EthPacketPtr +EtherSwitch::Interface::getInputPacket() +{ + if (inputFifo.empty()) + return nullptr; + + return inputFifo.front(); +} + +// if a packet was successfully sent then pop it from +// from the input queue +void +EtherSwitch::Interface::sendDone() +{ + if (broadcasting()) + --numBroadcasting; + + if (broadcasting()) { + DPRINTF(Ethernet, "packet sent, but port is still broadcasting " + "don't pop the queue\n"); + } else { + DPRINTF(Ethernet, "packet sent, popping input fifo\n"); + popInputPacket(); + } +} + +EtherSwitch::FabricLink::FabricLink(const string &name, EtherSwitch *p, + int num, double rate, Tick delay, Tick delay_var) + : objName(name), parent(p), number(num), txint(nullptr), rxint(nullptr), + ticksPerByte(rate), linkDelay(delay), delayVar(delay_var), + doneEvent(this), txQueueEvent(this) +{ } + +void +EtherSwitch::FabricLink::txComplete(EthPacketPtr packet) +{ + DDUMP(EthernetData, packet->data, packet->length); + rxint->deliverPacket(packet); +} + +void +EtherSwitch::FabricLink::txDone() +{ + if (linkDelay > 0) { + txQueue.emplace_back(std::make_pair(curTick() + linkDelay, packet)); + if (!txQueueEvent.scheduled()) + parent->schedule(txQueueEvent, txQueue.front().first); + } else { + assert(txQueue.empty()); + txComplete(packet); + } + + packet = 0; + assert(!busy()); + + txint->sendDone(); +} +void +EtherSwitch::FabricLink::processTxQueue() +{ + auto cur(txQueue.front()); + txQueue.pop_front(); + + // Schedule a new event to process the next packet in the queue. + if (!txQueue.empty()) { + auto next(txQueue.front()); + assert(next.first > curTick()); + parent->schedule(txQueueEvent, next.first); + } + + assert(cur.first == curTick()); + txComplete(cur.second); +} + +bool +EtherSwitch::FabricLink::transmit(EthPacketPtr pkt) +{ + if (busy()) { + return false; + } + + packet = pkt; + Tick delay = (Tick)ceil(((double)pkt->length * ticksPerByte) + 1.0); + if (delayVar != 0) + delay += random_mt.random(0, delayVar); + + parent->schedule(doneEvent, curTick() + delay); + + return true; +} + +void +EtherSwitch::FabricLink::serialize(const string &base, CheckpointOut &cp) const +{ + bool packet_exists = packet != nullptr; + paramOut(cp, base + ".packet_exists", packet_exists); + if (packet_exists) + packet->serialize(base + ".packet", cp); + + bool event_scheduled = doneEvent.scheduled(); + paramOut(cp, base + ".event_scheduled", event_scheduled); + if (event_scheduled) { + Tick event_time = doneEvent.when(); + paramOut(cp, base + ".event_time", event_time); + } + const size_t tx_queue_size(txQueue.size()); + paramOut(cp, base + ".tx_queue_size", tx_queue_size); + unsigned idx(0); + for (const auto &pe : txQueue) { + paramOut(cp, csprintf("%s.txQueue[%i].tick", base, idx), pe.first); + pe.second->serialize(csprintf("%s.txQueue[%i].packet", base, idx), cp); + + ++idx; + } +} + +void +EtherSwitch::FabricLink::unserialize(const string &base, CheckpointIn &cp) +{ + bool packet_exists; + paramIn(cp, base + ".packet_exists", packet_exists); + if (packet_exists) { + packet = make_shared(16384); + packet->unserialize(base + ".packet", 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(doneEvent, event_time); + } + + size_t tx_queue_size; + optParamIn(cp, base + ".tx_queue_size", tx_queue_size); + for (size_t idx = 0; idx < tx_queue_size; ++idx) { + Tick tick; + EthPacketPtr delayed_packet = make_shared(16384); + + paramIn(cp, csprintf("%s.txQueue[%i].tick", base, idx), tick); + delayed_packet->unserialize( + csprintf("%s.txQueue[%i].packet", base, idx), cp); + + fatal_if(!txQueue.empty() && txQueue.back().first > tick, + "Invalid txQueue packet order in FabricLink!\n"); + txQueue.emplace_back(std::make_pair(tick, delayed_packet)); + } + + if (!txQueue.empty()) + parent->schedule(txQueueEvent, txQueue.front().first); +} + +EtherSwitch::EtherFabric::EtherFabric(const std::string &name, + EtherSwitch &_etherSwitch, double rate) + : fabricName(name), etherSwitch(_etherSwitch), ticksPerByte(rate) +{ +} + +EtherSwitch::EtherFabric::~EtherFabric() +{ + for (auto it : fabricLinks) + delete it.second; + + fabricLinks.clear(); +} + +void +EtherSwitch::EtherFabric::connectAllLinks(std::vector _interfaces) +{ + interfaces = _interfaces; + int linkNum = 0; + + for (auto itr1 : interfaces) { + for (auto itr2 : interfaces) { + if (itr1 == itr2) + continue; + std::string linkName = csprintf("%s.link%d", name(), linkNum); + EtherSwitch::FabricLink *link = new EtherSwitch::FabricLink( + linkName, + ðerSwitch, + linkNum, ticksPerByte, + etherSwitch.params()->delay, + etherSwitch.params()->delay_var); + link->setTxInt(itr1); + link->setRxInt(itr2); + std::pair intPair + = std::make_pair(itr1, itr2); + + DPRINTF(Ethernet, "connecting port %s and port %s " + "over link %x\n", itr1->name(), itr2->name(), + link->name()); + fabricLinks.insert(std::pair, + EtherSwitch::FabricLink*>(intPair, link)); + ++linkNum; + } + } +} + +void +EtherSwitch::EtherFabric::forwardingEngine() +{ + Interface *receiver; + Interface *sender; + + // we use a temporary vector copy of interfaces because + // the objects in interfaces may be moved around in the + // for loop, i.e., the copy is used to ensure the iterators + // remain valid after rearranging the vector + std::vector tmpInterfaces(interfaces); + + DPRINTF(Ethernet, "forwarding packets over fabric\n"); + + // stop rxTick for now + // restart it if any packet couldn't go through fabric + etherSwitch.rxTick = false; + + for (auto it : tmpInterfaces) { + sender = it; + EthPacketPtr packet = sender->getInputPacket(); + + if (!packet) + continue; + + uint8_t srcAddr[ETH_ADDR_LEN]; + uint8_t destAddr[ETH_ADDR_LEN]; + memcpy(srcAddr, &packet->data[6], ETH_ADDR_LEN); + memcpy(destAddr, packet->data, ETH_ADDR_LEN); + Net::EthAddr destMacAddr(destAddr); + Net::EthAddr srcMacAddr(srcAddr); + + learnSenderAddr(srcMacAddr, sender); + receiver = lookupDestPort(destMacAddr); + + if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) { + broadcast(packet, sender); + } else { + DPRINTF(Ethernet, "sending packet from MAC %x on port " + "%s to MAC %x on port %s\n", uint64_t(srcMacAddr), + sender->name(), uint64_t(destMacAddr), receiver->name()); + auto linkItr = fabricLinks.find(std::make_pair(sender, receiver)); + assert(linkItr != fabricLinks.end()); + bool sent = linkItr->second->transmit(packet); + if (sent) + updateLRG(sender); + else + etherSwitch.rxTick = true; + } + } +} + +void +EtherSwitch::EtherFabric::transmissionEngine() +{ + DPRINTF(Ethernet, "transmitting packets to destination\n"); + + etherSwitch.txTick = false; + for (auto it : interfaces) + it->sendQueuedPacket(); +} + +EtherSwitch::Interface* +EtherSwitch::EtherFabric::lookupDestPort(Net::EthAddr destMacAddr) +{ + auto it = forwardingTable.find(uint64_t(destMacAddr)); + + if (it == forwardingTable.end()) { + DPRINTF(Ethernet, "no entry in forwaring table for MAC: " + "%x\n", uint64_t(destMacAddr)); + 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::EtherFabric::learnSenderAddr(Net::EthAddr srcMacAddr, + Interface *sender) +{ + // learn the port for the sending MAC address + auto it = forwardingTable.find(uint64_t(srcMacAddr)); + + // if the port for sender's MAC address is not cached, + // cache it now, otherwise just update arrival time + if (it == forwardingTable.end()) { + DPRINTF(Ethernet, "adding forwarding table entry for MAC " + " address %x on port %s\n", uint64_t(srcMacAddr), + sender->name()); + SwitchTableEntry forwardingTableEntry; + forwardingTableEntry.interface = sender; + forwardingTableEntry.arrivalTime = curTick(); + forwardingTable.insert(std::pair( + uint64_t(srcMacAddr), forwardingTableEntry)); + } else { + it->second.arrivalTime = curTick(); + } + // should also schedule the removal of an entry after it's TTL expires +} + +void +EtherSwitch::EtherFabric::updateLRG(Interface *interface) +{ + int i = interfaces.size() - 1; + Interface *next = interface; + + DPRINTF(Ethernet, "moving interface %s to MRG\n", + interface->name()); + + do { + assert(i >= 0); + Interface *tmp = interfaces.at(i); + interfaces.at(i) = next; + next = tmp; + --i; + } while (next != interface); +} + +void +EtherSwitch::EtherFabric::broadcast(EthPacketPtr packet, Interface *sender) +{ + // if this port didn't finish broadcasting previosly, + // pick up where it left off by trying to send the packet + // to the remaining ports + if (sender->broadcasting()) { + DPRINTF(Ethernet, "port already broadcasting; try again later\n"); + std::vector peers = sender->getBroadcastPeers(); + + if (peers.empty()) + return; + + auto begin = peers.begin(); + auto end = peers.end(); + auto it = begin; + + while (it != end) { + auto linkItr = fabricLinks.find(std::make_pair(sender, *it)); + assert(linkItr != fabricLinks.end()); + EtherSwitch::FabricLink *link = linkItr->second; + if (link->transmit(packet)) { + DPRINTF(Ethernet, "broadcast to link %s complete\n", + link->name()); + it = peers.erase(it); + } else { + DPRINTF(Ethernet, "broadcast to link %s unable to complete; " + "try again later\n", link->name()); + ++it; + etherSwitch.rxTick = true; + } + } + } else { + for (auto it : interfaces) { + if (it == sender) + continue; + + auto linkItr = fabricLinks.find(std::make_pair(sender, it)); + assert(linkItr != fabricLinks.end()); + EtherSwitch::FabricLink *link = linkItr->second; + DPRINTF(Ethernet, "broadcasting from port %s to port %s " + "over link %s\n", sender->name(), it->name(), link->name()); + if (!link->transmit(packet)) { + DPRINTF(Ethernet, "broadcast unable to complete, " + "retry later\n"); + sender->getBroadcastPeers().push_back(it); + etherSwitch.rxTick = true; + } + sender->incNumBroadcasting(); + } + } +} + +void +EtherSwitch::tick() +{ + DPRINTF(Ethernet, "EtherSwitch: -------------- Cycle --------------\n"); + + if (rxTick) + switchFabric.forwardingEngine(); + + if (txTick) + switchFabric.transmissionEngine(); + + if ((rxTick || txTick) && !tickEvent.scheduled()) + schedule(tickEvent, curTick() + clockPeriod); +} + +void +EtherSwitch::restartClock() +{ + if (!tickEvent.scheduled() && (rxTick || txTick)) + schedule(tickEvent, curTick() + clockPeriod); +} + +void +EtherSwitch::serialize(CheckpointOut &cp) const +{ + switchFabric.serialize(cp); + for (auto it : interfaces) + it->serialize(it->name(), cp); +} + +void +EtherSwitch::unserialize(CheckpointIn &cp) +{ + switchFabric.unserialize(cp); + for (auto it : interfaces) + it->unserialize(it->name(), cp); + rxTick = true; + txTick = true; +} + +void +EtherSwitch::EtherFabric::serialize(CheckpointOut &cp) const +{ + for (auto it : fabricLinks) + it.second->serialize(it.second->name(), cp); +} + +void +EtherSwitch::EtherFabric::unserialize(CheckpointIn &cp) +{ + for (auto it : fabricLinks) + it.second->unserialize(it.second->name(), cp); +} + +void +EtherSwitch::Interface::serialize(const std::string &base, CheckpointOut &cp) +const +{ + inputFifo.serialize(base + "rxfifo", cp); + outputFifo.serialize(base + "txfifo", cp); +} + +void +EtherSwitch::Interface::unserialize(const std::string &base, CheckpointIn &cp) +{ + inputFifo.unserialize(base + "rxfifo", cp); + outputFifo.unserialize(base + "txfifo", cp); +} + +EtherSwitch * +EtherSwitchParams::create() +{ + return new EtherSwitch(this); +}