Re-implement RTC current time calculation to eliminate the possibility of

losing time.

The problem with the earlier implementation was that the uptime value
used by 'vrtc_curtime()' could be different than the uptime value when
'vrtc_time_update()' actually updated 'base_uptime'.

Fix this by calculating and updating the (rtctime, uptime) tuple together.

MFC after:	2 weeks
This commit is contained in:
Neel Natu 2015-04-29 23:44:28 +00:00
parent be73922fcd
commit 787fb3d026

View File

@ -142,20 +142,23 @@ update_enabled(struct vrtc *vrtc)
}
static time_t
vrtc_curtime(struct vrtc *vrtc)
vrtc_curtime(struct vrtc *vrtc, sbintime_t *basetime)
{
sbintime_t now, delta;
time_t t;
time_t t, secs;
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
t = vrtc->base_rtctime;
*basetime = vrtc->base_uptime;
if (update_enabled(vrtc)) {
now = sbinuptime();
delta = now - vrtc->base_uptime;
KASSERT(delta >= 0, ("vrtc_curtime: uptime went backwards: "
"%#lx to %#lx", vrtc->base_uptime, now));
t += delta / SBT_1S;
secs = delta / SBT_1S;
t += secs;
*basetime += secs * SBT_1S;
}
return (t);
}
@ -390,9 +393,10 @@ rtc_to_secs(struct vrtc *vrtc)
}
static int
vrtc_time_update(struct vrtc *vrtc, time_t newtime)
vrtc_time_update(struct vrtc *vrtc, time_t newtime, sbintime_t newbase)
{
struct rtcdev *rtc;
sbintime_t oldbase;
time_t oldtime;
uint8_t alarm_sec, alarm_min, alarm_hour;
@ -404,16 +408,21 @@ vrtc_time_update(struct vrtc *vrtc, time_t newtime)
alarm_hour = rtc->alarm_hour;
oldtime = vrtc->base_rtctime;
VM_CTR2(vrtc->vm, "Updating RTC time from %#lx to %#lx",
VM_CTR2(vrtc->vm, "Updating RTC secs from %#lx to %#lx",
oldtime, newtime);
oldbase = vrtc->base_uptime;
VM_CTR2(vrtc->vm, "Updating RTC base uptime from %#lx to %#lx",
oldbase, newbase);
vrtc->base_uptime = newbase;
if (newtime == oldtime)
return (0);
/*
* If 'newtime' indicates that RTC updates are disabled then just
* record that and return. There is no need to do alarm interrupt
* processing or update 'base_uptime' in this case.
* processing in this case.
*/
if (newtime == VRTC_BROKEN_TIME) {
vrtc->base_rtctime = VRTC_BROKEN_TIME;
@ -459,8 +468,6 @@ vrtc_time_update(struct vrtc *vrtc, time_t newtime)
if (uintr_enabled(vrtc))
vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_UPDATE);
vrtc->base_uptime = sbinuptime();
return (0);
}
@ -531,7 +538,7 @@ static void
vrtc_callout_handler(void *arg)
{
struct vrtc *vrtc = arg;
sbintime_t freqsbt;
sbintime_t freqsbt, basetime;
time_t rtctime;
int error;
@ -553,8 +560,8 @@ vrtc_callout_handler(void *arg)
vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c | RTCIR_PERIOD);
if (aintr_enabled(vrtc) || uintr_enabled(vrtc)) {
rtctime = vrtc_curtime(vrtc);
error = vrtc_time_update(vrtc, rtctime);
rtctime = vrtc_curtime(vrtc, &basetime);
error = vrtc_time_update(vrtc, rtctime, basetime);
KASSERT(error == 0, ("%s: vrtc_time_update error %d",
__func__, error));
}
@ -619,7 +626,7 @@ static int
vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
{
struct rtcdev *rtc;
sbintime_t oldfreq, newfreq;
sbintime_t oldfreq, newfreq, basetime;
time_t curtime, rtctime;
int error;
uint8_t oldval, changed;
@ -640,12 +647,13 @@ vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
if (changed & RTCSB_HALT) {
if ((newval & RTCSB_HALT) == 0) {
rtctime = rtc_to_secs(vrtc);
basetime = sbinuptime();
if (rtctime == VRTC_BROKEN_TIME) {
if (rtc_flag_broken_time)
return (-1);
}
} else {
curtime = vrtc_curtime(vrtc);
curtime = vrtc_curtime(vrtc, &basetime);
KASSERT(curtime == vrtc->base_rtctime, ("%s: mismatch "
"between vrtc basetime (%#lx) and curtime (%#lx)",
__func__, vrtc->base_rtctime, curtime));
@ -664,7 +672,7 @@ vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
rtctime = VRTC_BROKEN_TIME;
rtc->reg_b &= ~RTCSB_UINTR;
}
error = vrtc_time_update(vrtc, rtctime);
error = vrtc_time_update(vrtc, rtctime, basetime);
KASSERT(error == 0, ("vrtc_time_update error %d", error));
}
@ -744,7 +752,7 @@ vrtc_set_time(struct vm *vm, time_t secs)
vrtc = vm_rtc(vm);
VRTC_LOCK(vrtc);
error = vrtc_time_update(vrtc, secs);
error = vrtc_time_update(vrtc, secs, sbinuptime());
VRTC_UNLOCK(vrtc);
if (error) {
@ -761,11 +769,12 @@ time_t
vrtc_get_time(struct vm *vm)
{
struct vrtc *vrtc;
sbintime_t basetime;
time_t t;
vrtc = vm_rtc(vm);
VRTC_LOCK(vrtc);
t = vrtc_curtime(vrtc);
t = vrtc_curtime(vrtc, &basetime);
VRTC_UNLOCK(vrtc);
return (t);
@ -802,6 +811,7 @@ int
vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval)
{
struct vrtc *vrtc;
sbintime_t basetime;
time_t curtime;
uint8_t *ptr;
@ -818,7 +828,7 @@ vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval)
* Update RTC date/time fields if necessary.
*/
if (offset < 10 || offset == RTC_CENTURY) {
curtime = vrtc_curtime(vrtc);
curtime = vrtc_curtime(vrtc, &basetime);
secs_to_rtc(curtime, vrtc, 0);
}
@ -858,6 +868,7 @@ vrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
{
struct vrtc *vrtc;
struct rtcdev *rtc;
sbintime_t basetime;
time_t curtime;
int error, offset;
@ -875,8 +886,8 @@ vrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
}
error = 0;
curtime = vrtc_curtime(vrtc);
vrtc_time_update(vrtc, curtime);
curtime = vrtc_curtime(vrtc, &basetime);
vrtc_time_update(vrtc, curtime, basetime);
/*
* Update RTC date/time fields if necessary.
@ -939,7 +950,7 @@ vrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
*/
if (offset == RTC_CENTURY && !rtc_halted(vrtc)) {
curtime = rtc_to_secs(vrtc);
error = vrtc_time_update(vrtc, curtime);
error = vrtc_time_update(vrtc, curtime, sbinuptime());
KASSERT(!error, ("vrtc_time_update error %d", error));
if (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time)
error = -1;
@ -993,7 +1004,7 @@ vrtc_init(struct vm *vm)
VRTC_LOCK(vrtc);
vrtc->base_rtctime = VRTC_BROKEN_TIME;
vrtc_time_update(vrtc, curtime);
vrtc_time_update(vrtc, curtime, sbinuptime());
secs_to_rtc(curtime, vrtc, 0);
VRTC_UNLOCK(vrtc);