etherswitch.cc revision 13766
1/*
2 * Copyright (c) 2014 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Anthony Gutierrez
29 *          Mohammad Alian
30 */
31
32/* @file
33 * Device model for an ethernet switch
34 */
35
36#include "dev/net/etherswitch.hh"
37
38#include "base/random.hh"
39#include "base/trace.hh"
40#include "debug/EthernetAll.hh"
41#include "sim/core.hh"
42
43using namespace std;
44
45EtherSwitch::EtherSwitch(const Params *p)
46    : SimObject(p), ttl(p->time_to_live)
47{
48    for (int i = 0; i < p->port_interface_connection_count; ++i) {
49        std::string interfaceName = csprintf("%s.interface%d", name(), i);
50        Interface *interface = new Interface(interfaceName, this,
51                                        p->output_buffer_size, p->delay,
52                                        p->delay_var, p->fabric_speed, i);
53        interfaces.push_back(interface);
54    }
55}
56
57EtherSwitch::~EtherSwitch()
58{
59    for (auto it : interfaces)
60        delete it;
61
62    interfaces.clear();
63}
64
65EtherInt*
66EtherSwitch::getEthPort(const std::string &if_name, int idx)
67{
68    if (idx < 0 || idx >= interfaces.size())
69        return nullptr;
70
71    Interface *interface = interfaces.at(idx);
72    panic_if(interface->getPeer(), "interface already connected\n");
73
74    return interface;
75}
76
77bool
78EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr, unsigned senderId)
79{
80    assert(ptr->length);
81
82    _size += ptr->length;
83    fifo.emplace_hint(fifo.end(), ptr, curTick(), senderId);
84
85    // Drop the extra pushed packets from end of the fifo
86    while (avail() < 0) {
87        DPRINTF(Ethernet, "Fifo is full. Drop packet: len=%d\n",
88                std::prev(fifo.end())->packet->length);
89
90        _size -= std::prev(fifo.end())->packet->length;
91        fifo.erase(std::prev(fifo.end()));
92    }
93
94    if (empty()) {
95        warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
96             "capacity of port fifo (%d)", ptr->length, _maxsize);
97    }
98
99    // Return true if the newly pushed packet gets inserted
100    // at the head of the queue, otherwise return false
101    // We need this information to deschedule the event that has been
102    // scheduled for the old head of queue packet and schedule a new one
103    if (!empty() && fifo.begin()->packet == ptr) {
104        return true;
105    }
106    return false;
107}
108
109void
110EtherSwitch::Interface::PortFifo::pop()
111{
112    if (empty())
113        return;
114
115    assert(_size >= fifo.begin()->packet->length);
116    // Erase the packet at the head of the queue
117    _size -= fifo.begin()->packet->length;
118    fifo.erase(fifo.begin());
119}
120
121void
122EtherSwitch::Interface::PortFifo::clear()
123{
124    fifo.clear();
125    _size = 0;
126}
127
128EtherSwitch::Interface::Interface(const std::string &name,
129                                  EtherSwitch *etherSwitch,
130                                  uint64_t outputBufferSize, Tick delay,
131                                  Tick delay_var, double rate, unsigned id)
132    : EtherInt(name), ticksPerByte(rate), switchDelay(delay),
133      delayVar(delay_var), interfaceId(id), parent(etherSwitch),
134      outputFifo(name + ".outputFifo", outputBufferSize),
135      txEvent([this]{ transmit(); }, name)
136{
137}
138
139bool
140EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
141{
142    Net::EthAddr destMacAddr(packet->data);
143    Net::EthAddr srcMacAddr(&packet->data[6]);
144
145    learnSenderAddr(srcMacAddr, this);
146    Interface *receiver = lookupDestPort(destMacAddr);
147
148    if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
149        for (auto it : parent->interfaces)
150            if (it != this)
151                it->enqueue(packet, interfaceId);
152    } else {
153        DPRINTF(Ethernet, "sending packet from MAC %x on port "
154                "%s to MAC %x on port %s\n", uint64_t(srcMacAddr),
155                this->name(), uint64_t(destMacAddr), receiver->name());
156
157        receiver->enqueue(packet, interfaceId);
158    }
159    // At the output port, we either have buffer space (no drop) or
160    // don't (drop packet); in both cases packet is received on
161    // the interface successfully and there is no notion of busy
162    // interface here (as we don't have inputFifo)
163    return true;
164}
165
166void
167EtherSwitch::Interface::enqueue(EthPacketPtr packet, unsigned senderId)
168{
169    // assuming per-interface transmission events,
170    // if the newly push packet gets inserted at the head of the queue
171    // (either there was nothing in the queue or the priority of the new
172    // packet was higher than the packets already in the fifo)
173    // then we need to schedule an event at
174    // "curTick" + "switchingDelay of the packet at the head of the fifo"
175    // to send this packet out the external link
176    // otherwise, there is already a txEvent scheduled
177    if (outputFifo.push(packet, senderId)) {
178        parent->reschedule(txEvent, curTick() + switchingDelay(), true);
179    }
180}
181
182void
183EtherSwitch::Interface::transmit()
184{
185    // there should be something in the output queue
186    assert(!outputFifo.empty());
187
188    if (!sendPacket(outputFifo.front())) {
189        DPRINTF(Ethernet, "output port busy...retry later\n");
190        if (!txEvent.scheduled())
191            parent->schedule(txEvent, curTick() + retryTime);
192    } else {
193        DPRINTF(Ethernet, "packet sent: len=%d\n", outputFifo.front()->length);
194        outputFifo.pop();
195        // schedule an event to send the pkt at
196        // the head of queue, if there is any
197        if (!outputFifo.empty()) {
198            parent->schedule(txEvent, curTick() + switchingDelay());
199        }
200    }
201}
202
203Tick
204EtherSwitch::Interface::switchingDelay()
205{
206    Tick delay = (Tick)ceil(((double)outputFifo.front()->simLength
207                                     * ticksPerByte) + 1.0);
208    if (delayVar != 0)
209                delay += random_mt.random<Tick>(0, delayVar);
210    delay += switchDelay;
211    return delay;
212}
213
214EtherSwitch::Interface*
215EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr)
216{
217    auto it = parent->forwardingTable.find(uint64_t(destMacAddr));
218
219    if (it == parent->forwardingTable.end()) {
220        DPRINTF(Ethernet, "no entry in forwaring table for MAC: "
221                "%x\n", uint64_t(destMacAddr));
222        return nullptr;
223    }
224
225    // check if this entry is valid based on TTL and lastUseTime
226    if ((curTick() - it->second.lastUseTime) > parent->ttl) {
227        // TTL for this mapping has been expired, so this item is not
228        // valide anymore, let's remove it from the map
229        parent->forwardingTable.erase(it);
230        return nullptr;
231    }
232
233    DPRINTF(Ethernet, "found entry for MAC address %x on port %s\n",
234            uint64_t(destMacAddr), it->second.interface->name());
235    return it->second.interface;
236}
237
238void
239EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr,
240                                          Interface *sender)
241{
242    // learn the port for the sending MAC address
243    auto it = parent->forwardingTable.find(uint64_t(srcMacAddr));
244
245    // if the port for sender's MAC address is not cached,
246    // cache it now, otherwise just update lastUseTime time
247    if (it == parent->forwardingTable.end()) {
248        DPRINTF(Ethernet, "adding forwarding table entry for MAC "
249                " address %x on port %s\n", uint64_t(srcMacAddr),
250                sender->name());
251        EtherSwitch::SwitchTableEntry forwardingTableEntry;
252        forwardingTableEntry.interface = sender;
253        forwardingTableEntry.lastUseTime = curTick();
254        parent->forwardingTable.insert(std::make_pair(uint64_t(srcMacAddr),
255            forwardingTableEntry));
256    } else {
257        it->second.lastUseTime = curTick();
258    }
259}
260
261void
262EtherSwitch::serialize(CheckpointOut &cp) const
263{
264    for (auto it : interfaces)
265        it->serializeSection(cp, it->name());
266
267}
268
269void
270EtherSwitch::unserialize(CheckpointIn &cp)
271{
272    for (auto it : interfaces)
273        it->unserializeSection(cp, it->name());
274
275}
276
277void
278EtherSwitch::Interface::serialize(CheckpointOut &cp) const
279{
280    bool event_scheduled = txEvent.scheduled();
281    SERIALIZE_SCALAR(event_scheduled);
282
283    if (event_scheduled) {
284        Tick event_time = txEvent.when();
285        SERIALIZE_SCALAR(event_time);
286    }
287    outputFifo.serializeSection(cp, "outputFifo");
288}
289
290void
291EtherSwitch::Interface::unserialize(CheckpointIn &cp)
292{
293    bool event_scheduled;
294    UNSERIALIZE_SCALAR(event_scheduled);
295
296    if (event_scheduled) {
297        Tick event_time;
298        UNSERIALIZE_SCALAR(event_time);
299        parent->schedule(txEvent, event_time);
300    }
301    outputFifo.unserializeSection(cp, "outputFifo");
302}
303
304void
305EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const
306{
307    packet->serialize("packet", cp);
308    SERIALIZE_SCALAR(recvTick);
309    SERIALIZE_SCALAR(srcId);
310}
311
312void
313EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn &cp)
314{
315    packet = make_shared<EthPacketData>(16384);
316    packet->unserialize("packet", cp);
317    UNSERIALIZE_SCALAR(recvTick);
318    UNSERIALIZE_SCALAR(srcId);
319}
320
321void
322EtherSwitch::Interface::PortFifo::serialize(CheckpointOut &cp) const
323{
324    SERIALIZE_SCALAR(_size);
325    int fifosize = fifo.size();
326
327    SERIALIZE_SCALAR(fifosize);
328
329    int i = 0;
330    for (const auto &entry : fifo)
331        entry.serializeSection(cp, csprintf("entry%d", i++));
332}
333
334void
335EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn &cp)
336{
337    UNSERIALIZE_SCALAR(_size);
338    int fifosize;
339
340    UNSERIALIZE_SCALAR(fifosize);
341    fifo.clear();
342
343    for (int i = 0; i < fifosize; ++i) {
344        PortFifoEntry entry(nullptr, 0, 0);
345
346        entry.unserializeSection(cp, csprintf("entry%d", i));
347
348        fifo.insert(entry);
349
350    }
351}
352
353EtherSwitch *
354EtherSwitchParams::create()
355{
356    return new EtherSwitch(this);
357}
358