2 * Copyright (c) 2014 The Regents of The University of Michigan
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.
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.
28 * Authors: Anthony Gutierrez
33 * Device model for an ethernet switch
36 #include "dev/net/etherswitch.hh"
38 #include "base/random.hh"
39 #include "base/trace.hh"
40 #include "debug/EthernetAll.hh"
41 #include "sim/core.hh"
45 EtherSwitch::EtherSwitch(const Params
*p
)
46 : EtherObject(p
), ttl(p
->time_to_live
)
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
);
57 EtherSwitch::~EtherSwitch()
59 for (auto it
: interfaces
)
66 EtherSwitch::getEthPort(const std::string
&if_name
, int idx
)
68 if (idx
< 0 || idx
>= interfaces
.size())
71 Interface
*interface
= interfaces
.at(idx
);
72 panic_if(interface
->getPeer(), "interface already connected\n");
78 EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr
, unsigned senderId
)
83 fifo
.emplace_hint(fifo
.end(), ptr
, curTick(), senderId
);
85 // Drop the extra pushed packets from end of the fifo
87 DPRINTF(Ethernet
, "Fifo is full. Drop packet: len=%d\n",
88 std::prev(fifo
.end())->packet
->length
);
90 _size
-= std::prev(fifo
.end())->packet
->length
;
91 fifo
.erase(std::prev(fifo
.end()));
95 warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
96 "capacity of port fifo (%d)", ptr
->length
, _maxsize
);
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
) {
110 EtherSwitch::Interface::PortFifo::pop()
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());
122 EtherSwitch::Interface::PortFifo::clear()
128 EtherSwitch::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
)
140 EtherSwitch::Interface::recvPacket(EthPacketPtr packet
)
142 Net::EthAddr
destMacAddr(packet
->data
);
143 Net::EthAddr
srcMacAddr(&packet
->data
[6]);
145 learnSenderAddr(srcMacAddr
, this);
146 Interface
*receiver
= lookupDestPort(destMacAddr
);
148 if (!receiver
|| destMacAddr
.multicast() || destMacAddr
.broadcast()) {
149 for (auto it
: parent
->interfaces
)
151 it
->enqueue(packet
, interfaceId
);
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());
157 receiver
->enqueue(packet
, interfaceId
);
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)
167 EtherSwitch::Interface::enqueue(EthPacketPtr packet
, unsigned senderId
)
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);
183 EtherSwitch::Interface::transmit()
185 // there should be something in the output queue
186 assert(!outputFifo
.empty());
188 if (!sendPacket(outputFifo
.front())) {
189 DPRINTF(Ethernet
, "output port busy...retry later\n");
190 if (!txEvent
.scheduled())
191 parent
->schedule(txEvent
, curTick() + retryTime
);
193 DPRINTF(Ethernet
, "packet sent: len=%d\n", outputFifo
.front()->length
);
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());
204 EtherSwitch::Interface::switchingDelay()
206 Tick delay
= (Tick
)ceil(((double)outputFifo
.front()->simLength
207 * ticksPerByte
) + 1.0);
209 delay
+= random_mt
.random
<Tick
>(0, delayVar
);
210 delay
+= switchDelay
;
214 EtherSwitch::Interface
*
215 EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr
)
217 auto it
= parent
->forwardingTable
.find(uint64_t(destMacAddr
));
219 if (it
== parent
->forwardingTable
.end()) {
220 DPRINTF(Ethernet
, "no entry in forwaring table for MAC: "
221 "%x\n", uint64_t(destMacAddr
));
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
);
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
;
239 EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr
,
242 // learn the port for the sending MAC address
243 auto it
= parent
->forwardingTable
.find(uint64_t(srcMacAddr
));
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
),
251 EtherSwitch::SwitchTableEntry forwardingTableEntry
;
252 forwardingTableEntry
.interface
= sender
;
253 forwardingTableEntry
.lastUseTime
= curTick();
254 parent
->forwardingTable
.insert(std::make_pair(uint64_t(srcMacAddr
),
255 forwardingTableEntry
));
257 it
->second
.lastUseTime
= curTick();
262 EtherSwitch::serialize(CheckpointOut
&cp
) const
264 for (auto it
: interfaces
)
265 it
->serializeSection(cp
, it
->name());
270 EtherSwitch::unserialize(CheckpointIn
&cp
)
272 for (auto it
: interfaces
)
273 it
->unserializeSection(cp
, it
->name());
278 EtherSwitch::Interface::serialize(CheckpointOut
&cp
) const
280 bool event_scheduled
= txEvent
.scheduled();
281 SERIALIZE_SCALAR(event_scheduled
);
283 if (event_scheduled
) {
284 Tick event_time
= txEvent
.when();
285 SERIALIZE_SCALAR(event_time
);
287 outputFifo
.serializeSection(cp
, "outputFifo");
291 EtherSwitch::Interface::unserialize(CheckpointIn
&cp
)
293 bool event_scheduled
;
294 UNSERIALIZE_SCALAR(event_scheduled
);
296 if (event_scheduled
) {
298 UNSERIALIZE_SCALAR(event_time
);
299 parent
->schedule(txEvent
, event_time
);
301 outputFifo
.unserializeSection(cp
, "outputFifo");
305 EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut
&cp
) const
307 packet
->serialize("packet", cp
);
308 SERIALIZE_SCALAR(recvTick
);
309 SERIALIZE_SCALAR(srcId
);
313 EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn
&cp
)
315 packet
= make_shared
<EthPacketData
>(16384);
316 packet
->unserialize("packet", cp
);
317 UNSERIALIZE_SCALAR(recvTick
);
318 UNSERIALIZE_SCALAR(srcId
);
322 EtherSwitch::Interface::PortFifo::serialize(CheckpointOut
&cp
) const
324 SERIALIZE_SCALAR(_size
);
325 int fifosize
= fifo
.size();
327 SERIALIZE_SCALAR(fifosize
);
330 for (const auto &entry
: fifo
)
331 entry
.serializeSection(cp
, csprintf("entry%d", i
++));
335 EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn
&cp
)
337 UNSERIALIZE_SCALAR(_size
);
340 UNSERIALIZE_SCALAR(fifosize
);
343 for (int i
= 0; i
< fifosize
; ++i
) {
344 PortFifoEntry
entry(nullptr, 0, 0);
346 entry
.unserializeSection(cp
, csprintf("entry%d", i
));
354 EtherSwitchParams::create()
356 return new EtherSwitch(this);