From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LenS3-0005vG-ED for qemu-devel@nongnu.org; Wed, 04 Mar 2009 04:31:43 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LenS2-0005v3-10 for qemu-devel@nongnu.org; Wed, 04 Mar 2009 04:31:42 -0500 Received: from [199.232.76.173] (port=57648 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LenS1-0005v0-RW for qemu-devel@nongnu.org; Wed, 04 Mar 2009 04:31:41 -0500 Received: from main.gmane.org ([80.91.229.2]:59890 helo=ciao.gmane.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1LenS0-000759-TY for qemu-devel@nongnu.org; Wed, 04 Mar 2009 04:31:41 -0500 Received: from list by ciao.gmane.org with local (Exim 4.43) id 1LenRw-00065P-Gg for qemu-devel@nongnu.org; Wed, 04 Mar 2009 09:31:36 +0000 Received: from h86-62-88-129.ln.rinet.ru ([86.62.88.129]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Wed, 04 Mar 2009 09:31:36 +0000 Received: from vladimir by h86-62-88-129.ln.rinet.ru with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Wed, 04 Mar 2009 09:31:36 +0000 From: Vladimir Prus Date: Wed, 04 Mar 2009 12:31:29 +0300 Message-ID: References: <200902121733.43146.vladimir@codesourcery.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart1321324.NzAIMV2VAF" Content-Transfer-Encoding: 7Bit Sender: news Subject: [Qemu-devel] Re: Fix ptimer_get_count overflow. Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --nextPart1321324.NzAIMV2VAF Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8Bit Vladimir Prus wrote: > > At present, ptimers have a slight inaccuracy that can lead to > overflow. The period is stored as 64.32 fixed point number. > ptimer_reload uses both integer and fractional part of the perion > when computing when the next event should happen. ptimer_get_count, > however, uses only integer part of the period. Therefore, when > ptimer_get_count is called very soon after ptimer_reload the value > returned may be greater than the value ptimer_reload has set. > And if the counter was 0xFFFFFFFF, this may result in overflow. > > This was observed in SH4A emulation as "time jumps", where system > time gets magically increased by 10 minutes sometimes. > > This patch fixes this. The math is not the most accurate possible, > but it's relatively fast and fixes the problems. Ping? - Volodya --nextPart1321324.NzAIMV2VAF Content-Type: text/x-diff; name="primer_overflow.diff" Content-Transfer-Encoding: 8Bit Content-Disposition: attachment; filename="primer_overflow.diff" commit f1b78f0ca2b7fe12cdaabcb97c08484d3c26b99f Author: Vladimir Prus Date: Thu Dec 25 17:20:38 2008 +0300 Fix ptimer_get_count overflow. * hw/ptimer.c (ptimer_get_count): Don't ignore fractional part of period. diff --git a/hw/ptimer.c b/hw/ptimer.c index 9d38627..9278f83 100644 --- a/hw/ptimer.c +++ b/hw/ptimer.c @@ -7,7 +7,7 @@ */ #include "hw.h" #include "qemu-timer.h" - +#include "host-utils.h" struct ptimer_state { @@ -78,10 +78,41 @@ uint64_t ptimer_get_count(ptimer_state *s) } else { uint64_t rem; uint64_t div; + uint32_t frac; + int clz1, clz2; + int shift; + + /* We need to divide time by period, where time is stored in + rem (64-bit integer) and period is stored in period/period_frac + (64.32 fixed point). The result should be rounded down, so that + we never get the value greater than the timer's limit. + + We normalize both numerator and demoninator by left-shifting + until one of them has non-zero MSB, and then do 64-bit division. + */ rem = s->next_event - now; div = s->period; - counter = rem / div; + + clz1 = clz64(rem); + clz2 = clz64(div); + shift = clz1 < clz2 ? clz1 : clz2; + + rem <<= shift; + div <<= shift; + if (shift <= 32) + div |= (unsigned long long)( + (s->period_frac >> (32 - shift)) & ((1ull << shift) - 1)); + else + div |= (s->period_frac << (shift - 32)); + + /* Look at remaining bits of period_frac and round div up if + necessary. */ + frac = s->period_frac << shift; + if (frac) + div += 1; + + counter = rem/div; } } else { counter = s->delta; --nextPart1321324.NzAIMV2VAF--