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.
30 * Device model for an ethernet switch
33 #include "dev/net/etherswitch.hh"
35 #include "base/random.hh"
36 #include "base/trace.hh"
37 #include "debug/EthernetAll.hh"
38 #include "sim/core.hh"
42 EtherSwitch::EtherSwitch(const Params
*p
)
43 : SimObject(p
), ttl(p
->time_to_live
)
45 for (int i
= 0; i
< p
->port_interface_connection_count
; ++i
) {
46 std::string interfaceName
= csprintf("%s.interface%d", name(), i
);
47 Interface
*interface
= new Interface(interfaceName
, this,
48 p
->output_buffer_size
, p
->delay
,
49 p
->delay_var
, p
->fabric_speed
, i
);
50 interfaces
.push_back(interface
);
54 EtherSwitch::~EtherSwitch()
56 for (auto it
: interfaces
)
63 EtherSwitch::getPort(const std::string
&if_name
, PortID idx
)
65 if (if_name
== "interface") {
66 panic_if(idx
< 0 || idx
>= interfaces
.size(), "index out of bounds");
67 return *interfaces
.at(idx
);
70 return SimObject::getPort(if_name
, idx
);
74 EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr
, unsigned senderId
)
79 fifo
.emplace_hint(fifo
.end(), ptr
, curTick(), senderId
);
81 // Drop the extra pushed packets from end of the fifo
83 DPRINTF(Ethernet
, "Fifo is full. Drop packet: len=%d\n",
84 std::prev(fifo
.end())->packet
->length
);
86 _size
-= std::prev(fifo
.end())->packet
->length
;
87 fifo
.erase(std::prev(fifo
.end()));
91 warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
92 "capacity of port fifo (%d)", ptr
->length
, _maxsize
);
95 // Return true if the newly pushed packet gets inserted
96 // at the head of the queue, otherwise return false
97 // We need this information to deschedule the event that has been
98 // scheduled for the old head of queue packet and schedule a new one
99 if (!empty() && fifo
.begin()->packet
== ptr
) {
106 EtherSwitch::Interface::PortFifo::pop()
111 assert(_size
>= fifo
.begin()->packet
->length
);
112 // Erase the packet at the head of the queue
113 _size
-= fifo
.begin()->packet
->length
;
114 fifo
.erase(fifo
.begin());
118 EtherSwitch::Interface::PortFifo::clear()
124 EtherSwitch::Interface::Interface(const std::string
&name
,
125 EtherSwitch
*etherSwitch
,
126 uint64_t outputBufferSize
, Tick delay
,
127 Tick delay_var
, double rate
, unsigned id
)
128 : EtherInt(name
), ticksPerByte(rate
), switchDelay(delay
),
129 delayVar(delay_var
), interfaceId(id
), parent(etherSwitch
),
130 outputFifo(name
+ ".outputFifo", outputBufferSize
),
131 txEvent([this]{ transmit(); }, name
)
136 EtherSwitch::Interface::recvPacket(EthPacketPtr packet
)
138 Net::EthAddr
destMacAddr(packet
->data
);
139 Net::EthAddr
srcMacAddr(&packet
->data
[6]);
141 learnSenderAddr(srcMacAddr
, this);
142 Interface
*receiver
= lookupDestPort(destMacAddr
);
144 if (!receiver
|| destMacAddr
.multicast() || destMacAddr
.broadcast()) {
145 for (auto it
: parent
->interfaces
)
147 it
->enqueue(packet
, interfaceId
);
149 DPRINTF(Ethernet
, "sending packet from MAC %x on port "
150 "%s to MAC %x on port %s\n", uint64_t(srcMacAddr
),
151 this->name(), uint64_t(destMacAddr
), receiver
->name());
153 receiver
->enqueue(packet
, interfaceId
);
155 // At the output port, we either have buffer space (no drop) or
156 // don't (drop packet); in both cases packet is received on
157 // the interface successfully and there is no notion of busy
158 // interface here (as we don't have inputFifo)
163 EtherSwitch::Interface::enqueue(EthPacketPtr packet
, unsigned senderId
)
165 // assuming per-interface transmission events,
166 // if the newly push packet gets inserted at the head of the queue
167 // (either there was nothing in the queue or the priority of the new
168 // packet was higher than the packets already in the fifo)
169 // then we need to schedule an event at
170 // "curTick" + "switchingDelay of the packet at the head of the fifo"
171 // to send this packet out the external link
172 // otherwise, there is already a txEvent scheduled
173 if (outputFifo
.push(packet
, senderId
)) {
174 parent
->reschedule(txEvent
, curTick() + switchingDelay(), true);
179 EtherSwitch::Interface::transmit()
181 // there should be something in the output queue
182 assert(!outputFifo
.empty());
184 if (!sendPacket(outputFifo
.front())) {
185 DPRINTF(Ethernet
, "output port busy...retry later\n");
186 if (!txEvent
.scheduled())
187 parent
->schedule(txEvent
, curTick() + retryTime
);
189 DPRINTF(Ethernet
, "packet sent: len=%d\n", outputFifo
.front()->length
);
191 // schedule an event to send the pkt at
192 // the head of queue, if there is any
193 if (!outputFifo
.empty()) {
194 parent
->schedule(txEvent
, curTick() + switchingDelay());
200 EtherSwitch::Interface::switchingDelay()
202 Tick delay
= (Tick
)ceil(((double)outputFifo
.front()->simLength
203 * ticksPerByte
) + 1.0);
205 delay
+= random_mt
.random
<Tick
>(0, delayVar
);
206 delay
+= switchDelay
;
210 EtherSwitch::Interface
*
211 EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr
)
213 auto it
= parent
->forwardingTable
.find(uint64_t(destMacAddr
));
215 if (it
== parent
->forwardingTable
.end()) {
216 DPRINTF(Ethernet
, "no entry in forwaring table for MAC: "
217 "%x\n", uint64_t(destMacAddr
));
221 // check if this entry is valid based on TTL and lastUseTime
222 if ((curTick() - it
->second
.lastUseTime
) > parent
->ttl
) {
223 // TTL for this mapping has been expired, so this item is not
224 // valide anymore, let's remove it from the map
225 parent
->forwardingTable
.erase(it
);
229 DPRINTF(Ethernet
, "found entry for MAC address %x on port %s\n",
230 uint64_t(destMacAddr
), it
->second
.interface
->name());
231 return it
->second
.interface
;
235 EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr
,
238 // learn the port for the sending MAC address
239 auto it
= parent
->forwardingTable
.find(uint64_t(srcMacAddr
));
241 // if the port for sender's MAC address is not cached,
242 // cache it now, otherwise just update lastUseTime time
243 if (it
== parent
->forwardingTable
.end()) {
244 DPRINTF(Ethernet
, "adding forwarding table entry for MAC "
245 " address %x on port %s\n", uint64_t(srcMacAddr
),
247 EtherSwitch::SwitchTableEntry forwardingTableEntry
;
248 forwardingTableEntry
.interface
= sender
;
249 forwardingTableEntry
.lastUseTime
= curTick();
250 parent
->forwardingTable
.insert(std::make_pair(uint64_t(srcMacAddr
),
251 forwardingTableEntry
));
253 it
->second
.lastUseTime
= curTick();
258 EtherSwitch::serialize(CheckpointOut
&cp
) const
260 for (auto it
: interfaces
)
261 it
->serializeSection(cp
, it
->name());
266 EtherSwitch::unserialize(CheckpointIn
&cp
)
268 for (auto it
: interfaces
)
269 it
->unserializeSection(cp
, it
->name());
274 EtherSwitch::Interface::serialize(CheckpointOut
&cp
) const
276 bool event_scheduled
= txEvent
.scheduled();
277 SERIALIZE_SCALAR(event_scheduled
);
279 if (event_scheduled
) {
280 Tick event_time
= txEvent
.when();
281 SERIALIZE_SCALAR(event_time
);
283 outputFifo
.serializeSection(cp
, "outputFifo");
287 EtherSwitch::Interface::unserialize(CheckpointIn
&cp
)
289 bool event_scheduled
;
290 UNSERIALIZE_SCALAR(event_scheduled
);
292 if (event_scheduled
) {
294 UNSERIALIZE_SCALAR(event_time
);
295 parent
->schedule(txEvent
, event_time
);
297 outputFifo
.unserializeSection(cp
, "outputFifo");
301 EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut
&cp
) const
303 packet
->serialize("packet", cp
);
304 SERIALIZE_SCALAR(recvTick
);
305 SERIALIZE_SCALAR(srcId
);
309 EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn
&cp
)
311 packet
= make_shared
<EthPacketData
>(16384);
312 packet
->unserialize("packet", cp
);
313 UNSERIALIZE_SCALAR(recvTick
);
314 UNSERIALIZE_SCALAR(srcId
);
318 EtherSwitch::Interface::PortFifo::serialize(CheckpointOut
&cp
) const
320 SERIALIZE_SCALAR(_size
);
321 int fifosize
= fifo
.size();
323 SERIALIZE_SCALAR(fifosize
);
326 for (const auto &entry
: fifo
)
327 entry
.serializeSection(cp
, csprintf("entry%d", i
++));
331 EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn
&cp
)
333 UNSERIALIZE_SCALAR(_size
);
336 UNSERIALIZE_SCALAR(fifosize
);
339 for (int i
= 0; i
< fifosize
; ++i
) {
340 PortFifoEntry
entry(nullptr, 0, 0);
342 entry
.unserializeSection(cp
, csprintf("entry%d", i
));
350 EtherSwitchParams::create()
352 return new EtherSwitch(this);