misc: merge branch 'release-staging-v19.0.0.0' into develop
[gem5.git] / src / dev / net / etherswitch.cc
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
29 /* @file
30 * Device model for an ethernet switch
31 */
32
33 #include "dev/net/etherswitch.hh"
34
35 #include "base/random.hh"
36 #include "base/trace.hh"
37 #include "debug/EthernetAll.hh"
38 #include "sim/core.hh"
39
40 using namespace std;
41
42 EtherSwitch::EtherSwitch(const Params *p)
43 : SimObject(p), ttl(p->time_to_live)
44 {
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);
51 }
52 }
53
54 EtherSwitch::~EtherSwitch()
55 {
56 for (auto it : interfaces)
57 delete it;
58
59 interfaces.clear();
60 }
61
62 Port &
63 EtherSwitch::getPort(const std::string &if_name, PortID idx)
64 {
65 if (if_name == "interface") {
66 panic_if(idx < 0 || idx >= interfaces.size(), "index out of bounds");
67 return *interfaces.at(idx);
68 }
69
70 return SimObject::getPort(if_name, idx);
71 }
72
73 bool
74 EtherSwitch::Interface::PortFifo::push(EthPacketPtr ptr, unsigned senderId)
75 {
76 assert(ptr->length);
77
78 _size += ptr->length;
79 fifo.emplace_hint(fifo.end(), ptr, curTick(), senderId);
80
81 // Drop the extra pushed packets from end of the fifo
82 while (avail() < 0) {
83 DPRINTF(Ethernet, "Fifo is full. Drop packet: len=%d\n",
84 std::prev(fifo.end())->packet->length);
85
86 _size -= std::prev(fifo.end())->packet->length;
87 fifo.erase(std::prev(fifo.end()));
88 }
89
90 if (empty()) {
91 warn("EtherSwitch: Packet length (%d) exceeds the maximum storage "
92 "capacity of port fifo (%d)", ptr->length, _maxsize);
93 }
94
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) {
100 return true;
101 }
102 return false;
103 }
104
105 void
106 EtherSwitch::Interface::PortFifo::pop()
107 {
108 if (empty())
109 return;
110
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());
115 }
116
117 void
118 EtherSwitch::Interface::PortFifo::clear()
119 {
120 fifo.clear();
121 _size = 0;
122 }
123
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)
132 {
133 }
134
135 bool
136 EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
137 {
138 Net::EthAddr destMacAddr(packet->data);
139 Net::EthAddr srcMacAddr(&packet->data[6]);
140
141 learnSenderAddr(srcMacAddr, this);
142 Interface *receiver = lookupDestPort(destMacAddr);
143
144 if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
145 for (auto it : parent->interfaces)
146 if (it != this)
147 it->enqueue(packet, interfaceId);
148 } else {
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());
152
153 receiver->enqueue(packet, interfaceId);
154 }
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)
159 return true;
160 }
161
162 void
163 EtherSwitch::Interface::enqueue(EthPacketPtr packet, unsigned senderId)
164 {
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);
175 }
176 }
177
178 void
179 EtherSwitch::Interface::transmit()
180 {
181 // there should be something in the output queue
182 assert(!outputFifo.empty());
183
184 if (!sendPacket(outputFifo.front())) {
185 DPRINTF(Ethernet, "output port busy...retry later\n");
186 if (!txEvent.scheduled())
187 parent->schedule(txEvent, curTick() + retryTime);
188 } else {
189 DPRINTF(Ethernet, "packet sent: len=%d\n", outputFifo.front()->length);
190 outputFifo.pop();
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());
195 }
196 }
197 }
198
199 Tick
200 EtherSwitch::Interface::switchingDelay()
201 {
202 Tick delay = (Tick)ceil(((double)outputFifo.front()->simLength
203 * ticksPerByte) + 1.0);
204 if (delayVar != 0)
205 delay += random_mt.random<Tick>(0, delayVar);
206 delay += switchDelay;
207 return delay;
208 }
209
210 EtherSwitch::Interface*
211 EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr)
212 {
213 auto it = parent->forwardingTable.find(uint64_t(destMacAddr));
214
215 if (it == parent->forwardingTable.end()) {
216 DPRINTF(Ethernet, "no entry in forwaring table for MAC: "
217 "%x\n", uint64_t(destMacAddr));
218 return nullptr;
219 }
220
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);
226 return nullptr;
227 }
228
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;
232 }
233
234 void
235 EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr,
236 Interface *sender)
237 {
238 // learn the port for the sending MAC address
239 auto it = parent->forwardingTable.find(uint64_t(srcMacAddr));
240
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),
246 sender->name());
247 EtherSwitch::SwitchTableEntry forwardingTableEntry;
248 forwardingTableEntry.interface = sender;
249 forwardingTableEntry.lastUseTime = curTick();
250 parent->forwardingTable.insert(std::make_pair(uint64_t(srcMacAddr),
251 forwardingTableEntry));
252 } else {
253 it->second.lastUseTime = curTick();
254 }
255 }
256
257 void
258 EtherSwitch::serialize(CheckpointOut &cp) const
259 {
260 for (auto it : interfaces)
261 it->serializeSection(cp, it->name());
262
263 }
264
265 void
266 EtherSwitch::unserialize(CheckpointIn &cp)
267 {
268 for (auto it : interfaces)
269 it->unserializeSection(cp, it->name());
270
271 }
272
273 void
274 EtherSwitch::Interface::serialize(CheckpointOut &cp) const
275 {
276 bool event_scheduled = txEvent.scheduled();
277 SERIALIZE_SCALAR(event_scheduled);
278
279 if (event_scheduled) {
280 Tick event_time = txEvent.when();
281 SERIALIZE_SCALAR(event_time);
282 }
283 outputFifo.serializeSection(cp, "outputFifo");
284 }
285
286 void
287 EtherSwitch::Interface::unserialize(CheckpointIn &cp)
288 {
289 bool event_scheduled;
290 UNSERIALIZE_SCALAR(event_scheduled);
291
292 if (event_scheduled) {
293 Tick event_time;
294 UNSERIALIZE_SCALAR(event_time);
295 parent->schedule(txEvent, event_time);
296 }
297 outputFifo.unserializeSection(cp, "outputFifo");
298 }
299
300 void
301 EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const
302 {
303 packet->serialize("packet", cp);
304 SERIALIZE_SCALAR(recvTick);
305 SERIALIZE_SCALAR(srcId);
306 }
307
308 void
309 EtherSwitch::Interface::PortFifoEntry::unserialize(CheckpointIn &cp)
310 {
311 packet = make_shared<EthPacketData>(16384);
312 packet->unserialize("packet", cp);
313 UNSERIALIZE_SCALAR(recvTick);
314 UNSERIALIZE_SCALAR(srcId);
315 }
316
317 void
318 EtherSwitch::Interface::PortFifo::serialize(CheckpointOut &cp) const
319 {
320 SERIALIZE_SCALAR(_size);
321 int fifosize = fifo.size();
322
323 SERIALIZE_SCALAR(fifosize);
324
325 int i = 0;
326 for (const auto &entry : fifo)
327 entry.serializeSection(cp, csprintf("entry%d", i++));
328 }
329
330 void
331 EtherSwitch::Interface::PortFifo::unserialize(CheckpointIn &cp)
332 {
333 UNSERIALIZE_SCALAR(_size);
334 int fifosize;
335
336 UNSERIALIZE_SCALAR(fifosize);
337 fifo.clear();
338
339 for (int i = 0; i < fifosize; ++i) {
340 PortFifoEntry entry(nullptr, 0, 0);
341
342 entry.unserializeSection(cp, csprintf("entry%d", i));
343
344 fifo.insert(entry);
345
346 }
347 }
348
349 EtherSwitch *
350 EtherSwitchParams::create()
351 {
352 return new EtherSwitch(this);
353 }