diff -r 5af4f2ab06f3 xen/arch/x86/hvm/pmtimer.c --- a/xen/arch/x86/hvm/pmtimer.c Tue Jan 22 09:33:10 2013 +0100 +++ b/xen/arch/x86/hvm/pmtimer.c Tue Feb 05 10:26:13 2013 +0900 @@ -84,28 +84,31 @@ void hvm_acpi_sleep_button(struct domain } /* Set the correct value in the timer, accounting for time elapsed - * since the last time we did that. */ -static void pmt_update_time(PMTState *s) + * since the last time we did that. + * return true if the counter's MSB has changed. */ +static bool_t pmt_count_up_and_test_msb(PMTState *s, uint64_t gtime) { - uint64_t curr_gtime, tmp; uint32_t tmr_val = s->pm.tmr_val, msb = tmr_val & TMR_VAL_MSB; - - ASSERT(spin_is_locked(&s->lock)); + uint64_t tmp = ((gtime - s->last_gtime) * s->scale) + s->not_accounted; - /* Update the timer */ - curr_gtime = hvm_get_guest_time(s->vcpu); - tmp = ((curr_gtime - s->last_gtime) * s->scale) + s->not_accounted; s->not_accounted = (uint32_t)tmp; tmr_val += tmp >> 32; tmr_val &= TMR_VAL_MASK; - s->last_gtime = curr_gtime; + s->last_gtime = gtime; /* Update timer value atomically wrt lock-free reads in handle_pmt_io(). */ *(volatile uint32_t *)&s->pm.tmr_val = tmr_val; - /* If the counter's MSB has changed, set the status bit */ - if ( (tmr_val & TMR_VAL_MSB) != msb ) + return ((tmr_val & TMR_VAL_MSB) != msb); +} + +static void pmt_update_time(PMTState *s) +{ + ASSERT(spin_is_locked(&s->lock)); + + if ( pmt_count_up_and_test_msb(s, hvm_get_guest_time(s->vcpu)) ) { + /* MSB has changed, set the status bit */ s->pm.pm1a_sts |= TMR_STS; pmt_update_sci(s); } @@ -244,21 +247,34 @@ static int handle_pmt_io( static int pmtimer_save(struct domain *d, hvm_domain_context_t *h) { PMTState *s = &d->arch.hvm_domain.pl_time.vpmt; - uint32_t x, msb = s->pm.tmr_val & TMR_VAL_MSB; + uint64_t guest_time; int rc; spin_lock(&s->lock); - /* Update the counter to the guest's current time. We always save - * with the domain paused, so the saved time should be after the - * last_gtime, but just in case, make sure we only go forwards */ - x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32; - if ( x < 1UL<<31 ) - s->pm.tmr_val += x; - if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb ) - s->pm.pm1a_sts |= TMR_STS; - /* No point in setting the SCI here because we'll already have saved the - * IRQ and *PIC state; we'll fix it up when we restore the domain */ + guest_time = s->vcpu->arch.hvm_vcpu.guest_time; + /* Update the counter to the guest's current time only if the + * timer mode is delay_for_missed_ticks */ + if ( guest_time != 0 ) + { + /* We always save with the domain paused, so the saved time + * should be after the last_gtime, but just in case, make sure + * we only go forwards */ + if ( (s64)guest_time - (s64)s->last_gtime < 0) + { + dprintk(XENLOG_WARNING, + "Last update of PMT is ahead of guest's time by %ld\n", + s->last_gtime - guest_time); + } + else + { + if ( pmt_count_up_and_test_msb(s, guest_time) ) + s->pm.pm1a_sts |= TMR_STS; + /* No point in setting the SCI here because we'll already + * have saved the IRQ and *PIC state; + * we'll fix it up when we restore the domain */ + } + } rc = hvm_save_entry(PMTIMER, 0, h, &s->pm);