Deleted Added
sdiff udiff text old ( 11317:766c3eb44fd8 ) new ( 11533:2aa4d7bd47ec )
full compact
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);
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
75EtherSwitch::Interface::Interface(const std::string &name,
76 EtherSwitch *etherSwitch,
77 uint64_t outputBufferSize, Tick delay,
78 Tick delay_var, double rate)
79 : EtherInt(name), ticksPerByte(rate), switchDelay(delay),
80 delayVar(delay_var), parent(etherSwitch),
81 outputFifo(outputBufferSize), txEvent(this)
82{
83}
84
85bool
86EtherSwitch::Interface::recvPacket(EthPacketPtr packet)
87{
88 Net::EthAddr destMacAddr(packet->data);
89 Net::EthAddr srcMacAddr(&packet->data[6]);
90
91 learnSenderAddr(srcMacAddr, this);
92 Interface *receiver = lookupDestPort(destMacAddr);
93
94 if (!receiver || destMacAddr.multicast() || destMacAddr.broadcast()) {
95 for (auto it : parent->interfaces)
96 if (it != this)
97 it->enqueue(packet);
98 } else {
99 DPRINTF(Ethernet, "sending packet from MAC %x on port "
100 "%s to MAC %x on port %s\n", uint64_t(srcMacAddr),
101 this->name(), uint64_t(destMacAddr), receiver->name());
102
103 receiver->enqueue(packet);
104 }
105 // At the output port, we either have buffer space (no drop) or
106 // don't (drop packet); in both cases packet is received on
107 // the interface successfully and there is no notion of busy
108 // interface here (as we don't have inputFifo)
109 return true;
110}
111
112void
113EtherSwitch::Interface::enqueue(EthPacketPtr packet)
114{
115 if (!outputFifo.push(packet)) {
116 // output buffer full, drop packet
117 DPRINTF(Ethernet, "output buffer full, drop packet\n");
118 return;
119 }
120
121 // assuming per-interface transmission events,
122 // if there was nothing in the Fifo before push the
123 // current packet, then we need to schedule an event at
124 // curTick + switchingDelay to send this packet out the external link
125 // otherwise, there is already a txEvent scheduled
126 if (!txEvent.scheduled()) {
127 parent->schedule(txEvent, curTick() + switchingDelay());
128 }
129}
130
131void
132EtherSwitch::Interface::transmit()
133{
134 // there should be something in the output queue
135 assert(!outputFifo.empty());
136
137 if (!sendPacket(outputFifo.front())) {
138 DPRINTF(Ethernet, "output port busy...retry later\n");
139 if (!txEvent.scheduled())
140 parent->schedule(txEvent, curTick() + retryTime);
141 } else {
142 DPRINTF(Ethernet, "packet sent: len=%d\n", outputFifo.front()->length);
143 outputFifo.pop();
144 // schedule an event to send the pkt at
145 // the head of queue, if there is any
146 if (!outputFifo.empty()) {
147 parent->schedule(txEvent, curTick() + switchingDelay());
148 }
149 }
150}
151
152Tick
153EtherSwitch::Interface::switchingDelay()
154{
155 Tick delay = (Tick)ceil(((double)outputFifo.front()->length
156 * ticksPerByte) + 1.0);
157 if (delayVar != 0)
158 delay += random_mt.random<Tick>(0, delayVar);
159 delay += switchDelay;
160 return delay;
161}
162
163EtherSwitch::Interface*
164EtherSwitch::Interface::lookupDestPort(Net::EthAddr destMacAddr)
165{
166 auto it = parent->forwardingTable.find(uint64_t(destMacAddr));
167
168 if (it == parent->forwardingTable.end()) {
169 DPRINTF(Ethernet, "no entry in forwaring table for MAC: "
170 "%x\n", uint64_t(destMacAddr));
171 return nullptr;
172 }
173
174 // check if this entry is valid based on TTL and lastUseTime
175 if ((curTick() - it->second.lastUseTime) > parent->ttl) {
176 // TTL for this mapping has been expired, so this item is not
177 // valide anymore, let's remove it from the map
178 parent->forwardingTable.erase(it);
179 return nullptr;
180 }
181
182 DPRINTF(Ethernet, "found entry for MAC address %x on port %s\n",
183 uint64_t(destMacAddr), it->second.interface->name());
184 return it->second.interface;
185}
186
187void
188EtherSwitch::Interface::learnSenderAddr(Net::EthAddr srcMacAddr,
189 Interface *sender)
190{
191 // learn the port for the sending MAC address
192 auto it = parent->forwardingTable.find(uint64_t(srcMacAddr));
193
194 // if the port for sender's MAC address is not cached,
195 // cache it now, otherwise just update lastUseTime time
196 if (it == parent->forwardingTable.end()) {
197 DPRINTF(Ethernet, "adding forwarding table entry for MAC "
198 " address %x on port %s\n", uint64_t(srcMacAddr),
199 sender->name());
200 EtherSwitch::SwitchTableEntry forwardingTableEntry;
201 forwardingTableEntry.interface = sender;
202 forwardingTableEntry.lastUseTime = curTick();
203 parent->forwardingTable.insert(std::make_pair(uint64_t(srcMacAddr),
204 forwardingTableEntry));
205 } else {
206 it->second.lastUseTime = curTick();
207 }
208}
209
210void
211EtherSwitch::serialize(CheckpointOut &cp) const
212{
213 for (auto it : interfaces)
214 it->serialize(it->name(), cp);
215}
216
217void
218EtherSwitch::unserialize(CheckpointIn &cp)
219{
220 for (auto it : interfaces)
221 it->unserialize(it->name(), cp);
222}
223
224void
225EtherSwitch::Interface::serialize(const std::string &base, CheckpointOut &cp)
226const
227{
228 bool event_scheduled = txEvent.scheduled();
229 paramOut(cp, base + ".event_scheduled", event_scheduled);
230 if (event_scheduled) {
231 Tick event_time = txEvent.when();
232 paramOut(cp, base + ".event_time", event_time);
233 }
234
235 outputFifo.serialize(base + "outputFifo", cp);
236}
237
238void
239EtherSwitch::Interface::unserialize(const std::string &base, CheckpointIn &cp)
240{
241 bool event_scheduled;
242 paramIn(cp, base + ".event_scheduled", event_scheduled);
243 if (event_scheduled) {
244 Tick event_time;
245 paramIn(cp, base + ".event_time", event_time);
246 parent->schedule(txEvent, event_time);
247 }
248
249 outputFifo.unserialize(base + "outputFifo", cp);
250}
251
252EtherSwitch *
253EtherSwitchParams::create()
254{
255 return new EtherSwitch(this);
256}