Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch - Recalibrate the APIC timer using the TSC, once th...



details:   https://anonhg.NetBSD.org/src/rev/8cbd0ad5a665
branches:  trunk
changeset: 933199:8cbd0ad5a665
user:      ad <ad%NetBSD.org@localhost>
date:      Thu May 21 21:12:30 2020 +0000

description:
- Recalibrate the APIC timer using the TSC, once the TSC has in turn been
  recalibrated using the HPET.  This gets the clock interrupt firing more
  closely to HZ.

- Undo change with recent Xen merge and go back to starting the clocks in
  initclocks() on the boot CPU, and in cpu_hatch() on secondary CPUs.

- On reflection don't use HPET delay any more, it works very well but means
  going over the bus.  It's enough to use HPET to calibrate the TSC and
  APIC.

Tested on amd64 native, xen and xen PVH.

diffstat:

 sys/arch/x86/acpi/acpi_wakeup.c  |    8 +-
 sys/arch/x86/include/i82489var.h |    6 +-
 sys/arch/x86/x86/cpu.c           |   22 ++-----
 sys/arch/x86/x86/lapic.c         |  117 +++++++++++++++++++++++++-------------
 sys/arch/x86/x86/x86_machdep.c   |   18 +++---
 sys/arch/xen/x86/cpu.c           |    8 +-
 sys/arch/xen/xen/hypervisor.c    |    6 +-
 sys/arch/xen/xen/xen_clock.c     |   19 +----
 8 files changed, 108 insertions(+), 96 deletions(-)

diffs (truncated from 503 to 300 lines):

diff -r 84d4d9767dca -r 8cbd0ad5a665 sys/arch/x86/acpi/acpi_wakeup.c
--- a/sys/arch/x86/acpi/acpi_wakeup.c   Thu May 21 20:43:23 2020 +0000
+++ b/sys/arch/x86/acpi/acpi_wakeup.c   Thu May 21 21:12:30 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: acpi_wakeup.c,v 1.52 2020/02/22 19:49:11 chs Exp $     */
+/*     $NetBSD: acpi_wakeup.c,v 1.53 2020/05/21 21:12:30 ad Exp $      */
 
 /*-
  * Copyright (c) 2002, 2011 The NetBSD Foundation, Inc.
@@ -59,7 +59,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi_wakeup.c,v 1.52 2020/02/22 19:49:11 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi_wakeup.c,v 1.53 2020/05/21 21:12:30 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -277,7 +277,7 @@
 #if NLAPIC > 0
        lapic_enable();
        lapic_set_lvt();
-       lapic_initclocks();
+       lapic_reset();
 #endif
 
        atomic_or_32(&ci->ci_flags, CPUF_RUNNING);
@@ -351,7 +351,7 @@
 #if NLAPIC > 0
        lapic_enable();
        lapic_set_lvt();
-       lapic_initclocks();
+       lapic_reset();
 #endif
 #if NIOAPIC > 0
        ioapic_reenable();
diff -r 84d4d9767dca -r 8cbd0ad5a665 sys/arch/x86/include/i82489var.h
--- a/sys/arch/x86/include/i82489var.h  Thu May 21 20:43:23 2020 +0000
+++ b/sys/arch/x86/include/i82489var.h  Thu May 21 21:12:30 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: i82489var.h,v 1.20 2019/12/01 08:23:09 maxv Exp $      */
+/*     $NetBSD: i82489var.h,v 1.21 2020/05/21 21:12:30 ad Exp $        */
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -88,8 +88,8 @@
 extern void lapic_boot_init(paddr_t);
 extern void lapic_set_lvt(void);
 extern void lapic_enable(void);
-extern void lapic_calibrate_timer(struct cpu_info *ci);
-extern void lapic_initclocks(void);
+extern void lapic_calibrate_timer(bool);
+extern void lapic_reset(void);
 
 extern uint32_t lapic_readreg(u_int);
 extern void lapic_writereg(u_int, uint32_t);
