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
parent
eb9b0bd275
commit
e63a6ea89b
2
clock.c
2
clock.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
43
clockadj.c
43
clockadj.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
Loading…
Reference in New Issue