etherswitch.cc revision 11562:a16337161285
12810SN/A/* 211375Sandreas.hansson@arm.com * Copyright (c) 2014 The Regents of The University of Michigan 39663Suri.wiener@arm.com * All rights reserved. 49663Suri.wiener@arm.com * 59663Suri.wiener@arm.com * Redistribution and use in source and binary forms, with or without 69663Suri.wiener@arm.com * modification, are permitted provided that the following conditions are 79663Suri.wiener@arm.com * met: redistributions of source code must retain the above copyright 89663Suri.wiener@arm.com * notice, this list of conditions and the following disclaimer; 99663Suri.wiener@arm.com * redistributions in binary form must reproduce the above copyright 109663Suri.wiener@arm.com * notice, this list of conditions and the following disclaimer in the 119663Suri.wiener@arm.com * documentation and/or other materials provided with the distribution; 129663Suri.wiener@arm.com * neither the name of the copyright holders nor the names of its 139663Suri.wiener@arm.com * contributors may be used to endorse or promote products derived from 142810SN/A * this software without specific prior written permission. 152810SN/A * 162810SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 172810SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 182810SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 192810SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 202810SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212810SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 222810SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 232810SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 242810SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 252810SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 262810SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 272810SN/A * 282810SN/A * Authors: Anthony Gutierrez 292810SN/A * Mohammad Alian 302810SN/A */ 312810SN/A 322810SN/A/* @file 332810SN/A * Device model for an ethernet switch 342810SN/A */ 352810SN/A 362810SN/A#include "dev/net/etherswitch.hh" 372810SN/A 382810SN/A#include "base/random.hh" 392810SN/A#include "debug/EthernetAll.hh" 402810SN/A 412810SN/Ausing namespace std; 422810SN/A 432810SN/AEtherSwitch::EtherSwitch(const Params *p) 442810SN/A : EtherObject(p), ttl(p->time_to_live) 452810SN/A{ 462810SN/A for (int i = 0; i < p->port_interface_connection_count; ++i) { 472810SN/A std::string interfaceName = csprintf("%s.interface%d", name(), i); 4810764Sandreas.hansson@arm.com Interface *interface = new Interface(interfaceName, this, 4910764Sandreas.hansson@arm.com p->output_buffer_size, p->delay, 502810SN/A p->delay_var, p->fabric_speed, i); 514626SN/A interfaces.push_back(interface); 524626SN/A } 535314SN/A} 5411375Sandreas.hansson@arm.com 552810SN/AEtherSwitch::~EtherSwitch() 5611375Sandreas.hansson@arm.com{ 572810SN/A for (auto it : interfaces) 582810SN/A delete it; 592810SN/A 603374SN/A interfaces.clear(); 619264Sdjordje.kovacevic@arm.com} 622810SN/A 6311375Sandreas.hansson@arm.comEtherInt* 644626SN/AEtherSwitch::getEthPort(const std::string &if_name, int idx) 654626SN/A{ 669725Sandreas.hansson@arm.com if (idx < 0 || idx >= interfaces.size()) 6711375Sandreas.hansson@arm.com return nullptr; 689725Sandreas.hansson@arm.com 6911375Sandreas.hansson@arm.com Interface *interface = interfaces.at(idx); 7011375Sandreas.hansson@arm.com panic_if(interface->getPeer(), "interface already connected\n"); 719725Sandreas.hansson@arm.com 729725Sandreas.hansson@arm.com return interface; 739725Sandreas.hansson@arm.com} 749725Sandreas.hansson@arm.com 759725Sandreas.hansson@arm.combool 769725Sandreas.hansson@arm.comEtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr, unsigned senderId) 779725Sandreas.hansson@arm.com{ 7811284Sandreas.hansson@arm.com assert(ptr->length); 7911284Sandreas.hansson@arm.com 8011284Sandreas.hansson@arm.com _size += ptr->length; 8111284Sandreas.hansson@arm.com fifo.emplace_hint(fifo.end(), ptr, curTick(), senderId); 8211284Sandreas.hansson@arm.com 8311284Sandreas.hansson@arm.com // Drop the extra pushed packets from end of the fifo 8411284Sandreas.hansson@arm.com while (avail() < 0) { 8511284Sandreas.hansson@arm.com DPRINTF(Ethernet, "Fifo is full. Drop packet: len=%d\n", 8611284Sandreas.hansson@arm.com std::prev(fifo.end())->packet->length); 8711284Sandreas.hansson@arm.com 8811284Sandreas.hansson@arm.com _size -= std::prev(fifo.end())->packet->length; 8911284Sandreas.hansson@arm.com fifo.erase(std::prev(fifo.end())); 9011284Sandreas.hansson@arm.com } 9111284Sandreas.hansson@arm.com 9211284Sandreas.hansson@arm.com if (empty()) { 9311284Sandreas.hansson@arm.com warn("EtherSwitch: Packet length (%d) exceeds the maximum storage " 9411284Sandreas.hansson@arm.com "capacity of port fifo (%d)", ptr->length, _maxsize); 9511284Sandreas.hansson@arm.com } 9611284Sandreas.hansson@arm.com 9711284Sandreas.hansson@arm.com // Return true if the newly pushed packet gets inserted 9811284Sandreas.hansson@arm.com // at the head of the queue, otherwise return false 9911284Sandreas.hansson@arm.com // We need this information to deschedule the event that has been 10011284Sandreas.hansson@arm.com // scheduled for the old head of queue packet and schedule a new one 10111284Sandreas.hansson@arm.com if (!empty() && fifo.begin()->packet == ptr) { 10211284Sandreas.hansson@arm.com return true; 1039725Sandreas.hansson@arm.com } 1049725Sandreas.hansson@arm.com return false; 1059725Sandreas.hansson@arm.com} 1069725Sandreas.hansson@arm.com 1079725Sandreas.hansson@arm.comvoid 1089725Sandreas.hansson@arm.comEtherSwitch::Interface::PortFifo::pop() 1099725Sandreas.hansson@arm.com{ 1102810SN/A if (empty()) 1114626SN/A return; 11211375Sandreas.hansson@arm.com 11311375Sandreas.hansson@arm.com assert(_size >= fifo.begin()->packet->length); 11411375Sandreas.hansson@arm.com // Erase the packet at the head of the queue 1154626SN/A _size -= fifo.begin()->packet->length; 1164626SN/A fifo.erase(fifo.begin()); 1175875Ssteve.reinhardt@amd.com} 1185875Ssteve.reinhardt@amd.com 1195875Ssteve.reinhardt@amd.comvoid 1205875Ssteve.reinhardt@amd.comEtherSwitch::Interface::PortFifo::clear() 1215875Ssteve.reinhardt@amd.com{ 1225875Ssteve.reinhardt@amd.com fifo.clear(); 1235875Ssteve.reinhardt@amd.com _size = 0; 12410766Sandreas.hansson@arm.com} 12510766Sandreas.hansson@arm.com 12610766Sandreas.hansson@arm.comEtherSwitch::Interface::Interface(const std::string &name, 12710766Sandreas.hansson@arm.com EtherSwitch *etherSwitch, 12810766Sandreas.hansson@arm.com uint64_t outputBufferSize, Tick delay, 12911742Snikos.nikoleris@arm.com Tick delay_var, double rate, unsigned id) 13011742Snikos.nikoleris@arm.com : EtherInt(name), ticksPerByte(rate), switchDelay(delay), 13111742Snikos.nikoleris@arm.com delayVar(delay_var), interfaceId(id), parent(etherSwitch), 13211742Snikos.nikoleris@arm.com outputFifo(name + ".outputFifo", outputBufferSize), txEvent(this) 13311742Snikos.nikoleris@arm.com{ 13411742Snikos.nikoleris@arm.com} 13511742Snikos.nikoleris@arm.com 13611742Snikos.nikoleris@arm.combool 13711742Snikos.nikoleris@arm.comEtherSwitch::Interface::recvPacket(EthPacketPtr packet) 13811742Snikos.nikoleris@arm.com{ 13911742Snikos.nikoleris@arm.com Net::EthAddr destMacAddr(packet->data); 14011742Snikos.nikoleris@arm.com Net::EthAddr srcMacAddr(&packet->data[6]); 14111742Snikos.nikoleris@arm.com 14211742Snikos.nikoleris@arm.com learnSenderAddr(srcMacAddr, this); 14311742Snikos.nikoleris@arm.com Interface *receiver = lookupDestPort(destMacAddr); 14411742Snikos.nikoleris@arm.com 14511742Snikos.nikoleris@arm.com if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) { 14611742Snikos.nikoleris@arm.com for (auto it : parent->interfaces) 14711741Snikos.nikoleris@arm.com if (it != this) 14811741Snikos.nikoleris@arm.com it->enqueue(packet, interfaceId); 1494626SN/A } else { 1505318SN/A DPRINTF(Ethernet, "sending packet from MAC %x on port " 15111741Snikos.nikoleris@arm.com "%s to MAC %x on port %s\n", uint64_t(srcMacAddr), 1527823Ssteve.reinhardt@amd.com this->name(), uint64_t(destMacAddr), receiver->name()); 15311741Snikos.nikoleris@arm.com 15411741Snikos.nikoleris@arm.com receiver->enqueue(packet, interfaceId); 1554626SN/A } 1564626SN/A // At the output port, we either have buffer space (no drop) or 1574626SN/A // don't (drop packet); in both cases packet is received on 1584903SN/A // the interface successfully and there is no notion of busy 1594903SN/A // interface here (as we don't have inputFifo) 1604903SN/A return true; 16111284Sandreas.hansson@arm.com} 1624903SN/A 16311741Snikos.nikoleris@arm.comvoid 16411741Snikos.nikoleris@arm.comEtherSwitch::Interface::enqueue(EthPacketPtr packet, unsigned senderId) 1654903SN/A{ 1664903SN/A // assuming per-interface transmission events, 16711740Snikos.nikoleris@arm.com // if the newly push packet gets inserted at the head of the queue 16811740Snikos.nikoleris@arm.com // (either there was nothing in the queue or the priority of the new 16911740Snikos.nikoleris@arm.com // packet was higher than the packets already in the fifo) 17011740Snikos.nikoleris@arm.com // then we need to schedule an event at 17111740Snikos.nikoleris@arm.com // "curTick" + "switchingDelay of the packet at the head of the fifo" 17211740Snikos.nikoleris@arm.com // to send this packet out the external link 17311740Snikos.nikoleris@arm.com // otherwise, there is already a txEvent scheduled 17411741Snikos.nikoleris@arm.com if (outputFifo.push(packet, senderId)) { 17511740Snikos.nikoleris@arm.com parent->reschedule(txEvent, curTick() + switchingDelay(), true); 17611741Snikos.nikoleris@arm.com } 17711741Snikos.nikoleris@arm.com} 17811740Snikos.nikoleris@arm.com 17911741Snikos.nikoleris@arm.comvoid 18011740Snikos.nikoleris@arm.comEtherSwitch::Interface::transmit() 18111740Snikos.nikoleris@arm.com{ 18211740Snikos.nikoleris@arm.com // there should be something in the output queue 18311740Snikos.nikoleris@arm.com assert(!outputFifo.empty()); 18411740Snikos.nikoleris@arm.com 18511740Snikos.nikoleris@arm.com if (!sendPacket(outputFifo.front())) { 18611740Snikos.nikoleris@arm.com DPRINTF(Ethernet, "output port busy...retry later\n"); 18711740Snikos.nikoleris@arm.com if (!txEvent.scheduled()) 18811740Snikos.nikoleris@arm.com parent->schedule(txEvent, curTick() + retryTime); 18911741Snikos.nikoleris@arm.com } else { 19011741Snikos.nikoleris@arm.com DPRINTF(Ethernet, "packet sent: len=%d\n", outputFifo.front()->length); 19111741Snikos.nikoleris@arm.com outputFifo.pop(); 19211741Snikos.nikoleris@arm.com // schedule an event to send the pkt at 19311741Snikos.nikoleris@arm.com // the head of queue, if there is any 19411741Snikos.nikoleris@arm.com if (!outputFifo.empty()) { 19511741Snikos.nikoleris@arm.com parent->schedule(txEvent, curTick() + switchingDelay()); 19611741Snikos.nikoleris@arm.com } 19711741Snikos.nikoleris@arm.com } 19811741Snikos.nikoleris@arm.com} 19911741Snikos.nikoleris@arm.com 20011741Snikos.nikoleris@arm.comTick 20111741Snikos.nikoleris@arm.comEtherSwitch::Interface::switchingDelay() 20211741Snikos.nikoleris@arm.com{ 20311741Snikos.nikoleris@arm.com Tick delay = (Tick)ceil(((double)outputFifo.front()->length 20411741Snikos.nikoleris@arm.com * ticksPerByte) + 1.0); 20511741Snikos.nikoleris@arm.com if (delayVar != 0) 20611741Snikos.nikoleris@arm.com delay += random_mt.random<Tick>(0, delayVar); 20711741Snikos.nikoleris@arm.com delay += switchDelay; 20811741Snikos.nikoleris@arm.com return delay; 2095318SN/A} 21011741Snikos.nikoleris@arm.com 21111741Snikos.nikoleris@arm.comEtherSwitch::Interface* 21211357Sstephan.diestelhorst@arm.comEtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr) 21311357Sstephan.diestelhorst@arm.com{ 21411357Sstephan.diestelhorst@arm.com auto it = parent->forwardingTable.find(uint64_t(destMacAddr)); 21511357Sstephan.diestelhorst@arm.com 21611357Sstephan.diestelhorst@arm.com if (it == parent->forwardingTable.end()) { 2174903SN/A DPRINTF(Ethernet, "no entry in forwaring table for MAC: " 21811357Sstephan.diestelhorst@arm.com "%x\n", uint64_t(destMacAddr)); 2194908SN/A return nullptr; 2204920SN/A } 2215314SN/A 2225314SN/A // check if this entry is valid based on TTL and lastUseTime 2234903SN/A if ((curTick() - it->second.lastUseTime) > parent->ttl) { 2244903SN/A // TTL for this mapping has been expired, so this item is not 2252810SN/A // valide anymore, let's remove it from the map 2262810SN/A parent->forwardingTable.erase(it); 2272810SN/A return nullptr; 2282810SN/A } 2294903SN/A 2307667Ssteve.reinhardt@amd.com DPRINTF(Ethernet, "found entry for MAC address %x on port %s\n", 2317667Ssteve.reinhardt@amd.com uint64_t(destMacAddr), it->second.interface->name()); 2327667Ssteve.reinhardt@amd.com return it->second.interface; 2337667Ssteve.reinhardt@amd.com} 2347667Ssteve.reinhardt@amd.com 23511284Sandreas.hansson@arm.comvoid 23611284Sandreas.hansson@arm.comEtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr, 2379725Sandreas.hansson@arm.com Interface *sender) 23811284Sandreas.hansson@arm.com{ 23911284Sandreas.hansson@arm.com // learn the port for the sending MAC address 2407667Ssteve.reinhardt@amd.com auto it = parent->forwardingTable.find(uint64_t(srcMacAddr)); 2417667Ssteve.reinhardt@amd.com 2427667Ssteve.reinhardt@amd.com // if the port for sender's MAC address is not cached, 2437667Ssteve.reinhardt@amd.com // cache it now, otherwise just update lastUseTime time 2447667Ssteve.reinhardt@amd.com if (it == parent->forwardingTable.end()) { 2457667Ssteve.reinhardt@amd.com DPRINTF(Ethernet, "adding forwarding table entry for MAC " 2467667Ssteve.reinhardt@amd.com " address %x on port %s\n", uint64_t(srcMacAddr), 2477667Ssteve.reinhardt@amd.com sender->name()); 2487667Ssteve.reinhardt@amd.com EtherSwitch::SwitchTableEntry forwardingTableEntry; 2494665SN/A forwardingTableEntry.interface = sender; 25011375Sandreas.hansson@arm.com forwardingTableEntry.lastUseTime = curTick(); 25111375Sandreas.hansson@arm.com parent->forwardingTable.insert(std::make_pair(uint64_t(srcMacAddr), 25211741Snikos.nikoleris@arm.com forwardingTableEntry)); 25311741Snikos.nikoleris@arm.com } else { 25411741Snikos.nikoleris@arm.com it->second.lastUseTime = curTick(); 2559725Sandreas.hansson@arm.com } 2564668SN/A} 2572810SN/A 2582810SN/Avoid 2592810SN/AEtherSwitch::serialize(CheckpointOut &cp) const 2602810SN/A{ 2612810SN/A for (auto it : interfaces) 2624626SN/A it->serializeSection(cp, it->name()); 2632810SN/A 2642810SN/A} 2652810SN/A 2662810SN/Avoid 2672810SN/AEtherSwitch::unserialize(CheckpointIn &cp) 2682810SN/A{ 2693374SN/A for (auto it : interfaces) 2709725Sandreas.hansson@arm.com it->unserializeSection(cp, it->name()); 2712810SN/A 2729725Sandreas.hansson@arm.com} 2734665SN/A 2749725Sandreas.hansson@arm.comvoid 2754626SN/AEtherSwitch::Interface::serialize(CheckpointOut &cp) const 2762810SN/A{ 2772810SN/A bool event_scheduled = txEvent.scheduled(); 27810764Sandreas.hansson@arm.com SERIALIZE_SCALAR(event_scheduled); 27910764Sandreas.hansson@arm.com 28010764Sandreas.hansson@arm.com if (event_scheduled) { 28110764Sandreas.hansson@arm.com Tick event_time = txEvent.when(); 28210764Sandreas.hansson@arm.com SERIALIZE_SCALAR(event_time); 28311197Sandreas.hansson@arm.com } 2842810SN/A outputFifo.serializeSection(cp, "outputFifo"); 28510764Sandreas.hansson@arm.com} 28611197Sandreas.hansson@arm.com 2872810SN/Avoid 28811375Sandreas.hansson@arm.comEtherSwitch::Interface::unserialize(CheckpointIn &cp) 2894908SN/A{ 2905318SN/A bool event_scheduled; 2915318SN/A UNSERIALIZE_SCALAR(event_scheduled); 2922810SN/A 2932810SN/A if (event_scheduled) { 2942810SN/A Tick event_time; 2952810SN/A UNSERIALIZE_SCALAR(event_time); 2962810SN/A parent->schedule(txEvent, event_time); 2972810SN/A } 2983374SN/A outputFifo.unserializeSection(cp, "outputFifo"); 2992810SN/A} 3002810SN/A 30111197Sandreas.hansson@arm.comvoid 30211197Sandreas.hansson@arm.comEtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const 3034902SN/A{ 3042810SN/A packet->serialize("packet", cp); 3052810SN/A SERIALIZE_SCALAR(recvTick); 3062810SN/A SERIALIZE_SCALAR(srcId); 3072810SN/A} 3082810SN/A 3092810SN/Avoid 3102810SN/AEtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn &cp) 3112810SN/A{ 3129725Sandreas.hansson@arm.com packet = make_shared<EthPacketData>(16384); 3139725Sandreas.hansson@arm.com packet->unserialize("packet", cp); 3142810SN/A UNSERIALIZE_SCALAR(recvTick); 3152810SN/A UNSERIALIZE_SCALAR(srcId); 31611742Snikos.nikoleris@arm.com} 31711742Snikos.nikoleris@arm.com 31811742Snikos.nikoleris@arm.comvoid 31911742Snikos.nikoleris@arm.comEtherSwitch::Interface::PortFifo::serialize(CheckpointOut &cp) const 32011742Snikos.nikoleris@arm.com{ 32111742Snikos.nikoleris@arm.com SERIALIZE_SCALAR(_size); 32211742Snikos.nikoleris@arm.com int fifosize = fifo.size(); 32311742Snikos.nikoleris@arm.com 32411742Snikos.nikoleris@arm.com SERIALIZE_SCALAR(fifosize); 32511742Snikos.nikoleris@arm.com 32611742Snikos.nikoleris@arm.com int i = 0; 32711742Snikos.nikoleris@arm.com for (const auto &entry : fifo) 32811742Snikos.nikoleris@arm.com entry.serializeSection(cp, csprintf("entry%d", i++)); 32911742Snikos.nikoleris@arm.com} 3304899SN/A 3314899SN/Avoid 3324899SN/AEtherSwitch::Interface::PortFifo::unserialize(CheckpointIn &cp) 3339725Sandreas.hansson@arm.com{ 3344899SN/A UNSERIALIZE_SCALAR(_size); 3354899SN/A int fifosize; 3362810SN/A 3372810SN/A UNSERIALIZE_SCALAR(fifosize); 3382810SN/A fifo.clear(); 3399725Sandreas.hansson@arm.com 3405730SSteve.Reinhardt@amd.com for (int i = 0; i < fifosize; ++i) { 3415730SSteve.Reinhardt@amd.com PortFifoEntry entry(nullptr, 0, 0); 3429725Sandreas.hansson@arm.com 3435730SSteve.Reinhardt@amd.com entry.unserializeSection(cp, csprintf("entry%d", i)); 3442810SN/A 3452810SN/A fifo.insert(entry); 3462810SN/A 3472810SN/A } 3482810SN/A} 3492810SN/A 3509725Sandreas.hansson@arm.comEtherSwitch * 3512810SN/AEtherSwitchParams::create() 3522810SN/A{ 3534665SN/A return new EtherSwitch(this); 3544665SN/A} 35511284Sandreas.hansson@arm.com