Have our dom0 periodically set Xen's time-of-day clock (much as Linux does).

That time is used by ~all domUs when they boot (or resume), and Linux by
default will pin the local clock to it; so, it's useful to correct its
drift as best we can.  A sysctl (machdep.xen_timepush_ticks) has been
added to allow changing the interval or disabling this feature.
This commit is contained in:
jld 2006-05-05 19:38:38 +00:00
parent 0f4e4cae54
commit 78e3490d47

View File

@ -1,4 +1,4 @@
/* $NetBSD: clock.c,v 1.22 2006/04/28 02:30:42 jld Exp $ */
/* $NetBSD: clock.c,v 1.23 2006/05/05 19:38:38 jld Exp $ */
/*
*
@ -34,13 +34,14 @@
#include "opt_xen.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.22 2006/04/28 02:30:42 jld Exp $");
__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.23 2006/05/05 19:38:38 jld Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/sysctl.h>
#include <machine/xen.h>
#include <machine/hypervisor.h>
@ -69,6 +70,12 @@ static int timeset;
static uint64_t processed_system_time;
#ifdef DOM0OPS
/* If we're dom0, send our time to Xen every minute or so. */
int xen_timepush_ticks = 0;
static struct callout xen_timepush_co = CALLOUT_INITIALIZER;
#endif
#define NS_PER_TICK (1000000000ULL/hz)
/*
@ -171,15 +178,15 @@ fstime:
printf("WARNING: CHECK AND RESET THE DATE!\n");
}
void
resettodr()
static void
resettodr_i(void)
{
#ifdef DOM0OPS
dom0_op_t op;
int s;
#endif
#ifdef DEBUG_CLOCK
struct clock_ymdhms dt;
struct timeval sent_delta;
#endif
/*
@ -190,10 +197,19 @@ resettodr()
return;
#ifdef DEBUG_CLOCK
clock_secs_to_ymdhms(time.tv_sec - rtc_offset * 60, &dt);
printf("setclock: %d/%d/%d %02d:%02d:%02d\n", dt.dt_year,
dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec);
{
char pm;
if (timercmp(&time, &shadow_tv, >)) {
timersub(&time, &shadow_tv, &sent_delta);
pm = '+';
} else {
timersub(&shadow_tv, &time, &sent_delta);
pm = '-';
}
printf("resettodr: stepping Xen clock by %c%ld.%06ld\n",
pm, sent_delta.tv_sec, sent_delta.tv_usec);
}
#endif
#ifdef DOM0OPS
if (xen_start_info.flags & SIF_PRIVILEGED) {
@ -214,6 +230,22 @@ resettodr()
#endif
}
/*
* When the clock is administratively set, in addition to resetting
* Xen's clock if possible, we should also allow xen_microtime to
* step backwards without complaint.
*/
void
resettodr()
{
CPU_INFO_ITERATOR cii;
struct cpu_info *ci;
resettodr_i();
for (CPU_INFO_FOREACH(cii, ci))
timerclear(&ci->ci_cc.cc_time);
}
void
startrtclock()
{
@ -301,11 +333,12 @@ xen_microtime(struct timeval *tv)
tv->tv_sec++;
tv->tv_usec -= 1000000;
}
/* Avoid backsteps, e.g. at the beginning of a negative adjustment. */
/* Avoid small backsteps, e.g. at the beginning of a negative adjustment. */
if (timerisset(&ci->ci_cc.cc_time) &&
timercmp(tv, &ci->ci_cc.cc_time, <)) {
struct timeval backstep; /* XXX hook resettodr() instead of doing this. */
struct timeval backstep;
/* XXXjld: not sure if this check can be safely removed now */
timersub(&ci->ci_cc.cc_time, tv, &backstep);
if (backstep.tv_sec == 0) { /* if it was < 1sec */
*tv = ci->ci_cc.cc_time;
@ -323,11 +356,52 @@ xen_microtime(struct timeval *tv)
splx(s);
}
#ifdef DOM0OPS
/* ARGSUSED */
static void
xen_timepush(void *arg)
{
struct callout *co = arg;
resettodr_i();
if (xen_timepush_ticks > 0)
callout_schedule(co, xen_timepush_ticks);
}
/* ARGSUSED */
static int
sysctl_xen_timepush(SYSCTLFN_ARGS)
{
int error, new_ticks;
struct sysctlnode node;
new_ticks = xen_timepush_ticks;
node = *rnode;
node.sysctl_data = &new_ticks;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return error;
if (new_ticks < 0)
return EINVAL;
if (new_ticks != xen_timepush_ticks) {
xen_timepush_ticks = new_ticks;
if (new_ticks > 0)
callout_schedule(&xen_timepush_co, new_ticks);
else
callout_stop(&xen_timepush_co);
}
return 0;
}
#endif
void
xen_initclocks()
{
int evtch = bind_virq_to_evtch(VIRQ_TIMER);
int evtch;
evtch = bind_virq_to_evtch(VIRQ_TIMER);
aprint_verbose("Xen clock: using event channel %d\n", evtch);
get_time_values_from_xen();
@ -336,6 +410,19 @@ xen_initclocks()
event_set_handler(evtch, (int (*)(void *))xen_timer_handler,
NULL, IPL_CLOCK, "clock");
hypervisor_enable_event(evtch);
#ifdef DOM0OPS
xen_timepush_ticks = 53 * hz + 3; /* avoid exact # of min/sec */
if (xen_start_info.flags & SIF_PRIVILEGED) {
sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
CTLTYPE_INT, "xen_timepush_ticks", SYSCTL_DESCR("How often"
" to update the hypervisor's time-of-day; 0 to disable"),
sysctl_xen_timepush, 0, &xen_timepush_ticks, 0,
CTL_MACHDEP, CTL_CREATE, CTL_EOL);
callout_reset(&xen_timepush_co, xen_timepush_ticks,
&xen_timepush, &xen_timepush_co);
}
#endif
}
static int