Module Name: src Committed By: jmcneill Date: Sun Sep 16 13:21:36 UTC 2018
Modified Files: src/sys/arch/arm/cortex: gtmr.c gtmr_var.h src/sys/arch/arm/sunxi: sunxi_platform.c Log Message: port-evbarm/53308: evbarm-earmv7hf performance regression under qemu Revert gtmr.c r1.27 and apply a workaround for Allwinner A64 SoCs based on analysis of the issue from LKML: https://lkml.org/lkml/2018/5/10/774 Since this bug is specific to the Allwinner A64 SoC, only apply the workaround when the root ("/") node of the device tree is compatible with "allwinner,sun50i-a64". To generate a diff of this commit: cvs rdiff -u -r1.34 -r1.35 src/sys/arch/arm/cortex/gtmr.c cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/cortex/gtmr_var.h cvs rdiff -u -r1.26 -r1.27 src/sys/arch/arm/sunxi/sunxi_platform.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/cortex/gtmr.c diff -u src/sys/arch/arm/cortex/gtmr.c:1.34 src/sys/arch/arm/cortex/gtmr.c:1.35 --- src/sys/arch/arm/cortex/gtmr.c:1.34 Mon Sep 10 10:55:02 2018 +++ src/sys/arch/arm/cortex/gtmr.c Sun Sep 16 13:21:36 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: gtmr.c,v 1.34 2018/09/10 10:55:02 skrll Exp $ */ +/* $NetBSD: gtmr.c,v 1.35 2018/09/16 13:21:36 jmcneill Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: gtmr.c,v 1.34 2018/09/10 10:55:02 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gtmr.c,v 1.35 2018/09/16 13:21:36 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -50,48 +50,6 @@ __KERNEL_RCSID(0, "$NetBSD: gtmr.c,v 1.3 #include <arm/cortex/gtmr_var.h> #include <arm/cortex/mpcore_var.h> -#define stable_write(reg) \ -static struct evcnt reg ## _write_ev; \ -static void \ -reg ## _stable_write(struct gtmr_softc *sc, uint64_t val) \ -{ \ - int retry; \ - reg ## _write(val); \ - retry = 0; \ - while (reg ## _read() != (val) && retry++ < 200) \ - reg ## _write(val); \ - if (retry > reg ## _write_ev.ev_count) { \ - reg ## _write_ev.ev_count = retry; \ - } \ -} - -stable_write(gtmr_cntv_tval); - -#define stable_read(reg) \ -static struct evcnt reg ## _read_ev; \ -static uint64_t \ -reg ## _stable_read(struct gtmr_softc *sc) \ -{ \ - uint64_t oval, val; \ - int retry = 0; \ - val = reg ## _read(); \ - while (++retry < 200) { \ - oval = val; \ - val = reg ## _read(); \ - if (val == oval) \ - break; \ - } \ - if (retry > reg ## _read_ev.ev_count) { \ - reg ## _read_ev.ev_count = retry; \ - } \ - return val; \ -} - -#ifdef DIAGNOSTIC -stable_read(gtmr_cntv_cval); -#endif -stable_read(gtmr_cntvct); - static int gtmr_match(device_t, cfdata_t, void *); static void gtmr_attach(device_t, device_t, void *); @@ -142,6 +100,7 @@ gtmr_attach(device_t parent, device_t se struct gtmr_softc *sc = >mr_sc; prop_dictionary_t dict = device_properties(self); char freqbuf[sizeof("X.XXX SHz")]; + bool flag; /* * This runs at a fixed frequency of 1 to 50MHz. @@ -156,6 +115,11 @@ gtmr_attach(device_t parent, device_t se aprint_naive("\n"); aprint_normal(": ARM Generic Timer (%s)\n", freqbuf); + if (prop_dictionary_get_bool(dict, "sun50i-a64-unstable-timer", &flag) && flag) { + sc->sc_flags |= GTMR_FLAG_SUN50I_A64_UNSTABLE_TIMER; + aprint_debug_dev(self, "enabling Allwinner A64 timer workaround\n"); + } + /* * Enable the virtual counter to be accessed from usermode. */ @@ -172,15 +136,6 @@ gtmr_attach(device_t parent, device_t se evcnt_attach_dynamic(&sc->sc_ev_missing_ticks, EVCNT_TYPE_MISC, NULL, device_xname(self), "missing interrupts"); - evcnt_attach_dynamic(>mr_cntv_tval_write_ev, EVCNT_TYPE_MISC, NULL, - device_xname(self), "CNTV_TVAL write retry max"); -#ifdef DIAGNOSTIC - evcnt_attach_dynamic(>mr_cntv_cval_read_ev, EVCNT_TYPE_MISC, NULL, - device_xname(self), "CNTV_CVAL read retry max"); -#endif - evcnt_attach_dynamic(>mr_cntvct_read_ev, EVCNT_TYPE_MISC, NULL, - device_xname(self), "CNTVCT read retry max"); - if (mpcaa->mpcaa_irq != -1) { sc->sc_global_ih = intr_establish(mpcaa->mpcaa_irq, IPL_CLOCK, IST_LEVEL | IST_MPSAFE, gtmr_intr, NULL); @@ -209,6 +164,27 @@ gtmr_attach(device_t parent, device_t se gtmr_cntv_ctl_write(0); } +static uint64_t +gtmr_read_cntvct(struct gtmr_softc *sc) +{ + if (ISSET(sc->sc_flags, GTMR_FLAG_SUN50I_A64_UNSTABLE_TIMER)) { + /* + * The Allwinner A64 SoC has an unstable architectural timer. + * To workaround this problem, ignore reads where the lower + * 11 bits are all 0s or 1s. + */ + uint64_t val; + u_int bits; + do { + val = gtmr_cntvct_read(); + bits = val & __BITS(10,0); + } while (bits == 0 || bits == __BITS(10,0)); + return val; + } + + return gtmr_cntvct_read(); +} + void gtmr_init_cpu_clock(struct cpu_info *ci) { @@ -227,10 +203,10 @@ gtmr_init_cpu_clock(struct cpu_info *ci) * Get now and update the compare timer. */ arm_isb(); - ci->ci_lastintr = gtmr_cntvct_stable_read(sc); - gtmr_cntv_tval_stable_write(sc, sc->sc_autoinc); + ci->ci_lastintr = gtmr_read_cntvct(sc); + gtmr_cntv_tval_write(sc->sc_autoinc); splx(s); - KASSERT(gtmr_cntvct_read() != 0); + KASSERT(gtmr_read_cntvct(sc) != 0); } void @@ -260,11 +236,11 @@ gtmr_delay(unsigned int n) int64_t ticks = (int64_t)n * incr_per_us; arm_isb(); - uint64_t last = gtmr_cntvct_stable_read(sc); + uint64_t last = gtmr_read_cntvct(sc); while (ticks > 0) { arm_isb(); - uint64_t curr = gtmr_cntvct_stable_read(sc); + uint64_t curr = gtmr_read_cntvct(sc); if (curr >= last) ticks -= (curr - last); else @@ -291,11 +267,11 @@ gtmr_intr(void *arg) if ((ctl & CNTCTL_ISTATUS) == 0) return 0; - const uint64_t now = gtmr_cntvct_stable_read(sc); + const uint64_t now = gtmr_read_cntvct(sc); uint64_t delta = now - ci->ci_lastintr; #ifdef DIAGNOSTIC - const uint64_t then = gtmr_cntv_cval_stable_read(sc); + const uint64_t then = gtmr_cntv_cval_read(); struct gtmr_percpu * const pc = percpu_getref(sc->sc_percpu); KASSERTMSG(then <= now, "%"PRId64, now - then); KASSERTMSG(then + pc->pc_delta >= ci->ci_lastintr + sc->sc_autoinc, @@ -316,7 +292,7 @@ gtmr_intr(void *arg) } else { delta = 0; } - gtmr_cntv_tval_stable_write(sc, sc->sc_autoinc - delta); + gtmr_cntv_tval_write(sc->sc_autoinc - delta); ci->ci_lastintr = now; @@ -343,5 +319,5 @@ gtmr_get_timecount(struct timecounter *t { struct gtmr_softc * const sc = tc->tc_priv; arm_isb(); // we want the time NOW, not some instructions later. - return (u_int) gtmr_cntvct_stable_read(sc); + return (u_int) gtmr_read_cntvct(sc); } Index: src/sys/arch/arm/cortex/gtmr_var.h diff -u src/sys/arch/arm/cortex/gtmr_var.h:1.10 src/sys/arch/arm/cortex/gtmr_var.h:1.11 --- src/sys/arch/arm/cortex/gtmr_var.h:1.10 Mon May 14 17:09:41 2018 +++ src/sys/arch/arm/cortex/gtmr_var.h Sun Sep 16 13:21:36 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: gtmr_var.h,v 1.10 2018/05/14 17:09:41 joerg Exp $ */ +/* $NetBSD: gtmr_var.h,v 1.11 2018/09/16 13:21:36 jmcneill Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. * All rights reserved. @@ -37,6 +37,8 @@ struct gtmr_softc { device_t sc_dev; struct evcnt sc_ev_missing_ticks; uint32_t sc_freq; + uint32_t sc_flags; +#define GTMR_FLAG_SUN50I_A64_UNSTABLE_TIMER __BIT(0) u_long sc_autoinc; void *sc_global_ih; #ifdef DIAGNOSTIC Index: src/sys/arch/arm/sunxi/sunxi_platform.c diff -u src/sys/arch/arm/sunxi/sunxi_platform.c:1.26 src/sys/arch/arm/sunxi/sunxi_platform.c:1.27 --- src/sys/arch/arm/sunxi/sunxi_platform.c:1.26 Mon Sep 10 11:05:12 2018 +++ src/sys/arch/arm/sunxi/sunxi_platform.c Sun Sep 16 13:21:36 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_platform.c,v 1.26 2018/09/10 11:05:12 ryo Exp $ */ +/* $NetBSD: sunxi_platform.c,v 1.27 2018/09/16 13:21:36 jmcneill Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -31,7 +31,7 @@ #include "opt_fdt_arm.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_platform.c,v 1.26 2018/09/10 11:05:12 ryo Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_platform.c,v 1.27 2018/09/16 13:21:36 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -165,6 +165,17 @@ sunxi_platform_device_register(device_t prop_dictionary_set_bool(prop, "no-rx-delay", true); } } + + if (device_is_a(self, "armgtmr")) { + /* Allwinner A64 has an unstable architectural timer */ + const char * compat[] = { + "allwinner,sun50i-a64", + NULL + }; + if (of_match_compatible(OF_finddevice("/"), compat)) { + prop_dictionary_set_bool(prop, "sun50i-a64-unstable-timer", true); + } + } } static u_int