X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=inline;f=lib%2Ftimeval.c;h=5d0170867255aef1a3dc3ea228bd4bce6f557e9f;hb=5054d6fd32d5dd3354c1aed1bfa9fb2b7979324e;hp=37b4353c7d2731e07a55a5957c1c799887e25294;hpb=31ef9f5178dee18a40ce609aad406e925411eb06;p=sliver-openvswitch.git diff --git a/lib/timeval.c b/lib/timeval.c index 37b4353c7..5d0170867 100644 --- a/lib/timeval.c +++ b/lib/timeval.c @@ -33,6 +33,7 @@ #include "hmap.h" #include "ovs-thread.h" #include "signals.h" +#include "seq.h" #include "unixctl.h" #include "util.h" #include "vlog.h" @@ -42,12 +43,12 @@ VLOG_DEFINE_THIS_MODULE(timeval); struct clock { clockid_t id; /* CLOCK_MONOTONIC or CLOCK_REALTIME. */ - /* Features for use by unit tests. Protected by 'rwlock'. */ - struct ovs_rwlock rwlock; - struct timespec warp; /* Offset added for unit tests. */ - bool stopped; /* Disables real-time updates if true. */ - - struct timespec cache; /* Last time read from kernel. */ + /* Features for use by unit tests. Protected by 'mutex'. */ + struct ovs_mutex mutex; + atomic_bool slow_path; /* True if warped or stopped. */ + struct timespec warp OVS_GUARDED; /* Offset added for unit tests. */ + bool stopped OVS_GUARDED; /* Disable real-time updates if true. */ + struct timespec cache OVS_GUARDED; /* Last time read from kernel. */ }; /* Our clocks. */ @@ -57,6 +58,14 @@ static struct clock wall_clock; /* CLOCK_REALTIME. */ /* The monotonic time at which the time module was initialized. */ static long long int boot_time; +/* True only when timeval_dummy_register() is called. */ +static bool timewarp_enabled; +/* Reference to the seq struct. Threads other than main thread can + * wait on timewarp_seq and be waken up when time is warped. */ +static struct seq *timewarp_seq; +/* Last value of 'timewarp_seq'. */ +DEFINE_STATIC_PER_THREAD_DATA(uint64_t, last_seq, 0); + /* Monotonic time in milliseconds at which to die with SIGALRM (if not * LLONG_MAX). */ static long long int deadline = LLONG_MAX; @@ -76,7 +85,10 @@ init_clock(struct clock *c, clockid_t id) { memset(c, 0, sizeof *c); c->id = id; + ovs_mutex_init(&c->mutex); + atomic_init(&c->slow_path, false); xclock_gettime(c->id, &c->cache); + timewarp_seq = seq_create(); } static void @@ -104,14 +116,28 @@ time_init(void) static void time_timespec__(struct clock *c, struct timespec *ts) { + bool slow_path; + time_init(); - if (!c->stopped) { + atomic_read_explicit(&c->slow_path, &slow_path, memory_order_relaxed); + if (!slow_path) { xclock_gettime(c->id, ts); } else { - ovs_rwlock_rdlock(&c->rwlock); - timespec_add(ts, &c->cache, &c->warp); - ovs_rwlock_unlock(&c->rwlock); + struct timespec warp; + struct timespec cache; + bool stopped; + + ovs_mutex_lock(&c->mutex); + stopped = c->stopped; + warp = c->warp; + cache = c->cache; + ovs_mutex_unlock(&c->mutex); + + if (!stopped) { + xclock_gettime(c->id, &cache); + } + timespec_add(ts, &cache, &warp); } } @@ -214,10 +240,11 @@ time_poll(struct pollfd *pollfds, int n_pollfds, long long int timeout_when, int retval; time_init(); + coverage_clear(); + coverage_run(); if (*last_wakeup) { log_poll_interval(*last_wakeup); } - coverage_clear(); start = time_msec(); timeout_when = MIN(timeout_when, deadline); @@ -296,6 +323,19 @@ xclock_gettime(clock_t id, struct timespec *ts) } } +/* Makes threads wait on timewarp_seq and be waken up when time is warped. + * This function will be no-op unless timeval_dummy_register() is called. */ +void +timewarp_wait(void) +{ + if (timewarp_enabled) { + uint64_t *last_seq = last_seq_get(); + + *last_seq = seq_read(timewarp_seq); + seq_wait(timewarp_seq, *last_seq); + } +} + static long long int timeval_diff_msec(const struct timeval *a, const struct timeval *b) { @@ -319,14 +359,24 @@ timespec_add(struct timespec *sum, *sum = tmp; } +static bool +is_warped(const struct clock *c) +{ + bool warped; + + ovs_mutex_lock(&c->mutex); + warped = monotonic_clock.warp.tv_sec || monotonic_clock.warp.tv_nsec; + ovs_mutex_unlock(&c->mutex); + + return warped; +} + static void log_poll_interval(long long int last_wakeup) { long long int interval = time_msec() - last_wakeup; - if (interval >= 1000 - && !monotonic_clock.warp.tv_sec - && !monotonic_clock.warp.tv_nsec) { + if (interval >= 1000 && !is_warped(&monotonic_clock)) { const struct rusage *last_rusage = get_recent_rusage(); struct rusage rusage; @@ -450,10 +500,11 @@ timeval_stop_cb(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { - ovs_rwlock_wrlock(&monotonic_clock.rwlock); + ovs_mutex_lock(&monotonic_clock.mutex); + atomic_store(&monotonic_clock.slow_path, true); monotonic_clock.stopped = true; xclock_gettime(monotonic_clock.id, &monotonic_clock.cache); - ovs_rwlock_unlock(&monotonic_clock.rwlock); + ovs_mutex_unlock(&monotonic_clock.mutex); unixctl_command_reply(conn, NULL); } @@ -479,17 +530,66 @@ timeval_warp_cb(struct unixctl_conn *conn, ts.tv_sec = msecs / 1000; ts.tv_nsec = (msecs % 1000) * 1000 * 1000; - ovs_rwlock_wrlock(&monotonic_clock.rwlock); + ovs_mutex_lock(&monotonic_clock.mutex); + atomic_store(&monotonic_clock.slow_path, true); timespec_add(&monotonic_clock.warp, &monotonic_clock.warp, &ts); - ovs_rwlock_unlock(&monotonic_clock.rwlock); - + ovs_mutex_unlock(&monotonic_clock.mutex); + seq_change(timewarp_seq); + poll(NULL, 0, 10); /* give threads (eg. monitor) some chances to run */ unixctl_command_reply(conn, "warped"); } void timeval_dummy_register(void) { + timewarp_enabled = true; unixctl_command_register("time/stop", "", 0, 0, timeval_stop_cb, NULL); unixctl_command_register("time/warp", "MSECS", 1, 1, timeval_warp_cb, NULL); } + + + +/* strftime() with an extension for high-resolution timestamps. Any '#'s in + * 'format' will be replaced by subseconds, e.g. use "%S.###" to obtain results + * like "01.123". */ +size_t +strftime_msec(char *s, size_t max, const char *format, + const struct tm_msec *tm) +{ + size_t n; + + n = strftime(s, max, format, &tm->tm); + if (n) { + char decimals[4]; + char *p; + + sprintf(decimals, "%03d", tm->msec); + for (p = strchr(s, '#'); p; p = strchr(p, '#')) { + char *d = decimals; + while (*p == '#') { + *p++ = *d ? *d++ : '0'; + } + } + } + + return n; +} + +struct tm_msec * +localtime_msec(long long int now, struct tm_msec *result) +{ + time_t now_sec = now / 1000; + localtime_r(&now_sec, &result->tm); + result->msec = now % 1000; + return result; +} + +struct tm_msec * +gmtime_msec(long long int now, struct tm_msec *result) +{ + time_t now_sec = now / 1000; + gmtime_r(&now_sec, &result->tm); + result->msec = now % 1000; + return result; +}