Home | History | Annotate | Line # | Download | only in kern
kern_cctr.c revision 1.11
      1  1.11  thorpej /*	$NetBSD: kern_cctr.c,v 1.11 2020/10/10 03:05:04 thorpej Exp $	*/
      2   1.1  tsutsui 
      3   1.1  tsutsui /*-
      4  1.11  thorpej  * Copyright (c) 2020 Jason R. Thorpe
      5  1.11  thorpej  * Copyright (c) 2018 Naruaki Etomi
      6   1.1  tsutsui  * All rights reserved.
      7   1.1  tsutsui  *
      8   1.1  tsutsui  * Redistribution and use in source and binary forms, with or without
      9   1.1  tsutsui  * modification, are permitted provided that the following conditions
     10   1.1  tsutsui  * are met:
     11   1.1  tsutsui  * 1. Redistributions of source code must retain the above copyright
     12   1.1  tsutsui  *    notice, this list of conditions and the following disclaimer.
     13   1.1  tsutsui  * 2. Redistributions in binary form must reproduce the above copyright
     14   1.1  tsutsui  *    notice, this list of conditions and the following disclaimer in the
     15   1.1  tsutsui  *    documentation and/or other materials provided with the distribution.
     16   1.1  tsutsui  *
     17  1.11  thorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  1.11  thorpej  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  1.11  thorpej  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  1.11  thorpej  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  1.11  thorpej  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  1.11  thorpej  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  1.11  thorpej  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  1.11  thorpej  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  1.11  thorpej  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  1.11  thorpej  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27   1.1  tsutsui  */
     28   1.1  tsutsui 
     29  1.11  thorpej /*
     30  1.11  thorpej  * Most of the following was adapted from the Linux/ia64 cycle counter
     31  1.11  thorpej  * synchronization algorithm:
     32  1.11  thorpej  *
     33  1.11  thorpej  *	IA-64 Linux Kernel: Design and Implementation p356-p361
     34  1.11  thorpej  *	(Hewlett-Packard Professional Books)
     35  1.11  thorpej  *
     36  1.11  thorpej  * Here's a rough description of how it works.
     37  1.11  thorpej  *
     38  1.11  thorpej  * The primary CPU is the reference monotonic counter.  Each secondary
     39  1.11  thorpej  * CPU is responsible for knowing the offset of its own cycle counter
     40  1.11  thorpej  * relative to the primary's.  When the time counter is read, the CC
     41  1.11  thorpej  * value is adjusted by this delta.
     42  1.11  thorpej  *
     43  1.11  thorpej  * Calibration happens periodically, and works like this:
     44  1.11  thorpej  *
     45  1.11  thorpej  * Secondary CPU                               Primary CPU
     46  1.11  thorpej  *   T0 = local CC
     47  1.11  thorpej  *   Send IPI to publish reference CC
     48  1.11  thorpej  *                                   --------->
     49  1.11  thorpej  *     (assume this happens at Tavg)           Publish reference CC
     50  1.11  thorpej  *                     <-----------------------
     51  1.11  thorpej  *   Notice publication
     52  1.11  thorpej  *   T1 = local CC
     53  1.11  thorpej  *
     54  1.11  thorpej  *   Tavg = (T0 + T1) / 2
     55   1.1  tsutsui  *
     56  1.11  thorpej  *   Delta = Tavg - Published primary CC value
     57   1.1  tsutsui  *
     58  1.11  thorpej  * We trigger calibration roughly once a second; the period is actually
     59  1.11  thorpej  * skewed based on the CPU index in order to avoid lock contention.  The
     60  1.11  thorpej  * calibration interval does not need to be precise, and so this is fine.
     61   1.1  tsutsui  */
     62   1.1  tsutsui 
     63   1.1  tsutsui #include <sys/cdefs.h>
     64  1.11  thorpej __KERNEL_RCSID(0, "$NetBSD: kern_cctr.c,v 1.11 2020/10/10 03:05:04 thorpej Exp $");
     65   1.1  tsutsui 
     66   1.1  tsutsui #include <sys/param.h>
     67  1.11  thorpej #include <sys/atomic.h>
     68   1.1  tsutsui #include <sys/systm.h>
     69   1.1  tsutsui #include <sys/sysctl.h>
     70  1.11  thorpej #include <sys/timepps.h>
     71   1.1  tsutsui #include <sys/time.h>
     72   1.1  tsutsui #include <sys/timetc.h>
     73   1.1  tsutsui #include <sys/kernel.h>
     74   1.1  tsutsui #include <sys/power.h>
     75   1.2       ad #include <sys/cpu.h>
     76   1.1  tsutsui #include <machine/cpu_counter.h>
     77   1.1  tsutsui 
     78   1.1  tsutsui /* XXX make cc_timecounter.tc_frequency settable by sysctl() */
     79   1.1  tsutsui 
     80  1.11  thorpej #if defined(MULTIPROCESSOR)
     81  1.11  thorpej static uint32_t cc_primary __cacheline_aligned;
     82  1.11  thorpej static uint32_t cc_calibration_state __cacheline_aligned;
     83  1.11  thorpej static kmutex_t cc_calibration_lock __cacheline_aligned;
     84  1.11  thorpej 
     85  1.11  thorpej #define	CC_CAL_START		0	/* initial state */
     86  1.11  thorpej #define	CC_CAL_PRIMARY_READY	1	/* primary CPU ready to respond */
     87  1.11  thorpej #define	CC_CAL_SECONDARY_READY	2	/* secondary CPU ready to receive */
     88  1.11  thorpej #define	CC_CAL_FINISHED		3	/* calibration attempt complete */
     89  1.11  thorpej #endif /* MULTIPROCESSOR */
     90   1.1  tsutsui 
     91   1.1  tsutsui static struct timecounter cc_timecounter = {
     92   1.1  tsutsui 	.tc_get_timecount	= cc_get_timecount,
     93  1.11  thorpej 	.tc_poll_pps		= NULL,
     94   1.1  tsutsui 	.tc_counter_mask	= ~0u,
     95   1.1  tsutsui 	.tc_frequency		= 0,
     96  1.10    skrll 	.tc_name		= "unknown cycle counter",
     97   1.1  tsutsui 	/*
     98   1.1  tsutsui 	 * don't pick cycle counter automatically
     99   1.1  tsutsui 	 * if frequency changes might affect cycle counter
    100   1.1  tsutsui 	 */
    101   1.1  tsutsui 	.tc_quality		= -100000,
    102   1.1  tsutsui 
    103   1.1  tsutsui 	.tc_priv		= NULL,
    104   1.1  tsutsui 	.tc_next		= NULL
    105   1.1  tsutsui };
    106   1.1  tsutsui 
    107   1.1  tsutsui /*
    108  1.11  thorpej  * Initialize cycle counter based timecounter.  This must be done on the
    109  1.11  thorpej  * primary CPU.
    110   1.1  tsutsui  */
    111   1.1  tsutsui struct timecounter *
    112   1.5  tsutsui cc_init(timecounter_get_t getcc, uint64_t freq, const char *name, int quality)
    113   1.1  tsutsui {
    114  1.11  thorpej 	static bool cc_init_done __diagused;
    115  1.11  thorpej 	struct cpu_info * const ci = curcpu();
    116  1.11  thorpej 
    117  1.11  thorpej 	KASSERT(!cc_init_done);
    118  1.11  thorpej 	KASSERT(cold);
    119  1.11  thorpej 	KASSERT(CPU_IS_PRIMARY(ci));
    120  1.11  thorpej 
    121  1.11  thorpej #if defined(MULTIPROCESSOR)
    122  1.11  thorpej 	mutex_init(&cc_calibration_lock, MUTEX_DEFAULT, IPL_HIGH);
    123  1.11  thorpej #endif
    124  1.11  thorpej 
    125  1.11  thorpej 	cc_init_done = true;
    126  1.11  thorpej 
    127  1.11  thorpej 	ci->ci_cc.cc_delta = 0;
    128  1.11  thorpej 	ci->ci_cc.cc_ticks = 0;
    129  1.11  thorpej 	ci->ci_cc.cc_cal_ticks = 0;
    130   1.1  tsutsui 
    131   1.5  tsutsui 	if (getcc != NULL)
    132   1.5  tsutsui 		cc_timecounter.tc_get_timecount = getcc;
    133   1.5  tsutsui 
    134   1.1  tsutsui 	cc_timecounter.tc_frequency = freq;
    135   1.1  tsutsui 	cc_timecounter.tc_name = name;
    136   1.1  tsutsui 	cc_timecounter.tc_quality = quality;
    137   1.1  tsutsui 	tc_init(&cc_timecounter);
    138   1.1  tsutsui 
    139   1.1  tsutsui 	return &cc_timecounter;
    140   1.1  tsutsui }
    141   1.1  tsutsui 
    142   1.1  tsutsui /*
    143  1.11  thorpej  * Initialize cycle counter timecounter calibration data on a secondary
    144  1.11  thorpej  * CPU.  Must be called on that secondary CPU.
    145  1.11  thorpej  */
    146  1.11  thorpej void
    147  1.11  thorpej cc_init_secondary(struct cpu_info * const ci)
    148  1.11  thorpej {
    149  1.11  thorpej 	KASSERT(!CPU_IS_PRIMARY(curcpu()));
    150  1.11  thorpej 	KASSERT(ci == curcpu());
    151  1.11  thorpej 
    152  1.11  thorpej 	ci->ci_cc.cc_ticks = 0;
    153  1.11  thorpej 
    154  1.11  thorpej 	/*
    155  1.11  thorpej 	 * It's not critical that calibration be performed in
    156  1.11  thorpej 	 * precise intervals, so skew when calibration is done
    157  1.11  thorpej 	 * on each secondary CPU based on it's CPU index to
    158  1.11  thorpej 	 * avoid contending on the calibration lock.
    159  1.11  thorpej 	 */
    160  1.11  thorpej 	ci->ci_cc.cc_cal_ticks = hz - cpu_index(ci);
    161  1.11  thorpej 	KASSERT(ci->ci_cc.cc_cal_ticks);
    162  1.11  thorpej 
    163  1.11  thorpej 	cc_calibrate_cpu(ci);
    164  1.11  thorpej }
    165  1.11  thorpej 
    166  1.11  thorpej /*
    167   1.1  tsutsui  * pick up tick count scaled to reference tick count
    168   1.1  tsutsui  */
    169   1.5  tsutsui u_int
    170   1.1  tsutsui cc_get_timecount(struct timecounter *tc)
    171   1.1  tsutsui {
    172  1.11  thorpej #if defined(MULTIPROCESSOR)
    173  1.11  thorpej 	int64_t rcc, ncsw;
    174   1.1  tsutsui 
    175   1.6       ad  retry:
    176   1.6       ad  	ncsw = curlwp->l_ncsw;
    177  1.11  thorpej 
    178  1.11  thorpej  	__insn_barrier();
    179  1.11  thorpej 	/* N.B. the delta is always 0 on the primary. */
    180  1.11  thorpej 	rcc = cpu_counter32() - curcpu()->ci_cc.cc_delta;
    181   1.6       ad  	__insn_barrier();
    182   1.1  tsutsui 
    183   1.6       ad  	if (ncsw != curlwp->l_ncsw) {
    184   1.6       ad  		/* Was preempted */
    185   1.6       ad  		goto retry;
    186   1.6       ad 	}
    187   1.1  tsutsui 
    188   1.1  tsutsui 	return rcc;
    189  1.11  thorpej #else
    190  1.11  thorpej 	return cpu_counter32();
    191  1.11  thorpej #endif /* MULTIPROCESSOR */
    192   1.1  tsutsui }
    193   1.1  tsutsui 
    194  1.11  thorpej #if defined(MULTIPROCESSOR)
    195  1.11  thorpej static inline bool
    196  1.11  thorpej cc_get_delta(struct cpu_info * const ci)
    197   1.1  tsutsui {
    198  1.11  thorpej 	int64_t t0, t1, tcenter = 0;
    199  1.11  thorpej 
    200  1.11  thorpej 	t0 = cpu_counter32();
    201   1.6       ad 
    202  1.11  thorpej 	atomic_store_release(&cc_calibration_state, CC_CAL_SECONDARY_READY);
    203   1.6       ad 
    204  1.11  thorpej 	for (;;) {
    205  1.11  thorpej 		if (atomic_load_acquire(&cc_calibration_state) ==
    206  1.11  thorpej 		    CC_CAL_FINISHED) {
    207  1.11  thorpej 			break;
    208  1.11  thorpej 		}
    209  1.11  thorpej 	}
    210   1.1  tsutsui 
    211  1.11  thorpej 	t1 = cpu_counter32();
    212  1.11  thorpej 
    213  1.11  thorpej 	if (t1 < t0) {
    214  1.11  thorpej 		/* Overflow! */
    215  1.11  thorpej 		return false;
    216  1.11  thorpej 	}
    217  1.11  thorpej 
    218  1.11  thorpej 	/* average t0 and t1 without overflow: */
    219  1.11  thorpej 	tcenter = (t0 >> 1) + (t1 >> 1);
    220  1.11  thorpej 	if ((t0 & 1) + (t1 & 1) == 2)
    221  1.11  thorpej 		tcenter++;
    222  1.11  thorpej 
    223  1.11  thorpej 	ci->ci_cc.cc_delta = tcenter - cc_primary;
    224  1.11  thorpej 
    225  1.11  thorpej 	return true;
    226   1.1  tsutsui }
    227  1.11  thorpej #endif /* MULTIPROCESSOR */
    228   1.1  tsutsui 
    229   1.1  tsutsui /*
    230  1.11  thorpej  * Called on secondary CPUs to calibrate their cycle counter offset
    231  1.11  thorpej  * relative to the primary CPU.
    232   1.1  tsutsui  */
    233   1.1  tsutsui void
    234  1.11  thorpej cc_calibrate_cpu(struct cpu_info * const ci)
    235   1.1  tsutsui {
    236  1.11  thorpej #if defined(MULTIPROCESSOR)
    237  1.11  thorpej 	KASSERT(!CPU_IS_PRIMARY(ci));
    238  1.11  thorpej 
    239  1.11  thorpej 	mutex_spin_enter(&cc_calibration_lock);
    240  1.11  thorpej 
    241  1.11  thorpej  retry:
    242  1.11  thorpej 	atomic_store_release(&cc_calibration_state, CC_CAL_START);
    243  1.11  thorpej 
    244  1.11  thorpej 	/* Trigger primary CPU. */
    245  1.11  thorpej 	cc_get_primary_cc();
    246  1.11  thorpej 
    247  1.11  thorpej 	for (;;) {
    248  1.11  thorpej 		if (atomic_load_acquire(&cc_calibration_state) ==
    249  1.11  thorpej 		    CC_CAL_PRIMARY_READY) {
    250  1.11  thorpej 			break;
    251  1.11  thorpej 		}
    252  1.11  thorpej 	}
    253   1.1  tsutsui 
    254  1.11  thorpej 	if (! cc_get_delta(ci)) {
    255  1.11  thorpej 		goto retry;
    256   1.1  tsutsui 	}
    257   1.1  tsutsui 
    258  1.11  thorpej 	mutex_exit(&cc_calibration_lock);
    259  1.11  thorpej #endif /* MULTIPROCESSOR */
    260  1.11  thorpej }
    261  1.11  thorpej 
    262  1.11  thorpej void
    263  1.11  thorpej cc_primary_cc(void)
    264  1.11  thorpej {
    265  1.11  thorpej #if defined(MULTIPROCESSOR)
    266  1.11  thorpej 	/* N.B. We expect all interrupts to be blocked. */
    267  1.11  thorpej 
    268  1.11  thorpej 	atomic_store_release(&cc_calibration_state, CC_CAL_PRIMARY_READY);
    269  1.11  thorpej 
    270  1.11  thorpej 	for (;;) {
    271  1.11  thorpej 		if (atomic_load_acquire(&cc_calibration_state) ==
    272  1.11  thorpej 		    CC_CAL_SECONDARY_READY) {
    273  1.11  thorpej 			break;
    274  1.11  thorpej 		}
    275  1.11  thorpej 	}
    276   1.1  tsutsui 
    277  1.11  thorpej 	cc_primary = cpu_counter32();
    278  1.11  thorpej 	atomic_store_release(&cc_calibration_state, CC_CAL_FINISHED);
    279  1.11  thorpej #endif /* MULTIPROCESSOR */
    280   1.1  tsutsui }
    281