sparc: Use big endian packet accessors.
[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 * 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
43 using namespace std;
44
45 EtherSwitch::EtherSwitch(const Params *p)
46 : EtherObject(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
57 EtherSwitch::~EtherSwitch()
58 {
59 for (auto it : interfaces)
60 delete it;
61
62 interfaces.clear();
63 }
64
65 EtherInt*
66 EtherSwitch::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
77 bool
78 EtherSwitch::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
109 void
110 EtherSwitch::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
121 void
122 EtherSwitch::Interface::PortFifo::clear()
123 {
124 fifo.clear();
125 _size = 0;
126 }
127
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)
136 {
137 }
138
139 bool
140 EtherSwitch::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
166 void
167 EtherSwitch::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
182 void
183 EtherSwitch::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
203 Tick
204 EtherSwitch::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
214 EtherSwitch::Interface*
215 EtherSwitch::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
238 void
239 EtherSwitch::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
261 void
262 EtherSwitch::serialize(CheckpointOut &cp) const
263 {
264 for (auto it : interfaces)
265 it->serializeSection(cp, it->name());
266
267 }
268
269 void
270 EtherSwitch::unserialize(CheckpointIn &cp)
271 {
272 for (auto it : interfaces)
273 it->unserializeSection(cp, it->name());
274
275 }
276
277 void
278 EtherSwitch::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
290 void
291 EtherSwitch::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
304 void
305 EtherSwitch::Interface::PortFifoEntry::serialize(CheckpointOut &cp) const
306 {
307 packet->serialize("packet", cp);
308 SERIALIZE_SCALAR(recvTick);
309 SERIALIZE_SCALAR(srcId);
310 }
311
312 void
313 EtherSwitch::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
321 void
322 EtherSwitch::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
334 void
335 EtherSwitch::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
353 EtherSwitch *
354 EtherSwitchParams::create()
355 {
356 return new EtherSwitch(this);
357 }