Part 2 of the patch compensates loss of timer interrupts caused by ... - delayed timer callback. - interrupt coalescing (x86 only). Lost timer interrupts are compensated by gradually injecting additional interrupts during the subsequent timer intervals, starting at a rate of one additional interrupt per interval. If further interrupts are lost while compensation is still in progress, the rate is increased. A limit is imposed on the rate and on the 'backlog' of lost interrupts that are to be injected. Signed-off-by: Ulrich Obergfell <uobergfe@xxxxxxxxxx> diff -up ./hw/apic.c.orig2 ./hw/apic.c --- ./hw/apic.c.orig2 2011-02-18 22:48:06.000000000 +0100 +++ ./hw/apic.c 2011-03-13 12:40:56.110991702 +0100 @@ -24,6 +24,9 @@ #include "sysbus.h" #include "trace.h" #include "kvm.h" +#ifdef CONFIG_HPET_DRIFTFIX +#include "sysemu.h" +#endif /* APIC Local Vector Table */ #define APIC_LVT_TIMER 0 @@ -1143,6 +1146,10 @@ static SysBusDeviceInfo apic_info = { static void apic_register_devices(void) { +#ifdef CONFIG_HPET_DRIFTFIX + qemu_get_irq_delivered = apic_get_irq_delivered; + qemu_reset_irq_delivered = apic_reset_irq_delivered; +#endif sysbus_register_withprop(&apic_info); } diff -up ./hw/hpet.c.orig2 ./hw/hpet.c --- ./hw/hpet.c.orig2 2011-02-18 22:48:06.000000000 +0100 +++ ./hw/hpet.c 2011-03-13 12:40:56.111991655 +0100 @@ -31,6 +31,9 @@ #include "hpet_emul.h" #include "sysbus.h" #include "mc146818rtc.h" +#ifdef CONFIG_HPET_DRIFTFIX +#include "sysemu.h" +#endif //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -41,6 +44,13 @@ #define HPET_MSI_SUPPORT 0 +#ifdef CONFIG_HPET_DRIFTFIX +#define MAX_IRQS_TO_INJECT (uint32_t)5000 +#define MAX_IRQ_RATE (uint32_t)10 + +extern int hpet_driftfix; +#endif + struct HPETState; typedef struct HPETTimer { /* timers */ uint8_t tn; /*timer number*/ @@ -55,6 +65,12 @@ typedef struct HPETTimer { /* timers */ uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit * mode. Next pop will be actual timer expiration. */ +#ifdef CONFIG_HPET_DRIFTFIX + uint64_t saved_period; + uint32_t irqs_to_inject; + uint32_t irq_rate; + uint32_t divisor; +#endif } HPETTimer; typedef struct HPETState { @@ -169,11 +185,12 @@ static inline uint64_t hpet_calculate_di } } -static void update_irq(struct HPETTimer *timer, int set) +static int update_irq(struct HPETTimer *timer, int set) { uint64_t mask; HPETState *s; int route; + int irq_delivered = 1; if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) { /* if LegacyReplacementRoute bit is set, HPET specification requires @@ -198,8 +215,19 @@ static void update_irq(struct HPETTimer qemu_irq_raise(s->irqs[route]); } else { s->isr &= ~mask; +#ifdef CONFIG_HPET_DRIFTFIX + if (hpet_driftfix && qemu_get_irq_delivered + && qemu_reset_irq_delivered) { + qemu_reset_irq_delivered(); + qemu_irq_raise(s->irqs[route]); + irq_delivered = qemu_get_irq_delivered(); + qemu_irq_lower(s->irqs[route]); + } + else +#endif qemu_irq_pulse(s->irqs[route]); } + return irq_delivered; } static void hpet_pre_save(void *opaque) @@ -246,7 +274,11 @@ static int hpet_post_load(void *opaque, static const VMStateDescription vmstate_hpet_timer = { .name = "hpet_timer", +#ifdef CONFIG_HPET_DRIFTFIX + .version_id = 3, +#else .version_id = 1, +#endif .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { @@ -256,6 +288,12 @@ static const VMStateDescription vmstate_ VMSTATE_UINT64(fsb, HPETTimer), VMSTATE_UINT64(period, HPETTimer), VMSTATE_UINT8(wrap_flag, HPETTimer), +#ifdef CONFIG_HPET_DRIFTFIX + VMSTATE_UINT64_V(saved_period, HPETTimer, 3), + VMSTATE_UINT32_V(irqs_to_inject, HPETTimer, 3), + VMSTATE_UINT32_V(irq_rate, HPETTimer, 3), + VMSTATE_UINT32_V(divisor, HPETTimer, 3), +#endif VMSTATE_TIMER(qemu_timer, HPETTimer), VMSTATE_END_OF_LIST() } @@ -263,7 +301,11 @@ static const VMStateDescription vmstate_ static const VMStateDescription vmstate_hpet = { .name = "hpet", +#ifdef CONFIG_HPET_DRIFTFIX + .version_id = 3, +#else .version_id = 2, +#endif .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_save = hpet_pre_save, @@ -287,7 +329,10 @@ static void hpet_timer(void *opaque) { HPETTimer *t = opaque; uint64_t diff; - +#ifdef CONFIG_HPET_DRIFTFIX + uint32_t irq_count = 0; + int irq_delivered = 0; +#endif uint64_t period = t->period; uint64_t cur_tick = hpet_get_ticks(t->state); @@ -295,13 +340,39 @@ static void hpet_timer(void *opaque) if (t->config & HPET_TN_32BIT) { while (hpet_time_after(cur_tick, t->cmp)) { t->cmp = (uint32_t)(t->cmp + t->period); +#ifdef CONFIG_HPET_DRIFTFIX + irq_count++; +#endif } } else { while (hpet_time_after64(cur_tick, t->cmp)) { t->cmp += period; +#ifdef CONFIG_HPET_DRIFTFIX + irq_count++; +#endif } } diff = hpet_calculate_diff(t, cur_tick); +#ifdef CONFIG_HPET_DRIFTFIX + if (hpet_driftfix) { + if (t->saved_period != t->period) { + t->irqs_to_inject = (t->irqs_to_inject * t->saved_period) + / t->period; + t->saved_period = t->period; + } + t->irqs_to_inject += irq_count; + t->irqs_to_inject = MIN(t->irqs_to_inject, MAX_IRQS_TO_INJECT); + if (t->irqs_to_inject > 1) { + if (irq_count > 1) { + t->irq_rate++; + t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE); + } + if (irq_count || t->divisor == 0) + t->divisor = t->irq_rate; + diff /= t->divisor--; + } + } +#endif qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff)); } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { @@ -312,6 +383,23 @@ static void hpet_timer(void *opaque) t->wrap_flag = 0; } } +#ifdef CONFIG_HPET_DRIFTFIX + if (hpet_driftfix && timer_is_periodic(t) && period != 0) { + if (t->irqs_to_inject) { + irq_delivered = update_irq(t, 1); + if (irq_delivered) { + t->irq_rate = MIN(t->irq_rate, t->irqs_to_inject); + t->irqs_to_inject--; + } else { + if (irq_count) { + t->irq_rate++; + t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE); + } + } + } + } + else +#endif update_irq(t, 1); } @@ -651,6 +739,12 @@ static void hpet_reset(DeviceState *d) timer->config |= 0x00000004ULL << 32; timer->period = 0ULL; timer->wrap_flag = 0; +#ifdef CONFIG_HPET_DRIFTFIX + timer->saved_period = 0; + timer->irqs_to_inject = 0; + timer->irq_rate = 1; + timer->divisor = 1; +#endif } s->hpet_counter = 0ULL; @@ -711,6 +805,12 @@ static int hpet_init(SysBusDevice *dev) timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer); timer->tn = i; timer->state = s; +#ifdef CONFIG_HPET_DRIFTFIX + timer->saved_period = 0; + timer->irqs_to_inject = 0; + timer->irq_rate = 1; + timer->divisor = 1; +#endif } /* 64-bit main counter; LegacyReplacementRoute. */ diff -up ./sysemu.h.orig2 ./sysemu.h --- ./sysemu.h.orig2 2011-02-18 22:48:06.000000000 +0100 +++ ./sysemu.h 2011-03-13 12:40:56.112991611 +0100 @@ -96,6 +96,11 @@ int qemu_savevm_state_complete(Monitor * void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); +#ifdef CONFIG_HPET_DRIFTFIX +extern int (*qemu_get_irq_delivered)(void); +extern void (*qemu_reset_irq_delivered)(void); +#endif + /* SLIRP */ void do_info_slirp(Monitor *mon); diff -up ./vl.c.orig2 ./vl.c --- ./vl.c.orig2 2011-03-13 12:38:35.167984285 +0100 +++ ./vl.c 2011-03-13 12:40:56.112991611 +0100 @@ -205,6 +205,8 @@ int win2k_install_hack = 0; int rtc_td_hack = 0; #ifdef CONFIG_HPET_DRIFTFIX int hpet_driftfix = 0; +int (*qemu_get_irq_delivered)(void) = 0; +void (*qemu_reset_irq_delivered)(void) = 0; #endif int usb_enabled = 0; int singlestep = 0; -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html