/* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Timer manipulations. * * Author: Alexandre Cassen, * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Copyright (C) 2001-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include "timer.h" #ifdef _TIMER_CHECK_ #include "logger.h" #endif /* time_now holds current time */ timeval_t time_now; #ifdef _TIMER_CHECK_ static timeval_t last_time; bool do_timer_check; #endif timeval_t timer_add_long(timeval_t a, unsigned long b) { if (b == TIMER_NEVER) { a.tv_usec = TIMER_HZ - 1; a.tv_sec = LONG_MAX; return a; } a.tv_usec += b % TIMER_HZ; a.tv_sec += b / TIMER_HZ; if (a.tv_usec >= TIMER_HZ) { a.tv_sec++; a.tv_usec -= TIMER_HZ; } return a; } timeval_t timer_sub_long(timeval_t a, unsigned long b) { if (a.tv_usec < (suseconds_t)(b % TIMER_HZ)) { a.tv_usec += TIMER_HZ; a.tv_sec--; } a.tv_usec -= b % TIMER_HZ; a.tv_sec -= b / TIMER_HZ; return a; } static void set_mono_offset(struct timespec *ts) { struct timespec realtime, realtime_1, mono_offset; /* Calculate the offset of the realtime clock from the monotonic * clock. We read the realtime clock twice and take the mean, * which should then make it very close to the time the monotonic * clock was read. */ clock_gettime(CLOCK_REALTIME, &realtime); clock_gettime(CLOCK_MONOTONIC, &mono_offset); clock_gettime(CLOCK_REALTIME, &realtime_1); /* Calculate the mean realtime */ realtime.tv_nsec = (realtime.tv_nsec + realtime_1.tv_nsec) / 2; if ((realtime.tv_sec + realtime_1.tv_sec) & 1) realtime.tv_nsec += NSEC_PER_SEC / 2; realtime.tv_sec = (realtime.tv_sec + realtime_1.tv_sec) / 2; if (realtime.tv_nsec < mono_offset.tv_nsec) { realtime.tv_nsec += NSEC_PER_SEC; realtime.tv_sec--; } realtime.tv_sec -= mono_offset.tv_sec; realtime.tv_nsec -= mono_offset.tv_nsec; *ts = realtime; } /* This function is a wrapper for gettimeofday(). It uses the monotonic clock to * guarantee that the returned time will always be monotonicly increasing. * When called for the first time it calculates the difference between the * monotonic clock and the realtime clock, and this difference is then subsequently * added to the monotonic clock to return a monotonic approximation to realtime. * * It is designed to be used as a drop-in replacement of gettimeofday(&now, NULL). * It will normally return 0, unless is NULL, in which case it will * return -1 and set errno to EFAULT. */ static int monotonic_gettimeofday(timeval_t *now) { static struct timespec mono_offset; static bool initialised = false; struct timespec cur_time; if (!now) { errno = EFAULT; return -1; } if (!initialised) { set_mono_offset(&mono_offset); initialised = true; } /* Read the monotonic clock and add the offset we initially * calculated of the realtime clock */ clock_gettime(CLOCK_MONOTONIC, &cur_time); cur_time.tv_sec += mono_offset.tv_sec; cur_time.tv_nsec += mono_offset.tv_nsec; if (cur_time.tv_nsec > NSEC_PER_SEC) { cur_time.tv_nsec -= NSEC_PER_SEC; cur_time.tv_sec++; } TIMESPEC_TO_TIMEVAL(now, &cur_time); return 0; } /* current time */ timeval_t #ifdef _TIMER_CHECK_ timer_now_r(const char *file, const char *function, int line_no) #else timer_now(void) #endif { timeval_t curr_time; /* init timer */ monotonic_gettimeofday(&curr_time); #ifdef _TIMER_CHECK_ if (do_timer_check) { unsigned long timediff = (curr_time.tv_sec - last_time.tv_sec) * 1000000 + curr_time.tv_usec - last_time.tv_usec; log_message(LOG_INFO, "timer_now called from %s %s:%d - difference %lu usec", file, function, line_no, timediff); last_time = curr_time; } #endif return curr_time; } /* sets and returns current time from system time */ timeval_t #ifdef _TIMER_CHECK_ set_time_now_r(const char *file, const char *function, int line_no) #else set_time_now(void) #endif { /* init timer */ monotonic_gettimeofday(&time_now); #ifdef _TIMER_CHECK_ if (do_timer_check) { unsigned long timediff = (time_now.tv_sec - last_time.tv_sec) * 1000000 + time_now.tv_usec - last_time.tv_usec; log_message(LOG_INFO, "set_time_now called from %s %s:%d, time %ld.%6.6lu difference %lu usec", file, function, line_no, time_now.tv_sec, time_now.tv_usec, timediff); last_time = time_now; } #endif return time_now; }