ntpd: drop delay outliers; run "unsync" handler if no replies for some time
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 22 May 2013 15:48:19 +0000 (17:48 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 22 May 2013 15:48:19 +0000 (17:48 +0200)
function                                             old     new   delta
ntpd_main                                            960    1029     +69
recv_and_process_peer_pkt                            834     892     +58
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 127/0)             Total: 127 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/ntpd.c

index 0f4319e..9ec2558 100644 (file)
@@ -95,6 +95,7 @@
 #define RETRY_INTERVAL  5       /* on error, retry in N secs */
 #define RESPONSE_INTERVAL 15    /* wait for reply up to N secs */
 #define INITIAL_SAMPLES 4       /* how many samples do we want for init */
+#define BAD_DELAY_GROWTH 4     /* drop packet if its delay grew by more than this */
 
 /* Clock discipline parameters and constants */
 
@@ -1616,6 +1617,7 @@ recv_and_process_peer_pkt(peer_t *p)
        ssize_t     size;
        msg_t       msg;
        double      T1, T2, T3, T4;
+       double      dv;
        unsigned    interval;
        datapoint_t *datapoint;
        peer_t      *q;
@@ -1665,9 +1667,8 @@ recv_and_process_peer_pkt(peer_t *p)
 // TODO: stratum 0 responses may have commands in 32-bit m_refid field:
 // "DENY", "RSTR" - peer does not like us at all
 // "RATE" - peer is overloaded, reduce polling freq
-               interval = poll_interval(0);
-               bb_error_msg("reply from %s: peer is unsynced, next query in %us", p->p_dotted, interval);
-               goto set_next_and_ret;
+               bb_error_msg("reply from %s: peer is unsynced", p->p_dotted);
+               goto pick_normal_interval;
        }
 
 //     /* Verify valid root distance */
@@ -1700,21 +1701,31 @@ recv_and_process_peer_pkt(peer_t *p)
        T4 = G.cur_time;
 
        p->lastpkt_recv_time = T4;
-
        VERB5 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
-       p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
-       datapoint = &p->filter_datapoint[p->datapoint_idx];
-       datapoint->d_recv_time = T4;
-       datapoint->d_offset    = ((T2 - T1) + (T3 - T4)) / 2;
+
        /* The delay calculation is a special case. In cases where the
         * server and client clocks are running at different rates and
         * with very fast networks, the delay can appear negative. In
         * order to avoid violating the Principle of Least Astonishment,
         * the delay is clamped not less than the system precision.
         */
+       dv = p->lastpkt_delay;
        p->lastpkt_delay = (T4 - T1) - (T3 - T2);
        if (p->lastpkt_delay < G_precision_sec)
                p->lastpkt_delay = G_precision_sec;
+       /*
+        * If this packet's delay is much bigger than the last one,
+        * it's better to just ignore it than use its much less precise value.
+        */
+       if (p->reachable_bits && p->lastpkt_delay > dv * BAD_DELAY_GROWTH) {
+               bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay);
+               goto pick_normal_interval;
+       }
+
+       p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
+       datapoint = &p->filter_datapoint[p->datapoint_idx];
+       datapoint->d_recv_time = T4;
+       datapoint->d_offset    = ((T2 - T1) + (T3 - T4)) / 2;
        datapoint->d_dispersion = LOG2D(msg.m_precision_exp) + G_precision_sec;
        if (!p->reachable_bits) {
                /* 1st datapoint ever - replicate offset in every element */
@@ -1811,6 +1822,7 @@ recv_and_process_peer_pkt(peer_t *p)
        }
 
        /* Decide when to send new query for this peer */
+ pick_normal_interval:
        interval = poll_interval(0);
 
  set_next_and_ret:
@@ -2195,6 +2207,20 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
                                gettime1900d(); /* sets G.cur_time */
                        }
                }
+
+               if (G.ntp_peers && G.stratum != MAXSTRAT) {
+                       for (item = G.ntp_peers; item != NULL; item = item->link) {
+                               peer_t *p = (peer_t *) item->data;
+                               if (p->reachable_bits)
+                                       goto have_reachable_peer;
+                       }
+                       /* No peer responded for last 8 packets, panic */
+                       G.polladj_count = 0;
+                       G.poll_exp = MINPOLL;
+                       G.stratum = MAXSTRAT;
+                       run_script("unsync", G.last_update_offset);
+ have_reachable_peer: ;
+               }
        } /* while (!bb_got_signal) */
 
        remove_pidfile(CONFIG_PID_FILE_PATH "/ntpd.pid");