On Sun, Dec 10 2017, Scott Cheloha <[email protected]> wrote:
> Hi,
>
> These timeouts in sunrpc need to be based on the monotonic
> clock to avoid a race with adjtime(2), settimeofday(2), etc.
>
> There are obvious possible improvements here and elsewhere
> in sunrpc.  Here especially the time-related variable names
> could be made more descriptive, the various BSD time macros
> could be more effectively employed, and ppoll(2) can (now) be
> leveraged to ignore EINTR and simplify those loops.  But that
> all belongs in a separate diff.
>
> Likewise, there are dead timevals elsewhere in RPC
> that I can remove in a separate diff.
>
> Then again, this is ancient upstream code.  Does cleanup here
> put too much burden on the project?  Like, I imagine that the
> sort of cleanup I'm seeing here could make merging changes
> from elsewhere painful.

Well, afaik upstream can be considered dead, and looking at cvs log
people have implemented a bunch of changes and improvements to these
files, so I think that further improvements are welcome.

> I have also included here my sys/time.h diff from like two
> seconds ago, which enables the use of TIMEVAL_TO_TIMESPEC
> in the body of the if statement in clnt_udp.c.  Its use makes
> the diff simpler.

(The sys/time.h change has since been committed.)

> This is probably not adequately tested, though it compiles
> and I'm not seeing any issues in my (small) NFS setup.
>
> Thoughts and feedback?

I'll schedule some time for a review tomorrow, more eyes would be
welcome.

