1 /*
2 * Copyright (c) 2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #ifndef _KERN_PERCPU_H_
30 #define _KERN_PERCPU_H_
31
32 #include <mach/vm_types.h>
33
34 __BEGIN_DECLS
35
36 #if XNU_KERNEL_PRIVATE
37 #include <libkern/section_keywords.h>
38
39 #pragma GCC visibility push(hidden)
40
41 /*!
42 * @macro PERCPU_DECL
43 *
44 * @abstract
45 * Declares a per-CPU variable in a header.
46 *
47 * @param type_t the per-CPU variable type
48 * @param name the per-CPU variable name
49 */
50 #define PERCPU_DECL(type_t, name) \
51 extern type_t __PERCPU_NAME(name)
52
53 /*!
54 * @macro PERCPU_DATA
55 *
56 * @abstract
57 * Defines a per-CPU variable in a translation unit.
58 *
59 * @discussion
60 * @c PERCPU_DECL can be used in headers to export the variable to clients.
61 *
62 * By default, per-cpu data is 0-initialized. Per-CPU data is allocated during
63 * the STARTUP_SUB_KMEM phase and can be initialized with a STARTUP
64 * callback in any later phase.
65 *
66 * Usage is:
67 * <code>
68 * [ static ] type PERCPU_DATA(name);
69 * </code>
70 *
71 * @param name the per-CPU variable name
72 */
73 #define PERCPU_DATA(name) \
74 __percpu __PERCPU_NAME(name) = {0}
75
76 /*!
77 * @macro PERCPU_GET
78 *
79 * @abstract
80 * Gets a pointer to the per-CPU instance of the variable for the processor the
81 * code is currently running on.
82 *
83 * @discussion
84 * It is expected that preemption or interrupts are disabled when this is used,
85 * as a context-switch might move the current thread to another CPU.
86 *
87 * It is also valid in code that wasn't already disabling preemption and cares
88 * about code-gen size a lot to use this outside of a preemption-disabled
89 * section provided that the data is modified using atomics.
90 *
91 * Note that if several per-CPU pointers are acquired in short succession,
92 * @c PERCPU_GET_WITH_BASE can be used to avoid the repeated calls to
93 * @c current_percpu_base() which the compiler wont't elide.
94 *
95 * @param name the per-CPU variable name
96 */
97 #define PERCPU_GET(name) \
98 __PERCPU_CAST(name, current_percpu_base() + __PERCPU_ADDR(name))
99
100 /*!
101 * @function current_percpu_base()
102 *
103 * @abstract
104 * Returns an offset that can be passed to @c PERCPU_GET_WITH_BASE().
105 *
106 * @see PERCPU_GET() for conditions of use.
107 */
108 extern vm_offset_t current_percpu_base(void);
109
110 /*!
111 * @function other_percpu_base()
112 *
113 * @abstract
114 * Returns an offset that can be passed to @c PERCPU_GET_WITH_BASE(),
115 * for the speficied cpu number.
116 *
117 * @param cpu_number the cpu number for which we want a base.
118 */
119 extern vm_offset_t other_percpu_base(int cpu_number);
120
121 /*!
122 * @macro PERCPU_GET_MASTER
123 *
124 * @abstract
125 * Gets a pointer to the master per-CPU instance of the variable.
126 *
127 * @param base the per-CPU base to use
128 * @param name the per-CPU variable name
129 */
130 #define PERCPU_GET_MASTER(name) \
131 (&__PERCPU_NAME(name))
132
133 /*!
134 * @macro PERCPU_GET_WITH_BASE
135 *
136 * @abstract
137 * Gets a pointer to the per-CPU instance of the variable for the specified
138 * base.
139 *
140 * @param base the per-CPU base to use
141 * @param name the per-CPU variable name
142 */
143 #define PERCPU_GET_WITH_BASE(base, name) \
144 __PERCPU_CAST(name, base + __PERCPU_ADDR(name))
145
146 /*!
147 * @macro PERCPU_GET_RELATIVE
148 *
149 * @abstract
150 * Gets a pointer to the per-CPU instance of a variable relative to another
151 * known one.
152 *
153 * @description
154 * When a per-CPU slot address is known, but the caller doesn't know the base
155 * from which it was derived, then this allows to compute another per-CPU slot
156 * address for a different variable but for the same CPU, without any loads.
157 *
158 * @param name the per-CPU variable name
159 * @param other the other per-CPU variable name
160 * @param ptr a pointer to the other variable slot
161 */
162 #define PERCPU_GET_RELATIVE(name, other, ptr) ({ \
163 __PERCPU_TYPE(other) __other_ptr = (ptr); /* type check */ \
164 vm_offset_t __offs = __PERCPU_ADDR(name) - __PERCPU_ADDR(other); \
165 __PERCPU_CAST(name, (vm_address_t)__other_ptr + __offs); \
166 })
167
168 /*!
169 * @macro percpu_foreach_base()
170 *
171 * @abstract
172 * Enumerates all Per-CPU variable bases.
173 *
174 * @param it the name of the iterator
175 */
176 #define percpu_foreach_base(it) \
177 for (vm_offset_t it = 0, \
178 __next_ ## it = percpu_base.start, \
179 __end_ ## it = percpu_base.end; \
180 \
181 it <= __end_ ## it; \
182 \
183 it = __next_ ## it, \
184 __next_ ## it += percpu_section_size())
185
186 /*!
187 * @macro percpu_foreach()
188 *
189 * @abstract
190 * Enumerates all Per-CPU variable instances.
191 *
192 * @param it the name of the iterator
193 * @param name the per-CPU variable name
194 */
195 #define percpu_foreach(it, name) \
196 for (__PERCPU_TYPE(name) it, \
197 __base_ ## it = NULL, \
198 __next_ ## it = (typeof(it))percpu_base.start, \
199 __end_ ## it = (typeof(it))percpu_base.end; \
200 \
201 (it = (typeof(it))(__PERCPU_ADDR(name) + (vm_address_t)__base_ ## it), \
202 __base_ ## it <= __end_ ## it); \
203 \
204 __base_ ## it = __next_ ## it, \
205 __next_ ## it = (typeof(it))((vm_address_t)__base_ ## it + percpu_section_size()))
206
207 /*!
208 * @macro percpu_foreach_secondary_base()
209 *
210 * @abstract
211 * Enumerates all Per-CPU variable bases, skipping the master slot.
212 *
213 * @param it the name of the iterator
214 */
215 #define percpu_foreach_secondary_base(it) \
216 for (vm_offset_t it = percpu_base.start, __end_ ## it = percpu_base.end; \
217 it <= __end_ ## it; it += percpu_section_size())
218
219 /*!
220 * @macro percpu_foreach_secondary()
221 *
222 * @abstract
223 * Enumerates all Per-CPU variable instances, skipping the master slot.
224 *
225 * @param it the name of the iterator
226 * @param name the per-CPU variable name
227 */
228 #define percpu_foreach_secondary(it, name) \
229 for (__PERCPU_TYPE(name) it, \
230 __base_ ## it = (typeof(it))percpu_base.start, \
231 __end_ ## it = (typeof(it))percpu_base.end; \
232 \
233 (it = (typeof(it))(__PERCPU_ADDR(name) + (vm_address_t)__base_ ## it), \
234 __base_ ## it <= __end_ ## it); \
235 \
236 __base_ ## it = (typeof(it))((vm_address_t)__base_ ## it + percpu_section_size()))
237
238 #pragma mark - implementation details
239
240 /*
241 * Below this point are implementation details that should not be used directly,
242 * except by the macros above, or architecture specific code.
243 */
244
245 #define __percpu __attribute__((section("__DATA, __percpu")))
246 #define __PERCPU_NAME(name) percpu_slot_ ## name
247 #define __PERCPU_ADDR(name) ((vm_offset_t)&__PERCPU_NAME(name))
248 #define __PERCPU_TYPE(name) typeof(&__PERCPU_NAME(name))
249 #define __PERCPU_CAST(name, expr) ((__PERCPU_TYPE(name))(expr))
250
251 /*
252 * Note for implementors:
253 *
254 * A `base` represents a pointer in the percpu allocation offset by
255 * `percpu_section_start()` so that PERCPU_GET() is a single addition.
256 *
257 * percpu_base.end is inclusive, so that percpu_foreach() and
258 * percpu_foreach_base() can do a `<=` comparison.
259 *
260 * Because the first base is `0` (because the master CPU is using the static
261 * percpu section), it allows for the compiler to know that for the first
262 * iteration the comparison is always true.
263 */
264 extern struct percpu_base {
265 vm_address_t start;
266 vm_address_t end;
267 vm_offset_t size;
268 } percpu_base;
269
270 static __pure2 inline vm_offset_t
percpu_section_start(void)271 percpu_section_start(void)
272 {
273 extern char __percpu_section_start[0] __SECTION_START_SYM("__DATA", "__percpu");
274 return (vm_offset_t)__percpu_section_start;
275 }
276
277 static __pure2 inline vm_offset_t
percpu_section_end(void)278 percpu_section_end(void)
279 {
280 extern char __percpu_section_end[0] __SECTION_END_SYM("__DATA", "__percpu");
281 return (vm_offset_t)__percpu_section_end;
282 }
283
284 static __pure2 inline vm_size_t
percpu_section_size(void)285 percpu_section_size(void)
286 {
287 return percpu_section_end() - percpu_section_start();
288 }
289
290 #pragma GCC visibility pop
291 #endif /* XNU_KERNEL_PRIVATE */
292
293 __END_DECLS
294
295 #endif /* _KERN_PERCPU_H_ */
296