From patchwork Mon May 9 07:03:21 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulrich Obergfell X-Patchwork-Id: 768422 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p4973ZpQ015908 for ; Mon, 9 May 2011 07:03:35 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752838Ab1EIHDc (ORCPT ); Mon, 9 May 2011 03:03:32 -0400 Received: from mx1.redhat.com ([209.132.183.28]:21100 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752422Ab1EIHDa (ORCPT ); Mon, 9 May 2011 03:03:30 -0400 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p4973HuR013199 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 9 May 2011 03:03:17 -0400 Received: from localhost.localdomain (vpn2-9-235.ams2.redhat.com [10.36.9.235]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p49732Hm030518; Mon, 9 May 2011 03:03:15 -0400 From: Ulrich Obergfell To: qemu-devel@nongnu.org Cc: kvm@vger.kernel.org, uobergfe@redhat.com, gcosta@redhat.com, avi@redhat.com, mtosatti@redhat.com, aliguori@us.ibm.com, jan.kiszka@siemens.com Subject: [PATCH v4 5/5] hpet 'driftfix': add code in hpet_timer() to compensate delayed callbacks and coalesced interrupts Date: Mon, 9 May 2011 09:03:21 +0200 Message-Id: <1304924601-3848-6-git-send-email-uobergfe@redhat.com> In-Reply-To: <1304924601-3848-1-git-send-email-uobergfe@redhat.com> References: <1304924601-3848-1-git-send-email-uobergfe@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Mon, 09 May 2011 07:03:35 +0000 (UTC) Loss of periodic timer interrupts caused by delayed callbacks and by interrupt coalescing is compensated by gradually injecting additional interrupts during subsequent timer intervals, starting at a rate of one additional interrupt per interval. The injection of additional interrupts is based on a backlog of unaccounted HPET clock periods (new HPETTimer field 'ticks_not_accounted'). The backlog increases due to delayed callbacks and coalesced interrupts, and it decreases if an interrupt was injected successfully. If the backlog increases while compensation is still in progress, the rate at which additional interrupts are injected is increased too. A limit is imposed on the backlog and on the rate. Injecting additional timer interrupts to compensate lost interrupts can alleviate long term time drift. However, on a short time scale, this method can have the side effect of making virtual machine time intermittently pass slower and faster than real time (depending on the guest's time keeping algorithm). Compensation is disabled by default and can be enabled for guests where this behaviour may be acceptable. Signed-off-by: Ulrich Obergfell --- hw/hpet.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 68 insertions(+), 2 deletions(-) diff --git a/hw/hpet.c b/hw/hpet.c index e57c654..519fc6b 100644 --- a/hw/hpet.c +++ b/hw/hpet.c @@ -31,6 +31,7 @@ #include "hpet_emul.h" #include "sysbus.h" #include "mc146818rtc.h" +#include //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -41,6 +42,9 @@ #define HPET_MSI_SUPPORT 0 +#define MAX_TICKS_NOT_ACCOUNTED (uint64_t)500000000 /* 5 sec */ +#define MAX_IRQ_RATE (uint32_t)10 + struct HPETState; typedef struct HPETTimer { /* timers */ uint8_t tn; /*timer number*/ @@ -324,14 +328,35 @@ static const VMStateDescription vmstate_hpet = { } }; +static void hpet_timer_driftfix_reset(HPETTimer *t) +{ + if (t->state->driftfix && timer_is_periodic(t)) { + t->ticks_not_accounted = t->prev_period = t->period; + t->irq_rate = 1; + t->divisor = 1; + } +} + +static bool hpet_timer_has_tick_backlog(HPETTimer *t) +{ + uint64_t backlog = 0; + + if (t->ticks_not_accounted >= t->period + t->prev_period) { + backlog = t->ticks_not_accounted - (t->period + t->prev_period); + } + return (backlog >= t->period); +} + /* * timer expiration callback */ static void hpet_timer(void *opaque) { HPETTimer *t = opaque; + HPETState *s = t->state; uint64_t diff; - + int irq_delivered = 0; + uint32_t period_count = 0; uint64_t period = t->period; uint64_t cur_tick = hpet_get_ticks(t->state); @@ -339,13 +364,37 @@ 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); + t->ticks_not_accounted += t->period; + period_count++; } } else { while (hpet_time_after64(cur_tick, t->cmp)) { t->cmp += period; + t->ticks_not_accounted += period; + period_count++; } } diff = hpet_calculate_diff(t, cur_tick); + if (s->driftfix) { + if (t->ticks_not_accounted > MAX_TICKS_NOT_ACCOUNTED) { + t->ticks_not_accounted = t->period + t->prev_period; + } + if (hpet_timer_has_tick_backlog(t)) { + if (t->irq_rate == 1 || period_count > 1) { + t->irq_rate++; + t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE); + } + if (t->divisor == 0) { + assert(period_count); + } + if (period_count) { + t->divisor = t->irq_rate; + } + diff /= t->divisor--; + } else { + t->irq_rate = 1; + } + } qemu_mod_timer(t->qemu_timer, qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff)); } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { @@ -356,7 +405,22 @@ static void hpet_timer(void *opaque) t->wrap_flag = 0; } } - update_irq(t, 1); + if (s->driftfix && timer_is_periodic(t) && period != 0) { + if (t->ticks_not_accounted >= t->period + t->prev_period) { + irq_delivered = update_irq(t, 1); + if (irq_delivered) { + t->ticks_not_accounted -= t->prev_period; + t->prev_period = t->period; + } else { + if (period_count) { + t->irq_rate++; + t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE); + } + } + } + } else { + update_irq(t, 1); + } } static void hpet_set_timer(HPETTimer *t) @@ -525,6 +589,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, timer->period = (uint32_t)timer->period; } if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) { + hpet_timer_driftfix_reset(timer); hpet_set_timer(timer); } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { hpet_del_timer(timer); @@ -599,6 +664,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock); for (i = 0; i < s->num_timers; i++) { if ((&s->timer[i])->cmp != ~0ULL) { + hpet_timer_driftfix_reset(&s->timer[i]); hpet_set_timer(&s->timer[i]); } }