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