xref: /freebsd/sys/i386/include/counter.h (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
14e76af6aSGleb Smirnoff /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
383ef78beSPedro F. Giffuni  *
44e76af6aSGleb Smirnoff  * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
54e76af6aSGleb Smirnoff  * All rights reserved.
64e76af6aSGleb Smirnoff  *
74e76af6aSGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
84e76af6aSGleb Smirnoff  * modification, are permitted provided that the following conditions
94e76af6aSGleb Smirnoff  * are met:
104e76af6aSGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
114e76af6aSGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
124e76af6aSGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
134e76af6aSGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
144e76af6aSGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
154e76af6aSGleb Smirnoff  *
164e76af6aSGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
174e76af6aSGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184e76af6aSGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194e76af6aSGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
204e76af6aSGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214e76af6aSGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224e76af6aSGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234e76af6aSGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244e76af6aSGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254e76af6aSGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264e76af6aSGleb Smirnoff  * SUCH DAMAGE.
274e76af6aSGleb Smirnoff  */
284e76af6aSGleb Smirnoff 
294e76af6aSGleb Smirnoff #ifndef __MACHINE_COUNTER_H__
304e76af6aSGleb Smirnoff #define __MACHINE_COUNTER_H__
314e76af6aSGleb Smirnoff 
324e76af6aSGleb Smirnoff #include <sys/pcpu.h>
334e76af6aSGleb Smirnoff #ifdef INVARIANTS
344e76af6aSGleb Smirnoff #include <sys/proc.h>
354e76af6aSGleb Smirnoff #endif
36*f53b28c6SKristof Provost #include <sys/systm.h>
374e76af6aSGleb Smirnoff #include <machine/md_var.h>
384e76af6aSGleb Smirnoff #include <machine/specialreg.h>
394e76af6aSGleb Smirnoff 
4083c9dea1SGleb Smirnoff #define	EARLY_COUNTER	&__pcpu[0].pc_early_dummy_counter
4183c9dea1SGleb Smirnoff 
424e76af6aSGleb Smirnoff #define	counter_enter()	do {				\
434e76af6aSGleb Smirnoff 	if ((cpu_feature & CPUID_CX8) == 0)		\
444e76af6aSGleb Smirnoff 		critical_enter();			\
454e76af6aSGleb Smirnoff } while (0)
464e76af6aSGleb Smirnoff 
474e76af6aSGleb Smirnoff #define	counter_exit()	do {				\
484e76af6aSGleb Smirnoff 	if ((cpu_feature & CPUID_CX8) == 0)		\
494e76af6aSGleb Smirnoff 		critical_exit();			\
504e76af6aSGleb Smirnoff } while (0)
514e76af6aSGleb Smirnoff 
524e76af6aSGleb Smirnoff static inline void
counter_64_inc_8b(uint64_t * p,int64_t inc)534e76af6aSGleb Smirnoff counter_64_inc_8b(uint64_t *p, int64_t inc)
544e76af6aSGleb Smirnoff {
554e76af6aSGleb Smirnoff 
564e76af6aSGleb Smirnoff 	__asm __volatile(
574e76af6aSGleb Smirnoff 	"movl	%%fs:(%%esi),%%eax\n\t"
584e76af6aSGleb Smirnoff 	"movl	%%fs:4(%%esi),%%edx\n"
594e76af6aSGleb Smirnoff "1:\n\t"
604e76af6aSGleb Smirnoff 	"movl	%%eax,%%ebx\n\t"
614e76af6aSGleb Smirnoff 	"movl	%%edx,%%ecx\n\t"
624e76af6aSGleb Smirnoff 	"addl	(%%edi),%%ebx\n\t"
634e76af6aSGleb Smirnoff 	"adcl	4(%%edi),%%ecx\n\t"
644e76af6aSGleb Smirnoff 	"cmpxchg8b %%fs:(%%esi)\n\t"
654e76af6aSGleb Smirnoff 	"jnz	1b"
664e76af6aSGleb Smirnoff 	:
67706c56e4SKonstantin Belousov 	: "S" ((char *)p - (char *)&__pcpu[0]), "D" (&inc)
684e76af6aSGleb Smirnoff 	: "memory", "cc", "eax", "edx", "ebx", "ecx");
694e76af6aSGleb Smirnoff }
704e76af6aSGleb Smirnoff 
7170a7dd5dSKonstantin Belousov #ifdef IN_SUBR_COUNTER_C
72cbb65b7eSKonstantin Belousov struct counter_u64_fetch_cx8_arg {
73cbb65b7eSKonstantin Belousov 	uint64_t res;
74cbb65b7eSKonstantin Belousov 	uint64_t *p;
75cbb65b7eSKonstantin Belousov };
76cbb65b7eSKonstantin Belousov 
77cbb65b7eSKonstantin Belousov static uint64_t
counter_u64_read_one_8b(uint64_t * p)7870a7dd5dSKonstantin Belousov counter_u64_read_one_8b(uint64_t *p)
7970a7dd5dSKonstantin Belousov {
8070a7dd5dSKonstantin Belousov 	uint32_t res_lo, res_high;
8170a7dd5dSKonstantin Belousov 
8270a7dd5dSKonstantin Belousov 	__asm __volatile(
8370a7dd5dSKonstantin Belousov 	"movl	%%eax,%%ebx\n\t"
8470a7dd5dSKonstantin Belousov 	"movl	%%edx,%%ecx\n\t"
8570a7dd5dSKonstantin Belousov 	"cmpxchg8b	(%2)"
8670a7dd5dSKonstantin Belousov 	: "=a" (res_lo), "=d"(res_high)
8770a7dd5dSKonstantin Belousov 	: "SD" (p)
8870a7dd5dSKonstantin Belousov 	: "cc", "ebx", "ecx");
8970a7dd5dSKonstantin Belousov 	return (res_lo + ((uint64_t)res_high << 32));
9070a7dd5dSKonstantin Belousov }
9170a7dd5dSKonstantin Belousov 
92cbb65b7eSKonstantin Belousov static void
counter_u64_fetch_cx8_one(void * arg1)93cbb65b7eSKonstantin Belousov counter_u64_fetch_cx8_one(void *arg1)
94cbb65b7eSKonstantin Belousov {
95cbb65b7eSKonstantin Belousov 	struct counter_u64_fetch_cx8_arg *arg;
96cbb65b7eSKonstantin Belousov 	uint64_t val;
97cbb65b7eSKonstantin Belousov 
98cbb65b7eSKonstantin Belousov 	arg = arg1;
99cbb65b7eSKonstantin Belousov 	val = counter_u64_read_one_8b((uint64_t *)((char *)arg->p +
100cbb65b7eSKonstantin Belousov 	    UMA_PCPU_ALLOC_SIZE * PCPU_GET(cpuid)));
101cbb65b7eSKonstantin Belousov 	atomic_add_64(&arg->res, val);
102cbb65b7eSKonstantin Belousov }
103cbb65b7eSKonstantin Belousov 
10470a7dd5dSKonstantin Belousov static inline uint64_t
counter_u64_fetch_inline(uint64_t * p)10570a7dd5dSKonstantin Belousov counter_u64_fetch_inline(uint64_t *p)
10670a7dd5dSKonstantin Belousov {
107cbb65b7eSKonstantin Belousov 	struct counter_u64_fetch_cx8_arg arg;
10870a7dd5dSKonstantin Belousov 	uint64_t res;
10970a7dd5dSKonstantin Belousov 	int i;
11070a7dd5dSKonstantin Belousov 
11170a7dd5dSKonstantin Belousov 	res = 0;
11270a7dd5dSKonstantin Belousov 	if ((cpu_feature & CPUID_CX8) == 0) {
11370a7dd5dSKonstantin Belousov 		/*
11470a7dd5dSKonstantin Belousov 		 * The machines without cmpxchg8b are not SMP.
11570a7dd5dSKonstantin Belousov 		 * Disabling the preemption provides atomicity of the
11670a7dd5dSKonstantin Belousov 		 * counter reading, since update is done in the
11770a7dd5dSKonstantin Belousov 		 * critical section as well.
11870a7dd5dSKonstantin Belousov 		 */
11970a7dd5dSKonstantin Belousov 		critical_enter();
12096c85efbSNathan Whitehorn 		CPU_FOREACH(i) {
12170a7dd5dSKonstantin Belousov 			res += *(uint64_t *)((char *)p +
122ab3059a8SMatt Macy 			    UMA_PCPU_ALLOC_SIZE * i);
12370a7dd5dSKonstantin Belousov 		}
12470a7dd5dSKonstantin Belousov 		critical_exit();
12570a7dd5dSKonstantin Belousov 	} else {
126cbb65b7eSKonstantin Belousov 		arg.p = p;
127cbb65b7eSKonstantin Belousov 		arg.res = 0;
128cbb65b7eSKonstantin Belousov 		smp_rendezvous(NULL, counter_u64_fetch_cx8_one, NULL, &arg);
129cbb65b7eSKonstantin Belousov 		res = arg.res;
13070a7dd5dSKonstantin Belousov 	}
13170a7dd5dSKonstantin Belousov 	return (res);
13270a7dd5dSKonstantin Belousov }
13370a7dd5dSKonstantin Belousov 
13470a7dd5dSKonstantin Belousov static inline void
counter_u64_zero_one_8b(uint64_t * p)13570a7dd5dSKonstantin Belousov counter_u64_zero_one_8b(uint64_t *p)
13670a7dd5dSKonstantin Belousov {
13770a7dd5dSKonstantin Belousov 
13870a7dd5dSKonstantin Belousov 	__asm __volatile(
13970a7dd5dSKonstantin Belousov 	"movl	(%0),%%eax\n\t"
14070a7dd5dSKonstantin Belousov 	"movl	4(%0),%%edx\n"
14170a7dd5dSKonstantin Belousov 	"xorl	%%ebx,%%ebx\n\t"
14270a7dd5dSKonstantin Belousov 	"xorl	%%ecx,%%ecx\n\t"
14370a7dd5dSKonstantin Belousov "1:\n\t"
14470a7dd5dSKonstantin Belousov 	"cmpxchg8b	(%0)\n\t"
14570a7dd5dSKonstantin Belousov 	"jnz	1b"
14670a7dd5dSKonstantin Belousov 	:
14770a7dd5dSKonstantin Belousov 	: "SD" (p)
14870a7dd5dSKonstantin Belousov 	: "memory", "cc", "eax", "edx", "ebx", "ecx");
14970a7dd5dSKonstantin Belousov }
15070a7dd5dSKonstantin Belousov 
15170a7dd5dSKonstantin Belousov static void
counter_u64_zero_one_cpu(void * arg)15270a7dd5dSKonstantin Belousov counter_u64_zero_one_cpu(void *arg)
15370a7dd5dSKonstantin Belousov {
15470a7dd5dSKonstantin Belousov 	uint64_t *p;
15570a7dd5dSKonstantin Belousov 
156ab3059a8SMatt Macy 	p = (uint64_t *)((char *)arg + UMA_PCPU_ALLOC_SIZE * PCPU_GET(cpuid));
15770a7dd5dSKonstantin Belousov 	counter_u64_zero_one_8b(p);
15870a7dd5dSKonstantin Belousov }
15970a7dd5dSKonstantin Belousov 
16070a7dd5dSKonstantin Belousov static inline void
counter_u64_zero_inline(counter_u64_t c)16170a7dd5dSKonstantin Belousov counter_u64_zero_inline(counter_u64_t c)
16270a7dd5dSKonstantin Belousov {
16370a7dd5dSKonstantin Belousov 	int i;
16470a7dd5dSKonstantin Belousov 
16570a7dd5dSKonstantin Belousov 	if ((cpu_feature & CPUID_CX8) == 0) {
16670a7dd5dSKonstantin Belousov 		critical_enter();
16796c85efbSNathan Whitehorn 		CPU_FOREACH(i)
168ab3059a8SMatt Macy 			*(uint64_t *)((char *)c + UMA_PCPU_ALLOC_SIZE * i) = 0;
16970a7dd5dSKonstantin Belousov 		critical_exit();
17070a7dd5dSKonstantin Belousov 	} else {
17167d955aaSPatrick Kelsey 		smp_rendezvous(smp_no_rendezvous_barrier,
17267d955aaSPatrick Kelsey 		    counter_u64_zero_one_cpu, smp_no_rendezvous_barrier, c);
17370a7dd5dSKonstantin Belousov 	}
17470a7dd5dSKonstantin Belousov }
17570a7dd5dSKonstantin Belousov #endif
17670a7dd5dSKonstantin Belousov 
1774e76af6aSGleb Smirnoff #define	counter_u64_add_protected(c, inc)	do {	\
1784e76af6aSGleb Smirnoff 	if ((cpu_feature & CPUID_CX8) == 0) {		\
1794e76af6aSGleb Smirnoff 		CRITICAL_ASSERT(curthread);		\
1804e76af6aSGleb Smirnoff 		*(uint64_t *)zpcpu_get(c) += (inc);	\
1814e76af6aSGleb Smirnoff 	} else						\
1824e76af6aSGleb Smirnoff 		counter_64_inc_8b((c), (inc));		\
1834e76af6aSGleb Smirnoff } while (0)
1844e76af6aSGleb Smirnoff 
1854e76af6aSGleb Smirnoff static inline void
counter_u64_add(counter_u64_t c,int64_t inc)1864e76af6aSGleb Smirnoff counter_u64_add(counter_u64_t c, int64_t inc)
1874e76af6aSGleb Smirnoff {
1884e76af6aSGleb Smirnoff 
1894e76af6aSGleb Smirnoff 	if ((cpu_feature & CPUID_CX8) == 0) {
1904e76af6aSGleb Smirnoff 		critical_enter();
1914e76af6aSGleb Smirnoff 		*(uint64_t *)zpcpu_get(c) += inc;
1924e76af6aSGleb Smirnoff 		critical_exit();
1934e76af6aSGleb Smirnoff 	} else {
1944e76af6aSGleb Smirnoff 		counter_64_inc_8b(c, inc);
1954e76af6aSGleb Smirnoff 	}
1964e76af6aSGleb Smirnoff }
1974e76af6aSGleb Smirnoff 
1984e76af6aSGleb Smirnoff #endif	/* ! __MACHINE_COUNTER_H__ */
199