From patchwork Thu May 4 11:59:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Xiao Guangrong X-Patchwork-Id: 9711743 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 58A3260362 for ; Thu, 4 May 2017 12:00:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3B4042623C for ; Thu, 4 May 2017 12:00:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2FB4528689; Thu, 4 May 2017 12:00:18 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 87CC22623C for ; Thu, 4 May 2017 12:00:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753132AbdEDMAQ (ORCPT ); Thu, 4 May 2017 08:00:16 -0400 Received: from mail-pf0-f195.google.com ([209.85.192.195]:33709 "EHLO mail-pf0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752854AbdEDMAO (ORCPT ); Thu, 4 May 2017 08:00:14 -0400 Received: by mail-pf0-f195.google.com with SMTP id b23so1845716pfc.0 for ; Thu, 04 May 2017 05:00:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RWjwBdDiGPbzFIUTbkZ97AcjRH2V5AGgQAmd2aDE4s4=; b=AVdSobYlPDMusfVZt45Ni23SViuuTjKICM465/F8uas5RKWuYotf46Alr9Wym6FFAZ qgM9q+OIxwPHiBUyWaVGYOq72YuyMQ2SdlbJRUQBq6+QSaRl09ZsUNURU+o5IAb0OGQj wtjRXO711KwzT5ZUTRa0na521CyqmIAnqzej30vpBDg7dg/13DtCvXJGe6qH54CRK0b2 c2KQE5hAwRy2gq/RplrvTTOLzHEccgzWNfIxiFjtF2+LgdtlcV1O0RM4XfaYQFAm6FlA m4Rmj5GRouAaPpv92//yN/uyh2g/TS0ttbGR6I+d4w1NL5V4WebbjJhpTTemR510P0Tj VJdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RWjwBdDiGPbzFIUTbkZ97AcjRH2V5AGgQAmd2aDE4s4=; b=nd2K/qI/zQSsQvd/M0+nH0hQfCmekqpPLiEVD38d7W4VMf8UeqNAVcdX+nzltYCW6X YIvbg47FtbFlngDbq30C3vHWEQ2hS4rLgnxOx4kSUVC5pIy3hT/QgCQfHQj8OLhePK/1 6DXWItCAWjT4x4yszGle8gy8swuulOicIo5SvgTny+4amOh6i7wJJqLNQ5K61ygewvf4 XIlJ5fuhL5WF/Pz2HWl2C0RnV3uQHGfbGe4pxtMkZsNNwtlLUyNz8GRMPZGd2B8IWf/Z dYKxh2u2K4/TmHsbKNz/lhq82YTXC4YOz9jqpoZw9p81gmRjGU8nuZI0tVQqdK/mf4gJ BnTg== X-Gm-Message-State: AN3rC/516mgkmT2JVC/b7x+wO+i3juDuqi8WO26dBbz2Tg/kqe1gnK4Y oH2hMRCxgyLAkQ== X-Received: by 10.99.117.67 with SMTP id f3mr45361179pgn.51.1493899212977; Thu, 04 May 2017 05:00:12 -0700 (PDT) Received: from eric.tencent.com ([203.205.141.37]) by smtp.gmail.com with ESMTPSA id p7sm3698261pfb.125.2017.05.04.05.00.10 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 04 May 2017 05:00:12 -0700 (PDT) From: guangrong.xiao@gmail.com X-Google-Original-From: xiaoguangrong@tencent.com To: pbonzini@redhat.com, mst@redhat.com, mtosatti@redhat.com Cc: qemu-devel@nongnu.org, kvm@vger.kernel.org, yunfangtai@tencent.com, Xiao Guangrong Subject: [PATCH v2 2/5] mc146818rtc: precisely count the clock for periodic timer Date: Thu, 4 May 2017 19:59:45 +0800 Message-Id: <20170504115948.3048-3-xiaoguangrong@tencent.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170504115948.3048-1-xiaoguangrong@tencent.com> References: <20170504115948.3048-1-xiaoguangrong@tencent.com> MIME-Version: 1.0 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Tai Yunfang There are two issues in current code: 1) If the period is changed by re-configuring RegA, the coalesced irq will be scaled to reflect the new period, however, it calculates the new interrupt number like this: s->irq_coalesced = (s->irq_coalesced * s->period) / period; There are some clocks will be lost if they are not enough to be squeezed to a single new period that will cause the VM clock slower In order to fix the issue, we calculate the interrupt window based on the precise clock rather than period, then the clocks lost during period is scaled can be compensated properly 2) If periodic_timer_update() is called due to RegA reconfiguration, i.e, the period is updated, current time is not the start point for the next periodic timer, instead, which should start from the last interrupt, otherwise, the clock in VM will become slow This patch takes the clocks from last interrupt to current clock into account and compensates the clocks for the next interrupt, especially,if a complete interrupt was lost in this window, the time can be caught up by LOST_TICK_POLICY_SLEW [ Xiao: redesign the algorithm based on Yunfang's original work. ] Signed-off-by: Xiao Guangrong Signed-off-by: Tai Yunfang --- hw/timer/mc146818rtc.c | 116 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 20 deletions(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 5cccb2a..14bde1a 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -146,31 +146,104 @@ static void rtc_coalesced_timer(void *opaque) } #endif -/* handle periodic timer */ -static void periodic_timer_update(RTCState *s, int64_t current_time) +static int period_code_to_clock(int period_code) +{ + /* periodic timer is disabled. */ + if (!period_code) { + return 0; + } + + if (period_code <= 2) { + period_code += 7; + } + + /* period in 32 Khz cycles */ + return 1 << (period_code - 1); +} + +/* + * handle periodic timer. @old_period indicates the periodic timer update + * is just due to period adjustment. + */ +static void +periodic_timer_update(RTCState *s, int64_t current_time, int old_period) { int period_code, period; - int64_t cur_clock, next_irq_clock; + int64_t cur_clock, next_irq_clock, lost_clock = 0; period_code = s->cmos_data[RTC_REG_A] & 0x0f; if (period_code != 0 && (s->cmos_data[RTC_REG_B] & REG_B_PIE)) { - if (period_code <= 2) - period_code += 7; - /* period in 32 Khz cycles */ - period = 1 << (period_code - 1); -#ifdef TARGET_I386 - if (period != s->period) { - s->irq_coalesced = (s->irq_coalesced * s->period) / period; - DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced); - } - s->period = period; -#endif + period = period_code_to_clock(period_code); + /* compute 32 khz clock */ cur_clock = muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); - next_irq_clock = (cur_clock & ~(period - 1)) + period; + /* + * if the periodic timer's update is due to period re-configuration, + * we should count the clock since last interrupt. + */ + if (old_period) { + int64_t last_periodic_clock; + + last_periodic_clock = muldiv64(s->next_periodic_time, + RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + /* + * if the next interrupt has not happened yet, we recall the last + * interrupt based on the original period. + */ + if (last_periodic_clock > cur_clock) { + last_periodic_clock -= period_code_to_clock(old_period); + + /* the last interrupt must have happened. */ + assert(cur_clock >= last_periodic_clock); + } + + /* calculate the clock since last interrupt. */ + lost_clock = cur_clock - last_periodic_clock; + } + +#ifdef TARGET_I386 + /* + * recalculate the coalesced irqs for two reasons: + * a) the lost_clock is more that a period, i,e. the timer + * interrupt has been lost, we should catch up the time. + * + * b) the period may be reconfigured, under this case, when + * switching from a shorter to a longer period, scale down + * the missing ticks since we expect the OS handler to + * treat the delayed ticks as longer. Any leftovers are + * put back into lost_clock. + * When switching to a shorter period, scale up the missing + * ticks since we expect the OS handler to treat the delayed + * ticks as shorter. + */ + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + uint32_t cur_irq_coalesced = s->irq_coalesced; + uint32_t cur_period = s->period; + + lost_clock += cur_irq_coalesced * cur_period; + s->irq_coalesced = lost_clock / period; + lost_clock %= period; + s->period = period; + if ((cur_irq_coalesced != s->irq_coalesced) || + (cur_period |= s->period)) { + DPRINTF_C("cmos: coalesced irqs scaled from %d to %d, " + "period scaled from %d to %d\n", cur_irq_coalesced, + s->irq_coalesced, cur_period, s->period); + rtc_coalesced_timer_update(s); + } + } +#endif + /* + * no way to compensate the interrupt if LOST_TICK_POLICY_SLEW + * is not used, we should make the time progress anyway. + */ + lost_clock = MIN(lost_clock, period); + assert(lost_clock >= 0); + + next_irq_clock = cur_clock + period - lost_clock; s->next_periodic_time = muldiv64(next_irq_clock, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE) + 1; timer_mod(s->periodic_timer, s->next_periodic_time); @@ -186,7 +259,7 @@ static void rtc_periodic_timer(void *opaque) { RTCState *s = opaque; - periodic_timer_update(s, s->next_periodic_time); + periodic_timer_update(s, s->next_periodic_time, 0); s->cmos_data[RTC_REG_C] |= REG_C_PF; if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { s->cmos_data[RTC_REG_C] |= REG_C_IRQF; @@ -391,6 +464,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { RTCState *s = opaque; + int cur_period; bool update_periodic_timer; if ((addr & 1) == 0) { @@ -424,6 +498,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, } break; case RTC_REG_A: + cur_period = s->cmos_data[RTC_REG_A] & 0xf; update_periodic_timer = (s->cmos_data[RTC_REG_A] ^ data) & 0x0f; if ((data & 0x60) == 0x60) { @@ -450,7 +525,8 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, (s->cmos_data[RTC_REG_A] & REG_A_UIP); if (update_periodic_timer) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), + cur_period); } check_update_timer(s); @@ -487,7 +563,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, s->cmos_data[RTC_REG_B] = data; if (update_periodic_timer) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), 0); } check_update_timer(s); @@ -757,7 +833,7 @@ static int rtc_post_load(void *opaque, int version_id) uint64_t now = qemu_clock_get_ns(rtc_clock); if (now < s->next_periodic_time || now > (s->next_periodic_time + get_max_clock_jump())) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), 0); } } @@ -822,7 +898,7 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data) int64_t now = *(int64_t *)data; rtc_set_date_from_host(ISA_DEVICE(s)); - periodic_timer_update(s, now); + periodic_timer_update(s, now, 0); check_update_timer(s); #ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {