etherswitch.cc revision 11562:a16337161285
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 "debug/EthernetAll.hh"
40
41using namespace std;
42
43EtherSwitch::EtherSwitch(const Params *p)
44    : EtherObject(p), ttl(p->time_to_live)
45{
46    for (int i = 0; i < p->port_interface_connection_count; ++i) {
47        std::string interfaceName = csprintf("%s.interface%d", name(), i);
48        Interface *interface = new Interface(interfaceName, this,
49                                        p->output_buffer_size, p->delay,
50                                        p->delay_var, p->fabric_speed, i);
51        interfaces.push_back(interface);
52    }
53}
54
55EtherSwitch::~EtherSwitch()
56{
57    for (auto it : interfaces)
58        delete it;
59
60    interfaces.clear();
61}
62
63EtherInt*
64EtherSwitch::getEthPort(const std::string &if_name, int idx)
65{
66    if (idx < 0 || idx >= interfaces.size())
67        return nullptr;
68
69    Interface *interface = interfaces.at(idx);
70    panic_if(interface->getPeer(), "interface already connected\n");
71
72    return interface;
73}
74
75bool
76EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr, unsigned senderId)
77{
78    assert(ptr->length);
79
80    _size += ptr->length;
81    fifo.emplace_hint(fifo.end(), ptr, curTick(), senderId);
82
83    // Drop the extra pushed packets from end of the fifo
84    while (avail() < 0) {
85        DPRINTF(Ethernet, "Fifo is full. Drop packet: len=%d\n",
86                std::prev(fifo.end())->packet->length);
87
88        _size -= std::prev(fifo.end())->packet->length;
89        fifo.erase(std::prev(fifo.end()));
90    }
91
92    if (empty()) {
93        warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
94             "capacity of port fifo (%d)", ptr->length, _maxsize);
95    }
96
97    // Return true if the newly pushed packet gets inserted
98    // at the head of the queue, otherwise return false
99    // We need this information to deschedule the event that has been
100    // scheduled for the old head of queue packet and schedule a new one
101    if (!empty() && fifo.begin()->packet == ptr) {
102        return true;
103    }
104    return false;
105}
106
107void
108EtherSwitch::Interface::PortFifo::pop()
109{
110    if (empty())
111        return;
112
113    assert(_size >= fifo.begin()->packet->length);
114    // Erase the packet at the head of the queue
115    _size -= fifo.begin()->packet->length;
116    fifo.erase(fifo.begin());
117}
118
119void
120EtherSwitch::Interface::PortFifo::clear()
121{
122    fifo.clear();
123    _size = 0;
124}
125
126EtherSwitch::Interface::Interface(const std::string &name,
127                                  EtherSwitch *etherSwitch,
128                                  uint64_t outputBufferSize, Tick delay,
129                                  Tick delay_var, double rate, unsigned id)
130    : EtherInt(name), ticksPerByte(rate), switchDelay(delay),
131      delayVar(delay_var), interfaceId(id), parent(etherSwitch),
132      outputFifo(name + ".outputFifo", outputBufferSize), txEvent(this)
133{
134}
135
136bool
137EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
138{
139    Net::EthAddr destMacAddr(packet->data);
140    Net::EthAddr srcMacAddr(&packet->data[6]);
141
142    learnSenderAddr(srcMacAddr, this);
143    Interface *receiver = lookupDestPort(destMacAddr);
144
145    if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
146        for (auto it : parent->interfaces)
147            if (it != this)
148                it->enqueue(packet, interfaceId);
149    } else {
150        DPRINTF(Ethernet, "sending packet from MAC %x on port "
151                "%s to MAC %x on port %s\n", uint64_t(srcMacAddr),
152                this->name(), uint64_t(destMacAddr), receiver->name());
153
154        receiver->enqueue(packet, interfaceId);
155    }
156    // At the output port, we either have buffer space (no drop) or
157    // don't (drop packet); in both cases packet is received on
158    // the interface successfully and there is no notion of busy
159    // interface here (as we don't have inputFifo)
160    return true;
161}
162
163void
164EtherSwitch::Interface::enqueue(EthPacketPtr packet, unsigned senderId)
165{
166    // assuming per-interface transmission events,
167    // if the newly push packet gets inserted at the head of the queue
168    // (either there was nothing in the queue or the priority of the new
169    // packet was higher than the packets already in the fifo)
170    // then we need to schedule an event at
171    // "curTick" + "switchingDelay of the packet at the head of the fifo"
172    // to send this packet out the external link
173    // otherwise, there is already a txEvent scheduled
174    if (outputFifo.push(packet, senderId)) {
175        parent->reschedule(txEvent, curTick() + switchingDelay(), true);
176    }
177}
178
179void
180EtherSwitch::Interface::transmit()
181{
182    // there should be something in the output queue
183    assert(!outputFifo.empty());
184
185    if (!sendPacket(outputFifo.front())) {
186        DPRINTF(Ethernet, "output port busy...retry later\n");
187        if (!txEvent.scheduled())
188            parent->schedule(txEvent, curTick() + retryTime);
189    } else {
190        DPRINTF(Ethernet, "packet sent: len=%d\n", outputFifo.front()->length);
191        outputFifo.pop();
192        // schedule an event to send the pkt at
193        // the head of queue, if there is any
194        if (!outputFifo.empty()) {
195            parent->schedule(txEvent, curTick() + switchingDelay());
196        }
197    }
198}
199
200Tick
201EtherSwitch::Interface::switchingDelay()
202{
203    Tick delay = (Tick)ceil(((double)outputFifo.front()->length
204                                     * ticksPerByte) + 1.0);
205    if (delayVar != 0)
206                delay += random_mt.random<Tick>(0, delayVar);
207    delay += switchDelay;
208    return delay;
209}
210
211EtherSwitch::Interface*
212EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr)
213{
214    auto it = parent->forwardingTable.find(uint64_t(destMacAddr));
215
216    if (it == parent->forwardingTable.end()) {
217        DPRINTF(Ethernet, "no entry in forwaring table for MAC: "
218                "%x\n", uint64_t(destMacAddr));
219        return nullptr;
220    }
221
222    // check if this entry is valid based on TTL and lastUseTime
223    if ((curTick() - it->second.lastUseTime) > parent->ttl) {
224        // TTL for this mapping has been expired, so this item is not
225        // valide anymore, let's remove it from the map
226        parent->forwardingTable.erase(it);
227        return nullptr;
228    }
229
230    DPRINTF(Ethernet, "found entry for MAC address %x on port %s\n",
231            uint64_t(destMacAddr), it->second.interface->name());
232    return it->second.interface;
233}
234
235void
236EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr,
237                                          Interface *sender)
238{
239    // learn the port for the sending MAC address
240    auto it = parent->forwardingTable.find(uint64_t(srcMacAddr));
241
242    // if the port for sender's MAC address is not cached,
243    // cache it now, otherwise just update lastUseTime time
244    if (it == parent->forwardingTable.end()) {
245        DPRINTF(Ethernet, "adding forwarding table entry for MAC "
246                " address %x on port %s\n", uint64_t(srcMacAddr),
247                sender->name());
248        EtherSwitch::SwitchTableEntry forwardingTableEntry;
249        forwardingTableEntry.interface = sender;
250        forwardingTableEntry.lastUseTime = curTick();
251        parent->forwardingTable.insert(std::make_pair(uint64_t(srcMacAddr),
252            forwardingTableEntry));
253    } else {
254        it->second.lastUseTime = curTick();
255    }
256}
257
258void
259EtherSwitch::serialize(CheckpointOut &cp) const
260{
261    for (auto it : interfaces)
262        it->serializeSection(cp, it->name());
263
264}
265
266void
267EtherSwitch::unserialize(CheckpointIn &cp)
268{
269    for (auto it : interfaces)
270        it->unserializeSection(cp, it->name());
271
272}
273
274void
275EtherSwitch::Interface::serialize(CheckpointOut &cp) const
276{
277    bool event_scheduled = txEvent.scheduled();
278    SERIALIZE_SCALAR(event_scheduled);
279
280    if (event_scheduled) {
281        Tick event_time = txEvent.when();
282        SERIALIZE_SCALAR(event_time);
283    }
284    outputFifo.serializeSection(cp, "outputFifo");
285}
286
287void
288EtherSwitch::Interface::unserialize(CheckpointIn &cp)
289{
290    bool event_scheduled;
291    UNSERIALIZE_SCALAR(event_scheduled);
292
293    if (event_scheduled) {
294        Tick event_time;
295        UNSERIALIZE_SCALAR(event_time);
296        parent->schedule(txEvent, event_time);
297    }
298    outputFifo.unserializeSection(cp, "outputFifo");
299}
300
301void
302EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const
303{
304    packet->serialize("packet", cp);
305    SERIALIZE_SCALAR(recvTick);
306    SERIALIZE_SCALAR(srcId);
307}
308
309void
310EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn &cp)
311{
312    packet = make_shared<EthPacketData>(16384);
313    packet->unserialize("packet", cp);
314    UNSERIALIZE_SCALAR(recvTick);
315    UNSERIALIZE_SCALAR(srcId);
316}
317
318void
319EtherSwitch::Interface::PortFifo::serialize(CheckpointOut &cp) const
320{
321    SERIALIZE_SCALAR(_size);
322    int fifosize = fifo.size();
323
324    SERIALIZE_SCALAR(fifosize);
325
326    int i = 0;
327    for (const auto &entry : fifo)
328        entry.serializeSection(cp, csprintf("entry%d", i++));
329}
330
331void
332EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn &cp)
333{
334    UNSERIALIZE_SCALAR(_size);
335    int fifosize;
336
337    UNSERIALIZE_SCALAR(fifosize);
338    fifo.clear();
339
340    for (int i = 0; i < fifosize; ++i) {
341        PortFifoEntry entry(nullptr, 0, 0);
342
343        entry.unserializeSection(cp, csprintf("entry%d", i));
344
345        fifo.insert(entry);
346
347    }
348}
349
350EtherSwitch *
351EtherSwitchParams::create()
352{
353    return new EtherSwitch(this);
354}
355