From patchwork Fri May 11 02:27:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Samuel Holland X-Patchwork-Id: 911673 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=sholland.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="IBGamusT"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=sholland.org header.i=@sholland.org header.b="oI9SesDo"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="iDsJnamk"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40hvFQ6280z9s19 for ; Fri, 11 May 2018 12:30:02 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=05tOyzVthoq2ewBYnIXatSBqqxbDYpMxHfgcrqfz9OQ=; b=IBGamusTF4M7JQ B0tZX26VXjteHtanO/1xNor1BnM2kDx+rQjMxqllRJjXQOc/cZINzWgJjc4vKy6FirkoSTNAWJuBH TbTi2Yo4o84ASJZjg8Fm9dkG6cKUjEsD/4d0+2zhJXS/ErjxakMCPeefFApTFcmdd8LuoI6cXdHlr AYK4qjBfCE7JilopY05ZJOv0oKpUQ3tS94dIjwPflHkoyMZCJXvLZ68+IwPLLowxGj+htZ9ckZORa eNYN4LAX4DVWyRvAMnpyTvWAVbHsq0+obWCSnB6P0uOo+L9weqhndOcLXmLkH+JxXHZIDVtUZkD+d I2aoeP+KjYRD+L/s6/+Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fGxoh-0001vx-Tj; Fri, 11 May 2018 02:29:56 +0000 Received: from out1-smtp.messagingengine.com ([66.111.4.25]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fGxn6-0001IC-JX for linux-arm-kernel@lists.infradead.org; Fri, 11 May 2018 02:28:20 +0000 Received: from compute5.internal (compute5.nyi.internal [10.202.2.45]) by mailout.nyi.internal (Postfix) with ESMTP id 134EE22657; Thu, 10 May 2018 22:28:02 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute5.internal (MEProxy); Thu, 10 May 2018 22:28:02 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sholland.org; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-sender :x-me-sender:x-sasl-enc; s=fm2; bh=eeOFfr0mO/DB7NatQYPp1Rrciw8KU q06c7D0aPH6wS0=; b=oI9SesDonognxzBfLuc9nrknXNJt8OQos4Lo2159bTXN4 T4XZZ9Q/m1IFyfBGox5G8b108UPi9BhgML6ZkdzK3vwMt/uH06GYhPQzHVeoMPye B2CLOP1S0dM84aq/y5hGSuy2yjZofBq/7CEt7ndP+6J1pqrt4Ck/QBN2DNWBt4Th rRCFOcNplCrGAJjqyv7PAopmHqvAHhYEx4zHATqLEIx6khyFsYLjfZQ3bAqHFuZo HFeN/UR2Ev3giSeSyl+Gh0PS2tbRAza8QiSgOGlGL/9AXxhD8kr5PKu5CyI6cl6v MIjUpM8VKnikmSSn46K8SGEy6SzlgpGZbNsNIjJ8w== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :date:from:in-reply-to:message-id:mime-version:references :subject:to:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; bh=eeOFfr 0mO/DB7NatQYPp1Rrciw8KUq06c7D0aPH6wS0=; b=iDsJnamk7Upgb6gGdOoUal k8+Taz6/ZHYLV7gUa5wywKcvVqxv2+gs6Y4Ti1YldMTJ37pOaDLn21hFIrNGAn8l cTuv5OocR85UKi37w78eurnSJAGerZqKO3AW6096SmLDLqfPRtEb/3PnIFmtzy/V evtlrET2AT+9IKUmDgu5YTUlw23bu1jp3poHyqgWoUPnUVvkUIhCn98MpBQY29zi nf2KzEpS8bHqXWSw+IR9KgDe5kXUI7qc5a8ODQRwNg0fEhZrqwF1KMOfywW+s0+N i6vuJpiPMZraGRAOIXDtURNkg3SFnZOGLpXpaeOUHoMu0yVI0BdfBrrYqhtliLmA == X-ME-Sender: Received: from sodium.col.sholland.net (unknown [99.198.199.144]) by mail.messagingengine.com (Postfix) with ESMTPA id C564DE479F; Thu, 10 May 2018 22:28:00 -0400 (EDT) From: Samuel Holland To: Maxime Ripard , Chen-Yu Tsai , Catalin Marinas , Will Deacon , Daniel Lezcano , Thomas Gleixner , Marc Zyngier Subject: [PATCH 1/2] arm64: arch_timer: Workaround for Allwinner A64 timer instability Date: Thu, 10 May 2018 21:27:50 -0500 Message-Id: <20180511022751.9096-2-samuel@sholland.org> X-Mailer: git-send-email 2.16.1 In-Reply-To: <20180511022751.9096-1-samuel@sholland.org> References: <20180511022751.9096-1-samuel@sholland.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180510_192816_826877_7F51FD84 X-CRM114-Status: GOOD ( 14.75 ) X-Spam-Score: -0.8 (/) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-0.8 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [66.111.4.25 listed in list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-sunxi@googlegroups.com, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Samuel Holland Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org The Allwinner A64 SoC is known [1] to have an unstable architectural timer, which manifests itself most obviously in the time jumping forward a multiple of 95 years [2][3]. This coincides with 2^56 cycles at a timer frequency of 24 MHz, implying that the time went slightly backward (and this was interpreted by the kernel as it jumping forward and wrapping around past the epoch). Further investigation revealed instability in the low bits of CNTVCT at the point a high bit rolls over. This leads to power-of-two cycle forward and backward jumps. (Testing shows that forward jumps are about twice as likely as backward jumps.) Without trapping reads to CNTVCT, a userspace program is able to read it in a loop faster than it changes. A test program running on all 4 CPU cores that reported jumps larger than 100 ms was run for 13.6 hours and reported the following: Count | Event -------+--------------------------- 9940 | jumped backward 699ms 268 | jumped backward 1398ms 1 | jumped backward 2097ms 16020 | jumped forward 175ms 6443 | jumped forward 699ms 2976 | jumped forward 1398ms 9 | jumped forward 356516ms 9 | jumped forward 357215ms 4 | jumped forward 714430ms 1 | jumped forward 3578440ms This works out to a jump larger than 100 ms about every 5.5 seconds on each CPU core. The largest jump (almost an hour!) was the following sequence of reads: 0x0000007fffffffff → 0x00000093feffffff → 0x0000008000000000 Note that the middle bits don't necessarily all read as all zeroes or all ones during the anomalous behavior; however the low 11 bits checked by the function in this patch have never been observed with any other value. Also note that smaller jumps are much more common, with the smallest backward jumps of 2048 cycles observed over 400 times per second on each core. (Of course, this is partially due to lower bits rolling over more frequently.) Any one of these could have caused the 95 year time skip. Similar anomalies were observed while reading CNTPCT (after patching the kernel to allow reads from userspace). However, the jumps are much less frequent, and only small jumps were observed. The same program as before (except now reading CNTPCT) observed after 72 hours: Count | Event -------+--------------------------- 17 | jumped backward 699ms 52 | jumped forward 175ms 2831 | jumped forward 699ms 5 | jumped forward 1398ms Acked-by: Maxime Ripard Tested-by: Andre Przywara ======================================================================== Because the CPU can read the CNTPCT/CNTVCT registers faster than they change, performing two reads of the register and comparing the high bits (like other workarounds) is not a workable solution. And because the timer can jump both forward and backward, no pair of reads can distinguish a good value from a bad one. The only way to guarantee a good value from consecutive reads would be to read _three_ times, and take the middle value iff the three values are 1) individually unique and 2) increasing. This takes at minimum 3 cycles (125 ns), or more if an anomaly is detected. However, since there is a distinct pattern to the bad values, we can optimize the common case (2046/2048 of the time) to a single read by simply ignoring values that match the pattern. This still takes no more than 3 cycles in the worst case, and requires much less code. [1]: https://github.com/armbian/build/commit/a08cd6fe7ae9 [2]: https://forum.armbian.com/topic/3458-a64-datetime-clock-issue/ [3]: https://irclog.whitequark.org/linux-sunxi/2018-01-26 Signed-off-by: Samuel Holland --- drivers/clocksource/Kconfig | 11 ++++++++++ drivers/clocksource/arm_arch_timer.c | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 8e8a09755d10..7a5d434dd30b 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -364,6 +364,17 @@ config ARM64_ERRATUM_858921 The workaround will be dynamically enabled when an affected core is detected. +config SUN50I_A64_UNSTABLE_TIMER + bool "Workaround for Allwinner A64 timer instability" + default y + depends on ARM_ARCH_TIMER && ARM64 && ARCH_SUNXI + select ARM_ARCH_TIMER_OOL_WORKAROUND + help + This option enables a workaround for instability in the timer on + the Allwinner A64 SoC. The workaround will only be active if the + allwinner,sun50i-a64-unstable-timer property is found in the + timer node. + config ARM_GLOBAL_TIMER bool "Support for the ARM global timer" if COMPILE_TEST select TIMER_OF if OF diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 57cb2f00fc07..66ce13578c52 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -319,6 +319,36 @@ static u64 notrace arm64_858921_read_cntvct_el0(void) } #endif +#ifdef CONFIG_SUN50I_A64_UNSTABLE_TIMER +/* + * The low bits of each register can transiently read as all ones or all zeroes + * when bit 11 or greater rolls over. Since the value can jump both backward + * (7ff -> 000 -> 800) and forward (7ff -> fff -> 800), it is simplest to just + * ignore register values with all ones or zeros in the low bits. + */ +static u64 notrace sun50i_a64_read_cntpct_el0(void) +{ + u64 val; + + do { + val = read_sysreg(cntpct_el0); + } while (((val + 1) & GENMASK(10, 0)) <= 1); + + return val; +} + +static u64 notrace sun50i_a64_read_cntvct_el0(void) +{ + u64 val; + + do { + val = read_sysreg(cntvct_el0); + } while (((val + 1) & GENMASK(10, 0)) <= 1); + + return val; +} +#endif + #ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround); EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround); @@ -408,6 +438,15 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = { .read_cntvct_el0 = arm64_858921_read_cntvct_el0, }, #endif +#ifdef CONFIG_SUN50I_A64_UNSTABLE_TIMER + { + .match_type = ate_match_dt, + .id = "allwinner,sun50i-a64-unstable-timer", + .desc = "Allwinner A64 timer instability", + .read_cntpct_el0 = sun50i_a64_read_cntpct_el0, + .read_cntvct_el0 = sun50i_a64_read_cntvct_el0, + }, +#endif }; typedef bool (*ate_match_fn_t)(const struct arch_timer_erratum_workaround *,