xref: /xnu-10063.121.3/osfmk/kern/counter.h (revision 2c2f96dc2b9a4408a43d3150ae9c105355ca3daa)
1*2c2f96dcSApple OSS Distributions /*
2*2c2f96dcSApple OSS Distributions  * Copyright (c) 2020 Apple Inc. All rights reserved.
3*2c2f96dcSApple OSS Distributions  *
4*2c2f96dcSApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*2c2f96dcSApple OSS Distributions  *
6*2c2f96dcSApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*2c2f96dcSApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*2c2f96dcSApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*2c2f96dcSApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*2c2f96dcSApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*2c2f96dcSApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*2c2f96dcSApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*2c2f96dcSApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*2c2f96dcSApple OSS Distributions  *
15*2c2f96dcSApple OSS Distributions  * Please obtain a copy of the License at
16*2c2f96dcSApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*2c2f96dcSApple OSS Distributions  *
18*2c2f96dcSApple OSS Distributions  * The Original Code and all software distributed under the License are
19*2c2f96dcSApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*2c2f96dcSApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*2c2f96dcSApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*2c2f96dcSApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*2c2f96dcSApple OSS Distributions  * Please see the License for the specific language governing rights and
24*2c2f96dcSApple OSS Distributions  * limitations under the License.
25*2c2f96dcSApple OSS Distributions  *
26*2c2f96dcSApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*2c2f96dcSApple OSS Distributions  */
28*2c2f96dcSApple OSS Distributions #ifdef XNU_KERNEL_PRIVATE
29*2c2f96dcSApple OSS Distributions 
30*2c2f96dcSApple OSS Distributions #ifndef _KERN_COUNTER_H
31*2c2f96dcSApple OSS Distributions #define _KERN_COUNTER_H
32*2c2f96dcSApple OSS Distributions 
33*2c2f96dcSApple OSS Distributions /*!
34*2c2f96dcSApple OSS Distributions  * @file <kern/counter.h>
35*2c2f96dcSApple OSS Distributions  *
36*2c2f96dcSApple OSS Distributions  * @brief
37*2c2f96dcSApple OSS Distributions  * Module for working with 64bit relaxed atomic counters.
38*2c2f96dcSApple OSS Distributions  *
39*2c2f96dcSApple OSS Distributions  * @discussion
40*2c2f96dcSApple OSS Distributions  * Different counter types have different speed-memory tradeoffs, but
41*2c2f96dcSApple OSS Distributions  * they all share a common interface.
42*2c2f96dcSApple OSS Distributions  *
43*2c2f96dcSApple OSS Distributions  * Counters can be statically allocated or dynamically allocated.
44*2c2f96dcSApple OSS Distributions  *
45*2c2f96dcSApple OSS Distributions  * Statically allocated counters are always backed by per-cpu storage which means
46*2c2f96dcSApple OSS Distributions  * writes take place on the current CPUs value and reads sum all of the per-cpu values.
47*2c2f96dcSApple OSS Distributions  *
48*2c2f96dcSApple OSS Distributions  * Dynamically allocated counters can be either per-cpu or use a single 64bit value.
49*2c2f96dcSApple OSS Distributions  * To create a per-cpu counter, use the scalable_counter_t type. Note that this
50*2c2f96dcSApple OSS Distributions  * trades of additional memory for better scalability.
51*2c2f96dcSApple OSS Distributions  * To create a single 64bit counter, use the atomic_counter_t type.
52*2c2f96dcSApple OSS Distributions  *
53*2c2f96dcSApple OSS Distributions  * For most counters you can just use the counter_t type and the choice of
54*2c2f96dcSApple OSS Distributions  * scalable or atomic will be made at compile time based on the target.
55*2c2f96dcSApple OSS Distributions  *
56*2c2f96dcSApple OSS Distributions  * The counter types are opaque handles. They ARE NOT COPYABLE. If you need
57*2c2f96dcSApple OSS Distributions  * to make a copy of a counter, you should do so like this:
58*2c2f96dcSApple OSS Distributions  * <code>
59*2c2f96dcSApple OSS Distributions  * counter_t original;
60*2c2f96dcSApple OSS Distributions  * ...
61*2c2f96dcSApple OSS Distributions  * counter_t copy;
62*2c2f96dcSApple OSS Distributions  * counter_alloc(&copy);
63*2c2f96dcSApple OSS Distributions  * counter_add(&copy, counter_load(&original));
64*2c2f96dcSApple OSS Distributions  * ...
65*2c2f96dcSApple OSS Distributions  * // Make sure to free them at some point.
66*2c2f96dcSApple OSS Distributions  * counter_free(&original);
67*2c2f96dcSApple OSS Distributions  * counter_free(&copy);
68*2c2f96dcSApple OSS Distributions  * </code>
69*2c2f96dcSApple OSS Distributions  *
70*2c2f96dcSApple OSS Distributions  * Static counter example:
71*2c2f96dcSApple OSS Distributions  * <code>
72*2c2f96dcSApple OSS Distributions  * SCALABLE_COUNTER_DEFINE(my_counter);
73*2c2f96dcSApple OSS Distributions  * ...
74*2c2f96dcSApple OSS Distributions  * counter_inc(&my_counter);
75*2c2f96dcSApple OSS Distributions  * assert(counter_load(&my_counter) == 1);
76*2c2f96dcSApple OSS Distributions  * </code>
77*2c2f96dcSApple OSS Distributions  *
78*2c2f96dcSApple OSS Distributions  * Dynamic Counter Example:
79*2c2f96dcSApple OSS Distributions  * <code>
80*2c2f96dcSApple OSS Distributions  * scalable_counter_t my_percpu_counter;
81*2c2f96dcSApple OSS Distributions  * atomic_counter_t my_atomic_counter;
82*2c2f96dcSApple OSS Distributions  * counter_t my_counter;
83*2c2f96dcSApple OSS Distributions  *
84*2c2f96dcSApple OSS Distributions  * // All three counters share the same interface. So to change the speed-memory
85*2c2f96dcSApple OSS Distributions  * // tradeoff just change the type.
86*2c2f96dcSApple OSS Distributions  * counter_init(&my_scalable_counter);
87*2c2f96dcSApple OSS Distributions  * counter_init(&my_atomic_counter);
88*2c2f96dcSApple OSS Distributions  * counter_init(&my_counter);
89*2c2f96dcSApple OSS Distributions  *
90*2c2f96dcSApple OSS Distributions  * counter_inc(&my_scalable_counter);
91*2c2f96dcSApple OSS Distributions  * counter_inc(&my_atomic_counter);
92*2c2f96dcSApple OSS Distributions  * counter_inc(&my_counter);
93*2c2f96dcSApple OSS Distributions  *
94*2c2f96dcSApple OSS Distributions  * assert(counter_load(&my_scalable_counter) == 1);
95*2c2f96dcSApple OSS Distributions  * assert(counter_load(&my_atomic_counter) == 1);
96*2c2f96dcSApple OSS Distributions  * assert(counter_load(&my_counter) == 1);
97*2c2f96dcSApple OSS Distributions  * </code>
98*2c2f96dcSApple OSS Distributions  */
99*2c2f96dcSApple OSS Distributions 
100*2c2f96dcSApple OSS Distributions #include <mach/mach_types.h>
101*2c2f96dcSApple OSS Distributions #include <kern/macro_help.h>
102*2c2f96dcSApple OSS Distributions #include <kern/startup.h>
103*2c2f96dcSApple OSS Distributions #include <kern/zalloc.h>
104*2c2f96dcSApple OSS Distributions 
105*2c2f96dcSApple OSS Distributions typedef uint64_t *__zpercpu scalable_counter_t;
106*2c2f96dcSApple OSS Distributions typedef uint64_t atomic_counter_t;
107*2c2f96dcSApple OSS Distributions /* Generic counter base type. Does not have an implementation. */
108*2c2f96dcSApple OSS Distributions struct generic_counter_t;
109*2c2f96dcSApple OSS Distributions 
110*2c2f96dcSApple OSS Distributions /*!
111*2c2f96dcSApple OSS Distributions  * @macro SCALABLE_COUNTER_DECLARE
112*2c2f96dcSApple OSS Distributions  *
113*2c2f96dcSApple OSS Distributions  * @abstract
114*2c2f96dcSApple OSS Distributions  * (optionally) declares a static per-cpu counter (in a header).
115*2c2f96dcSApple OSS Distributions  *
116*2c2f96dcSApple OSS Distributions  * @param var           the name of the counter.
117*2c2f96dcSApple OSS Distributions  */
118*2c2f96dcSApple OSS Distributions #define SCALABLE_COUNTER_DECLARE(name) \
119*2c2f96dcSApple OSS Distributions 	extern scalable_counter_t name;
120*2c2f96dcSApple OSS Distributions 
121*2c2f96dcSApple OSS Distributions /*!
122*2c2f96dcSApple OSS Distributions  * @macro SCALABLE_COUNTER_DEFINE
123*2c2f96dcSApple OSS Distributions  *
124*2c2f96dcSApple OSS Distributions  * @abstract
125*2c2f96dcSApple OSS Distributions  * Defines a static per-cpu counter.
126*2c2f96dcSApple OSS Distributions  * Counter can only be accessed after the TUNABLES phase of startup.
127*2c2f96dcSApple OSS Distributions  *
128*2c2f96dcSApple OSS Distributions  * @param var           the name of the counter.
129*2c2f96dcSApple OSS Distributions  */
130*2c2f96dcSApple OSS Distributions #define SCALABLE_COUNTER_DEFINE(name) \
131*2c2f96dcSApple OSS Distributions 	__startup_data uint64_t __ ##name##_early_storage = 0;                                   \
132*2c2f96dcSApple OSS Distributions 	scalable_counter_t name = {&__##name##_early_storage};                                   \
133*2c2f96dcSApple OSS Distributions 	STARTUP_ARG(TUNABLES, STARTUP_RANK_MIDDLE, scalable_counter_static_boot_mangle, &name);  \
134*2c2f96dcSApple OSS Distributions 	STARTUP_ARG(PERCPU, STARTUP_RANK_SECOND, scalable_counter_static_init, &name);
135*2c2f96dcSApple OSS Distributions 
136*2c2f96dcSApple OSS Distributions /*
137*2c2f96dcSApple OSS Distributions  * Initialize a per-cpu counter.
138*2c2f96dcSApple OSS Distributions  * May block and will never fail.
139*2c2f96dcSApple OSS Distributions  * This counter must be freed with counter_free.
140*2c2f96dcSApple OSS Distributions  */
141*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
142*2c2f96dcSApple OSS Distributions extern void counter_alloc(struct generic_counter_t *);
143*2c2f96dcSApple OSS Distributions 
144*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
145*2c2f96dcSApple OSS Distributions extern void counter_free(struct generic_counter_t *);
146*2c2f96dcSApple OSS Distributions /*
147*2c2f96dcSApple OSS Distributions  * Add amount to counter.
148*2c2f96dcSApple OSS Distributions  * @param amount The amount to add.
149*2c2f96dcSApple OSS Distributions  */
150*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
151*2c2f96dcSApple OSS Distributions extern void counter_add(struct generic_counter_t *, uint64_t amount);
152*2c2f96dcSApple OSS Distributions 
153*2c2f96dcSApple OSS Distributions /*
154*2c2f96dcSApple OSS Distributions  * Add 1 to this counter.
155*2c2f96dcSApple OSS Distributions  */
156*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
157*2c2f96dcSApple OSS Distributions extern void counter_inc(struct generic_counter_t *);
158*2c2f96dcSApple OSS Distributions 
159*2c2f96dcSApple OSS Distributions /*
160*2c2f96dcSApple OSS Distributions  * Subtract 1 from this counter.
161*2c2f96dcSApple OSS Distributions  */
162*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
163*2c2f96dcSApple OSS Distributions extern void counter_dec(struct generic_counter_t *);
164*2c2f96dcSApple OSS Distributions 
165*2c2f96dcSApple OSS Distributions /* Variants of the above operations where the caller takes responsibility for disabling preemption. */
166*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
167*2c2f96dcSApple OSS Distributions extern void counter_add_preemption_disabled(struct generic_counter_t *, uint64_t amount);
168*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
169*2c2f96dcSApple OSS Distributions extern void counter_inc_preemption_disabled(struct generic_counter_t *);
170*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
171*2c2f96dcSApple OSS Distributions extern void counter_dec_preemption_disabled(struct generic_counter_t *);
172*2c2f96dcSApple OSS Distributions 
173*2c2f96dcSApple OSS Distributions /*
174*2c2f96dcSApple OSS Distributions  * Read the value of the percpu counter.
175*2c2f96dcSApple OSS Distributions  * Note that this will cause synchronization of all the sharded values.
176*2c2f96dcSApple OSS Distributions  */
177*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE
178*2c2f96dcSApple OSS Distributions extern uint64_t counter_load(struct generic_counter_t *);
179*2c2f96dcSApple OSS Distributions 
180*2c2f96dcSApple OSS Distributions #pragma mark implementation details
181*2c2f96dcSApple OSS Distributions /* NB: Nothing below here should be used directly. */
182*2c2f96dcSApple OSS Distributions 
183*2c2f96dcSApple OSS Distributions __startup_func void scalable_counter_static_boot_mangle(scalable_counter_t *counter);
184*2c2f96dcSApple OSS Distributions __startup_func void scalable_counter_static_init(scalable_counter_t *counter);
185*2c2f96dcSApple OSS Distributions 
186*2c2f96dcSApple OSS Distributions #if XNU_TARGET_OS_WATCH || XNU_TARGET_OS_TV
187*2c2f96dcSApple OSS Distributions #define ATOMIC_COUNTER_USE_PERCPU 0
188*2c2f96dcSApple OSS Distributions #else
189*2c2f96dcSApple OSS Distributions #define ATOMIC_COUNTER_USE_PERCPU 1
190*2c2f96dcSApple OSS Distributions #endif /* XNU_TARGET_OS_OSX */
191*2c2f96dcSApple OSS Distributions 
192*2c2f96dcSApple OSS Distributions #if ATOMIC_COUNTER_USE_PERCPU
193*2c2f96dcSApple OSS Distributions typedef scalable_counter_t counter_t;
194*2c2f96dcSApple OSS Distributions #else
195*2c2f96dcSApple OSS Distributions typedef atomic_counter_t counter_t;
196*2c2f96dcSApple OSS Distributions #endif /* ATOMIC_COUNTER_USE_PERCPU */
197*2c2f96dcSApple OSS Distributions 
198*2c2f96dcSApple OSS Distributions #define COUNTER_MAKE_PROTOTYPES(counter_t)                                 \
199*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
200*2c2f96dcSApple OSS Distributions extern void counter_alloc(counter_t *);                                    \
201*2c2f96dcSApple OSS Distributions                                                                            \
202*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
203*2c2f96dcSApple OSS Distributions extern void counter_free(counter_t *);                                     \
204*2c2f96dcSApple OSS Distributions                                                                            \
205*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
206*2c2f96dcSApple OSS Distributions extern void counter_add(counter_t *, uint64_t amount);                     \
207*2c2f96dcSApple OSS Distributions                                                                            \
208*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
209*2c2f96dcSApple OSS Distributions extern void counter_inc(counter_t *);                                      \
210*2c2f96dcSApple OSS Distributions                                                                            \
211*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
212*2c2f96dcSApple OSS Distributions extern void counter_dec(counter_t *);                                      \
213*2c2f96dcSApple OSS Distributions                                                                            \
214*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
215*2c2f96dcSApple OSS Distributions extern void counter_add_preemption_disabled(counter_t *, uint64_t amount); \
216*2c2f96dcSApple OSS Distributions                                                                            \
217*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
218*2c2f96dcSApple OSS Distributions extern void counter_inc_preemption_disabled(counter_t *);                  \
219*2c2f96dcSApple OSS Distributions                                                                            \
220*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
221*2c2f96dcSApple OSS Distributions extern void counter_dec_preemption_disabled(counter_t *);                  \
222*2c2f96dcSApple OSS Distributions                                                                            \
223*2c2f96dcSApple OSS Distributions OS_OVERLOADABLE                                                            \
224*2c2f96dcSApple OSS Distributions extern uint64_t counter_load(counter_t *);
225*2c2f96dcSApple OSS Distributions 
226*2c2f96dcSApple OSS Distributions COUNTER_MAKE_PROTOTYPES(scalable_counter_t);
227*2c2f96dcSApple OSS Distributions COUNTER_MAKE_PROTOTYPES(atomic_counter_t);
228*2c2f96dcSApple OSS Distributions 
229*2c2f96dcSApple OSS Distributions #endif /* _KERN_COUNTER_H */
230*2c2f96dcSApple OSS Distributions 
231*2c2f96dcSApple OSS Distributions #endif /* XNU_KERNEL_PRIVATE */
232