[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH, v2] x86/HVM: assorted RTC emulation adjustments



On Wed, 22 Aug 2012, Jan Beulich wrote:
> - don't call rtc_timer_update() on REG_A writes when the value didn't
>   change (doing the call always was reported to cause wall clock time
>   lagging with the JVM running on Windows)
> - don't call rtc_timer_update() on REG_B writes at all
> - only call alarm_timer_update() on REG_B writes when relevant bits
>   change
> - only call check_update_timer() on REG_B writes when SET changes
> - instead properly handle AF and PF when the guest is not also setting
>   AIE/PIE respectively (for UF this was already the case, only a
>   comment was slightly inaccurate)
> - raise the RTC IRQ not only when UIE gets set while UF was already
>   set, but generalize this to cover AIE and PIE as well
> - properly mask off bit 7 when retrieving the hour values in
>   alarm_timer_update(), and properly use RTC_HOURS_ALARM's bit 7 when
>   converting from 12- to 24-hour value
> - also handle the two other possible clock bases
> - use RTC_* names in a couple of places where literal numbers were used
>   so far
> 
> Note that this only improves the situation described in the thread at
> http://lists.xen.org/archives/html/xen-devel/2012-08/msg00664.html,
> there are still problems with the emulation when invoked at a high rate
> as described there.
> 
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

Although this patch solves a real problem and should probably go in at
some point, I am a bit worried about drifting too much from the original
RTC emulator (that was taken from QEMU), because it would be nice to be
able to backport features like this one:

http://marc.info/?l=qemu-devel&m=134392375010304


