diff --git a/configure.ac b/configure.ac index ba0aa2e9c..2128c64c7 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,7 @@ AC_C_BIGENDIAN AC_SYS_LARGEFILE AC_SEARCH_LIBS([pow], [m]) +AC_SEARCH_LIBS([clock_gettime], [rt]) OVS_CHECK_COVERAGE OVS_CHECK_NDEBUG diff --git a/datapath/flow.c b/datapath/flow.c index 548c729af..bf50e94cd 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -111,7 +111,7 @@ void flow_used(struct sw_flow *flow, struct sk_buff *skb) } spin_lock_bh(&flow->lock); - getnstimeofday(&flow->used); + ktime_get_ts(&flow->used); flow->packet_count++; flow->byte_count += skb->len; flow->tcp_flags |= tcp_flags; diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h index ff9291cab..f443000fb 100644 --- a/include/openvswitch/datapath-protocol.h +++ b/include/openvswitch/datapath-protocol.h @@ -206,7 +206,7 @@ struct odp_port_group { struct odp_flow_stats { uint64_t n_packets; /* Number of matched packets. */ uint64_t n_bytes; /* Number of matched bytes. */ - uint64_t used_sec; /* Time last used. */ + uint64_t used_sec; /* Time last used, in system monotonic time. */ uint32_t used_nsec; uint8_t tcp_flags; uint8_t ip_tos; diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 7b1bb5db2..5021874e7 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -99,7 +99,7 @@ struct dp_netdev_flow { flow_t key; /* Statistics. */ - struct timeval used; /* Last used time, in milliseconds. */ + struct timespec used; /* Last used time. */ long long int packet_count; /* Number of packets matched. */ long long int byte_count; /* Number of bytes matched. */ uint8_t ip_tos; /* IP TOS value. */ @@ -680,7 +680,7 @@ answer_flow_query(struct dp_netdev_flow *flow, uint32_t query_flags, odp_flow->stats.n_packets = flow->packet_count; odp_flow->stats.n_bytes = flow->byte_count; odp_flow->stats.used_sec = flow->used.tv_sec; - odp_flow->stats.used_nsec = flow->used.tv_usec * 1000; + odp_flow->stats.used_nsec = flow->used.tv_nsec; odp_flow->stats.tcp_flags = TCP_FLAGS(flow->tcp_ctl); odp_flow->stats.ip_tos = flow->ip_tos; odp_flow->stats.error = 0; @@ -826,7 +826,7 @@ static void clear_stats(struct dp_netdev_flow *flow) { flow->used.tv_sec = 0; - flow->used.tv_usec = 0; + flow->used.tv_nsec = 0; flow->packet_count = 0; flow->byte_count = 0; flow->ip_tos = 0; @@ -1003,7 +1003,7 @@ static void dp_netdev_flow_used(struct dp_netdev_flow *flow, const flow_t *key, const struct ofpbuf *packet) { - time_timeval(&flow->used); + time_timespec(&flow->used); flow->packet_count++; flow->byte_count += packet->size; if (key->dl_type == htons(ETH_TYPE_IP)) { diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c index 79a7d8ee4..180a4301c 100644 --- a/lib/dynamic-string.c +++ b/lib/dynamic-string.c @@ -179,7 +179,7 @@ void ds_put_strftime(struct ds *ds, const char *template, const struct tm *tm) { if (!tm) { - time_t now = time_now(); + time_t now = time_wall(); tm = localtime(&now); } for (;;) { diff --git a/lib/netlink.c b/lib/netlink.c index e652a180f..c4bf31ef8 100644 --- a/lib/netlink.c +++ b/lib/netlink.c @@ -90,7 +90,7 @@ nl_sock_create(int protocol, int multicast_group, if (next_seq == 0) { /* Pick initial sequence number. */ - next_seq = getpid() ^ time_now(); + next_seq = getpid() ^ time_wall(); } *sockp = NULL; diff --git a/lib/timeval.c b/lib/timeval.c index ab564a12b..f3e65837f 100644 --- a/lib/timeval.c +++ b/lib/timeval.c @@ -34,11 +34,17 @@ /* Initialized? */ static bool inited; +/* Does this system have monotonic timers? */ +static bool monotonic_inited; +static clockid_t monotonic_clock; + /* Has a timer tick occurred? */ -static volatile sig_atomic_t tick; +static volatile sig_atomic_t wall_tick; +static volatile sig_atomic_t monotonic_tick; /* The current time, as of the last refresh. */ -static struct timeval now; +static struct timespec wall_time; +static struct timespec monotonic_time; /* Time at which to die with SIGALRM (if not TIME_MIN). */ static time_t deadline = TIME_MIN; @@ -46,7 +52,8 @@ static time_t deadline = TIME_MIN; static void set_up_timer(void); static void set_up_signal(int flags); static void sigalrm_handler(int); -static void refresh_if_ticked(void); +static void refresh_wall_if_ticked(void); +static void refresh_monotonic_if_ticked(void); static time_t time_add(time_t, time_t); static void block_sigalrm(sigset_t *); static void unblock_sigalrm(const sigset_t *); @@ -64,13 +71,32 @@ time_init(void) coverage_init(); inited = true; - gettimeofday(&now, NULL); - tick = false; + time_refresh(); set_up_signal(SA_RESTART); set_up_timer(); } +static void +set_up_monotonic(void) +{ + int err; + + if (monotonic_inited) { + return; + } + + err = clock_gettime(CLOCK_MONOTONIC, &monotonic_time); + if (!err) { + monotonic_clock = CLOCK_MONOTONIC; + } else { + monotonic_clock = CLOCK_REALTIME; + VLOG_DBG("monotonic timer not available"); + } + + monotonic_inited = true; +} + static void set_up_signal(int flags) { @@ -115,13 +141,21 @@ time_enable_restart(void) static void set_up_timer(void) { - struct itimerval itimer; + timer_t timer_id; + struct itimerspec itimer; + + set_up_monotonic(); + + if (timer_create(monotonic_clock, NULL, &timer_id)) { + ovs_fatal(errno, "timer_create failed"); + } itimer.it_interval.tv_sec = 0; - itimer.it_interval.tv_usec = TIME_UPDATE_INTERVAL * 1000; + itimer.it_interval.tv_nsec = TIME_UPDATE_INTERVAL * 1000 * 1000; itimer.it_value = itimer.it_interval; - if (setitimer(ITIMER_REAL, &itimer, NULL)) { - ovs_fatal(errno, "setitimer failed"); + + if (timer_settime(timer_id, 0, &itimer, NULL)) { + ovs_fatal(errno, "timer_settime failed"); } } @@ -136,39 +170,96 @@ time_postfork(void) set_up_timer(); } +static void +refresh_wall(void) +{ + clock_gettime(CLOCK_REALTIME, &wall_time); + wall_tick = false; +} + +static void +refresh_monotonic(void) +{ + set_up_monotonic(); + + if (monotonic_clock == CLOCK_MONOTONIC) { + clock_gettime(monotonic_clock, &monotonic_time); + } else { + refresh_wall_if_ticked(); + monotonic_time = wall_time; + } + + monotonic_tick = false; +} + /* Forces a refresh of the current time from the kernel. It is not usually * necessary to call this function, since the time will be refreshed * automatically at least every TIME_UPDATE_INTERVAL milliseconds. */ void time_refresh(void) { - gettimeofday(&now, NULL); - tick = false; + wall_tick = monotonic_tick = true; +} + +/* Returns a monotonic timer, in seconds. */ +time_t +time_now(void) +{ + refresh_monotonic_if_ticked(); + return monotonic_time.tv_sec; +} + +/* Same as time_now() except does not write to static variables, for use in + * signal handlers. set_up_monotonic() must have already been called. */ +static time_t +time_now_sig(void) +{ + struct timespec cur_time; + + clock_gettime(monotonic_clock, &cur_time); + return cur_time.tv_sec; } /* Returns the current time, in seconds. */ time_t -time_now(void) +time_wall(void) { - refresh_if_ticked(); - return now.tv_sec; + refresh_wall_if_ticked(); + return wall_time.tv_sec; +} + +/* Returns a monotonic timer, in ms (within TIME_UPDATE_INTERVAL ms). */ +long long int +time_msec(void) +{ + refresh_monotonic_if_ticked(); + return timespec_to_msec(&monotonic_time); } /* Returns the current time, in ms (within TIME_UPDATE_INTERVAL ms). */ long long int -time_msec(void) +time_wall_msec(void) { - refresh_if_ticked(); - return timeval_to_msec(&now); + refresh_wall_if_ticked(); + return timespec_to_msec(&wall_time); +} + +/* Stores a monotonic timer, accurate within TIME_UPDATE_INTERVAL ms, into + * '*ts'. */ +void +time_timespec(struct timespec *ts) +{ + refresh_monotonic_if_ticked(); + *ts = monotonic_time; } /* Stores the current time, accurate within TIME_UPDATE_INTERVAL ms, into - * '*tv'. */ + * '*ts'. */ void -time_timeval(struct timeval *tv) +time_wall_timespec(struct timespec *ts) { - refresh_if_ticked(); - *tv = now; + refresh_wall_if_ticked(); + *ts = wall_time; } /* Configures the program to die with SIGALRM 'secs' seconds from now, if @@ -252,18 +343,28 @@ time_add(time_t a, time_t b) static void sigalrm_handler(int sig_nr) { - tick = true; - if (deadline != TIME_MIN && time(0) > deadline) { + wall_tick = true; + monotonic_tick = true; + if (deadline != TIME_MIN && time_now_sig() > deadline) { fatal_signal_handler(sig_nr); } } static void -refresh_if_ticked(void) +refresh_wall_if_ticked(void) { assert(inited); - if (tick) { - time_refresh(); + if (wall_tick) { + refresh_wall(); + } +} + +static void +refresh_monotonic_if_ticked(void) +{ + assert(inited); + if (monotonic_tick) { + refresh_monotonic(); } } @@ -286,6 +387,12 @@ unblock_sigalrm(const sigset_t *oldsigs) } } +long long int +timespec_to_msec(const struct timespec *ts) +{ + return (long long int) ts->tv_sec * 1000 + ts->tv_nsec / (1000 * 1000); +} + long long int timeval_to_msec(const struct timeval *tv) { diff --git a/lib/timeval.h b/lib/timeval.h index 89abe8036..cc50cb3c4 100644 --- a/lib/timeval.h +++ b/lib/timeval.h @@ -26,6 +26,7 @@ extern "C" { #endif struct pollfd; +struct timespec; struct timeval; /* POSIX allows floating-point time_t, but we don't support it. */ @@ -38,10 +39,9 @@ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t)); #define TIME_MAX TYPE_MAXIMUM(time_t) #define TIME_MIN TYPE_MINIMUM(time_t) -/* Interval between updates to the time reported by time_gettimeofday(), in ms. - * This should not be adjusted much below 10 ms or so with the current - * implementation, or too much time will be wasted in signal handlers and calls - * to time(0). */ +/* Interval between updates to the reported time, in ms. This should not be + * adjusted much below 10 ms or so with the current implementation, or too + * much time will be wasted in signal handlers and calls to clock_gettime(). */ #define TIME_UPDATE_INTERVAL 100 void time_init(void); @@ -50,11 +50,15 @@ void time_enable_restart(void); void time_postfork(void); void time_refresh(void); time_t time_now(void); +time_t time_wall(void); long long int time_msec(void); -void time_timeval(struct timeval *); +long long int time_wall_msec(void); +void time_timespec(struct timespec *); +void time_wall_timespec(struct timespec *); void time_alarm(unsigned int secs); int time_poll(struct pollfd *, int n_pollfds, int timeout); +long long int timespec_to_msec(const struct timespec *); long long int timeval_to_msec(const struct timeval *); #ifdef __cplusplus diff --git a/lib/vlog.c b/lib/vlog.c index b534d1965..e086e7984 100644 --- a/lib/vlog.c +++ b/lib/vlog.c @@ -428,7 +428,7 @@ vlog_init(void) vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_INFO); boot_time = time_msec(); - now = time_now(); + now = time_wall(); if (now < 0) { struct tm tm; char s[128]; diff --git a/ofproto/netflow.c b/ofproto/netflow.c index 34d571f08..67932f0f7 100644 --- a/ofproto/netflow.c +++ b/ofproto/netflow.c @@ -109,7 +109,7 @@ netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow, { struct netflow_v5_header *nf_hdr; struct netflow_v5_record *nf_rec; - struct timeval now; + struct timespec now; nf_flow->last_expired += nf->active_timeout; @@ -120,7 +120,7 @@ netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow, return; } - time_timeval(&now); + time_wall_timespec(&now); if (!nf->packet.size) { nf_hdr = ofpbuf_put_zeros(&nf->packet, sizeof *nf_hdr); @@ -128,7 +128,7 @@ netflow_expire(struct netflow *nf, struct netflow_flow *nf_flow, nf_hdr->count = htons(0); nf_hdr->sysuptime = htonl(time_msec() - nf->boot_time); nf_hdr->unix_secs = htonl(now.tv_sec); - nf_hdr->unix_nsecs = htonl(now.tv_usec * 1000); + nf_hdr->unix_nsecs = htonl(now.tv_nsec); nf_hdr->flow_seq = htonl(nf->netflow_cnt++); nf_hdr->engine_type = nf->engine_type; nf_hdr->engine_id = nf->engine_id; diff --git a/ofproto/ofproto-sflow.c b/ofproto/ofproto-sflow.c index 37c1bb7f4..a96d8b496 100644 --- a/ofproto/ofproto-sflow.c +++ b/ofproto/ofproto-sflow.c @@ -415,7 +415,7 @@ ofproto_sflow_set_options(struct ofproto_sflow *os, sfl_agent_release(os->sflow_agent); } os->sflow_agent = xcalloc(1, sizeof *os->sflow_agent); - now = time_now(); + now = time_wall(); sfl_agent_init(os->sflow_agent, &agentIP, options->sub_id, @@ -597,7 +597,7 @@ ofproto_sflow_run(struct ofproto_sflow *os) if (ofproto_sflow_is_enabled(os)) { time_t now = time_now(); if (now >= os->next_tick) { - sfl_agent_tick(os->sflow_agent, now); + sfl_agent_tick(os->sflow_agent, time_wall()); os->next_tick = now + 1; } } diff --git a/ovsdb/file.c b/ovsdb/file.c index cf5bb41d4..f0913e9d3 100644 --- a/ovsdb/file.c +++ b/ovsdb/file.c @@ -737,7 +737,7 @@ ovsdb_file_txn_commit(struct json *json, const char *comment, if (comment) { json_object_put_string(json, "_comment", comment); } - json_object_put(json, "_date", json_integer_create(time_now())); + json_object_put(json, "_date", json_integer_create(time_wall())); error = ovsdb_log_write(log, json); json_destroy(json); diff --git a/tests/test-timeval.c b/tests/test-timeval.c index 533f81aed..f0552f881 100644 --- a/tests/test-timeval.c +++ b/tests/test-timeval.c @@ -48,10 +48,11 @@ do_test(void) * setitimer()). Then ensure that, if time has really advanced by * TIME_UPDATE_INTERVAL, then time_msec() reports that it advanced. */ - long long int start_time_msec; + long long int start_time_msec, start_time_wall; long long int start_gtod; start_time_msec = time_msec(); + start_time_wall = time_wall_msec(); start_gtod = gettimeofday_in_msec(); for (;;) { /* Wait up to 1 second. Using select() to do the timeout avoids @@ -70,6 +71,7 @@ do_test(void) if (gettimeofday_in_msec() - start_gtod >= TIME_UPDATE_INTERVAL) { assert(time_msec() - start_time_msec >= TIME_UPDATE_INTERVAL); + assert(time_wall_msec() - start_time_wall >= TIME_UPDATE_INTERVAL); break; } }