00db10
commit cf0bd2f73bd65beab613865bba567d7787836888
00db10
Author: Florian Weimer <fweimer@redhat.com>
00db10
Date:   Tue Feb 28 15:28:45 2017 +0100
00db10
00db10
    sunrpc: Improvements for UDP client timeout handling [BZ #20257]
00db10
    
00db10
    This commit fixes various aspects in the UDP client timeout handling.
00db10
    Timeouts are now applied in a more consistent fashion.  Discarded UDP
00db10
    packets no longer prevent the timeout from happening at all.
00db10
00db10
Index: b/inet/deadline.c
00db10
===================================================================
00db10
--- /dev/null
00db10
+++ b/inet/deadline.c
00db10
@@ -0,0 +1,122 @@
00db10
+/* Computing deadlines for timeouts.
00db10
+   Copyright (C) 2017 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <net-internal.h>
00db10
+
00db10
+#include <assert.h>
00db10
+#include <limits.h>
00db10
+#include <stdio.h>
00db10
+#include <stdint.h>
00db10
+#include <time.h>
00db10
+
00db10
+struct deadline_current_time internal_function
00db10
+__deadline_current_time (void)
00db10
+{
00db10
+  struct deadline_current_time result;
00db10
+  if (__clock_gettime (CLOCK_MONOTONIC, &result.current) != 0)
00db10
+    {
00db10
+      struct timeval current_tv;
00db10
+      if (__gettimeofday (&current_tv, NULL) == 0)
00db10
+        __libc_fatal ("Fatal error: gettimeofday system call failed\n");
00db10
+      result.current.tv_sec = current_tv.tv_sec;
00db10
+      result.current.tv_nsec = current_tv.tv_usec * 1000;
00db10
+    }
00db10
+  assert (result.current.tv_sec >= 0);
00db10
+  return result;
00db10
+}
00db10
+
00db10
+/* A special deadline value for which __deadline_is_infinite is
00db10
+   true.  */
00db10
+static inline struct deadline
00db10
+infinite_deadline (void)
00db10
+{
00db10
+  return (struct deadline) { { -1, -1 } };
00db10
+}
00db10
+
00db10
+struct deadline internal_function
00db10
+__deadline_from_timeval (struct deadline_current_time current,
00db10
+                         struct timeval tv)
00db10
+{
00db10
+  assert (__is_timeval_valid_timeout (tv));
00db10
+
00db10
+  /* Compute second-based deadline.  Perform the addition in
00db10
+     uintmax_t, which is unsigned, to simply overflow detection.  */
00db10
+  uintmax_t sec = current.current.tv_sec;
00db10
+  sec += tv.tv_sec;
00db10
+  if (sec < (uintmax_t) tv.tv_sec)
00db10
+    return infinite_deadline ();
00db10
+
00db10
+  /* Compute nanosecond deadline.  */
00db10
+  int nsec = current.current.tv_nsec + tv.tv_usec * 1000;
00db10
+  if (nsec >= 1000 * 1000 * 1000)
00db10
+    {
00db10
+      /* Carry nanosecond overflow to seconds.  */
00db10
+      nsec -= 1000 * 1000 * 1000;
00db10
+      if (sec + 1 < sec)
00db10
+        return infinite_deadline ();
00db10
+      ++sec;
00db10
+    }
00db10
+  /* This uses a GCC extension, otherwise these casts for detecting
00db10
+     overflow would not be defined.  */
00db10
+  if ((time_t) sec < 0 || sec != (uintmax_t) (time_t) sec)
00db10
+    return infinite_deadline ();
00db10
+
00db10
+  return (struct deadline) { { sec, nsec } };
00db10
+}
00db10
+
00db10
+int internal_function
00db10
+__deadline_to_ms (struct deadline_current_time current,
00db10
+                  struct deadline deadline)
00db10
+{
00db10
+  if (__deadline_is_infinite (deadline))
00db10
+    return INT_MAX;
00db10
+
00db10
+  if (current.current.tv_sec > deadline.absolute.tv_sec
00db10
+      || (current.current.tv_sec == deadline.absolute.tv_sec
00db10
+          && current.current.tv_nsec >= deadline.absolute.tv_nsec))
00db10
+    return 0;
00db10
+  time_t sec = deadline.absolute.tv_sec - current.current.tv_sec;
00db10
+  if (sec >= INT_MAX)
00db10
+    /* This value will overflow below.  */
00db10
+    return INT_MAX;
00db10
+  int nsec = deadline.absolute.tv_nsec - current.current.tv_nsec;
00db10
+  if (nsec < 0)
00db10
+    {
00db10
+      /* Borrow from the seconds field.  */
00db10
+      assert (sec > 0);
00db10
+      --sec;
00db10
+      nsec += 1000 * 1000 * 1000;
00db10
+    }
00db10
+
00db10
+  /* Prepare for rounding up to milliseconds.  */
00db10
+  nsec += 999999;
00db10
+  if (nsec > 1000 * 1000 * 1000)
00db10
+    {
00db10
+      assert (sec < INT_MAX);
00db10
+      ++sec;
00db10
+      nsec -= 1000 * 1000 * 1000;
00db10
+    }
00db10
+
00db10
+  unsigned int msec = nsec / (1000 * 1000);
00db10
+  if (sec > INT_MAX / 1000)
00db10
+    return INT_MAX;
00db10
+  msec += sec * 1000;
00db10
+  if (msec > INT_MAX)
00db10
+    return INT_MAX;
00db10
+  return msec;
00db10
+}
00db10
Index: b/inet/tst-deadline.c
00db10
===================================================================
00db10
--- /dev/null
00db10
+++ b/inet/tst-deadline.c
00db10
@@ -0,0 +1,188 @@
00db10
+/* Tests for computing deadlines for timeouts.
00db10
+   Copyright (C) 2017 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <inet/net-internal.h>
00db10
+#include <limits.h>
00db10
+#include <stdbool.h>
00db10
+#include <stdint.h>
00db10
+#include <support/check.h>
00db10
+
00db10
+/* Find the maximum value which can be represented in a time_t.  */
00db10
+static time_t
00db10
+time_t_max (void)
00db10
+{
00db10
+  _Static_assert (0 > (time_t) -1, "time_t is signed");
00db10
+  uintmax_t current = 1;
00db10
+  while (true)
00db10
+    {
00db10
+      uintmax_t next = current * 2;
00db10
+      /* This cannot happen because time_t is signed.  */
00db10
+      TEST_VERIFY_EXIT (next > current);
00db10
+      ++next;
00db10
+      if ((time_t) next < 0 || next != (uintmax_t) (time_t) next)
00db10
+        /* Value cannot be represented in time_t.  Return the previous
00db10
+           value. */
00db10
+        return current;
00db10
+      current = next;
00db10
+    }
00db10
+}
00db10
+
00db10
+static int
00db10
+do_test (void)
00db10
+{
00db10
+  {
00db10
+    struct deadline_current_time current_time = __deadline_current_time ();
00db10
+    TEST_VERIFY (current_time.current.tv_sec >= 0);
00db10
+    current_time = __deadline_current_time ();
00db10
+    /* Due to CLOCK_MONOTONIC, either seconds or nanoseconds are
00db10
+       greater than zero.  This is also true for the gettimeofday
00db10
+       fallback.  */
00db10
+    TEST_VERIFY (current_time.current.tv_sec >= 0);
00db10
+    TEST_VERIFY (current_time.current.tv_sec > 0
00db10
+                 || current_time.current.tv_nsec > 0);
00db10
+  }
00db10
+
00db10
+  /* Check basic computations of deadlines.  */
00db10
+  struct deadline_current_time current_time = { { 1, 123456789 } };
00db10
+  struct deadline deadline = __deadline_from_timeval
00db10
+    (current_time, (struct timeval) { 0, 1 });
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 123457789);
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1);
00db10
+
00db10
+  deadline = __deadline_from_timeval
00db10
+    (current_time, ((struct timeval) { 0, 2 }));
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 123458789);
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1);
00db10
+
00db10
+  deadline = __deadline_from_timeval
00db10
+    (current_time, ((struct timeval) { 1, 0 }));
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 2);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 123456789);
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
00db10
+
00db10
+  /* Check if timeouts are correctly rounded up to the next
00db10
+     millisecond.  */
00db10
+  for (int i = 0; i < 999999; ++i)
00db10
+    {
00db10
+      ++current_time.current.tv_nsec;
00db10
+      TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
00db10
+    }
00db10
+
00db10
+  /* A full millisecond has elapsed, so the time to the deadline is
00db10
+     now less than 1000.  */
00db10
+  ++current_time.current.tv_nsec;
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 999);
00db10
+
00db10
+  /* Check __deadline_to_ms carry-over.  */
00db10
+  current_time = (struct deadline_current_time) { { 9, 123456789 } };
00db10
+  deadline = (struct deadline) { { 10, 122456789 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 999);
00db10
+  deadline = (struct deadline) { { 10, 122456790 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
00db10
+  deadline = (struct deadline) { { 10, 123456788 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
00db10
+  deadline = (struct deadline) { { 10, 123456789 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 1000);
00db10
+
00db10
+  /* Check __deadline_to_ms overflow.  */
00db10
+  deadline = (struct deadline) { { INT_MAX - 1, 1 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == INT_MAX);
00db10
+
00db10
+  /* Check __deadline_to_ms for elapsed deadlines.  */
00db10
+  current_time = (struct deadline_current_time) { { 9, 123456789 } };
00db10
+  deadline.absolute = current_time.current;
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
00db10
+  current_time = (struct deadline_current_time) { { 9, 123456790 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
00db10
+  current_time = (struct deadline_current_time) { { 10, 0 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
00db10
+  current_time = (struct deadline_current_time) { { 10, 123456788 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
00db10
+  current_time = (struct deadline_current_time) { { 10, 123456789 } };
00db10
+  TEST_VERIFY (__deadline_to_ms (current_time, deadline) == 0);
00db10
+
00db10
+  /* Check carry-over in __deadline_from_timeval.  */
00db10
+  current_time = (struct deadline_current_time) { { 9, 998000001 } };
00db10
+  for (int i = 0; i < 2000; ++i)
00db10
+    {
00db10
+      deadline = __deadline_from_timeval
00db10
+        (current_time, (struct timeval) { 1, i });
00db10
+      TEST_VERIFY (deadline.absolute.tv_sec == 10);
00db10
+      TEST_VERIFY (deadline.absolute.tv_nsec == 998000001 + i * 1000);
00db10
+    }
00db10
+  for (int i = 2000; i < 3000; ++i)
00db10
+    {
00db10
+      deadline = __deadline_from_timeval
00db10
+        (current_time, (struct timeval) { 2, i });
00db10
+      TEST_VERIFY (deadline.absolute.tv_sec == 12);
00db10
+      TEST_VERIFY (deadline.absolute.tv_nsec == 1 + (i - 2000) * 1000);
00db10
+    }
00db10
+
00db10
+  /* Check infinite deadlines.  */
00db10
+  deadline = __deadline_from_timeval
00db10
+    ((struct deadline_current_time) { { 0, 1000 * 1000 * 1000 - 1000 } },
00db10
+     (struct timeval) { time_t_max (), 1 });
00db10
+  TEST_VERIFY (__deadline_is_infinite (deadline));
00db10
+  deadline = __deadline_from_timeval
00db10
+    ((struct deadline_current_time) { { 0, 1000 * 1000 * 1000 - 1001 } },
00db10
+     (struct timeval) { time_t_max (), 1 });
00db10
+  TEST_VERIFY (!__deadline_is_infinite (deadline));
00db10
+  deadline = __deadline_from_timeval
00db10
+    ((struct deadline_current_time)
00db10
+       { { time_t_max (), 1000 * 1000 * 1000 - 1000 } },
00db10
+     (struct timeval) { 0, 1 });
00db10
+  TEST_VERIFY (__deadline_is_infinite (deadline));
00db10
+  deadline = __deadline_from_timeval
00db10
+    ((struct deadline_current_time)
00db10
+       { { time_t_max () / 2 + 1, 0 } },
00db10
+     (struct timeval) { time_t_max () / 2 + 1, 0 });
00db10
+  TEST_VERIFY (__deadline_is_infinite (deadline));
00db10
+
00db10
+  /* Check __deadline_first behavior.  */
00db10
+  deadline = __deadline_first
00db10
+    ((struct deadline) { { 1, 2 } },
00db10
+     (struct deadline) { { 1, 3 } });
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 2);
00db10
+  deadline = __deadline_first
00db10
+    ((struct deadline) { { 1, 3 } },
00db10
+     (struct deadline) { { 1, 2 } });
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 2);
00db10
+  deadline = __deadline_first
00db10
+    ((struct deadline) { { 1, 2 } },
00db10
+     (struct deadline) { { 2, 1 } });
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 2);
00db10
+  deadline = __deadline_first
00db10
+    ((struct deadline) { { 1, 2 } },
00db10
+     (struct deadline) { { 2, 4 } });
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 2);
00db10
+  deadline = __deadline_first
00db10
+    ((struct deadline) { { 2, 4 } },
00db10
+     (struct deadline) { { 1, 2 } });
00db10
+  TEST_VERIFY (deadline.absolute.tv_sec == 1);
00db10
+  TEST_VERIFY (deadline.absolute.tv_nsec == 2);
00db10
+
00db10
+  return 0;
00db10
+}
00db10
+
00db10
+#include <support/test-driver.c>
00db10
Index: b/sunrpc/Makefile
00db10
===================================================================
00db10
--- a/sunrpc/Makefile
00db10
+++ b/sunrpc/Makefile
00db10
@@ -96,11 +96,13 @@ others += rpcgen
00db10
 
00db10
 all: # Make this the default target; it will be defined in Rules.
00db10
 
00db10
-tests = tst-xdrmem tst-xdrmem2
00db10
+tests = tst-xdrmem tst-xdrmem2 tst-udp-timeout \
00db10
+  tst-udp-nonblocking
00db10
 xtests := tst-getmyaddr
00db10
 
00db10
 ifeq ($(have-thread-library),yes)
00db10
 xtests += thrsvc
00db10
+tests += tst-udp-garbage
00db10
 endif
00db10
 
00db10
 headers += $(rpcsvc:%.x=rpcsvc/%.h)
00db10
@@ -225,3 +227,8 @@ endif
00db10
 endif
00db10
 
00db10
 $(objpfx)thrsvc: $(common-objpfx)linkobj/libc.so $(shared-thread-library)
00db10
+
00db10
+$(objpfx)tst-udp-timeout: $(common-objpfx)linkobj/libc.so
00db10
+$(objpfx)tst-udp-nonblocking: $(common-objpfx)linkobj/libc.so
00db10
+$(objpfx)tst-udp-garbage: \
00db10
+  $(common-objpfx)linkobj/libc.so $(shared-thread-library)
00db10
Index: b/sunrpc/clnt_udp.c
00db10
===================================================================
00db10
--- a/sunrpc/clnt_udp.c
00db10
+++ b/sunrpc/clnt_udp.c
00db10
@@ -54,6 +54,7 @@
00db10
 #endif
00db10
 
00db10
 #include <kernel-features.h>
00db10
+#include <inet/net-internal.h>
00db10
 
00db10
 extern u_long _create_xid (void);
00db10
 
00db10
@@ -79,7 +80,9 @@ static const struct clnt_ops udp_ops =
00db10
 };
00db10
 
00db10
 /*
00db10
- * Private data kept per client handle
00db10
+ * Private data kept per client handle.  This private struct is
00db10
+ * unfortunately part of the ABI; ypbind contains a copy of it and
00db10
+ * accesses it through CLIENT::cl_private field.
00db10
  */
00db10
 struct cu_data
00db10
   {
00db10
@@ -309,28 +312,38 @@ clntudp_call (cl, proc, xargs, argsp, xr
00db10
   int inlen;
00db10
   socklen_t fromlen;
00db10
   struct pollfd fd;
00db10
-  int milliseconds = (cu->cu_wait.tv_sec * 1000) +
00db10
-    (cu->cu_wait.tv_usec / 1000);
00db10
   struct sockaddr_in from;
00db10
   struct rpc_msg reply_msg;
00db10
   XDR reply_xdrs;
00db10
-  struct timeval time_waited;
00db10
   bool_t ok;
00db10
   int nrefreshes = 2;		/* number of times to refresh cred */
00db10
-  struct timeval timeout;
00db10
   int anyup;			/* any network interface up */
00db10
 
00db10
-  if (cu->cu_total.tv_usec == -1)
00db10
-    {
00db10
-      timeout = utimeout;	/* use supplied timeout */
00db10
-    }
00db10
-  else
00db10
-    {
00db10
-      timeout = cu->cu_total;	/* use default timeout */
00db10
-    }
00db10
+  struct deadline_current_time current_time = __deadline_current_time ();
00db10
+  struct deadline total_deadline; /* Determined once by overall timeout.  */
00db10
+  struct deadline response_deadline; /* Determined anew for each query.  */
00db10
+
00db10
+  /* Choose the timeout value.  For non-sending usage (xargs == NULL),
00db10
+     the total deadline does not matter, only cu->cu_wait is used
00db10
+     below.  */
00db10
+  if (xargs != NULL)
00db10
+    {
00db10
+      struct timeval tv;
00db10
+      if (cu->cu_total.tv_usec == -1)
00db10
+	/* Use supplied timeout.  */
00db10
+	tv = utimeout;
00db10
+      else
00db10
+	/* Use default timeout.  */
00db10
+	tv = cu->cu_total;
00db10
+      if (!__is_timeval_valid_timeout (tv))
00db10
+	return (cu->cu_error.re_status = RPC_TIMEDOUT);
00db10
+      total_deadline = __deadline_from_timeval (current_time, tv);
00db10
+    }
00db10
+
00db10
+  /* Guard against bad timeout specification.  */
00db10
+  if (!__is_timeval_valid_timeout (cu->cu_wait))
00db10
+    return (cu->cu_error.re_status = RPC_TIMEDOUT);
00db10
 
00db10
-  time_waited.tv_sec = 0;
00db10
-  time_waited.tv_usec = 0;
00db10
 call_again:
00db10
   xdrs = &(cu->cu_outxdrs);
00db10
   if (xargs == NULL)
00db10
@@ -356,27 +369,46 @@ send_again:
00db10
       return (cu->cu_error.re_status = RPC_CANTSEND);
00db10
     }
00db10
 
00db10
-  /*
00db10
-   * Hack to provide rpc-based message passing
00db10
-   */
00db10
-  if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
00db10
-    {
00db10
-      return (cu->cu_error.re_status = RPC_TIMEDOUT);
00db10
-    }
00db10
+  /* sendto may have blocked, so recompute the current time.  */
00db10
+  current_time = __deadline_current_time ();
00db10
  get_reply:
00db10
-  /*
00db10
-   * sub-optimal code appears here because we have
00db10
-   * some clock time to spare while the packets are in flight.
00db10
-   * (We assume that this is actually only executed once.)
00db10
-   */
00db10
+  response_deadline = __deadline_from_timeval (current_time, cu->cu_wait);
00db10
+
00db10
   reply_msg.acpted_rply.ar_verf = _null_auth;
00db10
   reply_msg.acpted_rply.ar_results.where = resultsp;
00db10
   reply_msg.acpted_rply.ar_results.proc = xresults;
00db10
   fd.fd = cu->cu_sock;
00db10
   fd.events = POLLIN;
00db10
   anyup = 0;
00db10
+
00db10
+  /* Per-response retry loop.  current_time must be up-to-date at the
00db10
+     top of the loop.  */
00db10
   for (;;)
00db10
     {
00db10
+      int milliseconds;
00db10
+      if (xargs != NULL)
00db10
+	{
00db10
+	  if (__deadline_elapsed (current_time, total_deadline))
00db10
+	    /* Overall timeout expired.  */
00db10
+	    return (cu->cu_error.re_status = RPC_TIMEDOUT);
00db10
+	  milliseconds = __deadline_to_ms
00db10
+	    (current_time, __deadline_first (total_deadline,
00db10
+					     response_deadline));
00db10
+	  if (milliseconds == 0)
00db10
+	    /* Per-query timeout expired.  */
00db10
+	    goto send_again;
00db10
+	}
00db10
+      else
00db10
+	{
00db10
+	  /* xatgs == NULL.  Collect a response without sending a
00db10
+	     query.  In this mode, we need to ignore the total
00db10
+	     deadline.  */
00db10
+	  milliseconds = __deadline_to_ms (current_time, response_deadline);
00db10
+	  if (milliseconds == 0)
00db10
+	    /* Cannot send again, so bail out.  */
00db10
+	    return (cu->cu_error.re_status = RPC_CANTSEND);
00db10
+	}
00db10
+
00db10
       switch (__poll (&fd, 1, milliseconds))
00db10
 	{
00db10
 
00db10
@@ -387,27 +419,10 @@ send_again:
00db10
 	      if (!anyup)
00db10
 		return (cu->cu_error.re_status = RPC_CANTRECV);
00db10
 	    }
00db10
-
00db10
-	  time_waited.tv_sec += cu->cu_wait.tv_sec;
00db10
-	  time_waited.tv_usec += cu->cu_wait.tv_usec;
00db10
-	  while (time_waited.tv_usec >= 1000000)
00db10
-	    {
00db10
-	      time_waited.tv_sec++;
00db10
-	      time_waited.tv_usec -= 1000000;
00db10
-	    }
00db10
-	  if ((time_waited.tv_sec < timeout.tv_sec) ||
00db10
-	      ((time_waited.tv_sec == timeout.tv_sec) &&
00db10
-	       (time_waited.tv_usec < timeout.tv_usec)))
00db10
-	    goto send_again;
00db10
-	  return (cu->cu_error.re_status = RPC_TIMEDOUT);
00db10
-
00db10
-	  /*
00db10
-	   * buggy in other cases because time_waited is not being
00db10
-	   * updated.
00db10
-	   */
00db10
+	  goto next_response;
00db10
 	case -1:
00db10
 	  if (errno == EINTR)
00db10
-	    continue;
00db10
+	    goto next_response;
00db10
 	  cu->cu_error.re_errno = errno;
00db10
 	  return (cu->cu_error.re_status = RPC_CANTRECV);
00db10
 	}
00db10
@@ -463,20 +478,22 @@ send_again:
00db10
       if (inlen < 0)
00db10
 	{
00db10
 	  if (errno == EWOULDBLOCK)
00db10
-	    continue;
00db10
+	    goto next_response;
00db10
 	  cu->cu_error.re_errno = errno;
00db10
 	  return (cu->cu_error.re_status = RPC_CANTRECV);
00db10
 	}
00db10
-      if (inlen < 4)
00db10
-	continue;
00db10
+      /* Accept the response if the packet is sufficiently long and
00db10
+	 the transaction ID matches the query (if available).  */
00db10
+      if (inlen >= 4
00db10
+	  && (xargs == NULL
00db10
+	      || memcmp (cu->cu_inbuf, cu->cu_outbuf,
00db10
+			 sizeof (u_int32_t)) == 0))
00db10
+	break;
00db10
 
00db10
-      /* see if reply transaction id matches sent id.
00db10
-	Don't do this if we only wait for a replay */
00db10
-      if (xargs != NULL
00db10
-	  && memcmp (cu->cu_inbuf, cu->cu_outbuf, sizeof (u_int32_t)) != 0)
00db10
-	continue;
00db10
-      /* we now assume we have the proper reply */
00db10
-      break;
00db10
+    next_response:
00db10
+      /* Update the current time because poll and recvmsg waited for
00db10
+	 an unknown time.  */
00db10
+      current_time = __deadline_current_time ();
00db10
     }
00db10
 
00db10
   /*
00db10
Index: b/sunrpc/tst-udp-garbage.c
00db10
===================================================================
00db10
--- /dev/null
00db10
+++ b/sunrpc/tst-udp-garbage.c
00db10
@@ -0,0 +1,104 @@
00db10
+/* Test that garbage packets do not affect timeout handling.
00db10
+   Copyright (C) 2017 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <netinet/in.h>
00db10
+#include <rpc/clnt.h>
00db10
+#include <rpc/svc.h>
00db10
+#include <stdbool.h>
00db10
+#include <support/check.h>
00db10
+#include <support/namespace.h>
00db10
+#include <support/xsocket.h>
00db10
+#include <support/xthread.h>
00db10
+#include <sys/socket.h>
00db10
+#include <unistd.h>
00db10
+
00db10
+/* Descriptor for the server UDP socket.  */
00db10
+static int server_fd;
00db10
+
00db10
+static void *
00db10
+garbage_sender_thread (void *unused)
00db10
+{
00db10
+  while (true)
00db10
+    {
00db10
+      struct sockaddr_storage sa;
00db10
+      socklen_t salen = sizeof (sa);
00db10
+      char buf[1];
00db10
+      if (recvfrom (server_fd, buf, sizeof (buf), 0,
00db10
+                    (struct sockaddr *) &sa, &salen) < 0)
00db10
+        FAIL_EXIT1 ("recvfrom: %m");
00db10
+
00db10
+      /* Send garbage packets indefinitely.  */
00db10
+      buf[0] = 0;
00db10
+      while (true)
00db10
+        {
00db10
+          /* sendto can fail if the client closed the socket.  */
00db10
+          if (sendto (server_fd, buf, sizeof (buf), 0,
00db10
+                      (struct sockaddr *) &sa, salen) < 0)
00db10
+            break;
00db10
+
00db10
+          /* Wait a bit, to avoid burning too many CPU cycles in a
00db10
+             tight loop.  The wait period must be much shorter than
00db10
+             the client timeouts configured below.  */
00db10
+          usleep (50 * 1000);
00db10
+        }
00db10
+    }
00db10
+}
00db10
+
00db10
+static int
00db10
+do_test (void)
00db10
+{
00db10
+  support_become_root ();
00db10
+  support_enter_network_namespace ();
00db10
+
00db10
+  server_fd = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
00db10
+  struct sockaddr_in server_address =
00db10
+    {
00db10
+      .sin_family = AF_INET,
00db10
+      .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
00db10
+    };
00db10
+  xbind (server_fd,
00db10
+         (struct sockaddr *) &server_address, sizeof (server_address));
00db10
+  {
00db10
+    socklen_t sinlen = sizeof (server_address);
00db10
+    xgetsockname (server_fd, (struct sockaddr *) &server_address, &sinlen);
00db10
+    TEST_VERIFY (sizeof (server_address) == sinlen);
00db10
+  }
00db10
+
00db10
+  /* Garbage packet source.  */
00db10
+  xpthread_detach (xpthread_create (NULL, garbage_sender_thread, NULL));
00db10
+
00db10
+  /* Test client.  Use an arbitrary timeout of one second, which is
00db10
+     much longer than the garbage packet interval, but still
00db10
+     reasonably short, so that the test completes quickly.  */
00db10
+  int client_fd = RPC_ANYSOCK;
00db10
+  CLIENT *clnt = clntudp_create (&server_address,
00db10
+                                 1, 2, /* Arbitrary RPC endpoint numbers.  */
00db10
+                                 (struct timeval) { 1, 0 },
00db10
+                                 &client_fd);
00db10
+  if (clnt == NULL)
00db10
+    FAIL_EXIT1 ("clntudp_create: %m");
00db10
+
00db10
+  TEST_VERIFY (clnt_call (clnt, 3, /* Arbitrary RPC procedure number.  */
00db10
+                          (xdrproc_t) xdr_void, NULL,
00db10
+                          (xdrproc_t) xdr_void, NULL,
00db10
+                          ((struct timeval) { 1, 0 })));
00db10
+
00db10
+  return 0;
00db10
+}
00db10
+
00db10
+#include <support/test-driver.c>
00db10
Index: b/sunrpc/tst-udp-nonblocking.c
00db10
===================================================================
00db10
--- /dev/null
00db10
+++ b/sunrpc/tst-udp-nonblocking.c
00db10
@@ -0,0 +1,333 @@
00db10
+/* Test non-blocking use of the UDP client.
00db10
+   Copyright (C) 2017 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <netinet/in.h>
00db10
+#include <rpc/clnt.h>
00db10
+#include <rpc/svc.h>
00db10
+#include <stdbool.h>
00db10
+#include <string.h>
00db10
+#include <support/check.h>
00db10
+#include <support/namespace.h>
00db10
+#include <support/test-driver.h>
00db10
+#include <support/xsocket.h>
00db10
+#include <support/xunistd.h>
00db10
+#include <sys/socket.h>
00db10
+#include <time.h>
00db10
+#include <unistd.h>
00db10
+
00db10
+/* Test data serialization and deserialization.   */
00db10
+
00db10
+struct test_query
00db10
+{
00db10
+  uint32_t a;
00db10
+  uint32_t b;
00db10
+  uint32_t timeout_ms;
00db10
+};
00db10
+
00db10
+static bool_t
00db10
+xdr_test_query (XDR *xdrs, void *data, ...)
00db10
+{
00db10
+  struct test_query *p = data;
00db10
+  return xdr_uint32_t (xdrs, &p->a)
00db10
+    && xdr_uint32_t (xdrs, &p->b)
00db10
+    && xdr_uint32_t (xdrs, &p->timeout_ms);
00db10
+}
00db10
+
00db10
+struct test_response
00db10
+{
00db10
+  uint32_t server_id;
00db10
+  uint32_t seq;
00db10
+  uint32_t sum;
00db10
+};
00db10
+
00db10
+static bool_t
00db10
+xdr_test_response (XDR *xdrs, void *data, ...)
00db10
+{
00db10
+  struct test_response *p = data;
00db10
+  return xdr_uint32_t (xdrs, &p->server_id)
00db10
+    && xdr_uint32_t (xdrs, &p->seq)
00db10
+    && xdr_uint32_t (xdrs, &p->sum);
00db10
+}
00db10
+
00db10
+/* Implementation of the test server.  */
00db10
+
00db10
+enum
00db10
+  {
00db10
+    /* Number of test servers to run. */
00db10
+    SERVER_COUNT = 3,
00db10
+
00db10
+    /* RPC parameters, chosen at random.  */
00db10
+    PROGNUM = 8242,
00db10
+    VERSNUM = 19654,
00db10
+
00db10
+    /* Main RPC operation.  */
00db10
+    PROC_ADD = 1,
00db10
+
00db10
+    /* Request process termination.  */
00db10
+    PROC_EXIT,
00db10
+
00db10
+    /* Special exit status to mark successful processing.  */
00db10
+    EXIT_MARKER = 55,
00db10
+  };
00db10
+
00db10
+/* Set by the parent process to tell test servers apart.  */
00db10
+static int server_id;
00db10
+
00db10
+/* Implementation of the test server.  */
00db10
+static void
00db10
+server_dispatch (struct svc_req *request, SVCXPRT *transport)
00db10
+{
00db10
+  /* Query sequence number.  */
00db10
+  static uint32_t seq = 0;
00db10
+  ++seq;
00db10
+  static bool proc_add_seen;
00db10
+
00db10
+  if (test_verbose)
00db10
+    printf ("info: server_dispatch server_id=%d seq=%u rq_proc=%lu\n",
00db10
+            server_id, seq, request->rq_proc);
00db10
+
00db10
+  switch (request->rq_proc)
00db10
+    {
00db10
+    case PROC_ADD:
00db10
+      {
00db10
+        struct test_query query;
00db10
+        memset (&query, 0xc0, sizeof (query));
00db10
+        TEST_VERIFY_EXIT
00db10
+          (svc_getargs (transport, xdr_test_query,
00db10
+                        (void *) &query));
00db10
+
00db10
+        if (test_verbose)
00db10
+          printf ("  a=%u b=%u timeout_ms=%u\n",
00db10
+                  query.a, query.b, query.timeout_ms);
00db10
+
00db10
+        usleep (query.timeout_ms * 1000);
00db10
+
00db10
+        struct test_response response =
00db10
+          {
00db10
+            .server_id = server_id,
00db10
+            .seq = seq,
00db10
+            .sum = query.a + query.b,
00db10
+          };
00db10
+        TEST_VERIFY (svc_sendreply (transport, xdr_test_response,
00db10
+                                    (void *) &response));
00db10
+        if (test_verbose)
00db10
+          printf ("  server id %d response seq=%u sent\n", server_id, seq);
00db10
+        proc_add_seen = true;
00db10
+      }
00db10
+      break;
00db10
+
00db10
+    case PROC_EXIT:
00db10
+      TEST_VERIFY (proc_add_seen);
00db10
+      TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
00db10
+      _exit (EXIT_MARKER);
00db10
+      break;
00db10
+
00db10
+    default:
00db10
+      FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
00db10
+      break;
00db10
+    }
00db10
+}
00db10
+
00db10
+/* Return the number seconds since an arbitrary point in time.  */
00db10
+static double
00db10
+get_ticks (void)
00db10
+{
00db10
+  {
00db10
+    struct timespec ts;
00db10
+    if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
00db10
+      return ts.tv_sec + ts.tv_nsec * 1e-9;
00db10
+  }
00db10
+  {
00db10
+    struct timeval tv;
00db10
+    TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0);
00db10
+    return tv.tv_sec + tv.tv_usec * 1e-6;
00db10
+  }
00db10
+}
00db10
+
00db10
+static int
00db10
+do_test (void)
00db10
+{
00db10
+  support_become_root ();
00db10
+  support_enter_network_namespace ();
00db10
+
00db10
+  /* Information about the test servers.  */
00db10
+  struct
00db10
+  {
00db10
+    SVCXPRT *transport;
00db10
+    struct sockaddr_in address;
00db10
+    pid_t pid;
00db10
+    uint32_t xid;
00db10
+  } servers[SERVER_COUNT];
00db10
+
00db10
+  /* Spawn the test servers.  */
00db10
+  for (int i = 0; i < SERVER_COUNT; ++i)
00db10
+    {
00db10
+      servers[i].transport = svcudp_create (RPC_ANYSOCK);
00db10
+      TEST_VERIFY_EXIT (servers[i].transport != NULL);
00db10
+      servers[i].address = (struct sockaddr_in)
00db10
+        {
00db10
+          .sin_family = AF_INET,
00db10
+          .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
00db10
+          .sin_port = htons (servers[i].transport->xp_port),
00db10
+        };
00db10
+      servers[i].xid = 0xabcd0101 + i;
00db10
+      if (test_verbose)
00db10
+        printf ("info: setting up server %d xid=%x on port %d\n",
00db10
+                i, servers[i].xid, servers[i].transport->xp_port);
00db10
+
00db10
+      server_id = i;
00db10
+      servers[i].pid = xfork ();
00db10
+      if (servers[i].pid == 0)
00db10
+        {
00db10
+          TEST_VERIFY (svc_register (servers[i].transport,
00db10
+                                     PROGNUM, VERSNUM, server_dispatch, 0));
00db10
+          svc_run ();
00db10
+          FAIL_EXIT1 ("supposed to be unreachable");
00db10
+        }
00db10
+      /* We need to close the socket so that we do not accidentally
00db10
+         consume the request.  */
00db10
+      TEST_VERIFY (close (servers[i].transport->xp_sock) == 0);
00db10
+    }
00db10
+
00db10
+
00db10
+  /* The following code mirrors what ypbind does.  */
00db10
+
00db10
+  /* Copied from clnt_udp.c (like ypbind).  */
00db10
+  struct cu_data
00db10
+  {
00db10
+    int cu_sock;
00db10
+    bool_t cu_closeit;
00db10
+    struct sockaddr_in cu_raddr;
00db10
+    int cu_rlen;
00db10
+    struct timeval cu_wait;
00db10
+    struct timeval cu_total;
00db10
+    struct rpc_err cu_error;
00db10
+    XDR cu_outxdrs;
00db10
+    u_int cu_xdrpos;
00db10
+    u_int cu_sendsz;
00db10
+    char *cu_outbuf;
00db10
+    u_int cu_recvsz;
00db10
+    char cu_inbuf[1];
00db10
+  };
00db10
+
00db10
+  int client_socket = xsocket (AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
00db10
+  CLIENT *clnt = clntudp_create (&servers[0].address, PROGNUM, VERSNUM,
00db10
+                                 /* 5 seconds per-response timeout.  */
00db10
+                                 ((struct timeval) { 5, 0 }),
00db10
+                                 &client_socket);
00db10
+  TEST_VERIFY (clnt != NULL);
00db10
+  clnt->cl_auth = authunix_create_default ();
00db10
+  {
00db10
+    struct timeval zero = { 0, 0 };
00db10
+    TEST_VERIFY (clnt_control (clnt, CLSET_TIMEOUT, (void *) &zero));
00db10
+  }
00db10
+
00db10
+  /* Poke at internal data structures (like ypbind).  */
00db10
+  struct cu_data *cu = (struct cu_data *) clnt->cl_private;
00db10
+
00db10
+  /* Send a ping to each server.  */
00db10
+  double before_pings = get_ticks ();
00db10
+  for (int i = 0; i < SERVER_COUNT; ++i)
00db10
+    {
00db10
+      if (test_verbose)
00db10
+        printf ("info: sending server %d ping\n", i);
00db10
+      /* Reset the xid because it is changed by each invocation of
00db10
+         clnt_call.  Subtract one to compensate for the xid update
00db10
+         during the call.  */
00db10
+      *((u_int32_t *) (cu->cu_outbuf)) = servers[i].xid - 1;
00db10
+      cu->cu_raddr = servers[i].address;
00db10
+
00db10
+      struct test_query query = { .a = 100, .b = i + 1 };
00db10
+      if (i == 1)
00db10
+        /* Shorter timeout to prefer this server.  These timeouts must
00db10
+           be much shorter than the 5-second per-response timeout
00db10
+           configured with clntudp_create.  */
00db10
+        query.timeout_ms = 700;
00db10
+      else
00db10
+        query.timeout_ms = 1400;
00db10
+      struct test_response response = { 0 };
00db10
+      /* NB: Do not check the return value.  The server reply will
00db10
+         prove that the call worked.  */
00db10
+      double before_one_ping = get_ticks ();
00db10
+      clnt_call (clnt, PROC_ADD,
00db10
+                 xdr_test_query, (void *) &query,
00db10
+                 xdr_test_response, (void *) &response,
00db10
+                 ((struct timeval) { 0, 0 }));
00db10
+      double after_one_ping = get_ticks ();
00db10
+      if (test_verbose)
00db10
+        printf ("info: non-blocking send took %f seconds\n",
00db10
+                after_one_ping - before_one_ping);
00db10
+      /* clnt_call should return immediately.  Accept some delay in
00db10
+         case the process is descheduled.  */
00db10
+      TEST_VERIFY (after_one_ping - before_one_ping < 0.3);
00db10
+    }
00db10
+
00db10
+  /* Collect the non-blocking response.  */
00db10
+  if (test_verbose)
00db10
+    printf ("info: collecting response\n");
00db10
+  struct test_response response = { 0 };
00db10
+  TEST_VERIFY
00db10
+    (clnt_call (clnt, PROC_ADD, NULL, NULL,
00db10
+                xdr_test_response, (void *) &response,
00db10
+                ((struct timeval) { 0, 0 })) == RPC_SUCCESS);
00db10
+  double after_pings = get_ticks ();
00db10
+  if (test_verbose)
00db10
+    printf ("info: send/receive took %f seconds\n",
00db10
+            after_pings - before_pings);
00db10
+  /* Expected timeout is 0.7 seconds.  */
00db10
+  TEST_VERIFY (0.7 <= after_pings - before_pings);
00db10
+  TEST_VERIFY (after_pings - before_pings < 1.2);
00db10
+
00db10
+  uint32_t xid;
00db10
+  memcpy (&xid, &cu->cu_inbuf, sizeof (xid));
00db10
+  if (test_verbose)
00db10
+    printf ("info: non-blocking response: xid=%x server_id=%u seq=%u sum=%u\n",
00db10
+            xid, response.server_id, response.seq, response.sum);
00db10
+  /* Check that the reply from the preferred server was used.  */
00db10
+  TEST_VERIFY (servers[1].xid == xid);
00db10
+  TEST_VERIFY (response.server_id == 1);
00db10
+  TEST_VERIFY (response.seq == 1);
00db10
+  TEST_VERIFY (response.sum == 102);
00db10
+
00db10
+  auth_destroy (clnt->cl_auth);
00db10
+  clnt_destroy (clnt);
00db10
+
00db10
+  for (int i = 0; i < SERVER_COUNT; ++i)
00db10
+    {
00db10
+      if (test_verbose)
00db10
+        printf ("info: requesting server %d termination\n", i);
00db10
+      client_socket = RPC_ANYSOCK;
00db10
+      clnt = clntudp_create (&servers[i].address, PROGNUM, VERSNUM,
00db10
+                             ((struct timeval) { 5, 0 }),
00db10
+                             &client_socket);
00db10
+      TEST_VERIFY_EXIT (clnt != NULL);
00db10
+      TEST_VERIFY (clnt_call (clnt, PROC_EXIT,
00db10
+                              (xdrproc_t) xdr_void, NULL,
00db10
+                              (xdrproc_t) xdr_void, NULL,
00db10
+                              ((struct timeval) { 3, 0 })) == RPC_SUCCESS);
00db10
+      clnt_destroy (clnt);
00db10
+
00db10
+      int status;
00db10
+      xwaitpid (servers[i].pid, &status, 0);
00db10
+      TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER);
00db10
+    }
00db10
+
00db10
+  return 0;
00db10
+}
00db10
+
00db10
+#include <support/test-driver.c>
00db10
Index: b/sunrpc/tst-udp-timeout.c
00db10
===================================================================
00db10
--- /dev/null
00db10
+++ b/sunrpc/tst-udp-timeout.c
00db10
@@ -0,0 +1,402 @@
00db10
+/* Test timeout handling in the UDP client.
00db10
+   Copyright (C) 2017 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <netinet/in.h>
00db10
+#include <rpc/clnt.h>
00db10
+#include <rpc/svc.h>
00db10
+#include <stdbool.h>
00db10
+#include <string.h>
00db10
+#include <support/check.h>
00db10
+#include <support/namespace.h>
00db10
+#include <support/test-driver.h>
00db10
+#include <support/xsocket.h>
00db10
+#include <support/xunistd.h>
00db10
+#include <sys/socket.h>
00db10
+#include <time.h>
00db10
+#include <unistd.h>
00db10
+
00db10
+/* Test data serialization and deserialization.   */
00db10
+
00db10
+struct test_query
00db10
+{
00db10
+  uint32_t a;
00db10
+  uint32_t b;
00db10
+  uint32_t timeout_ms;
00db10
+  uint32_t wait_for_seq;
00db10
+  uint32_t garbage_packets;
00db10
+};
00db10
+
00db10
+static bool_t
00db10
+xdr_test_query (XDR *xdrs, void *data, ...)
00db10
+{
00db10
+  struct test_query *p = data;
00db10
+  return xdr_uint32_t (xdrs, &p->a)
00db10
+    && xdr_uint32_t (xdrs, &p->b)
00db10
+    && xdr_uint32_t (xdrs, &p->timeout_ms)
00db10
+    && xdr_uint32_t (xdrs, &p->wait_for_seq)
00db10
+    && xdr_uint32_t (xdrs, &p->garbage_packets);
00db10
+}
00db10
+
00db10
+struct test_response
00db10
+{
00db10
+  uint32_t seq;
00db10
+  uint32_t sum;
00db10
+};
00db10
+
00db10
+static bool_t
00db10
+xdr_test_response (XDR *xdrs, void *data, ...)
00db10
+{
00db10
+  struct test_response *p = data;
00db10
+  return xdr_uint32_t (xdrs, &p->seq)
00db10
+    && xdr_uint32_t (xdrs, &p->sum);
00db10
+}
00db10
+
00db10
+/* Implementation of the test server.  */
00db10
+
00db10
+enum
00db10
+  {
00db10
+    /* RPC parameters, chosen at random.  */
00db10
+    PROGNUM = 15717,
00db10
+    VERSNUM = 13689,
00db10
+
00db10
+    /* Main RPC operation.  */
00db10
+    PROC_ADD = 1,
00db10
+
00db10
+    /* Reset the sequence number.  */
00db10
+    PROC_RESET_SEQ,
00db10
+
00db10
+    /* Request process termination.  */
00db10
+    PROC_EXIT,
00db10
+
00db10
+    /* Special exit status to mark successful processing.  */
00db10
+    EXIT_MARKER = 55,
00db10
+  };
00db10
+
00db10
+static void
00db10
+server_dispatch (struct svc_req *request, SVCXPRT *transport)
00db10
+{
00db10
+  /* Query sequence number.  */
00db10
+  static uint32_t seq = 0;
00db10
+  ++seq;
00db10
+
00db10
+  if (test_verbose)
00db10
+    printf ("info: server_dispatch seq=%u rq_proc=%lu\n",
00db10
+            seq, request->rq_proc);
00db10
+
00db10
+  switch (request->rq_proc)
00db10
+    {
00db10
+    case PROC_ADD:
00db10
+      {
00db10
+        struct test_query query;
00db10
+        memset (&query, 0xc0, sizeof (query));
00db10
+        TEST_VERIFY_EXIT
00db10
+          (svc_getargs (transport, xdr_test_query,
00db10
+                        (void *) &query));
00db10
+
00db10
+        if (test_verbose)
00db10
+          printf ("  a=%u b=%u timeout_ms=%u wait_for_seq=%u"
00db10
+                  " garbage_packets=%u\n",
00db10
+                  query.a, query.b, query.timeout_ms, query.wait_for_seq,
00db10
+                  query.garbage_packets);
00db10
+
00db10
+        if (seq < query.wait_for_seq)
00db10
+          {
00db10
+            /* No response at this point.  */
00db10
+            if (test_verbose)
00db10
+              printf ("  skipped response\n");
00db10
+            break;
00db10
+          }
00db10
+
00db10
+        if (query.garbage_packets > 0)
00db10
+          {
00db10
+            int per_packet_timeout;
00db10
+            if (query.timeout_ms > 0)
00db10
+              per_packet_timeout
00db10
+                = query.timeout_ms * 1000 / query.garbage_packets;
00db10
+            else
00db10
+              per_packet_timeout = 0;
00db10
+
00db10
+            char buf[20];
00db10
+            memset (&buf, 0xc0, sizeof (buf));
00db10
+            for (int i = 0; i < query.garbage_packets; ++i)
00db10
+              {
00db10
+                /* 13 is relatively prime to 20 = sizeof (buf) + 1, so
00db10
+                   the len variable will cover the entire interval
00db10
+                   [0, 20] if query.garbage_packets is sufficiently
00db10
+                   large.  */
00db10
+                size_t len = (i * 13 + 1) % (sizeof (buf) + 1);
00db10
+                TEST_VERIFY (sendto (transport->xp_sock,
00db10
+                                     buf, len, MSG_NOSIGNAL,
00db10
+                                     (struct sockaddr *) &transport->xp_raddr,
00db10
+                                     transport->xp_addrlen) == len);
00db10
+                if (per_packet_timeout > 0)
00db10
+                  usleep (per_packet_timeout);
00db10
+              }
00db10
+          }
00db10
+        else if (query.timeout_ms > 0)
00db10
+          usleep (query.timeout_ms * 1000);
00db10
+
00db10
+        struct test_response response =
00db10
+          {
00db10
+            .seq = seq,
00db10
+            .sum = query.a + query.b,
00db10
+          };
00db10
+        TEST_VERIFY (svc_sendreply (transport, xdr_test_response,
00db10
+                                    (void *) &response));
00db10
+      }
00db10
+      break;
00db10
+
00db10
+    case PROC_RESET_SEQ:
00db10
+      seq = 0;
00db10
+      TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
00db10
+      break;
00db10
+
00db10
+    case PROC_EXIT:
00db10
+      TEST_VERIFY (svc_sendreply (transport, (xdrproc_t) xdr_void, NULL));
00db10
+      _exit (EXIT_MARKER);
00db10
+      break;
00db10
+
00db10
+    default:
00db10
+      FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
00db10
+      break;
00db10
+    }
00db10
+}
00db10
+
00db10
+/* Implementation of the test client.  */
00db10
+
00db10
+static struct test_response
00db10
+test_call (CLIENT *clnt, int proc, struct test_query query,
00db10
+           struct timeval timeout)
00db10
+{
00db10
+  if (test_verbose)
00db10
+    printf ("info: test_call proc=%d timeout=%lu.%06lu\n",
00db10
+            proc, (unsigned long) timeout.tv_sec,
00db10
+            (unsigned long) timeout.tv_usec);
00db10
+  struct test_response response;
00db10
+  TEST_VERIFY_EXIT (clnt_call (clnt, proc,
00db10
+                               xdr_test_query, (void *) &query,
00db10
+                               xdr_test_response, (void *) &response,
00db10
+                               timeout)
00db10
+                    == RPC_SUCCESS);
00db10
+  return response;
00db10
+}
00db10
+
00db10
+static void
00db10
+test_call_timeout (CLIENT *clnt, int proc, struct test_query query,
00db10
+                   struct timeval timeout)
00db10
+{
00db10
+  struct test_response response;
00db10
+  TEST_VERIFY (clnt_call (clnt, proc,
00db10
+                          xdr_test_query, (void *) &query,
00db10
+                          xdr_test_response, (void *) &response,
00db10
+                          timeout)
00db10
+               == RPC_TIMEDOUT);
00db10
+}
00db10
+
00db10
+/* Complete one regular RPC call to drain the server socket
00db10
+   buffer.  Resets the sequence number.  */
00db10
+static void
00db10
+test_call_flush (CLIENT *clnt)
00db10
+{
00db10
+  /* This needs a longer timeout to flush out all pending requests.
00db10
+     The choice of 5 seconds is larger than the per-response timeouts
00db10
+     requested via the timeout_ms field.  */
00db10
+  if (test_verbose)
00db10
+    printf ("info: flushing pending queries\n");
00db10
+  TEST_VERIFY_EXIT (clnt_call (clnt, PROC_RESET_SEQ,
00db10
+                               (xdrproc_t) xdr_void, NULL,
00db10
+                               (xdrproc_t) xdr_void, NULL,
00db10
+                               ((struct timeval) { 5, 0 }))
00db10
+                    == RPC_SUCCESS);
00db10
+}
00db10
+
00db10
+/* Return the number seconds since an arbitrary point in time.  */
00db10
+static double
00db10
+get_ticks (void)
00db10
+{
00db10
+  {
00db10
+    struct timespec ts;
00db10
+    if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
00db10
+      return ts.tv_sec + ts.tv_nsec * 1e-9;
00db10
+  }
00db10
+  {
00db10
+    struct timeval tv;
00db10
+    TEST_VERIFY_EXIT (gettimeofday (&tv, NULL) == 0);
00db10
+    return tv.tv_sec + tv.tv_usec * 1e-6;
00db10
+  }
00db10
+}
00db10
+
00db10
+static void
00db10
+test_udp_server (int port)
00db10
+{
00db10
+  struct sockaddr_in sin =
00db10
+    {
00db10
+      .sin_family = AF_INET,
00db10
+      .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
00db10
+      .sin_port = htons (port)
00db10
+    };
00db10
+  int sock = RPC_ANYSOCK;
00db10
+
00db10
+  /* The client uses a 1.5 second timeout for retries.  The timeouts
00db10
+     are arbitrary, but chosen so that there is a substantial gap
00db10
+     between them, but the total time spent waiting is not too
00db10
+     large.  */
00db10
+  CLIENT *clnt = clntudp_create (&sin, PROGNUM, VERSNUM,
00db10
+                                 (struct timeval) { 1, 500 * 1000 },
00db10
+                                 &sock);
00db10
+  TEST_VERIFY_EXIT (clnt != NULL);
00db10
+
00db10
+  /* Basic call/response test.  */
00db10
+  struct test_response response = test_call
00db10
+    (clnt, PROC_ADD,
00db10
+     (struct test_query) { .a = 17, .b = 4 },
00db10
+     (struct timeval) { 3, 0 });
00db10
+  TEST_VERIFY (response.sum == 21);
00db10
+  TEST_VERIFY (response.seq == 1);
00db10
+
00db10
+  /* Check that garbage packets do not interfere with timeout
00db10
+     processing.  */
00db10
+  double before = get_ticks ();
00db10
+  response = test_call
00db10
+    (clnt, PROC_ADD,
00db10
+     (struct test_query) {
00db10
+       .a = 19, .b = 4, .timeout_ms = 500, .garbage_packets = 21,
00db10
+     },
00db10
+     (struct timeval) { 3, 0 });
00db10
+  TEST_VERIFY (response.sum == 23);
00db10
+  TEST_VERIFY (response.seq == 2);
00db10
+  double after = get_ticks ();
00db10
+  if (test_verbose)
00db10
+    printf ("info: 21 garbage packets took %f seconds\n", after - before);
00db10
+  /* Expected timeout is 0.5 seconds.  Add some slack in case process
00db10
+     scheduling delays processing the query or response, but do not
00db10
+     accept a retry (which would happen at 1.5 seconds).  */
00db10
+  TEST_VERIFY (0.5 <= after - before);
00db10
+  TEST_VERIFY (after - before < 1.2);
00db10
+  test_call_flush (clnt);
00db10
+
00db10
+  /* Check that missing a response introduces a 1.5 second timeout, as
00db10
+     requested when calling clntudp_create.  */
00db10
+  before = get_ticks ();
00db10
+  response = test_call
00db10
+    (clnt, PROC_ADD,
00db10
+     (struct test_query) { .a = 170, .b = 40, .wait_for_seq = 2 },
00db10
+     (struct timeval) { 3, 0 });
00db10
+  TEST_VERIFY (response.sum == 210);
00db10
+  TEST_VERIFY (response.seq == 2);
00db10
+  after = get_ticks ();
00db10
+  if (test_verbose)
00db10
+    printf ("info: skipping one response took %f seconds\n",
00db10
+            after - before);
00db10
+  /* Expected timeout is 1.5 seconds.  Do not accept a second retry
00db10
+     (which would happen at 3 seconds).  */
00db10
+  TEST_VERIFY (1.5 <= after - before);
00db10
+  TEST_VERIFY (after - before < 2.9);
00db10
+  test_call_flush (clnt);
00db10
+
00db10
+  /* Check that the overall timeout wins against the per-query
00db10
+     timeout.  */
00db10
+  before = get_ticks ();
00db10
+  test_call_timeout
00db10
+    (clnt, PROC_ADD,
00db10
+     (struct test_query) { .a = 170, .b = 41, .wait_for_seq = 2 },
00db10
+     (struct timeval) { 0, 750 * 1000 });
00db10
+  after = get_ticks ();
00db10
+  if (test_verbose)
00db10
+    printf ("info: 0.75 second timeout took %f seconds\n",
00db10
+            after - before);
00db10
+  TEST_VERIFY (0.75 <= after - before);
00db10
+  TEST_VERIFY (after - before < 1.4);
00db10
+  test_call_flush (clnt);
00db10
+
00db10
+  for (int with_garbage = 0; with_garbage < 2; ++with_garbage)
00db10
+    {
00db10
+      /* Check that no response at all causes the client to bail out.  */
00db10
+      before = get_ticks ();
00db10
+      test_call_timeout
00db10
+        (clnt, PROC_ADD,
00db10
+         (struct test_query) {
00db10
+           .a = 170, .b = 40, .timeout_ms = 1200,
00db10
+           .garbage_packets = with_garbage * 21
00db10
+         },
00db10
+         (struct timeval) { 0, 750 * 1000 });
00db10
+      after = get_ticks ();
00db10
+      if (test_verbose)
00db10
+        printf ("info: test_udp_server: 0.75 second timeout took %f seconds"
00db10
+                " (garbage %d)\n",
00db10
+                after - before, with_garbage);
00db10
+      TEST_VERIFY (0.75 <= after - before);
00db10
+      TEST_VERIFY (after - before < 1.4);
00db10
+      test_call_flush (clnt);
00db10
+
00db10
+      /* As above, but check the total timeout.  */
00db10
+      before = get_ticks ();
00db10
+      test_call_timeout
00db10
+        (clnt, PROC_ADD,
00db10
+         (struct test_query) {
00db10
+           .a = 170, .b = 40, .timeout_ms = 3000,
00db10
+           .garbage_packets = with_garbage * 30
00db10
+         },
00db10
+         (struct timeval) { 2, 300 * 1000 });
00db10
+      after = get_ticks ();
00db10
+      if (test_verbose)
00db10
+        printf ("info: test_udp_server: 2.3 second timeout took %f seconds"
00db10
+                " (garbage %d)\n",
00db10
+                after - before, with_garbage);
00db10
+      TEST_VERIFY (2.3 <= after - before);
00db10
+      TEST_VERIFY (after - before < 3.0);
00db10
+      test_call_flush (clnt);
00db10
+    }
00db10
+
00db10
+  TEST_VERIFY_EXIT (clnt_call (clnt, PROC_EXIT,
00db10
+                               (xdrproc_t) xdr_void, NULL,
00db10
+                               (xdrproc_t) xdr_void, NULL,
00db10
+                               ((struct timeval) { 5, 0 }))
00db10
+                    == RPC_SUCCESS);
00db10
+  clnt_destroy (clnt);
00db10
+}
00db10
+
00db10
+static int
00db10
+do_test (void)
00db10
+{
00db10
+  support_become_root ();
00db10
+  support_enter_network_namespace ();
00db10
+
00db10
+  SVCXPRT *transport = svcudp_create (RPC_ANYSOCK);
00db10
+  TEST_VERIFY_EXIT (transport != NULL);
00db10
+  TEST_VERIFY (svc_register (transport, PROGNUM, VERSNUM, server_dispatch, 0));
00db10
+
00db10
+  pid_t pid = xfork ();
00db10
+  if (pid == 0)
00db10
+    {
00db10
+      svc_run ();
00db10
+      FAIL_EXIT1 ("supposed to be unreachable");
00db10
+    }
00db10
+  test_udp_server (transport->xp_port);
00db10
+
00db10
+  int status;
00db10
+  xwaitpid (pid, &status, 0);
00db10
+  TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_MARKER);
00db10
+
00db10
+  SVC_DESTROY (transport);
00db10
+  return 0;
00db10
+}
00db10
+
00db10
+/* The minimum run time is around 17 seconds.  */
00db10
+#define TIMEOUT 25
00db10
+#include <support/test-driver.c>
00db10
Index: b/inet/net-internal.h
00db10
===================================================================
00db10
--- /dev/null
00db10
+++ b/inet/net-internal.h
00db10
@@ -0,0 +1,112 @@
00db10
+/* Network-related functions for internal library use.
00db10
+   Copyright (C) 2016-2017 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#ifndef _NET_INTERNAL_H
00db10
+#define _NET_INTERNAL_H 1
00db10
+
00db10
+#include <stdbool.h>
00db10
+#include <stdint.h>
00db10
+#include <sys/time.h>
00db10
+
00db10
+/* Deadline handling for enforcing timeouts.
00db10
+
00db10
+   Code should call __deadline_current_time to obtain the current time
00db10
+   and cache it locally.  The cache needs updating after every
00db10
+   long-running or potentially blocking operation.  Deadlines relative
00db10
+   to the current time can be computed using __deadline_from_timeval.
00db10
+   The deadlines may have to be recomputed in response to certain
00db10
+   events (such as an incoming packet), but they are absolute (not
00db10
+   relative to the current time).  A timeout suitable for use with the
00db10
+   poll function can be computed from such a deadline using
00db10
+   __deadline_to_ms.
00db10
+
00db10
+   The fields in the structs defined belowed should only be used
00db10
+   within the implementation.  */
00db10
+
00db10
+/* Cache of the current time.  Used to compute deadlines from relative
00db10
+   timeouts and vice versa.  */
00db10
+struct deadline_current_time
00db10
+{
00db10
+  struct timespec current;
00db10
+};
00db10
+
00db10
+/* Return the current time.  Terminates the process if the current
00db10
+   time is not available.  */
00db10
+struct deadline_current_time __deadline_current_time (void)
00db10
+  internal_function attribute_hidden;
00db10
+
00db10
+/* Computed absolute deadline.  */
00db10
+struct deadline
00db10
+{
00db10
+  struct timespec absolute;
00db10
+};
00db10
+
00db10
+
00db10
+/* For internal use only.  */
00db10
+static inline bool
00db10
+__deadline_is_infinite (struct deadline deadline)
00db10
+{
00db10
+  return deadline.absolute.tv_nsec < 0;
00db10
+}
00db10
+
00db10
+/* Return true if the current time is at the deadline or past it.  */
00db10
+static inline bool
00db10
+__deadline_elapsed (struct deadline_current_time current,
00db10
+                    struct deadline deadline)
00db10
+{
00db10
+  return !__deadline_is_infinite (deadline)
00db10
+    && (current.current.tv_sec > deadline.absolute.tv_sec
00db10
+        || (current.current.tv_sec == deadline.absolute.tv_sec
00db10
+            && current.current.tv_nsec >= deadline.absolute.tv_nsec));
00db10
+}
00db10
+
00db10
+/* Return the deadline which occurs first.  */
00db10
+static inline struct deadline
00db10
+__deadline_first (struct deadline left, struct deadline right)
00db10
+{
00db10
+  if (__deadline_is_infinite (right)
00db10
+      || left.absolute.tv_sec < right.absolute.tv_sec
00db10
+      || (left.absolute.tv_sec == right.absolute.tv_sec
00db10
+          && left.absolute.tv_nsec < right.absolute.tv_nsec))
00db10
+    return left;
00db10
+  else
00db10
+    return right;
00db10
+}
00db10
+
00db10
+/* Add TV to the current time and return it.  Returns a special
00db10
+   infinite absolute deadline on overflow.  */
00db10
+struct deadline __deadline_from_timeval (struct deadline_current_time,
00db10
+                                         struct timeval tv)
00db10
+  internal_function attribute_hidden;
00db10
+
00db10
+/* Compute the number of milliseconds until the specified deadline,
00db10
+   from the current time in the argument.  The result is mainly for
00db10
+   use with poll.  If the deadline has already passed, return 0.  If
00db10
+   the result would overflow an int, return INT_MAX.  */
00db10
+int __deadline_to_ms (struct deadline_current_time, struct deadline)
00db10
+  internal_function attribute_hidden;
00db10
+
00db10
+/* Return true if TV.tv_sec is non-negative and TV.tv_usec is in the
00db10
+   interval [0, 999999].  */
00db10
+static inline bool
00db10
+__is_timeval_valid_timeout (struct timeval tv)
00db10
+{
00db10
+  return tv.tv_sec >= 0 && tv.tv_usec >= 0 && tv.tv_usec < 1000 * 1000;
00db10
+}
00db10
+
00db10
+#endif /* _NET_INTERNAL_H */
00db10
Index: b/inet/Makefile
00db10
===================================================================
00db10
--- a/inet/Makefile
00db10
+++ b/inet/Makefile
00db10
@@ -44,13 +44,18 @@ routines := htonl htons		\
00db10
 	    getaliasent_r getaliasent getaliasname getaliasname_r \
00db10
 	    in6_addr getnameinfo if_index ifaddrs inet6_option \
00db10
 	    getipv4sourcefilter setipv4sourcefilter \
00db10
-	    getsourcefilter setsourcefilter inet6_opt inet6_rth
00db10
+	    getsourcefilter setsourcefilter inet6_opt inet6_rth \
00db10
+	    deadline
00db10
 
00db10
 aux := check_pf check_native ifreq
00db10
 
00db10
 tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \
00db10
 	 tst-gethnm test-ifaddrs bug-if1 test-inet6_opt tst-ether_line \
00db10
-	 tst-getni1 tst-getni2 tst-inet6_rth tst-checks
00db10
+	 tst-getni1 tst-getni2 tst-inet6_rth tst-checks tst-deadline
00db10
+
00db10
+# tst-deadline must be linked statically so that we can access
00db10
+# internal functions.
00db10
+tests-static += tst-deadline
00db10
 
00db10
 include ../Rules
00db10