public:
TapEvent(EtherTapBase *_tap, int fd, int e)
: PollEvent(fd, e), tap(_tap) {}
- virtual void process(int revent) { tap->recvReal(revent); }
+
+ void
+ process(int revent) override
+ {
+ // Ensure that our event queue is active. It may not be since we get
+ // here from the PollQueue whenever a real packet happens to arrive.
+ EventQueue::ScopedMigration migrate(tap->eventQueue());
+
+ tap->recvReal(revent);
+ }
};
EtherTapBase::EtherTapBase(const Params *p)
- : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL),
+ : SimObject(p), buflen(p->bufsz), dump(p->dump), event(NULL),
interface(NULL),
txEvent([this]{ retransmit(); }, "EtherTapBase retransmit")
{
}
-EtherInt*
-EtherTapBase::getEthPort(const std::string &if_name, int idx)
+Port &
+EtherTapBase::getPort(const std::string &if_name, PortID idx)
{
- if (if_name == "tap") {
- if (interface->getPeer())
- panic("Interface already connected to\n");
- return interface;
- }
- return NULL;
+ if (if_name == "tap")
+ return *interface;
+ return SimObject::getPort(if_name, idx);
}
bool
EtherTap::EtherTap(const Params *p) : EtherTapBase(p)
{
- int fd = open(p->tun_clone_device.c_str(), O_RDWR);
+ int fd = open(p->tun_clone_device.c_str(), O_RDWR | O_NONBLOCK);
if (fd < 0)
panic("Couldn't open %s.\n", p->tun_clone_device);
if (!(revent & POLLIN))
return;
- ssize_t ret = read(tap, buffer, buflen);
- if (ret < 0)
- panic("Failed to read from tap device.\n");
+ ssize_t ret;
+ while ((ret = read(tap, buffer, buflen))) {
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ break;
+ panic("Failed to read from tap device.\n");
+ }
- sendSimulated(buffer, ret);
+ sendSimulated(buffer, ret);
+ }
}
bool
EtherTap::sendReal(const void *data, size_t len)
{
- if (write(tap, data, len) != len)
- panic("Failed to write data to tap device.\n");
+ int n;
+ pollfd pfd[1];
+ pfd->fd = tap;
+ pfd->events = POLLOUT;
+
+ // `tap` is a nonblock fd. Here we try to write until success, and use
+ // poll to make a blocking wait.
+ while ((n = write(tap, data, len)) != len) {
+ if (errno != EAGAIN)
+ panic("Failed to write data to tap device.\n");
+ pfd->revents = 0;
+ int ret = poll(pfd, 1, -1);
+ // timeout is set to inf, we shouldn't get 0 in any case.
+ assert(ret != 0);
+ if (ret == -1 || (ret == 1 && (pfd->revents & POLLERR))) {
+ panic("Failed when polling to write data to tap device.\n");
+ }
+ }
return true;
}