> --
> Scott Cheloha
>
> Index: lib/libc/rpc/clnt_tcp.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/rpc/clnt_tcp.c,v
> retrieving revision 1.29
> diff -u -p -r1.29 clnt_tcp.c
> --- lib/libc/rpc/clnt_tcp.c   1 Nov 2015 03:45:29 -0000       1.29
> +++ lib/libc/rpc/clnt_tcp.c   10 Dec 2017 06:08:55 -0000
> @@ -385,25 +385,26 @@ static int
>  readtcp(struct ct_data *ct, caddr_t buf, int len)
>  {
>       struct pollfd pfd[1];
> -     struct timeval start, after, duration, tmp;
> -     int delta, r, save_errno;
> +     struct timespec start, after, duration, tmp, delta, wait;
> +     int r, save_errno;
>  
>       if (len == 0)
>               return (0);
>  
>       pfd[0].fd = ct->ct_sock;
>       pfd[0].events = POLLIN;
> -     delta = ct->ct_wait.tv_sec * 1000 + ct->ct_wait.tv_usec / 1000;
> -     gettimeofday(&start, NULL);
> +     TIMEVAL_TO_TIMESPEC(&ct->ct_wait, &wait);
> +     delta = wait;
> +     clock_gettime(CLOCK_MONOTONIC, &start);
>       for (;;) {
> -             r = poll(pfd, 1, delta);
> +             r = ppoll(pfd, 1, &delta, NULL);
>               save_errno = errno;
>  
> -             gettimeofday(&after, NULL);
> -             timersub(&start, &after, &duration);
> -             timersub(&ct->ct_wait, &duration, &tmp);
> -             delta = tmp.tv_sec * 1000 + tmp.tv_usec / 1000;
> -             if (delta <= 0)
> +             clock_gettime(CLOCK_MONOTONIC, &after);
> +             timespecsub(&start, &after, &duration);
> +             timespecsub(&wait, &duration, &tmp);
> +             delta = tmp;
> +             if (delta.tv_sec < 0 || !timespecisset(&delta))
>                       r = 0;
>  
>               switch (r) {
> Index: lib/libc/rpc/clnt_udp.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/rpc/clnt_udp.c,v
> retrieving revision 1.32
> diff -u -p -r1.32 clnt_udp.c
> --- lib/libc/rpc/clnt_udp.c   1 Nov 2015 03:45:29 -0000       1.32
> +++ lib/libc/rpc/clnt_udp.c   10 Dec 2017 06:08:55 -0000
> @@ -216,19 +216,20 @@ clntudp_call(CLIENT *cl,        /* client handl
>       struct sockaddr_in from;
>       struct rpc_msg reply_msg;
>       XDR reply_xdrs;
> -     struct timeval time_waited, start, after, tmp1, tmp2;
> +     struct timespec time_waited, start, after, tmp1, tmp2, wait;
>       bool_t ok;
>       int nrefreshes = 2;     /* number of times to refresh cred */
> -     struct timeval timeout;
> +     struct timespec timeout;
>  
>       if (cu->cu_total.tv_usec == -1)
> -             timeout = utimeout;     /* use supplied timeout */
> +             TIMEVAL_TO_TIMESPEC(&utimeout, &timeout);     /* use supplied 
> timeout */
>       else
> -             timeout = cu->cu_total; /* use default timeout */
> +             TIMEVAL_TO_TIMESPEC(&cu->cu_total, &timeout); /* use default 
> timeout */
>  
>       pfd[0].fd = cu->cu_sock;
>       pfd[0].events = POLLIN;
> -     timerclear(&time_waited);
> +     timespecclear(&time_waited);
> +     TIMEVAL_TO_TIMESPEC(&cu->cu_wait, &wait);
>  call_again:
>       xdrs = &(cu->cu_outxdrs);
>       xdrs->x_op = XDR_ENCODE;
> @@ -254,7 +255,7 @@ send_again:
>       /*
>        * Hack to provide rpc-based message passing
>        */
> -     if (!timerisset(&timeout))
> +     if (!timespecisset(&timeout))
>               return (cu->cu_error.re_status = RPC_TIMEDOUT);
>  
>       /*
> @@ -266,14 +267,13 @@ send_again:
>       reply_msg.acpted_rply.ar_results.where = resultsp;
>       reply_msg.acpted_rply.ar_results.proc = xresults;
>  
> -     gettimeofday(&start, NULL);
> +     clock_gettime(CLOCK_MONOTONIC, &start);
>       for (;;) {
> -             switch (poll(pfd, 1,
> -                 cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000)) {
> +             switch (ppoll(pfd, 1, &wait, NULL)) {
>               case 0:
> -                     timeradd(&time_waited, &cu->cu_wait, &tmp1);
> +                     timespecadd(&time_waited, &wait, &tmp1);
>                       time_waited = tmp1;
> -                     if (timercmp(&time_waited, &timeout, <))
> +                     if (timespeccmp(&time_waited, &timeout, <))
>                               goto send_again;
>                       return (cu->cu_error.re_status = RPC_TIMEDOUT);
>               case 1:
> @@ -286,11 +286,11 @@ send_again:
>                       /* FALLTHROUGH */
>               case -1:
>                       if (errno == EINTR) {
> -                             gettimeofday(&after, NULL);
> -                             timersub(&after, &start, &tmp1);
> -                             timeradd(&time_waited, &tmp1, &tmp2);
> +                             clock_gettime(CLOCK_MONOTONIC, &after);
> +                             timespecsub(&after, &start, &tmp1);
> +                             timespecadd(&time_waited, &tmp1, &tmp2);
>                               time_waited = tmp2;
> -                             if (timercmp(&time_waited, &timeout, <))
> +                             if (timespeccmp(&time_waited, &timeout, <))
>                                       continue;
>                               return (cu->cu_error.re_status = RPC_TIMEDOUT);
>                       }
> Index: lib/libc/rpc/svc_tcp.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/rpc/svc_tcp.c,v
> retrieving revision 1.37
> diff -u -p -r1.37 svc_tcp.c
> --- lib/libc/rpc/svc_tcp.c    1 Nov 2015 03:45:29 -0000       1.37
> +++ lib/libc/rpc/svc_tcp.c    10 Dec 2017 06:08:55 -0000
> @@ -321,7 +321,7 @@ svctcp_destroy(SVCXPRT *xprt)
>   * All read operations timeout after 35 seconds.
>   * A timeout is fatal for the connection.
>   */
> -static struct timeval wait_per_try = { 35, 0 };
> +static struct timespec wait_per_try = { 35, 0 };
>  
>  /*
>   * reads data from the tcp conection.
> @@ -332,31 +332,30 @@ static int
>  readtcp(SVCXPRT *xprt, caddr_t buf, int len)
>  {
>       int sock = xprt->xp_sock;
> -     int delta, nready;
> -     struct timeval start;
> -     struct timeval tmp1, tmp2;
> +     int nready;
> +     struct timespec start, delta, tmp1, tmp2;
>       struct pollfd pfd[1];
>  
>       /*
>        * All read operations timeout after 35 seconds.
>        * A timeout is fatal for the connection.
>        */
> -     delta = wait_per_try.tv_sec * 1000;
> -     gettimeofday(&start, NULL);
> +     delta = wait_per_try;
> +     clock_gettime(CLOCK_MONOTONIC, &start);
>       pfd[0].fd = sock;
>       pfd[0].events = POLLIN;
>       do {
> -             nready = poll(pfd, 1, delta);
> +             nready = ppoll(pfd, 1, &delta, NULL);
>               switch (nready) {
>               case -1:
>                       if (errno != EINTR)
>                               goto fatal_err;
> -                     gettimeofday(&tmp1, NULL);
> -                     timersub(&tmp1, &start, &tmp2);
> -                     timersub(&wait_per_try, &tmp2, &tmp1);
> -                     if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
> +                     clock_gettime(CLOCK_MONOTONIC, &tmp1);
> +                     timespecsub(&tmp1, &start, &tmp2);
> +                     timespecsub(&wait_per_try, &tmp2, &tmp1);
> +                     if (tmp1.tv_sec < 0 || !timespecisset(&tmp1))
>                               goto fatal_err;
> -                     delta = tmp1.tv_sec * 1000 + tmp1.tv_usec / 1000;
> +                     delta = tmp1;
>                       continue;
>               case 0:
>                       goto fatal_err;
> Index: sys/sys/time.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/time.h,v
> retrieving revision 1.36
> diff -u -p -r1.36 time.h
> --- sys/sys/time.h    12 Sep 2016 19:41:20 -0000      1.36
> +++ sys/sys/time.h    10 Dec 2017 06:09:22 -0000
> @@ -60,14 +60,14 @@ struct timespec {
>  };
>  #endif
>  
> -#define      TIMEVAL_TO_TIMESPEC(tv, ts) {                                   
> \
> +#define      TIMEVAL_TO_TIMESPEC(tv, ts) do {                                
> \
>       (ts)->tv_sec = (tv)->tv_sec;                                    \
>       (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
> -}
> -#define      TIMESPEC_TO_TIMEVAL(tv, ts) {                                   
> \
> +} while (0)
> +#define      TIMESPEC_TO_TIMEVAL(tv, ts) do {                                
> \
>       (tv)->tv_sec = (ts)->tv_sec;                                    \
>       (tv)->tv_usec = (ts)->tv_nsec / 1000;                           \
> -}
> +} while (0)
>  
>  struct timezone {
>       int     tz_minuteswest; /* minutes west of Greenwich */
>

-- 
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply via email to