diff -r 84d4d9767dca -r 8cbd0ad5a665 sys/arch/x86/x86/cpu.c
--- a/sys/arch/x86/x86/cpu.c    Thu May 21 20:43:23 2020 +0000
+++ b/sys/arch/x86/x86/cpu.c    Thu May 21 21:12:30 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: cpu.c,v 1.191 2020/05/12 06:32:05 msaitoh Exp $        */
+/*     $NetBSD: cpu.c,v 1.192 2020/05/21 21:12:30 ad Exp $     */
 
 /*
  * Copyright (c) 2000-2020 NetBSD Foundation, Inc.
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.191 2020/05/12 06:32:05 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.192 2020/05/21 21:12:30 ad Exp $");
 
 #include "opt_ddb.h"
 #include "opt_mpbios.h"                /* for MPDEBUG */
@@ -456,7 +456,7 @@
                        lapic_enable();
                        lapic_set_lvt();
                        if (!vm_guest_is_xenpvh_or_pvhvm())
-                               lapic_calibrate_timer(ci);
+                               lapic_calibrate_timer(false);
                }
 #endif
                kcsan_cpu_init(ci);
@@ -471,7 +471,6 @@
                cpu_identify(ci);
                x86_errata();
                x86_cpu_idle_init();
-               (*x86_cpu_initclock_func)();
 #ifdef XENPVHVM
                xen_hvm_init_cpu(ci);
 #endif
@@ -485,7 +484,6 @@
 #ifdef XENPVHVM
                xen_hvm_init_cpu(ci);
 #endif
-               (*x86_cpu_initclock_func)();
                break;
 
 #ifdef MULTIPROCESSOR
@@ -739,14 +737,6 @@
        kcpuset_t *cpus;
        u_long i;
 
-#if NHPET > 0
-       /* Use HPET delay, and re-calibrate TSC on boot CPU using HPET. */
-       if (hpet_delay_p() && x86_delay == i8254_delay) {
-               delay_func = x86_delay = hpet_delay;
-               cpu_get_tsc_freq(curcpu());
-       }
-#endif
-
        /* Now that we know the number of CPUs, patch the text segment. */
        x86_patch(false);
 
@@ -1010,7 +1000,7 @@
 #ifdef XENPVHVM
        xen_hvm_init_cpu(ci);
 #endif
-       (*x86_cpu_initclock_func)();
+       (*x86_initclock_func)();
        cpu_get_tsc_freq(ci);
 
        s = splhigh();
@@ -1346,7 +1336,7 @@
                        overhead = 0;
                        for (int i = 0; i <= 8; i++) {
                                t0 = cpu_counter();
-                               x86_delay(0);
+                               delay_func(0);
                                t1 = cpu_counter();
                                if (i > 0) {
                                        overhead += (t1 - t0);
@@ -1356,7 +1346,7 @@
 
                        /* Now do the calibration. */
                        t0 = cpu_counter();
-                       x86_delay(100000);
+                       delay_func(100000);
                        t1 = cpu_counter();
                        freq = (t1 - t0 - overhead) * 10;
                }
diff -r 84d4d9767dca -r 8cbd0ad5a665 sys/arch/x86/x86/lapic.c
--- a/sys/arch/x86/x86/lapic.c  Thu May 21 20:43:23 2020 +0000
+++ b/sys/arch/x86/x86/lapic.c  Thu May 21 21:12:30 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: lapic.c,v 1.80 2020/05/20 02:23:35 msaitoh Exp $       */
+/*     $NetBSD: lapic.c,v 1.81 2020/05/21 21:12:30 ad Exp $    */
 
 /*-
  * Copyright (c) 2000, 2008, 2020 The NetBSD Foundation, Inc.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.80 2020/05/20 02:23:35 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.81 2020/05/21 21:12:30 ad Exp $");
 
 #include "acpica.h"
 #include "ioapic.h"
@@ -580,11 +580,10 @@
 }
 
 void
-lapic_initclocks(void)
+lapic_reset(void)
 {
+
        /*
-        * Start local apic countdown timer running, in repeated mode.
-        *
         * Mask the clock interrupt and set mode,
         * then set divisor,
         * then unmask and set the vector.
@@ -598,6 +597,29 @@
        lapic_writereg(LAPIC_EOI, 0);
 }
 
+static void
+lapic_initclock(void)
+{
+
+       if (curcpu() == &cpu_info_primary) {
+               /*
+                * Recalibrate the timer using the cycle counter, now that
+                * the cycle counter itself has been recalibrated.
+                */
+               lapic_calibrate_timer(true);
+
+               /*
+                * Hook up time counter.  This assume that all LAPICs have
+                * the same frequency.
+                */
+               lapic_timecounter.tc_frequency = lapic_per_second;
+               tc_init(&lapic_timecounter);
+       }
+
+       /* Start local apic countdown timer running, in repeated mode. */
+       lapic_reset();
+}
+
 /*
  * Calibrate the local apic count-down timer (which is running at
  * bus-clock speed) vs. the i8254 counter/timer (which is running at
@@ -610,50 +632,71 @@
  * We're actually using the IRQ0 timer.  Hmm.
  */
 void
