mirror of
https://github.com/openvswitch/ovs
synced 2025-10-17 14:28:02 +00:00
timeval: Use monotonic time where appropriate.
Most of the timekeeping needs of OVS are simply to measure intervals, which means that it is sensitive to changes in the clock. This commit replaces the existing clocks with monotonic timers. An additional set of wall clock timers are added and used in locations that need absolute time. Bug #1858
This commit is contained in:
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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)) {
|
||||
|
@@ -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 (;;) {
|
||||
|
@@ -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;
|
||||
|
159
lib/timeval.c
159
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)
|
||||
{
|
||||
|
@@ -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
|
||||
|
@@ -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];
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user