Adjust tick length with system clock.

This increases the maximum frequency adjustment of the system clock
from 500 ppm to 10%.

Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
master
Miroslav Lichvar 2013-12-19 18:39:11 +01:00 committed by Richard Cochran
parent eb9b0bd275
commit e63a6ea89b
4 changed files with 49 additions and 3 deletions

View File

@ -616,9 +616,11 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
pr_err("clock is not adjustable"); pr_err("clock is not adjustable");
return NULL; return NULL;
} }
clockadj_init(c->clkid);
} else { } else {
c->clkid = CLOCK_REALTIME; c->clkid = CLOCK_REALTIME;
c->utc_timescale = 1; c->utc_timescale = 1;
clockadj_init(c->clkid);
max_adj = sysclk_max_freq(); max_adj = sysclk_max_freq();
sysclk_set_leap(0); sysclk_set_leap(0);
} }

View File

@ -17,7 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include <math.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "clockadj.h" #include "clockadj.h"
#include "missing.h" #include "missing.h"
@ -26,12 +28,37 @@
#define NS_PER_SEC 1000000000LL #define NS_PER_SEC 1000000000LL
static int realtime_leap_bit; static int realtime_leap_bit;
static long realtime_hz;
static long realtime_nominal_tick;
void clockadj_init(clockid_t clkid)
{
#ifdef _SC_CLK_TCK
if (clkid == CLOCK_REALTIME) {
/* This is USER_HZ in the kernel. */
realtime_hz = sysconf(_SC_CLK_TCK);
if (realtime_hz > 0) {
/* This is TICK_USEC in the kernel. */
realtime_nominal_tick =
(1000000 + realtime_hz / 2) / realtime_hz;
}
}
#endif
}
void clockadj_set_freq(clockid_t clkid, double freq) void clockadj_set_freq(clockid_t clkid, double freq)
{ {
struct timex tx; struct timex tx;
memset(&tx, 0, sizeof(tx)); memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_FREQUENCY;
/* With system clock set also the tick length. */
if (clkid == CLOCK_REALTIME && realtime_nominal_tick) {
tx.modes |= ADJ_TICK;
tx.tick = round(freq / 1e3 / realtime_hz) + realtime_nominal_tick;
freq -= 1e3 * realtime_hz * (tx.tick - realtime_nominal_tick);
}
tx.modes |= ADJ_FREQUENCY;
tx.freq = (long) (freq * 65.536); tx.freq = (long) (freq * 65.536);
if (clock_adjtime(clkid, &tx) < 0) if (clock_adjtime(clkid, &tx) < 0)
pr_err("failed to adjust the clock: %m"); pr_err("failed to adjust the clock: %m");
@ -42,10 +69,13 @@ double clockadj_get_freq(clockid_t clkid)
double f = 0.0; double f = 0.0;
struct timex tx; struct timex tx;
memset(&tx, 0, sizeof(tx)); memset(&tx, 0, sizeof(tx));
if (clock_adjtime(clkid, &tx) < 0) if (clock_adjtime(clkid, &tx) < 0) {
pr_err("failed to read out the clock frequency adjustment: %m"); pr_err("failed to read out the clock frequency adjustment: %m");
else } else {
f = tx.freq / 65.536; f = tx.freq / 65.536;
if (clkid == CLOCK_REALTIME && realtime_nominal_tick)
f += 1e3 * realtime_hz * (tx.tick - realtime_nominal_tick);
}
return f; return f;
} }
@ -111,6 +141,13 @@ int sysclk_max_freq(void)
f = tx.tolerance / 65.536; f = tx.tolerance / 65.536;
if (!f) if (!f)
f = 500000; f = 500000;
/* The kernel allows the tick length to be adjusted up to 10%. But use
it only if the overall frequency of the clock can be adjusted
continuously with the tick and freq fields (i.e. hz <= 1000). */
if (realtime_nominal_tick && 2 * f >= 1000 * realtime_hz)
f = realtime_nominal_tick / 10 * 1000 * realtime_hz;
return f; return f;
} }

View File

@ -23,6 +23,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <time.h> #include <time.h>
/**
* Initialize state needed when adjusting or reading the clock.
* @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME.
*/
void clockadj_init(clockid_t clkid);
/** /**
* Set clock's frequency offset. * Set clock's frequency offset.
* @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME. * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME.

View File

@ -779,6 +779,7 @@ int main(int argc, char *argv[])
close_pmc(&dst_clock); close_pmc(&dst_clock);
} }
clockadj_init(dst_clock.clkid);
ppb = clockadj_get_freq(dst_clock.clkid); ppb = clockadj_get_freq(dst_clock.clkid);
/* The reading may silently fail and return 0, reset the frequency to /* The reading may silently fail and return 0, reset the frequency to
make sure ppb is the actual frequency of the clock. */ make sure ppb is the actual frequency of the clock. */