libsa: Fix IP recv timeout

readip() doesn't, at the moment, properly indicate to callers that it has
timed out. One can tell that it's timed out if errno == EAGAIN when it
returns, but this is not ideal. Restructure it a little bit to explicitly
set errno to ETIMEDOUT if we've exhausted tleft.

I found two places that care about where it timed out or not: sendrecv in
net.c and sendrecv_tftp. Both are structured to pass smaller timeout values
to readip while tracking a larger timeout. Neither of them were able to do
this properly with readip not indicating ETIMEDOUT, so fix it.

While here, straighten out the time (t/t1) usage in sendrecv_tftp.

This would have manifested itself in periodic failures to NFS/TFTP boot for
no apparent reason because MINTMO/MAXTMO were not actually being respected
properly. Problems were not reported with NFS, only TFTP.

Reported by:	sbruno
Reviewed by:	sbruno, tsoome
MFC after:	3 days
Differential Revision:	https://reviews.freebsd.org/D14350
This commit is contained in:
Kyle Evans 2018-02-14 15:40:13 +00:00
parent d793587fe2
commit 502b7cf99b
3 changed files with 15 additions and 4 deletions

View File

@ -416,8 +416,13 @@ readip(struct iodesc *d, void **pkt, void **payload, time_t tleft,
while ((getsecs() - t) < tleft) {
errno = 0;
ret = readipv4(d, pkt, payload, tleft, proto);
if (ret >= 0)
return (ret);
/* Bubble up the error if it wasn't successful */
if (errno != EAGAIN)
break;
return (-1);
}
return (ret);
/* We've exhausted tleft; timeout */
errno = ETIMEDOUT;
return (-1);
}

View File

@ -118,7 +118,7 @@ sendrecv(struct iodesc *d,
/* Try to get a packet and process it. */
cc = (*rproc)(d, pkt, payload, tleft);
/* Return on data, EOF or real error. */
if (cc != -1 || errno != 0)
if (cc != -1 || (errno != 0 && errno != ETIMEDOUT))
return (cc);
/* Timed out or didn't get the packet we're waiting for */

View File

@ -638,14 +638,20 @@ sendrecv_tftp(struct tftp_handle *h,
if (cc == -1) {
/* Error on transmit; wait before retrying */
while ((getsecs() - t1) < tleft);
t1 = getsecs();
continue;
}
t = t1 = getsecs();
recvnext:
if ((getsecs() - t) > MAXTMO) {
errno = ETIMEDOUT;
return -1;
}
/* Try to get a packet and process it. */
cc = (*rproc)(h, pkt, payload, tleft, rtype);
/* Return on data, EOF or real error. */
if (cc != -1 || errno != 0)
if (cc != -1 || (errno != 0 && errno != ETIMEDOUT))
return (cc);
if ((getsecs() - t1) < tleft) {
goto recvnext;