-lapic_calibrate_timer(struct cpu_info *ci)
+lapic_calibrate_timer(bool secondpass)
 {
-       unsigned int seen, delta, initial_i8254, initial_lapic;
-       unsigned int cur_i8254, cur_lapic;
+       struct cpu_info *ci = curcpu();
        uint64_t tmp;
        int i;
        char tbuf[9];
 
-       if (lapic_per_second != 0)
-               goto calibrate_done;
+       KASSERT(ci == &cpu_info_primary);
 
-       aprint_debug_dev(ci->ci_dev, "calibrating local timer\n");
+       aprint_debug_dev(ci->ci_dev, "[re]calibrating local timer\n");
 
        /*
         * Configure timer to one-shot, interrupt masked,
         * large positive number.
         */
+       x86_disable_intr();
        lapic_writereg(LAPIC_LVT_TIMER, LAPIC_LVT_MASKED);
        lapic_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
        lapic_writereg(LAPIC_ICR_TIMER, 0x80000000);
-
-       x86_disable_intr();
+       (void)lapic_gettick();
 
-       initial_lapic = lapic_gettick();
-       initial_i8254 = gettick();
+       if (secondpass && cpu_hascounter()) {
+               /*
+                * Second pass calibration, using the TSC which has ideally
+                * been calibrated using the HPET or information gleaned
+                * from MSRs by this point.
+                */
+               uint64_t l0, l1, t0, t1;
+
+               (void)cpu_counter();
+               t0 = cpu_counter();
+               l0 = lapic_gettick();
+               t0 += cpu_counter();
+               DELAY(50000);
+               t1 = cpu_counter();
+               l1 = lapic_gettick();
+               t1 += cpu_counter();
 
-       for (seen = 0; seen < TIMER_FREQ / 100; seen += delta) {
-               cur_i8254 = gettick();
-               if (cur_i8254 > initial_i8254)
-                       delta = x86_rtclock_tval - (cur_i8254 - initial_i8254);
-               else
-                       delta = initial_i8254 - cur_i8254;
-               initial_i8254 = cur_i8254;
+               tmp = (l0 - l1) * cpu_frequency(ci) / ((t1 - t0 + 1) / 2);
+               lapic_per_second = rounddown(tmp + 500, 1000);
+       } else if (lapic_per_second == 0) {
+               /*
+                * Inaccurate first pass calibration using the i8254.
+                */
+               unsigned int seen, delta, initial_i8254, initial_lapic;
+               unsigned int cur_i8254, cur_lapic;
+
+               (void)gettick();
+               initial_lapic = lapic_gettick();
+               initial_i8254 = gettick();
+               for (seen = 0; seen < TIMER_FREQ / 100; seen += delta) {
+                       cur_i8254 = gettick();
+                       if (cur_i8254 > initial_i8254)
+                               delta = x86_rtclock_tval - (cur_i8254 - initial_i8254);
+                       else
+                               delta = initial_i8254 - cur_i8254;
+                       initial_i8254 = cur_i8254;
+               }
+               cur_lapic = lapic_gettick();
+               tmp = initial_lapic - cur_lapic;
+               lapic_per_second = (tmp * TIMER_FREQ + seen / 2) / seen;
        }
-       cur_lapic = lapic_gettick();
-
        x86_enable_intr();
 
-       tmp = initial_lapic - cur_lapic;
-       lapic_per_second = (tmp * TIMER_FREQ + seen / 2) / seen;
-
-calibrate_done:
        humanize_number(tbuf, sizeof(tbuf), lapic_per_second, "Hz", 1000);
-
        aprint_debug_dev(ci->ci_dev, "apic clock running at %s\n", tbuf);
 
        if (lapic_per_second != 0) {
@@ -703,18 +746,10 @@



Home | Main Index | Thread Index | Old Index