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