diff options
-rw-r--r-- | src/NetworkManagerUtils.c | 53 | ||||
-rw-r--r-- | src/NetworkManagerUtils.h | 1 | ||||
-rw-r--r-- | src/tests/test-general-with-expect.c | 37 |
3 files changed, 86 insertions, 5 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 0f957814d3..ae2e83e482 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1818,15 +1818,15 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection /**************************************************************************/ static gint64 monotonic_timestamp_offset_sec; +static int monotonic_timestamp_clock_mode = 0; static void monotonic_timestamp_get (struct timespec *tp) { - static int clock_mode = 0; - gboolean first_time = FALSE; + int clock_mode = 0; int err = 0; - switch (clock_mode) { + switch (monotonic_timestamp_clock_mode) { case 0: /* the clock is not yet initialized (first run) */ err = clock_gettime (CLOCK_BOOTTIME, tp); @@ -1835,7 +1835,6 @@ monotonic_timestamp_get (struct timespec *tp) err = clock_gettime (CLOCK_MONOTONIC, tp); } else clock_mode = 1; - first_time = TRUE; break; case 1: /* default, return CLOCK_BOOTTIME */ @@ -1851,7 +1850,7 @@ monotonic_timestamp_get (struct timespec *tp) g_assert (err == 0); (void)err; g_assert (tp->tv_nsec >= 0 && tp->tv_nsec < NM_UTILS_NS_PER_SECOND); - if (G_LIKELY (!first_time)) + if (G_LIKELY (clock_mode == 0)) return; /* Calculate an offset for the time stamp. @@ -1868,6 +1867,7 @@ monotonic_timestamp_get (struct timespec *tp) * wraps (~68 years). **/ monotonic_timestamp_offset_sec = (- ((gint64) tp->tv_sec)) + 1; + monotonic_timestamp_clock_mode = clock_mode; if (nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) { time_t now = time (NULL); @@ -2232,6 +2232,49 @@ out: g_array_free (sorted_hashes, TRUE); } +/** + * nm_utils_monotonic_timestamp_as_boottime: + * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME. + * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if + * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano + * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc. + * + * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime(). + * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick. + * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. + * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp + * is always positive. + * + * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC. + **/ +gint64 +nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick) +{ + gint64 offset; + + /* only support ns-per-tick being a multiple of 10. */ + g_return_val_if_fail (timestamp_ns_per_tick == 1 + || (timestamp_ns_per_tick > 0 && + timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND && + timestamp_ns_per_tick % 10 == 0), + -1); + + /* Check that the timestamp is in a valid range. */ + g_return_val_if_fail (timestamp >= 0, -1); + + /* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful + * value (because he has no idea what these timestamps would be). That would be a bug. */ + g_return_val_if_fail (monotonic_timestamp_clock_mode != 0, -1); + + /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ + offset = monotonic_timestamp_offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick); + + /* check for overflow. */ + g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64); + + return timestamp - offset; +} + #define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/" #define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/" diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 99de3b66b9..16c5ba39bd 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -141,6 +141,7 @@ gint64 nm_utils_get_monotonic_timestamp_ns (void); gint64 nm_utils_get_monotonic_timestamp_us (void); gint64 nm_utils_get_monotonic_timestamp_ms (void); gint32 nm_utils_get_monotonic_timestamp_s (void); +gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns); const char *ASSERT_VALID_PATH_COMPONENT (const char *name) G_GNUC_WARN_UNUSED_RESULT; const char *nm_utils_ip6_property_path (const char *ifname, const char *property); diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 4318b594c6..304543df58 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -34,6 +34,42 @@ /*******************************************/ +static void +test_nm_utils_monotonic_timestamp_as_boottime () +{ + gint64 timestamp_ns_per_tick, now, now_boottime, now_boottime_2, now_boottime_3; + struct timespec tp; + clockid_t clockid; + guint i; + + if (clock_gettime (CLOCK_BOOTTIME, &tp) != 0 && errno == EINVAL) + clockid = CLOCK_MONOTONIC; + else + clockid = CLOCK_BOOTTIME; + + for (i = 0; i < 10; i++) { + + if (clock_gettime (clockid, &tp) != 0) + g_assert_not_reached (); + now_boottime = ( ((gint64) tp.tv_sec) * NM_UTILS_NS_PER_SECOND ) + ((gint64) tp.tv_nsec); + + now = nm_utils_get_monotonic_timestamp_ns (); + + now_boottime_2 = nm_utils_monotonic_timestamp_as_boottime (now, 1); + g_assert_cmpint (now_boottime_2, >=, 0); + g_assert_cmpint (now_boottime_2, >=, now_boottime); + g_assert_cmpint (now_boottime_2 - now_boottime, <=, NM_UTILS_NS_PER_SECOND / 1000); + + for (timestamp_ns_per_tick = 1; timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND; timestamp_ns_per_tick *= 10) { + now_boottime_3 = nm_utils_monotonic_timestamp_as_boottime (now / timestamp_ns_per_tick, timestamp_ns_per_tick); + + g_assert_cmpint (now_boottime_2 / timestamp_ns_per_tick, ==, now_boottime_3); + } + } +} + +/*******************************************/ + struct test_nm_utils_kill_child_async_data { GMainLoop *loop; @@ -465,6 +501,7 @@ main (int argc, char **argv) { nmtst_init_assert_logging (&argc, &argv, "DEBUG", "DEFAULT"); + g_test_add_func ("/general/nm_utils_monotonic_timestamp_as_boottime", test_nm_utils_monotonic_timestamp_as_boottime); g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); g_test_add_func ("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid); |