code part 2 ----------- Implements compensation of lost interrupts for HPET periodic timers. diff -up ./hw/hpet.c.orig2 ./hw/hpet.c --- ./hw/hpet.c.orig2 2011-01-21 23:34:47.000000000 +0100 +++ ./hw/hpet.c 2011-02-01 19:20:24.619247214 +0100 @@ -41,6 +41,13 @@ #define HPET_MSI_SUPPORT 0 +#ifdef TARGET_I386 +#define MAX_IRQS_TO_INJECT (uint32_t)100 +#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 +62,13 @@ 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 TARGET_I386 + /* for lost interrupt compensation */ + uint64_t saved_period; + uint32_t irqs_to_inject; + uint32_t irq_rate; + uint32_t divisor; +#endif } HPETTimer; typedef struct HPETState { @@ -248,7 +262,11 @@ static int hpet_post_load(void *opaque, static const VMStateDescription vmstate_hpet_timer = { .name = "hpet_timer", +#ifdef TARGET_I386 + .version_id = 3, +#else .version_id = 1, +#endif .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { @@ -258,6 +276,13 @@ static const VMStateDescription vmstate_ VMSTATE_UINT64(fsb, HPETTimer), VMSTATE_UINT64(period, HPETTimer), VMSTATE_UINT8(wrap_flag, HPETTimer), +#ifdef TARGET_I386 + /* for lost interrupt compensation */ + 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() } @@ -265,7 +290,11 @@ static const VMStateDescription vmstate_ static const VMStateDescription vmstate_hpet = { .name = "hpet", +#ifdef TARGET_I386 + .version_id = 3, +#else .version_id = 2, +#endif .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_save = hpet_pre_save, @@ -289,7 +318,9 @@ static void hpet_timer(void *opaque) { HPETTimer *t = opaque; uint64_t diff; - +#ifdef TARGET_I386 + uint32_t irq_count = 0; +#endif uint64_t period = t->period; uint64_t cur_tick = hpet_get_ticks(t->state); @@ -297,13 +328,88 @@ 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 TARGET_I386 + /* count one irq per period */ + ++irq_count; +#endif } } else { while (hpet_time_after64(cur_tick, t->cmp)) { t->cmp += period; +#ifdef TARGET_I386 + /* count one irq per period */ + ++irq_count; +#endif } } diff = hpet_calculate_diff(t, cur_tick); +#ifdef TARGET_I386 + if (hpet_driftfix) { + /* + * If the period value changed since the previous callback, + * scale the number of irqs to inject to the new period value + * and save the new period value. + */ + if (t->saved_period != t->period) { + t->irqs_to_inject = (t->irqs_to_inject * t->saved_period) + / t->period; + t->saved_period = t->period; + } + /* + * Add the irq count of the current callback to the number + * of irqs to inject. Make sure the result does not exceed + * the limit. + */ + t->irqs_to_inject += irq_count; + if (t->irqs_to_inject > MAX_IRQS_TO_INJECT) + t->irqs_to_inject = MAX_IRQS_TO_INJECT; + /* + * One irq will be injected during the current callback. If + * the number of irqs to inject is greater than 1, there is + * is a backlog of 'irqs_to_inject - 1' interrupts that were + * lost and need to be compensated. + */ + if (t->irqs_to_inject > 1) { + /* + * If the irq count of the current callback is greater + * than 1, 'irq_count - 1' interrupts were lost since + * the previous callback. Increase the rate at which + * additional irqs will be injected to compensate the + * lost interrupts. Make sure the rate does not exceed + * the limit. + */ + if (irq_count > 1) { + t->irq_rate++; + if (t->irq_rate > MAX_IRQ_RATE) + t->irq_rate = MAX_IRQ_RATE; + } + /* + * The irq count of the current callback is expected to + * be zero while additional irqs are injected between two + * regular timer interrupts. If the irq count is not zero + * this marks the start of the next timer period. Hence, + * re-initialize the divisor. + */ + if (irq_count) + t->divisor = t->irq_rate; + /* + * 'diff' contains the number of HPET ticks between the + * current callback and the start of the next timer period. + * Divide 'diff' to get additional callbacks. The goal of + * decrementing the divisor is to get additional callbacks + * at similar intervals. [Ideally, the intervals between + * additional callbacks should be very similar. However, + * this cannot be guaranteed because callbacks can occur + * delayed.] Make sure the divisor is not zero at the next + * callback. + */ + diff /= t->divisor; + t->divisor--; + if (t->divisor == 0) + t->divisor = t->irq_rate; + } + } +#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)) { @@ -315,6 +421,18 @@ static void hpet_timer(void *opaque) } } update_irq(t, 1); +#ifdef TARGET_I386 + if (hpet_driftfix) { + /* + * Re-initialize the rate and the divisor if the backlog + * of lost interrupts has been compensated. + */ + if (t->irqs_to_inject && --t->irqs_to_inject == 0) { + t->irq_rate = 1; + t->divisor = 1; + } + } +#endif } static void hpet_set_timer(HPETTimer *t) @@ -653,6 +771,13 @@ static void hpet_reset(DeviceState *d) timer->config |= 0x00000004ULL << 32; timer->period = 0ULL; timer->wrap_flag = 0; +#ifdef TARGET_I386 + /* for lost interrupt compensation */ + timer->saved_period = 0; + timer->irqs_to_inject = 0; + timer->irq_rate = 1; + timer->divisor = 1; +#endif } s->hpet_counter = 0ULL; @@ -713,6 +838,13 @@ static int hpet_init(SysBusDevice *dev) timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer); timer->tn = i; timer->state = s; +#ifdef TARGET_I386 + /* for lost interrupt compensation */ + timer->saved_period = 0; + timer->irqs_to_inject = 0; + timer->irq_rate = 1; + timer->divisor = 1; +#endif } /* 64-bit main counter; LegacyReplacementRoute. */ -- 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