Blame sys_generic.c

Packit 96c956
/*
Packit 96c956
  chronyd/chronyc - Programs for keeping computer clocks accurate.
Packit 96c956
Packit 96c956
 **********************************************************************
Packit 96c956
 * Copyright (C) Miroslav Lichvar  2014-2015
Packit 96c956
 * 
Packit 96c956
 * This program is free software; you can redistribute it and/or modify
Packit 96c956
 * it under the terms of version 2 of the GNU General Public License as
Packit 96c956
 * published by the Free Software Foundation.
Packit 96c956
 * 
Packit 96c956
 * This program is distributed in the hope that it will be useful, but
Packit 96c956
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 96c956
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 96c956
 * General Public License for more details.
Packit 96c956
 * 
Packit 96c956
 * You should have received a copy of the GNU General Public License along
Packit 96c956
 * with this program; if not, write to the Free Software Foundation, Inc.,
Packit 96c956
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Packit 96c956
 * 
Packit 96c956
 **********************************************************************
Packit 96c956
Packit 96c956
  =======================================================================
Packit 96c956
Packit 96c956
  Generic driver functions to complete system-specific drivers
Packit 96c956
  */
Packit 96c956
Packit 96c956
#include "config.h"
Packit 96c956
Packit 96c956
#include "sysincl.h"
Packit 96c956
Packit 96c956
#include "sys_generic.h"
Packit 96c956
Packit 96c956
#include "conf.h"
Packit 96c956
#include "local.h"
Packit 96c956
#include "localp.h"
Packit 96c956
#include "logging.h"
Packit 96c956
#include "privops.h"
Packit 96c956
#include "sched.h"
Packit 96c956
#include "util.h"
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* System clock drivers */
Packit 96c956
static lcl_ReadFrequencyDriver drv_read_freq;
Packit 96c956
static lcl_SetFrequencyDriver drv_set_freq;
Packit 96c956
static lcl_SetSyncStatusDriver drv_set_sync_status;
Packit 96c956
static lcl_AccrueOffsetDriver drv_accrue_offset;
Packit 96c956
static lcl_OffsetCorrectionDriver drv_get_offset_correction;
Packit 96c956
Packit 96c956
/* Current frequency as requested by the local module (in ppm) */
Packit 96c956
static double base_freq;
Packit 96c956
Packit 96c956
/* Maximum frequency that can be set by drv_set_freq (in ppm) */
Packit 96c956
static double max_freq;
Packit 96c956
Packit 96c956
/* Maximum expected delay in the actual frequency change (e.g. kernel ticks)
Packit 96c956
   in local time */
Packit 96c956
static double max_freq_change_delay;
Packit 96c956
Packit 96c956
/* Maximum allowed frequency offset relative to the base frequency */
Packit 96c956
static double max_corr_freq;
Packit 96c956
Packit 96c956
/* Amount of outstanding offset to process */
Packit 96c956
static double offset_register;
Packit 96c956
Packit 96c956
/* Minimum offset to correct */
Packit 96c956
#define MIN_OFFSET_CORRECTION 1.0e-9
Packit 96c956
Packit 96c956
/* Current frequency offset between base_freq and the real clock frequency
Packit 96c956
   as set by drv_set_freq (not in ppm) */
Packit 96c956
static double slew_freq;
Packit 96c956
Packit 96c956
/* Time (raw) of last update of slewing frequency and offset */
Packit 96c956
static struct timespec slew_start;
Packit 96c956
Packit 96c956
/* Limits for the slew timeout */
Packit 96c956
#define MIN_SLEW_TIMEOUT 1.0
Packit 96c956
#define MAX_SLEW_TIMEOUT 1.0e4
Packit 96c956
Packit 96c956
/* Scheduler timeout ID for ending of the currently running slew */
Packit 96c956
static SCH_TimeoutID slew_timeout_id;
Packit 96c956
Packit 96c956
/* Suggested offset correction rate (correction time * offset) */
Packit 96c956
static double correction_rate;
Packit 96c956
Packit 96c956
/* Maximum expected offset correction error caused by delayed change in the
Packit 96c956
   real frequency of the clock */
Packit 96c956
static double slew_error;
Packit 96c956
Packit 96c956
/* Minimum offset that the system driver can slew faster than the maximum
Packit 96c956
   frequency offset that it allows to be set directly */
