diff options
author | Gabe Black <gabeblack@google.com> | 2017-06-03 05:03:18 -0700 |
---|---|---|
committer | Gabe Black <gabeblack@google.com> | 2017-06-03 15:23:46 +0000 |
commit | 2ce045341b73166cad2ef95b37c87d8d4c590d80 (patch) | |
tree | eb46ce8c910cd04d39477da459915802105c5729 /src/dev/net/ethertap.cc | |
parent | f9ad4066d71748829c11868b5b92ba092ee9513b (diff) | |
download | gem5-2ce045341b73166cad2ef95b37c87d8d4c590d80.tar.xz |
dev: Refactor the EtherTapStub to make room for using tap.
A lot of the implementation of EtherTapStub can be shared with a version
which uses a tap device directly. This change factors out those parts to
accommodate that.
Change-Id: I9c2e31f1be139ca73859a83f05457cef90101006
Reviewed-on: https://gem5-review.googlesource.com/3645
Reviewed-by: Nathan Binkert <nate@binkert.org>
Maintainer: Nathan Binkert <nate@binkert.org>
Diffstat (limited to 'src/dev/net/ethertap.cc')
-rw-r--r-- | src/dev/net/ethertap.cc | 385 |
1 files changed, 210 insertions, 175 deletions
diff --git a/src/dev/net/ethertap.cc b/src/dev/net/ethertap.cc index e7b928972..f08de0ebf 100644 --- a/src/dev/net/ethertap.cc +++ b/src/dev/net/ethertap.cc @@ -56,39 +56,180 @@ using namespace std; -/** - */ +class TapEvent : public PollEvent +{ + protected: + EtherTapBase *tap; + + public: + TapEvent(EtherTapBase *_tap, int fd, int e) + : PollEvent(fd, e), tap(_tap) {} + virtual void process(int revent) { tap->recvReal(revent); } +}; + +EtherTapBase::EtherTapBase(const Params *p) + : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL), + interface(NULL), txEvent(this) +{ + buffer = new uint8_t[buflen]; + interface = new EtherTapInt(name() + ".interface", this); +} + +EtherTapBase::~EtherTapBase() +{ + delete buffer; + delete event; + delete interface; +} + +void +EtherTapBase::serialize(CheckpointOut &cp) const +{ + SERIALIZE_SCALAR(buflen); + uint8_t *buffer = (uint8_t *)this->buffer; + SERIALIZE_ARRAY(buffer, buflen); + + bool tapevent_present = false; + if (event) { + tapevent_present = true; + SERIALIZE_SCALAR(tapevent_present); + event->serialize(cp); + } else { + SERIALIZE_SCALAR(tapevent_present); + } +} + +void +EtherTapBase::unserialize(CheckpointIn &cp) +{ + UNSERIALIZE_SCALAR(buflen); + uint8_t *buffer = (uint8_t *)this->buffer; + UNSERIALIZE_ARRAY(buffer, buflen); + + bool tapevent_present; + UNSERIALIZE_SCALAR(tapevent_present); + if (tapevent_present) { + event = new TapEvent(this, 0, 0); + event->unserialize(cp); + if (event->queued()) + pollQueue.schedule(event); + } +} + + +void +EtherTapBase::pollFd(int fd) +{ + assert(!event); + event = new TapEvent(this, fd, POLLIN|POLLERR); + pollQueue.schedule(event); +} + +void +EtherTapBase::stopPolling() +{ + assert(event); + delete event; + event = NULL; +} + + +EtherInt* +EtherTapBase::getEthPort(const std::string &if_name, int idx) +{ + if (if_name == "tap") { + if (interface->getPeer()) + panic("Interface already connected to\n"); + return interface; + } + return NULL; +} + +bool +EtherTapBase::recvSimulated(EthPacketPtr packet) +{ + if (dump) + dump->dump(packet); + + DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + + bool success = sendReal(packet->data, packet->length); + + interface->recvDone(); + + return success; +} + +void +EtherTapBase::sendSimulated(void *data, size_t len) +{ + EthPacketPtr packet; + packet = make_shared<EthPacketData>(len); + packet->length = len; + packet->simLength = len; + memcpy(packet->data, data, len); + + DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + if (!packetBuffer.empty() || !interface->sendPacket(packet)) { + DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); + packetBuffer.push(packet); + if (!txEvent.scheduled()) + schedule(txEvent, curTick() + retryTime); + } else if (dump) { + dump->dump(packet); + } +} + +void +EtherTapBase::retransmit() +{ + if (packetBuffer.empty()) + return; + + EthPacketPtr packet = packetBuffer.front(); + if (interface->sendPacket(packet)) { + if (dump) + dump->dump(packet); + DPRINTF(Ethernet, "EtherTap retransmit\n"); + packetBuffer.front() = NULL; + packetBuffer.pop(); + } + + if (!packetBuffer.empty() && !txEvent.scheduled()) + schedule(txEvent, curTick() + retryTime); +} + + class TapListener { protected: - /** - */ class Event : public PollEvent { protected: TapListener *listener; public: - Event(TapListener *l, int fd, int e) - : PollEvent(fd, e), listener(l) {} + Event(TapListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {} - virtual void process(int revent) { listener->accept(); } + void process(int revent) override { listener->accept(); } }; friend class Event; Event *event; + void accept(); + protected: ListenSocket listener; EtherTapStub *tap; int port; public: - TapListener(EtherTapStub *t, int p) - : event(NULL), tap(t), port(p) {} - ~TapListener() { if (event) delete event; } + TapListener(EtherTapStub *t, int p) : event(NULL), tap(t), port(p) {} + ~TapListener() { delete event; } - void accept(); void listen(); }; @@ -121,226 +262,120 @@ TapListener::accept() tap->attach(sfd); } -/** - */ -class TapEvent : public PollEvent -{ - protected: - EtherTapStub *tap; - - public: - TapEvent(EtherTapStub *_tap, int fd, int e) - : PollEvent(fd, e), tap(_tap) {} - virtual void process(int revent) { tap->process(revent); } -}; -EtherTapStub::EtherTapStub(const Params *p) - : EtherObject(p), event(NULL), socket(-1), buflen(p->bufsz), dump(p->dump), - interface(NULL), txEvent(this) +EtherTapStub::EtherTapStub(const Params *p) : EtherTapBase(p), socket(-1) { if (ListenSocket::allDisabled()) fatal("All listeners are disabled! EtherTapStub can't work!"); - buffer = new char[buflen]; listener = new TapListener(this, p->port); listener->listen(); - interface = new EtherTapInt(name() + ".interface", this); } EtherTapStub::~EtherTapStub() { - if (event) - delete event; - if (buffer) - delete [] buffer; - - delete interface; delete listener; } void +EtherTapStub::serialize(CheckpointOut &cp) const +{ + EtherTapBase::serialize(cp); + + SERIALIZE_SCALAR(socket); + SERIALIZE_SCALAR(buffer_used); + SERIALIZE_SCALAR(frame_len); +} + +void +EtherTapStub::unserialize(CheckpointIn &cp) +{ + EtherTapBase::unserialize(cp); + + UNSERIALIZE_SCALAR(socket); + UNSERIALIZE_SCALAR(buffer_used); + UNSERIALIZE_SCALAR(frame_len); +} + + +void EtherTapStub::attach(int fd) { if (socket != -1) close(fd); - buffer_offset = 0; - data_len = 0; + buffer_used = 0; + frame_len = 0; socket = fd; DPRINTF(Ethernet, "EtherTapStub attached\n"); - event = new TapEvent(this, socket, POLLIN|POLLERR); - pollQueue.schedule(event); + pollFd(socket); } void EtherTapStub::detach() { DPRINTF(Ethernet, "EtherTapStub detached\n"); - delete event; - event = 0; + stopPolling(); close(socket); socket = -1; } -bool -EtherTapStub::recvPacket(EthPacketPtr packet) -{ - if (dump) - dump->dump(packet); - - DPRINTF(Ethernet, "EtherTapStub output len=%d\n", packet->length); - DDUMP(EthernetData, packet->data, packet->length); - uint32_t len = htonl(packet->length); - ssize_t ret = write(socket, &len, sizeof(len)); - if (ret != sizeof(len)) - return false; - ret = write(socket, packet->data, packet->length); - if (ret != packet->length) - return false; - - interface->recvDone(); - - return true; -} - void -EtherTapStub::sendDone() -{} - -void -EtherTapStub::process(int revent) +EtherTapStub::recvReal(int revent) { if (revent & POLLERR) { detach(); return; } - char *data = buffer + sizeof(uint32_t); if (!(revent & POLLIN)) return; - if (buffer_offset < data_len + sizeof(uint32_t)) { - int len = read(socket, buffer + buffer_offset, buflen - buffer_offset); - if (len == 0) { - detach(); - return; - } - - buffer_offset += len; - - if (data_len == 0) - data_len = ntohl(*(uint32_t *)buffer); - - DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d " - "data_len=%d\n", len, buffer_offset, data_len); - } - - while (data_len != 0 && buffer_offset >= data_len + sizeof(uint32_t)) { - EthPacketPtr packet; - packet = make_shared<EthPacketData>(data_len); - packet->length = data_len; - packet->simLength = data_len; - memcpy(packet->data, data, data_len); - - assert(buffer_offset >= data_len + sizeof(uint32_t)); - buffer_offset -= data_len + sizeof(uint32_t); - if (buffer_offset > 0) { - memmove(buffer, data + data_len, buffer_offset); - data_len = ntohl(*(uint32_t *)buffer); - } else - data_len = 0; - - DPRINTF(Ethernet, "EtherTapStub input len=%d\n", packet->length); - DDUMP(EthernetData, packet->data, packet->length); - if (!interface->sendPacket(packet)) { - DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); - packetBuffer.push(packet); - if (!txEvent.scheduled()) - schedule(txEvent, curTick() + retryTime); - } else if (dump) { - dump->dump(packet); - } - } -} - -void -EtherTapStub::retransmit() -{ - if (packetBuffer.empty()) + // Read in as much of the new data as we can. + int len = read(socket, buffer + buffer_used, buflen - buffer_used); + if (len == 0) { + detach(); return; - - EthPacketPtr packet = packetBuffer.front(); - if (interface->sendPacket(packet)) { - if (dump) - dump->dump(packet); - DPRINTF(Ethernet, "EtherTapStub retransmit\n"); - packetBuffer.front() = NULL; - packetBuffer.pop(); } + buffer_used += len; - if (!packetBuffer.empty() && !txEvent.scheduled()) - schedule(txEvent, curTick() + retryTime); -} + // If there's not enough data for the frame length, wait for more. + if (buffer_used < sizeof(uint32_t)) + return; -EtherInt* -EtherTapStub::getEthPort(const std::string &if_name, int idx) -{ - if (if_name == "tap") { - if (interface->getPeer()) - panic("Interface already connected to\n"); - return interface; - } - return NULL; -} + if (frame_len == 0) + frame_len = ntohl(*(uint32_t *)buffer); + DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d " + "frame_len=%d\n", len, buffer_used, frame_len); -//===================================================================== + uint8_t *frame_start = &buffer[sizeof(uint32_t)]; + while (frame_len != 0 && buffer_used >= frame_len + sizeof(uint32_t)) { + sendSimulated(frame_start, frame_len); -void -EtherTapStub::serialize(CheckpointOut &cp) const -{ - SERIALIZE_SCALAR(socket); - SERIALIZE_SCALAR(buflen); - uint8_t *buffer = (uint8_t *)this->buffer; - SERIALIZE_ARRAY(buffer, buflen); - SERIALIZE_SCALAR(buffer_offset); - SERIALIZE_SCALAR(data_len); + // Bookkeeping. + buffer_used -= frame_len + sizeof(uint32_t); + if (buffer_used > 0) { + // If there's still any data left, move it into position. + memmove(buffer, frame_start + frame_len, buffer_used); + } + frame_len = 0; - bool tapevent_present = false; - if (event) { - tapevent_present = true; - SERIALIZE_SCALAR(tapevent_present); - event->serialize(cp); - } - else { - SERIALIZE_SCALAR(tapevent_present); + if (buffer_used >= sizeof(uint32_t)) + frame_len = ntohl(*(uint32_t *)buffer); } } -void -EtherTapStub::unserialize(CheckpointIn &cp) +bool +EtherTapStub::sendReal(const void *data, size_t len) { - UNSERIALIZE_SCALAR(socket); - UNSERIALIZE_SCALAR(buflen); - uint8_t *buffer = (uint8_t *)this->buffer; - UNSERIALIZE_ARRAY(buffer, buflen); - UNSERIALIZE_SCALAR(buffer_offset); - UNSERIALIZE_SCALAR(data_len); - - bool tapevent_present; - UNSERIALIZE_SCALAR(tapevent_present); - if (tapevent_present) { - event = new TapEvent(this, socket, POLLIN|POLLERR); - - event->unserialize(cp); - - if (event->queued()) { - pollQueue.schedule(event); - } - } + uint32_t frame_len = htonl(len); + ssize_t ret = write(socket, &frame_len, sizeof(frame_len)); + if (ret != sizeof(frame_len)) + return false; + return write(socket, data, len) == len; } -//===================================================================== EtherTapStub * EtherTapStubParams::create() |