summaryrefslogtreecommitdiff
path: root/src/dev
diff options
context:
space:
mode:
authorPin-Yen Lin <treapking@google.com>2018-08-14 14:23:25 +0800
committerEarl Ou <shunhsingou@google.com>2018-09-11 06:29:32 +0000
commit36276ba2392f030250fa9dd1e1ea28a026bc51c6 (patch)
tree432bffacd68395f8ab9514fdd00f68ebbe33cb2e /src/dev
parent34b16aa817238c3db88462dc31173faf27cfdcba (diff)
downloadgem5-36276ba2392f030250fa9dd1e1ea28a026bc51c6.tar.xz
net: Fix a bug when receiving fragamented packets
In the previous implementation, the function EtherTap::recvReal will only read one packet when received some ``interrupt'' (explicitly, when async_IO set to true). When someone tries to send a large message to the simulated device, the message will be divided to several packets due to packet fragmentation. In this situation recvReal will only read one packet and left the other packets in the buffer. This significantly increases the networking latency. So before reading from socket, I change the socket into non-blocking mode and keep reading from it until there's no packet left. Change-Id: Ieb94a8532cd3994862b6f3eb9474caf7ccf617da Reviewed-on: https://gem5-review.googlesource.com/12338 Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com> Reviewed-by: Jason Lowe-Power <jason@lowepower.com> Maintainer: Jason Lowe-Power <jason@lowepower.com>
Diffstat (limited to 'src/dev')
-rw-r--r--src/dev/net/ethertap.cc35
1 files changed, 28 insertions, 7 deletions
diff --git a/src/dev/net/ethertap.cc b/src/dev/net/ethertap.cc
index 8d08cc2d2..4e32a8c46 100644
--- a/src/dev/net/ethertap.cc
+++ b/src/dev/net/ethertap.cc
@@ -406,7 +406,7 @@ EtherTapStub::sendReal(const void *data, size_t len)
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);
@@ -438,18 +438,39 @@ EtherTap::recvReal(int revent)
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;
}