Commit 93b665693dd4afd32c89b0d5ee2b407b26a7a3bc

Authored by aliguori
1 parent 2ed51f5b

Change RTC time drift IRQ re-injection (Gleb Natapov)

Currently IRQ are reinjected as soon as they are acknowledged to
the RTC, but Windows sometimes do acknowledgement in a loop with
global interrupt disabled waiting for interrupt to be cleared and
it does not mask RTC vector in PIC/APIC while doing this. In such
situation interrupt injection always fails and RTC interrupt is never
cleared.

Instead of reinjecting coalesced IRQs on acknowledgement the patch below
reinjects them by accelerating RTC clock a bit. This way RTC interrupt
is not constantly raced after coalesced interrupt.

Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7231 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 47 additions and 15 deletions
hw/mc146818rtc.c
@@ -73,6 +73,7 @@ struct RTCState { @@ -73,6 +73,7 @@ struct RTCState {
73 #ifdef TARGET_I386 73 #ifdef TARGET_I386
74 uint32_t irq_coalesced; 74 uint32_t irq_coalesced;
75 uint32_t period; 75 uint32_t period;
  76 + QEMUTimer *coalesced_timer;
76 #endif 77 #endif
77 QEMUTimer *second_timer; 78 QEMUTimer *second_timer;
78 QEMUTimer *second_timer2; 79 QEMUTimer *second_timer2;
@@ -93,6 +94,37 @@ static void rtc_irq_raise(qemu_irq irq) { @@ -93,6 +94,37 @@ static void rtc_irq_raise(qemu_irq irq) {
93 static void rtc_set_time(RTCState *s); 94 static void rtc_set_time(RTCState *s);
94 static void rtc_copy_date(RTCState *s); 95 static void rtc_copy_date(RTCState *s);
95 96
  97 +#ifdef TARGET_I386
  98 +static void rtc_coalesced_timer_update(RTCState *s)
  99 +{
  100 + if (s->irq_coalesced == 0) {
  101 + qemu_del_timer(s->coalesced_timer);
  102 + } else {
  103 + /* divide each RTC interval to 2 - 8 smaller intervals */
  104 + int c = MIN(s->irq_coalesced, 7) + 1;
  105 + int64_t next_clock = qemu_get_clock(vm_clock) +
  106 + muldiv64(s->period / c, ticks_per_sec, 32768);
  107 + qemu_mod_timer(s->coalesced_timer, next_clock);
  108 + }
  109 +}
  110 +
  111 +static void rtc_coalesced_timer(void *opaque)
  112 +{
  113 + RTCState *s = opaque;
  114 +
  115 + if (s->irq_coalesced != 0) {
  116 + apic_reset_irq_delivered();
  117 + s->cmos_data[RTC_REG_C] |= 0xc0;
  118 + rtc_irq_raise(s->irq);
  119 + if (apic_get_irq_delivered()) {
  120 + s->irq_coalesced--;
  121 + }
  122 + }
  123 +
  124 + rtc_coalesced_timer_update(s);
  125 +}
  126 +#endif
  127 +
96 static void rtc_timer_update(RTCState *s, int64_t current_time) 128 static void rtc_timer_update(RTCState *s, int64_t current_time)
97 { 129 {
98 int period_code, period; 130 int period_code, period;
@@ -138,14 +170,18 @@ static void rtc_periodic_timer(void *opaque) @@ -138,14 +170,18 @@ static void rtc_periodic_timer(void *opaque)
138 RTCState *s = opaque; 170 RTCState *s = opaque;
139 171
140 rtc_timer_update(s, s->next_periodic_time); 172 rtc_timer_update(s, s->next_periodic_time);
141 -#ifdef TARGET_I386  
142 - if ((s->cmos_data[RTC_REG_C] & 0xc0) && rtc_td_hack) {  
143 - s->irq_coalesced++;  
144 - return;  
145 - }  
146 -#endif  
147 if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { 173 if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
148 s->cmos_data[RTC_REG_C] |= 0xc0; 174 s->cmos_data[RTC_REG_C] |= 0xc0;
  175 +#ifdef TARGET_I386
  176 + if(rtc_td_hack) {
  177 + apic_reset_irq_delivered();
  178 + rtc_irq_raise(s->irq);
  179 + if (!apic_get_irq_delivered()) {
  180 + s->irq_coalesced++;
  181 + rtc_coalesced_timer_update(s);
  182 + }
  183 + } else
  184 +#endif
149 rtc_irq_raise(s->irq); 185 rtc_irq_raise(s->irq);
150 } 186 }
151 if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) { 187 if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) {
@@ -415,15 +451,6 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) @@ -415,15 +451,6 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
415 case RTC_REG_C: 451 case RTC_REG_C:
416 ret = s->cmos_data[s->cmos_index]; 452 ret = s->cmos_data[s->cmos_index];
417 qemu_irq_lower(s->irq); 453 qemu_irq_lower(s->irq);
418 -#ifdef TARGET_I386  
419 - if(s->irq_coalesced) {  
420 - apic_reset_irq_delivered();  
421 - qemu_irq_raise(s->irq);  
422 - if (apic_get_irq_delivered())  
423 - s->irq_coalesced--;  
424 - break;  
425 - }  
426 -#endif  
427 s->cmos_data[RTC_REG_C] = 0x00; 454 s->cmos_data[RTC_REG_C] = 0x00;
428 break; 455 break;
429 default: 456 default:
@@ -536,6 +563,7 @@ static int rtc_load_td(QEMUFile *f, void *opaque, int version_id) @@ -536,6 +563,7 @@ static int rtc_load_td(QEMUFile *f, void *opaque, int version_id)
536 563
537 s->irq_coalesced = qemu_get_be32(f); 564 s->irq_coalesced = qemu_get_be32(f);
538 s->period = qemu_get_be32(f); 565 s->period = qemu_get_be32(f);
  566 + rtc_coalesced_timer_update(s);
539 return 0; 567 return 0;
540 } 568 }
541 #endif 569 #endif
@@ -558,6 +586,10 @@ RTCState *rtc_init_sqw(int base, qemu_irq irq, qemu_irq sqw_irq, int base_year) @@ -558,6 +586,10 @@ RTCState *rtc_init_sqw(int base, qemu_irq irq, qemu_irq sqw_irq, int base_year)
558 586
559 s->periodic_timer = qemu_new_timer(vm_clock, 587 s->periodic_timer = qemu_new_timer(vm_clock,
560 rtc_periodic_timer, s); 588 rtc_periodic_timer, s);
  589 +#ifdef TARGET_I386
  590 + if (rtc_td_hack)
  591 + s->coalesced_timer = qemu_new_timer(vm_clock, rtc_coalesced_timer, s);
  592 +#endif
561 s->second_timer = qemu_new_timer(vm_clock, 593 s->second_timer = qemu_new_timer(vm_clock,
562 rtc_update_second, s); 594 rtc_update_second, s);
563 s->second_timer2 = qemu_new_timer(vm_clock, 595 s->second_timer2 = qemu_new_timer(vm_clock,