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>
This commit is contained in:
Gabe Black
2017-06-03 05:03:18 -07:00
parent f9ad4066d7
commit 2ce045341b
3 changed files with 326 additions and 246 deletions

View File

@@ -95,13 +95,18 @@ class EtherSwitch(EtherObject):
delay_var = Param.Latency('0ns', "packet transmit delay variability")
time_to_live = Param.Latency('10ms', "time to live of MAC address maping")
class EtherTapStub(EtherObject):
type = 'EtherTapStub'
class EtherTapBase(EtherObject):
type = 'EtherTapBase'
abstract = True
cxx_header = "dev/net/ethertap.hh"
bufsz = Param.Int(10000, "tap buffer size")
dump = Param.EtherDump(NULL, "dump object")
tap = SlavePort("Ethernet interface to connect to gem5's network")
class EtherTapStub(EtherTapBase):
type = 'EtherTapStub'
cxx_header = "dev/net/ethertap.hh"
port = Param.UInt16(3500, "Port helper should send packets to")
tap = SlavePort("Ethernet interface to gem5's network")
class EtherDump(SimObject):
type = 'EtherDump'

View File

@@ -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);
// 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;
}
buffer_used += 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())
// If there's not enough data for the frame length, wait for more.
if (buffer_used < sizeof(uint32_t))
return;
EthPacketPtr packet = packetBuffer.front();
if (interface->sendPacket(packet)) {
if (dump)
dump->dump(packet);
DPRINTF(Ethernet, "EtherTapStub retransmit\n");
packetBuffer.front() = NULL;
packetBuffer.pop();
}
if (frame_len == 0)
frame_len = ntohl(*(uint32_t *)buffer);
if (!packetBuffer.empty() && !txEvent.scheduled())
schedule(txEvent, curTick() + retryTime);
}
DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d "
"frame_len=%d\n", len, buffer_used, frame_len);
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;
}
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);
bool tapevent_present = false;
if (event) {
tapevent_present = true;
SERIALIZE_SCALAR(tapevent_present);
event->serialize(cp);
}
else {
SERIALIZE_SCALAR(tapevent_present);
}
}
void
EtherTapStub::unserialize(CheckpointIn &cp)
{
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);
// 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;
if (buffer_used >= sizeof(uint32_t))
frame_len = ntohl(*(uint32_t *)buffer);
}
}
//=====================================================================
bool
EtherTapStub::sendReal(const void *data, size_t len)
{
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()

View File

@@ -47,65 +47,14 @@
#include "sim/sim_object.hh"
class TapEvent;
class TapListener;
class EtherTapInt;
/*
* Interface to connect a simulated ethernet device to the real world. An
* external helper program bridges between this object's TCP port and a
* source/sink for Ethernet frames. Each frame going in either direction is
* prepended with the frame's length in a 32 bit integer in network byte order.
*/
class EtherTapStub : public EtherObject
class EtherTapBase : public EtherObject
{
protected:
friend class TapEvent;
TapEvent *event;
protected:
friend class TapListener;
TapListener *listener;
int socket;
char *buffer;
int buflen;
uint32_t buffer_offset;
uint32_t data_len;
EtherDump *dump;
void attach(int fd);
void detach();
protected:
std::string device;
std::queue<EthPacketPtr> packetBuffer;
EtherTapInt *interface;
void process(int revent);
void enqueue(EthPacketData *packet);
void retransmit();
/*
*/
class TxEvent : public Event
{
protected:
EtherTapStub *tap;
public:
TxEvent(EtherTapStub *_tap) : tap(_tap) {}
void process() { tap->retransmit(); }
virtual const char *description() const
{ return "EtherTapStub retransmit"; }
};
friend class TxEvent;
TxEvent txEvent;
public:
typedef EtherTapStubParams Params;
EtherTapStub(const Params *p);
virtual ~EtherTapStub();
typedef EtherTapBaseParams Params;
EtherTapBase(const Params *p);
virtual ~EtherTapBase();
const Params *
params() const
@@ -113,26 +62,117 @@ class EtherTapStub : public EtherObject
return dynamic_cast<const Params *>(_params);
}
EtherInt *getEthPort(const std::string &if_name, int idx) override;
virtual bool recvPacket(EthPacketPtr packet);
virtual void sendDone();
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
protected:
uint8_t *buffer;
int buflen;
EtherDump *dump;
/*
* Interface to the real network.
*/
protected:
friend class TapEvent;
TapEvent *event;
void pollFd(int fd);
void stopPolling();
// Receive data from the real network.
virtual void recvReal(int revent) = 0;
// Prepare and send data out to the real network.
virtual bool sendReal(const void *data, size_t len) = 0;
/*
* Interface to the simulated network.
*/
protected:
EtherTapInt *interface;
public:
EtherInt *getEthPort(const std::string &if_name, int idx) override;
bool recvSimulated(EthPacketPtr packet);
void sendSimulated(void *data, size_t len);
protected:
std::queue<EthPacketPtr> packetBuffer;
void retransmit();
class TxEvent : public Event
{
protected:
EtherTapBase *tap;
public:
TxEvent(EtherTapBase *_tap) : tap(_tap) {}
void process() { tap->retransmit(); }
virtual const char *description() const
{ return "EtherTapBase retransmit"; }
};
friend class TxEvent;
TxEvent txEvent;
};
class EtherTapInt : public EtherInt
{
private:
EtherTapStub *tap;
EtherTapBase *tap;
public:
EtherTapInt(const std::string &name, EtherTapStub *t)
: EtherInt(name), tap(t)
EtherTapInt(const std::string &name, EtherTapBase *t) :
EtherInt(name), tap(t)
{ }
virtual bool recvPacket(EthPacketPtr pkt) { return tap->recvPacket(pkt); }
virtual void sendDone() { tap->sendDone(); }
bool recvPacket(EthPacketPtr pkt) override
{ return tap->recvSimulated(pkt); }
void sendDone() override {}
};
class TapListener;
/*
* Interface to connect a simulated ethernet device to the real world. An
* external helper program bridges between this object's TCP port and a
* source/sink for Ethernet frames. Each frame going in either direction is
* prepended with the frame's length in a 32 bit integer in network byte order.
*/
class EtherTapStub : public EtherTapBase
{
public:
typedef EtherTapStubParams Params;
EtherTapStub(const Params *p);
~EtherTapStub();
const Params *
params() const
{
return dynamic_cast<const Params *>(_params);
}
void serialize(CheckpointOut &cp) const override;
void unserialize(CheckpointIn &cp) override;
protected:
friend class TapListener;
TapListener *listener;
int socket;
void attach(int fd);
void detach();
uint32_t buffer_used;
uint32_t frame_len;
void recvReal(int revent) override;
bool sendReal(const void *data, size_t len) override;
};