]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
1 | /* |
2 | * Copyright (c) 2017 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 | #include <kern/assert.h> | |
30 | #include <kern/monotonic.h> | |
31 | #include <kern/thread.h> | |
32 | #include <machine/atomic.h> | |
33 | #include <machine/monotonic.h> | |
34 | #include <mach/mach_traps.h> | |
35 | #include <stdatomic.h> | |
36 | #include <sys/errno.h> | |
37 | ||
38 | bool mt_debug = false; | |
39 | _Atomic uint64_t mt_pmis = 0; | |
40 | _Atomic uint64_t mt_retrograde = 0; | |
41 | ||
42 | #define MT_KDBG_INSTRS_CYCLES(CODE) \ | |
43 | KDBG_EVENTID(DBG_MONOTONIC, DBG_MT_INSTRS_CYCLES, CODE) | |
44 | ||
45 | #define MT_KDBG_IC_CPU_CSWITCH MT_KDBG_INSTRS_CYCLES(1) | |
46 | ||
47 | /* | |
48 | * Updating the thread counters takes place in the context switch path, so it | |
49 | * cannot introduce too much overhead. Thus, updating takes no locks, instead | |
50 | * updating a generation count to an odd value to indicate that it's in the | |
51 | * critical section and that readers should wait until the generation count | |
52 | * returns to an even value. | |
53 | * | |
54 | * Reading the counters also needs to not see any "torn" states of the counters, | |
55 | * where a few of the counters are from a previous state and the rest are from | |
56 | * the current state. For this reason, the reader redrives the entire read | |
57 | * operation if it sees mismatching generation counts at the beginning and end | |
58 | * of reading. | |
59 | */ | |
60 | ||
61 | #define MAXSPINS 100 | |
62 | #define MAXRETRIES 10 | |
63 | ||
d9a64523 A |
64 | /* |
65 | * Write the fixed counter values for the thread `thread` into `counts_out`. | |
66 | * | |
67 | * This function does not include the accumulated counter values since the | |
68 | * thread's last context switch or quantum expiration. | |
69 | */ | |
5ba3f43e A |
70 | int |
71 | mt_fixed_thread_counts(thread_t thread, uint64_t *counts_out) | |
72 | { | |
73 | uint64_t start_gen, end_gen; | |
74 | uint64_t spins = 0, retries = 0; | |
75 | uint64_t counts[MT_CORE_NFIXED]; | |
76 | ||
77 | /* | |
78 | * Try to read a thread's counter values by ensuring its gen count is | |
79 | * even. If it's odd, it means that a thread is trying to update its | |
80 | * counters. | |
81 | * | |
82 | * Spin until the gen count is even. | |
83 | */ | |
84 | spin: | |
85 | start_gen = atomic_load_explicit(&thread->t_monotonic.mth_gen, | |
0a7de745 | 86 | memory_order_acquire); |
5ba3f43e A |
87 | retry: |
88 | if (start_gen & 1) { | |
89 | spins++; | |
90 | if (spins > MAXSPINS) { | |
91 | return EBUSY; | |
92 | } | |
93 | goto spin; | |
94 | } | |
95 | ||
96 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
97 | counts[i] = thread->t_monotonic.mth_counts[i]; | |
98 | } | |
99 | ||
100 | /* | |
101 | * After reading the counters, check the gen count again. If it is | |
102 | * different from the value that we started with, the thread raced | |
103 | * writing its counters with us reading them. We need to redrive the | |
104 | * entire operation. | |
105 | * | |
106 | * Go back to check if the value we just read was even and try to read | |
107 | * again. | |
108 | */ | |
109 | end_gen = atomic_load_explicit(&thread->t_monotonic.mth_gen, | |
0a7de745 | 110 | memory_order_acquire); |
5ba3f43e A |
111 | if (end_gen != start_gen) { |
112 | retries++; | |
113 | if (retries > MAXRETRIES) { | |
114 | return EAGAIN; | |
115 | } | |
116 | start_gen = end_gen; | |
117 | goto retry; | |
118 | } | |
119 | ||
120 | /* | |
121 | * Only after getting a consistent snapshot of the counters should we | |
122 | * write them into the provided buffer. | |
123 | */ | |
124 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
125 | counts_out[i] = counts[i]; | |
126 | } | |
127 | return 0; | |
128 | } | |
129 | ||
130 | static void mt_fixed_counts_internal(uint64_t *counts, uint64_t *counts_since); | |
131 | ||
132 | bool | |
133 | mt_update_thread(thread_t thread) | |
134 | { | |
135 | if (!mt_core_supported) { | |
136 | return false; | |
137 | } | |
138 | ||
139 | assert(ml_get_interrupts_enabled() == FALSE); | |
140 | ||
141 | uint64_t counts[MT_CORE_NFIXED], counts_since[MT_CORE_NFIXED]; | |
142 | mt_fixed_counts_internal(counts, counts_since); | |
143 | ||
144 | /* | |
145 | * Enter the update cycle by incrementing the gen count to be odd -- | |
146 | * this tells any readers to spin on the gen count, waiting for it to go | |
147 | * even. | |
148 | */ | |
149 | __assert_only uint64_t enter_gen = atomic_fetch_add_explicit( | |
0a7de745 | 150 | &thread->t_monotonic.mth_gen, 1, memory_order_release); |
5ba3f43e A |
151 | /* |
152 | * Should not have pre-empted a modification to the counts. | |
153 | */ | |
154 | assert((enter_gen & 1) == 0); | |
155 | ||
156 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
157 | thread->t_monotonic.mth_counts[i] += counts_since[i]; | |
158 | } | |
159 | ||
160 | /* | |
161 | * Exit the update by making the gen count even again. Readers check | |
162 | * the gen count for equality, and will redrive the reads if the values | |
163 | * before and after reading don't match. | |
164 | */ | |
165 | __assert_only uint64_t exit_gen = atomic_fetch_add_explicit( | |
0a7de745 | 166 | &thread->t_monotonic.mth_gen, 1, memory_order_release); |
5ba3f43e A |
167 | /* |
168 | * Make sure no other writers came through behind us. | |
169 | */ | |
170 | assert(exit_gen == (enter_gen + 1)); | |
171 | ||
172 | return true; | |
173 | } | |
174 | ||
175 | void | |
176 | mt_sched_update(thread_t thread) | |
177 | { | |
178 | bool updated = mt_update_thread(thread); | |
179 | if (!updated) { | |
180 | return; | |
181 | } | |
182 | ||
183 | if (kdebug_debugid_explicitly_enabled(MT_KDBG_IC_CPU_CSWITCH)) { | |
184 | struct mt_cpu *mtc = mt_cur_cpu(); | |
185 | ||
186 | KDBG_RELEASE(MT_KDBG_IC_CPU_CSWITCH, | |
187 | #ifdef MT_CORE_INSTRS | |
0a7de745 | 188 | mtc->mtc_counts[MT_CORE_INSTRS], |
5ba3f43e | 189 | #else /* defined(MT_CORE_INSTRS) */ |
0a7de745 | 190 | 0, |
5ba3f43e | 191 | #endif /* !defined(MT_CORE_INSTRS) */ |
0a7de745 | 192 | mtc->mtc_counts[MT_CORE_CYCLES]); |
5ba3f43e A |
193 | } |
194 | } | |
195 | ||
196 | int | |
197 | mt_fixed_task_counts(task_t task, uint64_t *counts_out) | |
198 | { | |
199 | assert(task != TASK_NULL); | |
200 | assert(counts_out != NULL); | |
201 | ||
5ba3f43e | 202 | if (!mt_core_supported) { |
e8c3f781 A |
203 | memset(counts_out, 0, sizeof(*counts_out) * MT_CORE_NFIXED); |
204 | return 1; | |
5ba3f43e A |
205 | } |
206 | ||
207 | task_lock(task); | |
208 | ||
e8c3f781 | 209 | uint64_t counts[MT_CORE_NFIXED] = { 0 }; |
5ba3f43e A |
210 | for (int i = 0; i < MT_CORE_NFIXED; i++) { |
211 | counts[i] = task->task_monotonic.mtk_counts[i]; | |
212 | } | |
213 | ||
e8c3f781 | 214 | uint64_t thread_counts[MT_CORE_NFIXED] = { 0 }; |
5ba3f43e A |
215 | thread_t thread = THREAD_NULL; |
216 | thread_t curthread = current_thread(); | |
217 | bool needs_current = false; | |
218 | int r = 0; | |
219 | queue_iterate(&task->threads, thread, thread_t, task_threads) { | |
220 | /* | |
221 | * Get the current thread's counters after doing this | |
222 | * processing, without holding the task lock. | |
223 | */ | |
224 | if (thread == curthread) { | |
225 | needs_current = true; | |
226 | continue; | |
227 | } else { | |
228 | r = mt_fixed_thread_counts(thread, thread_counts); | |
229 | if (r) { | |
230 | goto error; | |
231 | } | |
232 | } | |
233 | ||
234 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
235 | counts[i] += thread_counts[i]; | |
236 | } | |
237 | } | |
238 | ||
239 | task_unlock(task); | |
240 | ||
241 | if (needs_current) { | |
242 | mt_cur_thread_fixed_counts(thread_counts); | |
243 | } | |
244 | ||
245 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
246 | if (needs_current) { | |
247 | counts[i] += thread_counts[i]; | |
248 | } | |
249 | counts_out[i] = counts[i]; | |
250 | } | |
251 | return 0; | |
252 | ||
253 | error: | |
254 | task_unlock(task); | |
255 | return r; | |
256 | } | |
257 | ||
258 | uint64_t | |
259 | mt_mtc_update_count(struct mt_cpu *mtc, unsigned int ctr) | |
260 | { | |
261 | uint64_t snap = mt_core_snap(ctr); | |
262 | if (snap < mtc->mtc_snaps[ctr]) { | |
263 | if (mt_debug) { | |
264 | kprintf("monotonic: cpu %d: thread %#llx: " | |
0a7de745 A |
265 | "retrograde counter %u value: %llu, last read = %llu\n", |
266 | cpu_number(), thread_tid(current_thread()), ctr, snap, | |
267 | mtc->mtc_snaps[ctr]); | |
5ba3f43e A |
268 | } |
269 | (void)atomic_fetch_add_explicit(&mt_retrograde, 1, | |
0a7de745 | 270 | memory_order_relaxed); |
5ba3f43e A |
271 | mtc->mtc_snaps[ctr] = snap; |
272 | return 0; | |
273 | } | |
274 | ||
275 | uint64_t count = snap - mtc->mtc_snaps[ctr]; | |
276 | mtc->mtc_snaps[ctr] = snap; | |
277 | ||
278 | return count; | |
279 | } | |
280 | ||
281 | uint64_t | |
282 | mt_cpu_update_count(cpu_data_t *cpu, unsigned int ctr) | |
283 | { | |
284 | return mt_mtc_update_count(&cpu->cpu_monotonic, ctr); | |
285 | } | |
286 | ||
287 | static void | |
288 | mt_fixed_counts_internal(uint64_t *counts, uint64_t *counts_since) | |
289 | { | |
290 | assert(ml_get_interrupts_enabled() == FALSE); | |
291 | ||
292 | struct mt_cpu *mtc = mt_cur_cpu(); | |
293 | assert(mtc != NULL); | |
294 | ||
295 | mt_mtc_update_fixed_counts(mtc, counts, counts_since); | |
296 | } | |
297 | ||
298 | void | |
299 | mt_mtc_update_fixed_counts(struct mt_cpu *mtc, uint64_t *counts, | |
0a7de745 | 300 | uint64_t *counts_since) |
5ba3f43e A |
301 | { |
302 | if (!mt_core_supported) { | |
303 | return; | |
304 | } | |
305 | ||
306 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
307 | uint64_t last_delta; | |
308 | uint64_t count; | |
309 | ||
310 | last_delta = mt_mtc_update_count(mtc, i); | |
311 | count = mtc->mtc_counts[i] + last_delta; | |
312 | ||
313 | if (counts) { | |
314 | counts[i] = count; | |
315 | } | |
316 | if (counts_since) { | |
317 | assert(counts != NULL); | |
318 | counts_since[i] = count - mtc->mtc_counts_last[i]; | |
319 | mtc->mtc_counts_last[i] = count; | |
320 | } | |
321 | ||
322 | mtc->mtc_counts[i] = count; | |
323 | } | |
324 | } | |
325 | ||
326 | void | |
327 | mt_update_fixed_counts(void) | |
328 | { | |
329 | assert(ml_get_interrupts_enabled() == FALSE); | |
330 | ||
331 | #if defined(__x86_64__) | |
332 | __builtin_ia32_lfence(); | |
333 | #elif defined(__arm__) || defined(__arm64__) | |
334 | __builtin_arm_isb(ISB_SY); | |
335 | #endif /* !defined(__x86_64__) && (defined(__arm__) || defined(__arm64__)) */ | |
336 | ||
337 | mt_fixed_counts_internal(NULL, NULL); | |
338 | } | |
339 | ||
340 | void | |
341 | mt_fixed_counts(uint64_t *counts) | |
342 | { | |
343 | #if defined(__x86_64__) | |
344 | __builtin_ia32_lfence(); | |
345 | #elif defined(__arm__) || defined(__arm64__) | |
346 | __builtin_arm_isb(ISB_SY); | |
347 | #endif /* !defined(__x86_64__) && (defined(__arm__) || defined(__arm64__)) */ | |
348 | ||
349 | int intrs_en = ml_set_interrupts_enabled(FALSE); | |
350 | mt_fixed_counts_internal(counts, NULL); | |
351 | ml_set_interrupts_enabled(intrs_en); | |
352 | } | |
353 | ||
354 | void | |
355 | mt_cur_thread_fixed_counts(uint64_t *counts) | |
356 | { | |
357 | if (!mt_core_supported) { | |
e8c3f781 | 358 | memset(counts, 0, sizeof(*counts) * MT_CORE_NFIXED); |
5ba3f43e A |
359 | return; |
360 | } | |
361 | ||
362 | thread_t curthread = current_thread(); | |
363 | int intrs_en = ml_set_interrupts_enabled(FALSE); | |
364 | (void)mt_update_thread(curthread); | |
365 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
366 | counts[i] = curthread->t_monotonic.mth_counts[i]; | |
367 | } | |
368 | ml_set_interrupts_enabled(intrs_en); | |
369 | } | |
370 | ||
371 | void | |
372 | mt_cur_task_fixed_counts(uint64_t *counts) | |
373 | { | |
374 | task_t curtask = current_task(); | |
375 | ||
376 | mt_fixed_task_counts(curtask, counts); | |
377 | } | |
378 | ||
379 | /* FIXME these should only update the counter that is being accessed */ | |
380 | ||
381 | uint64_t | |
382 | mt_cur_thread_instrs(void) | |
383 | { | |
384 | #ifdef MT_CORE_INSTRS | |
385 | thread_t curthread = current_thread(); | |
386 | boolean_t intrs_en; | |
387 | uint64_t count; | |
388 | ||
389 | if (!mt_core_supported) { | |
390 | return 0; | |
391 | } | |
392 | ||
393 | intrs_en = ml_set_interrupts_enabled(FALSE); | |
394 | (void)mt_update_thread(curthread); | |
395 | count = curthread->t_monotonic.mth_counts[MT_CORE_INSTRS]; | |
396 | ml_set_interrupts_enabled(intrs_en); | |
397 | ||
398 | return count; | |
399 | #else /* defined(MT_CORE_INSTRS) */ | |
400 | return 0; | |
401 | #endif /* !defined(MT_CORE_INSTRS) */ | |
402 | } | |
403 | ||
404 | uint64_t | |
405 | mt_cur_thread_cycles(void) | |
406 | { | |
407 | thread_t curthread = current_thread(); | |
408 | boolean_t intrs_en; | |
409 | uint64_t count; | |
410 | ||
411 | if (!mt_core_supported) { | |
412 | return 0; | |
413 | } | |
414 | ||
415 | intrs_en = ml_set_interrupts_enabled(FALSE); | |
416 | (void)mt_update_thread(curthread); | |
417 | count = curthread->t_monotonic.mth_counts[MT_CORE_CYCLES]; | |
418 | ml_set_interrupts_enabled(intrs_en); | |
419 | ||
420 | return count; | |
421 | } | |
422 | ||
423 | uint64_t | |
424 | mt_cur_cpu_instrs(void) | |
425 | { | |
426 | #ifdef MT_CORE_INSTRS | |
427 | uint64_t counts[MT_CORE_NFIXED]; | |
428 | ||
429 | if (!mt_core_supported) { | |
430 | return 0; | |
431 | } | |
432 | ||
433 | mt_fixed_counts(counts); | |
434 | return counts[MT_CORE_INSTRS]; | |
435 | #else /* defined(MT_CORE_INSTRS) */ | |
436 | return 0; | |
437 | #endif /* !defined(MT_CORE_INSTRS) */ | |
438 | } | |
439 | ||
440 | uint64_t | |
441 | mt_cur_cpu_cycles(void) | |
442 | { | |
443 | uint64_t counts[MT_CORE_NFIXED]; | |
444 | ||
445 | if (!mt_core_supported) { | |
446 | return 0; | |
447 | } | |
448 | ||
449 | mt_fixed_counts(counts); | |
450 | return counts[MT_CORE_CYCLES]; | |
451 | } | |
452 | ||
453 | void | |
454 | mt_update_task(task_t task, thread_t thread) | |
455 | { | |
456 | task_lock_assert_owned(task); | |
457 | ||
458 | if (!mt_core_supported) { | |
459 | return; | |
460 | } | |
461 | ||
462 | for (int i = 0; i < MT_CORE_NFIXED; i++) { | |
463 | task->task_monotonic.mtk_counts[i] += thread->t_monotonic.mth_counts[i]; | |
464 | } | |
465 | } | |
466 | ||
467 | void | |
468 | mt_terminate_update(task_t task, thread_t thread) | |
469 | { | |
470 | mt_update_task(task, thread); | |
471 | } | |
472 | ||
473 | void | |
474 | mt_perfcontrol(uint64_t *instrs, uint64_t *cycles) | |
475 | { | |
476 | if (!mt_core_supported) { | |
477 | *instrs = 0; | |
478 | *cycles = 0; | |
479 | return; | |
480 | } | |
481 | ||
482 | struct mt_cpu *mtc = mt_cur_cpu(); | |
483 | ||
484 | /* | |
485 | * The performance controller queries the hardware directly, so provide the | |
486 | * last snapshot we took for the core. This is the value from when we | |
487 | * updated the thread counts. | |
488 | */ | |
489 | ||
490 | #ifdef MT_CORE_INSTRS | |
491 | *instrs = mtc->mtc_snaps[MT_CORE_INSTRS]; | |
492 | #else /* defined(MT_CORE_INSTRS) */ | |
493 | *instrs = 0; | |
494 | #endif /* !defined(MT_CORE_INSTRS) */ | |
495 | ||
496 | *cycles = mtc->mtc_snaps[MT_CORE_CYCLES]; | |
497 | } | |
498 | ||
499 | void | |
500 | mt_stackshot_thread(thread_t thread, uint64_t *instrs, uint64_t *cycles) | |
501 | { | |
502 | assert(mt_core_supported); | |
503 | ||
504 | #ifdef MT_CORE_INSTRS | |
505 | *instrs = thread->t_monotonic.mth_counts[MT_CORE_INSTRS]; | |
506 | #else /* defined(MT_CORE_INSTRS) */ | |
507 | *instrs = 0; | |
508 | #endif /* !defined(MT_CORE_INSTRS) */ | |
509 | ||
510 | *cycles = thread->t_monotonic.mth_counts[MT_CORE_CYCLES]; | |
511 | } | |
512 | ||
513 | void | |
514 | mt_stackshot_task(task_t task, uint64_t *instrs, uint64_t *cycles) | |
515 | { | |
516 | assert(mt_core_supported); | |
517 | ||
518 | #ifdef MT_CORE_INSTRS | |
519 | *instrs = task->task_monotonic.mtk_counts[MT_CORE_INSTRS]; | |
520 | #else /* defined(MT_CORE_INSTRS) */ | |
521 | *instrs = 0; | |
522 | #endif /* !defined(MT_CORE_INSTRS) */ | |
523 | ||
524 | *cycles = task->task_monotonic.mtk_counts[MT_CORE_CYCLES]; | |
525 | } | |
d9a64523 A |
526 | |
527 | /* | |
528 | * Maintain reset values for the fixed instruction and cycle counters so | |
529 | * clients can be notified after a given number of those events occur. This is | |
530 | * only used by microstackshot. | |
531 | */ | |
532 | ||
533 | bool mt_microstackshots = false; | |
534 | unsigned int mt_microstackshot_ctr = 0; | |
535 | mt_pmi_fn mt_microstackshot_pmi_handler = NULL; | |
536 | void *mt_microstackshot_ctx = NULL; | |
537 | uint64_t mt_core_reset_values[MT_CORE_NFIXED] = { 0 }; | |
538 | ||
539 | #define MT_MIN_FIXED_PERIOD (10 * 1000 * 1000) | |
540 | ||
541 | int | |
542 | mt_microstackshot_start(unsigned int ctr, uint64_t period, mt_pmi_fn handler, | |
0a7de745 | 543 | void *ctx) |
d9a64523 A |
544 | { |
545 | assert(ctr < MT_CORE_NFIXED); | |
546 | ||
547 | if (period < MT_MIN_FIXED_PERIOD) { | |
548 | return EINVAL; | |
549 | } | |
550 | if (mt_microstackshots) { | |
551 | return EBUSY; | |
552 | } | |
553 | ||
554 | mt_microstackshot_ctr = ctr; | |
555 | mt_microstackshot_pmi_handler = handler; | |
556 | mt_microstackshot_ctx = ctx; | |
557 | ||
558 | int error = mt_microstackshot_start_arch(period); | |
559 | if (error) { | |
0a7de745 A |
560 | mt_microstackshot_ctr = 0; |
561 | mt_microstackshot_pmi_handler = NULL; | |
562 | mt_microstackshot_ctx = NULL; | |
d9a64523 A |
563 | return error; |
564 | } | |
565 | ||
566 | mt_microstackshots = true; | |
567 | ||
568 | return 0; | |
569 | } | |
570 | ||
571 | int | |
572 | mt_microstackshot_stop(void) | |
573 | { | |
574 | mt_microstackshots = false; | |
575 | memset(mt_core_reset_values, 0, sizeof(mt_core_reset_values)); | |
576 | ||
577 | return 0; | |
578 | } |