Packit 96c956
static double fastslew_min_offset;
Packit 96c956
Packit 96c956
/* Maximum slew rate of the system driver */
Packit 96c956
static double fastslew_max_rate;
Packit 96c956
Packit 96c956
/* Flag indicating that the system driver is currently slewing */
Packit 96c956
static int fastslew_active;
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void handle_end_of_slew(void *anything);
Packit 96c956
static void update_slew(void);
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Adjust slew_start on clock step */
Packit 96c956
Packit 96c956
static void
Packit 96c956
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
Packit 96c956
            double doffset, LCL_ChangeType change_type, void *anything)
Packit 96c956
{
Packit 96c956
  if (change_type == LCL_ChangeUnknownStep) {
Packit 96c956
    /* Reset offset and slewing */
Packit 96c956
    slew_start = *raw;
Packit 96c956
    offset_register = 0.0;
Packit 96c956
    update_slew();
Packit 96c956
  } else if (change_type == LCL_ChangeStep) {
Packit 96c956
    UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
Packit 96c956
  }
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void
Packit 96c956
start_fastslew(void)
Packit 96c956
{
Packit 96c956
  if (!drv_accrue_offset)
Packit 96c956
    return;
Packit 96c956
Packit 96c956
  drv_accrue_offset(offset_register, 0.0);
Packit 96c956
Packit 96c956
  DEBUG_LOG("fastslew offset=%e", offset_register);
Packit 96c956
Packit 96c956
  offset_register = 0.0;
Packit 96c956
  fastslew_active = 1;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void
Packit 96c956
stop_fastslew(struct timespec *now)
Packit 96c956
{
Packit 96c956
  double corr;
Packit 96c956
Packit 96c956
  if (!drv_get_offset_correction || !fastslew_active)
Packit 96c956
    return;
Packit 96c956
Packit 96c956
  /* Cancel the remaining offset */
Packit 96c956
  drv_get_offset_correction(now, &corr, NULL);
Packit 96c956
  drv_accrue_offset(corr, 0.0);
Packit 96c956
  offset_register -= corr;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static double
Packit 96c956
clamp_freq(double freq)
Packit 96c956
{
Packit 96c956
  if (freq > max_freq)
Packit 96c956
    return max_freq;
Packit 96c956
  if (freq < -max_freq)
Packit 96c956
    return -max_freq;
Packit 96c956
  return freq;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* End currently running slew and start a new one */
Packit 96c956
Packit 96c956
static void
Packit 96c956
update_slew(void)
Packit 96c956
{
Packit 96c956
  struct timespec now, end_of_slew;
Packit 96c956
  double old_slew_freq, total_freq, corr_freq, duration;
Packit 96c956
Packit 96c956
  /* Remove currently running timeout */
Packit 96c956
  SCH_RemoveTimeout(slew_timeout_id);
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&now;;
Packit 96c956
Packit 96c956
  /* Adjust the offset register by achieved slew */
Packit 96c956
  duration = UTI_DiffTimespecsToDouble(&now, &slew_start);
Packit 96c956
  offset_register -= slew_freq * duration;
Packit 96c956
Packit 96c956
  stop_fastslew(&now;;
Packit 96c956
Packit 96c956
  /* Estimate how long should the next slew take */
Packit 96c956
  if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
Packit 96c956
    duration = MAX_SLEW_TIMEOUT;
Packit 96c956
  } else {
Packit 96c956
    duration = correction_rate / fabs(offset_register);
Packit 96c956
    if (duration < MIN_SLEW_TIMEOUT)
Packit 96c956
      duration = MIN_SLEW_TIMEOUT;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  /* Get frequency offset needed to slew the offset in the duration
Packit 96c956
     and clamp it to the allowed maximum */
Packit 96c956
  corr_freq = offset_register / duration;
Packit 96c956
  if (corr_freq < -max_corr_freq)
Packit 96c956
    corr_freq = -max_corr_freq;
Packit 96c956
  else if (corr_freq > max_corr_freq)
Packit 96c956
    corr_freq = max_corr_freq;
Packit 96c956
Packit 96c956
  /* Let the system driver perform the slew if the requested frequency
Packit 96c956
     offset is too large for the frequency driver */
Packit 96c956
  if (drv_accrue_offset && fabs(corr_freq) >= fastslew_max_rate &&
Packit 96c956
      fabs(offset_register) > fastslew_min_offset) {
Packit 96c956
    start_fastslew();
Packit 96c956
    corr_freq = 0.0;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  /* Get the new real frequency and clamp it */
Packit 96c956
  total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq));
Packit 96c956
Packit 96c956
  /* Set the new frequency (the actual frequency returned by the call may be
Packit 96c956
     slightly different from the requested frequency due to rounding) */
Packit 96c956
  total_freq = (*drv_set_freq)(total_freq);
Packit 96c956
Packit 96c956
  /* Compute the new slewing frequency, it's relative to the real frequency to
Packit 96c956
     make the calculation in offset_convert() cheaper */
Packit 96c956
  old_slew_freq = slew_freq;
Packit 96c956
  slew_freq = (total_freq - base_freq) / (1.0e6 - total_freq);
Packit 96c956
Packit 96c956
  /* Compute the dispersion introduced by changing frequency and add it
Packit 96c956
     to all statistics held at higher levels in the system */
Packit 96c956
  slew_error = fabs((old_slew_freq - slew_freq) * max_freq_change_delay);
Packit 96c956
  if (slew_error >= MIN_OFFSET_CORRECTION)
Packit 96c956
    lcl_InvokeDispersionNotifyHandlers(slew_error);
Packit 96c956
Packit 96c956
  /* Compute the duration of the slew and clamp it.  If the slewing frequency
Packit 96c956
     is zero or has wrong sign (e.g. due to rounding in the frequency driver or
Packit 96c956
     when base_freq is larger than max_freq, or fast slew is active), use the
Packit 96c956
     maximum timeout and try again on the next update. */
Packit 96c956
  if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
Packit 96c956
      offset_register * slew_freq <= 0.0) {
Packit 96c956
    duration = MAX_SLEW_TIMEOUT;
Packit 96c956
  } else {
Packit 96c956
    duration = offset_register / slew_freq;
Packit 96c956
    if (duration < MIN_SLEW_TIMEOUT)
Packit 96c956
      duration = MIN_SLEW_TIMEOUT;
Packit 96c956
    else if (duration > MAX_SLEW_TIMEOUT)
Packit 96c956
      duration = MAX_SLEW_TIMEOUT;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  /* Restart timer for the next update */
Packit 96c956
  UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
Packit 96c956
  slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
Packit 96c956
  slew_start = now;
Packit 96c956
Packit 96c956
  DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
Packit 96c956
      offset_register, correction_rate, base_freq, total_freq, slew_freq,
Packit 96c956
      duration, slew_error);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void
Packit 96c956
handle_end_of_slew(void *anything)
Packit 96c956
{
Packit 96c956
  slew_timeout_id = 0;
Packit 96c956
  update_slew();
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static double
Packit 96c956
read_frequency(void)
Packit 96c956
{
Packit 96c956
  return base_freq;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static double
Packit 96c956
set_frequency(double freq_ppm)
Packit 96c956
{
Packit 96c956
  base_freq = freq_ppm;
Packit 96c956
  update_slew();
Packit 96c956
Packit 96c956
  return base_freq;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void
Packit 96c956
accrue_offset(double offset, double corr_rate)
Packit 96c956
{
Packit 96c956
  offset_register += offset;
Packit 96c956
  correction_rate = corr_rate;
Packit 96c956
Packit 96c956
  update_slew();
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Determine the correction to generate the cooked time for given raw time */
Packit 96c956
Packit 96c956
static void
Packit 96c956
offset_convert(struct timespec *raw,
Packit 96c956
               double *corr, double *err)
Packit 96c956
{
Packit 96c956
  double duration, fastslew_corr, fastslew_err;
Packit 96c956
Packit 96c956
  duration = UTI_DiffTimespecsToDouble(raw, &slew_start);
Packit 96c956
Packit 96c956
  if (drv_get_offset_correction && fastslew_active) {
Packit 96c956
    drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
Packit 96c956
    if (fastslew_corr == 0.0 && fastslew_err == 0.0)
Packit 96c956
      fastslew_active = 0;
Packit 96c956
  } else {
Packit 96c956
    fastslew_corr = fastslew_err = 0.0;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  *corr = slew_freq * duration + fastslew_corr - offset_register;
Packit 96c956
Packit 96c956
  if (err) {
Packit 96c956
    *err = fastslew_err;
Packit 96c956
    if (fabs(duration) <= max_freq_change_delay)
Packit 96c956
      *err += slew_error;
Packit 96c956
  }
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Positive means currently fast of true time, i.e. jump backwards */
Packit 96c956
Packit 96c956
static int
Packit 96c956
apply_step_offset(double offset)
Packit 96c956
{
Packit 96c956
  struct timespec old_time, new_time;
Packit 96c956
  struct timeval new_time_tv;
Packit 96c956
  double err;
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&old_time);
Packit 96c956
  UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
Packit 96c956
  UTI_TimespecToTimeval(&new_time, &new_time_tv);
Packit 96c956
Packit 96c956
  if (PRV_SetTime(&new_time_tv, NULL) < 0) {
Packit 96c956
    DEBUG_LOG("settimeofday() failed");
Packit 96c956
    return 0;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&old_time);
Packit 96c956
  err = UTI_DiffTimespecsToDouble(&old_time, &new_time);
Packit 96c956
Packit 96c956
  lcl_InvokeDispersionNotifyHandlers(fabs(err));
Packit 96c956
Packit 96c956
  return 1;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void
Packit 96c956
set_sync_status(int synchronised, double est_error, double max_error)
Packit 96c956
{
Packit 96c956
  double offset;
Packit 96c956
Packit 96c956
  offset = fabs(offset_register);
Packit 96c956
  if (est_error < offset)
Packit 96c956
    est_error = offset;
Packit 96c956
  max_error += offset;
Packit 96c956
Packit 96c956
  if (drv_set_sync_status)
Packit 96c956
    drv_set_sync_status(synchronised, est_error, max_error);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay,
Packit 96c956
                               lcl_ReadFrequencyDriver sys_read_freq,
Packit 96c956
                               lcl_SetFrequencyDriver sys_set_freq,
Packit 96c956
                               lcl_ApplyStepOffsetDriver sys_apply_step_offset,
Packit 96c956
                               double min_fastslew_offset, double max_fastslew_rate,
Packit 96c956
                               lcl_AccrueOffsetDriver sys_accrue_offset,
Packit 96c956
                               lcl_OffsetCorrectionDriver sys_get_offset_correction,
Packit 96c956
                               lcl_SetLeapDriver sys_set_leap,
Packit 96c956
                               lcl_SetSyncStatusDriver sys_set_sync_status)
Packit 96c956
{
Packit 96c956
  max_freq = max_set_freq_ppm;
Packit 96c956
  max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
Packit 96c956
  drv_read_freq = sys_read_freq;
Packit 96c956
  drv_set_freq = sys_set_freq;
Packit 96c956
  drv_accrue_offset = sys_accrue_offset;
Packit 96c956
  drv_get_offset_correction = sys_get_offset_correction;
Packit 96c956
  drv_set_sync_status = sys_set_sync_status;
Packit 96c956
Packit 96c956
  base_freq = (*drv_read_freq)();
Packit 96c956
  slew_freq = 0.0;
Packit 96c956
  offset_register = 0.0;
Packit 96c956
Packit 96c956
  max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
Packit 96c956
Packit 96c956
  fastslew_min_offset = min_fastslew_offset;
Packit 96c956
  fastslew_max_rate = max_fastslew_rate / 1.0e6;
Packit 96c956
  fastslew_active = 0;
Packit 96c956
Packit 96c956
  lcl_RegisterSystemDrivers(read_frequency, set_frequency,
Packit 96c956
                            accrue_offset, sys_apply_step_offset ?
Packit 96c956
                              sys_apply_step_offset : apply_step_offset,
Packit 96c956
                            offset_convert, sys_set_leap, set_sync_status);
Packit 96c956
Packit 96c956
  LCL_AddParameterChangeHandler(handle_step, NULL);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
SYS_Generic_Finalise(void)
Packit 96c956
{
Packit 96c956
  struct timespec now;
Packit 96c956
Packit 96c956
  /* Must *NOT* leave a slew running - clock could drift way off
Packit 96c956
     if the daemon is not restarted */
Packit 96c956
Packit 96c956
  SCH_RemoveTimeout(slew_timeout_id);
Packit 96c956
  slew_timeout_id = 0;
Packit 96c956
Packit 96c956
  (*drv_set_freq)(clamp_freq(base_freq));
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&now;;
Packit 96c956
  stop_fastslew(&now;;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */