add module parameter "sleep_on_enobufs" to work around -ENOBUFS

AF_PACKET sockets have these incredibly useful semantics in where
for both non-blocking and blocking I/O, they will tell you the
socket is rwite-able, but then still return -1 and sett errno=ENOBUFS
if the current socket buffer / transmit queue is full.

All we can do is usleep and retry.  The new module parameter, if set
to non-zero, determines the number of microseconds we shall sleep before
any retry.  If set to zero, the existing behavior is preserved:
TTCN_error().

Related: SYS#5343
Change-Id: I1608403d94a10ae52c7e1de0f1b02687b048c01e
This commit is contained in:
Harald Welte 2021-01-29 20:28:38 +01:00
parent cd698095c7
commit 7d6688f9bc
2 changed files with 17 additions and 5 deletions

View File

@ -108,6 +108,8 @@ void AF__PACKET__PT_PROVIDER::set_parameter(const char *parameter_name, const ch
mNetdev_name = NULL;
}
mNetdev_name = strdup(parameter_value);
} else if (!strcmp(parameter_name, "sleep_on_enobufs")) {
mSleepUsOnEnobufs = atoi(parameter_value);
} else
TTCN_error("Unsupported test port parameter `%s'.", parameter_name);
}
@ -204,13 +206,22 @@ void AF__PACKET__PT_PROVIDER::user_stop()
void AF__PACKET__PT_PROVIDER::outgoing_send(const AF__PACKET__Unitdata& send_par)
{
int rc;
assert(mSocket >= 0);
rc = write(mSocket, send_par.data(), send_par.data().lengthof());
if (rc < send_par.data().lengthof())
TTCN_error("Short write on AF_PACKET socket: %s", strerror(errno));
while (true) {
int rc = write(mSocket, send_par.data(), send_par.data().lengthof());
if (rc == send_par.data().lengthof())
break;
if (mSleepUsOnEnobufs && rc == -1 && errno == ENOBUFS) {
/* This is fscking insane. Even select() would tell us the FD
* is write-able, but then we still get -ENOBUFS. The only way
* to do this os to sleep. */
usleep(mSleepUsOnEnobufs);
} else if (rc < send_par.data().lengthof()) {
TTCN_error("Short write on AF_PACKET socket: %s", strerror(errno));
break;
}
}
}

View File

@ -50,6 +50,7 @@ public:
private:
char *mNetdev_name; /* name of the network interface */
int mSleepUsOnEnobufs; /* how many us to sleep on ENOBUFS */
int mIfindex; /* interface index of the network device */
int mSocket; /* socket/file descriptor of the AF_PACKET socket */
uint8_t mRxBuf[2048]; /* read buffer */