> v2: Small adjustment to the pt_update_irq() change, avoiding to call
>     the RTC code for a HPET event (which also may pass 8 [aka RTC_IRQ]
>     as create_periodic_time()'s "irq" argument.
> 
> --- a/xen/arch/x86/hvm/rtc.c
> +++ b/xen/arch/x86/hvm/rtc.c
> @@ -50,11 +50,24 @@ static void rtc_set_time(RTCState *s);
>  static inline int from_bcd(RTCState *s, int a);
>  static inline int convert_hour(RTCState *s, int hour);
> 
> -static void rtc_periodic_cb(struct vcpu *v, void *opaque)
> +static void rtc_toggle_irq(RTCState *s)
> +{
> +    struct domain *d = vrtc_domain(s);
> +
> +    ASSERT(spin_is_locked(&s->lock));
> +    s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
> +    hvm_isa_irq_deassert(d, RTC_IRQ);
> +    hvm_isa_irq_assert(d, RTC_IRQ);
> +}
> +
> +void rtc_periodic_interrupt(void *opaque)
>  {
>      RTCState *s = opaque;
> +
>      spin_lock(&s->lock);
> -    s->hw.cmos_data[RTC_REG_C] |= 0xc0;
> +    s->hw.cmos_data[RTC_REG_C] |= RTC_PF;
> +    if ( s->hw.cmos_data[RTC_REG_B] & RTC_PIE )
> +        rtc_toggle_irq(s);
>      spin_unlock(&s->lock);
>  }
> 
> @@ -68,19 +81,25 @@ static void rtc_timer_update(RTCState *s
>      ASSERT(spin_is_locked(&s->lock));
> 
>      period_code = s->hw.cmos_data[RTC_REG_A] & RTC_RATE_SELECT;
> -    if ( (period_code != 0) && (s->hw.cmos_data[RTC_REG_B] & RTC_PIE) )
> +    switch ( s->hw.cmos_data[RTC_REG_A] & RTC_DIV_CTL )
>      {
> -        if ( period_code <= 2 )
> +    case RTC_REF_CLCK_32KHZ:
> +        if ( (period_code != 0) && (period_code <= 2) )
>              period_code += 7;
> -
> -        period = 1 << (period_code - 1); /* period in 32 Khz cycles */
> -        period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns 
> */
> -        create_periodic_time(v, &s->pt, period, period, RTC_IRQ,
> -                             rtc_periodic_cb, s);
> -    }
> -    else
> -    {
> +        /* fall through */
> +    case RTC_REF_CLCK_1MHZ:
> +    case RTC_REF_CLCK_4MHZ:
> +        if ( period_code != 0 )
> +        {
> +            period = 1 << (period_code - 1); /* period in 32 Khz cycles */
> +            period = DIV_ROUND(period * 1000000000ULL, 32768); /* in ns */
> +            create_periodic_time(v, &s->pt, period, period, RTC_IRQ, NULL, 
> s);
> +            break;
> +        }
> +        /* fall through */
> +    default:
>          destroy_periodic_time(&s->pt);
> +        break;
>      }
>  }
> 
> @@ -102,7 +121,7 @@ static void check_update_timer(RTCState
>          guest_usec = get_localtime_us(d) % USEC_PER_SEC;
>          if (guest_usec >= (USEC_PER_SEC - 244))
>          {
> -            /* RTC is in update cycle when enabling UIE */
> +            /* RTC is in update cycle */
>              s->hw.cmos_data[RTC_REG_A] |= RTC_UIP;
>              next_update_time = (USEC_PER_SEC - guest_usec) * NS_PER_USEC;
>              expire_time = NOW() + next_update_time;
> @@ -144,7 +163,6 @@ static void rtc_update_timer(void *opaqu
>  static void rtc_update_timer2(void *opaque)
>  {
>      RTCState *s = opaque;
> -    struct domain *d = vrtc_domain(s);
> 
>      spin_lock(&s->lock);
>      if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
> @@ -152,11 +170,7 @@ static void rtc_update_timer2(void *opaq
>          s->hw.cmos_data[RTC_REG_C] |= RTC_UF;
>          s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;
>          if ((s->hw.cmos_data[RTC_REG_B] & RTC_UIE))
> -        {
> -            s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
> -            hvm_isa_irq_deassert(d, RTC_IRQ);
> -            hvm_isa_irq_assert(d, RTC_IRQ);
> -        }
> +            rtc_toggle_irq(s);
>          check_update_timer(s);
>      }
>      spin_unlock(&s->lock);
> @@ -175,21 +189,18 @@ static void alarm_timer_update(RTCState
> 
>      stop_timer(&s->alarm_timer);
> 
> -    if ((s->hw.cmos_data[RTC_REG_B] & RTC_AIE) &&
> -            !(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
> +    if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) )
>      {
>          s->current_tm = gmtime(get_localtime(d));
>          rtc_copy_date(s);
> 
>          alarm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS_ALARM]);
>          alarm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES_ALARM]);
> -        alarm_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
> -        alarm_hour = convert_hour(s, alarm_hour);
> +        alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
> 
>          cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
>          cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
> -        cur_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS]);
> -        cur_hour = convert_hour(s, cur_hour);
> +        cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
> 
>          next_update_time = USEC_PER_SEC - (get_localtime_us(d) % 
> USEC_PER_SEC);
>          next_update_time = next_update_time * NS_PER_USEC + NOW();
> @@ -343,7 +354,6 @@ static void alarm_timer_update(RTCState
>  static void rtc_alarm_cb(void *opaque)
>  {
>      RTCState *s = opaque;
> -    struct domain *d = vrtc_domain(s);
> 
>      spin_lock(&s->lock);
>      if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
> @@ -351,11 +361,7 @@ static void rtc_alarm_cb(void *opaque)
>          s->hw.cmos_data[RTC_REG_C] |= RTC_AF;
>          /* alarm interrupt */
>          if (s->hw.cmos_data[RTC_REG_B] & RTC_AIE)
> -        {
> -            s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
> -            hvm_isa_irq_deassert(d, RTC_IRQ);
> -            hvm_isa_irq_assert(d, RTC_IRQ);
> -        }
> +            rtc_toggle_irq(s);
>          alarm_timer_update(s);
>      }
>      spin_unlock(&s->lock);
> @@ -365,6 +371,7 @@ static int rtc_ioport_write(void *opaque
>  {
>      RTCState *s = opaque;
>      struct domain *d = vrtc_domain(s);
> +    uint32_t orig, mask;
> 
>      spin_lock(&s->lock);
> 
> @@ -382,6 +389,7 @@ static int rtc_ioport_write(void *opaque
>          return 0;
>      }
> 
> +    orig = s->hw.cmos_data[s->hw.cmos_index];
>      switch ( s->hw.cmos_index )
>      {
>      case RTC_SECONDS_ALARM:
> @@ -405,9 +413,9 @@ static int rtc_ioport_write(void *opaque
>          break;
>      case RTC_REG_A:
>          /* UIP bit is read only */
> -        s->hw.cmos_data[RTC_REG_A] = (data & ~RTC_UIP) |
> -            (s->hw.cmos_data[RTC_REG_A] & RTC_UIP);
> -        rtc_timer_update(s);
> +        s->hw.cmos_data[RTC_REG_A] = (data & ~RTC_UIP) | (orig & RTC_UIP);
> +        if ( (data ^ orig) & (RTC_RATE_SELECT | RTC_DIV_CTL) )
> +            rtc_timer_update(s);
>          break;
>      case RTC_REG_B:
>          if ( data & RTC_SET )
> @@ -415,7 +423,7 @@ static int rtc_ioport_write(void *opaque
>              /* set mode: reset UIP mode */
>              s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;
>              /* adjust cmos before stopping */
> -            if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
> +            if (!(orig & RTC_SET))
>              {
>                  s->current_tm = gmtime(get_localtime(d));
>                  rtc_copy_date(s);
> @@ -424,21 +432,26 @@ static int rtc_ioport_write(void *opaque
>          else
>          {
>              /* if disabling set mode, update the time */
> -            if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET )
> +            if ( orig & RTC_SET )
>                  rtc_set_time(s);
>          }
> -        /* if the interrupt is already set when the interrupt become
> -         * enabled, raise an interrupt immediately*/
> -        if ((data & RTC_UIE) && !(s->hw.cmos_data[RTC_REG_B] & RTC_UIE))
> -            if (s->hw.cmos_data[RTC_REG_C] & RTC_UF)
> +        /*
> +         * If the interrupt is already set when the interrupt becomes
> +         * enabled, raise an interrupt immediately.
> +         * NB: RTC_{A,P,U}IE == RTC_{A,P,U}F respectively.
> +         */
> +        for ( mask = RTC_UIE; mask <= RTC_PIE; mask <<= 1 )
> +            if ( (data & mask) && !(orig & mask) &&
> +                 (s->hw.cmos_data[RTC_REG_C] & mask) )
>              {
> -                hvm_isa_irq_deassert(d, RTC_IRQ);
> -                hvm_isa_irq_assert(d, RTC_IRQ);
> +                rtc_toggle_irq(s);
> +                break;
>              }
>          s->hw.cmos_data[RTC_REG_B] = data;
> -        rtc_timer_update(s);
> -        check_update_timer(s);
> -        alarm_timer_update(s);
> +        if ( (data ^ orig) & RTC_SET )
> +            check_update_timer(s);
> +        if ( (data ^ orig) & (RTC_24H | RTC_DM_BINARY | RTC_SET) )
> +            alarm_timer_update(s);
>          break;
>      case RTC_REG_C:
>      case RTC_REG_D:
> @@ -453,7 +466,7 @@ static int rtc_ioport_write(void *opaque
> 
>  static inline int to_bcd(RTCState *s, int a)
>  {
> -    if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
> +    if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
>          return a;
>      else
>          return ((a / 10) << 4) | (a % 10);
> @@ -461,7 +474,7 @@ static inline int to_bcd(RTCState *s, in
> 
>  static inline int from_bcd(RTCState *s, int a)
>  {
> -    if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
> +    if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
>          return a;
>      else
>          return ((a >> 4) * 10) + (a & 0x0f);
> @@ -469,12 +482,14 @@ static inline int from_bcd(RTCState *s,
> 
>  /* Hours in 12 hour mode are in 1-12 range, not 0-11.
>   * So we need convert it before using it*/
> -static inline int convert_hour(RTCState *s, int hour)
> +static inline int convert_hour(RTCState *s, int raw)
>  {
> +    int hour = from_bcd(s, raw & 0x7f);
> +
>      if (!(s->hw.cmos_data[RTC_REG_B] & RTC_24H))
>      {
>          hour %= 12;
> -        if (s->hw.cmos_data[RTC_HOURS] & 0x80)
> +        if (raw & 0x80)
>              hour += 12;
>      }
>      return hour;
> @@ -493,8 +508,7 @@ static void rtc_set_time(RTCState *s)
> 
>      tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
>      tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
> -    tm->tm_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS] & 0x7f);
> -    tm->tm_hour = convert_hour(s, tm->tm_hour);
> +    tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
>      tm->tm_wday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_WEEK]);
>      tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]);
>      tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1;
> --- a/xen/arch/x86/hvm/vpt.c
> +++ b/xen/arch/x86/hvm/vpt.c
> @@ -22,6 +22,7 @@
>  #include <asm/hvm/vpt.h>
>  #include <asm/event.h>
>  #include <asm/apic.h>
> +#include <asm/mc146818rtc.h>
> 
>  #define mode_is(d, name) \
>      ((d)->arch.hvm_domain.params[HVM_PARAM_TIMER_MODE] == HVMPTM_##name)
> @@ -218,6 +219,7 @@ void pt_update_irq(struct vcpu *v)
>      struct periodic_time *pt, *temp, *earliest_pt = NULL;
>      uint64_t max_lag = -1ULL;
>      int irq, is_lapic;
> +    void *pt_priv;
> 
>      spin_lock(&v->arch.hvm_vcpu.tm_lock);
> 
> @@ -251,13 +253,14 @@ void pt_update_irq(struct vcpu *v)
>      earliest_pt->irq_issued = 1;
>      irq = earliest_pt->irq;
>      is_lapic = (earliest_pt->source == PTSRC_lapic);
> +    pt_priv = earliest_pt->priv;
> 
>      spin_unlock(&v->arch.hvm_vcpu.tm_lock);
> 
>      if ( is_lapic )
> -    {
>          vlapic_set_irq(vcpu_vlapic(v), irq, 0);
> -    }
> +    else if ( irq == RTC_IRQ && pt_priv )
> +        rtc_periodic_interrupt(pt_priv);
>      else
>      {
>          hvm_isa_irq_deassert(v->domain, irq);
> --- a/xen/include/asm-x86/hvm/vpt.h
> +++ b/xen/include/asm-x86/hvm/vpt.h
> @@ -181,6 +181,7 @@ void rtc_migrate_timers(struct vcpu *v);
>  void rtc_deinit(struct domain *d);
>  void rtc_reset(struct domain *d);
>  void rtc_update_clock(struct domain *d);
> +void rtc_periodic_interrupt(void *);
> 
>  void pmtimer_init(struct vcpu *v);
>  void pmtimer_deinit(struct domain *d);
> 
> 
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.