1 /*
2 * Copyright (c) 2007-2021 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 * @OSF_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System Copyright (c) 1991,1990,1989,1988,1987 Carnegie
33 * Mellon University All Rights Reserved.
34 *
35 * Permission to use, copy, modify and distribute this software and its
36 * documentation is hereby granted, provided that both the copyright notice
37 * and this permission notice appear in all copies of the software,
38 * derivative works or modified versions, and any portions thereof, and that
39 * both notices appear in supporting documentation.
40 *
41 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.
42 * CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES
43 * WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
44 *
45 * Carnegie Mellon requests users of this software to return to
46 *
47 * Software Distribution Coordinator or [email protected]
48 * School of Computer Science Carnegie Mellon University Pittsburgh PA
49 * 15213-3890
50 *
51 * any improvements or extensions that they make and grant Carnegie Mellon the
52 * rights to redistribute these changes.
53 */
54 /*
55 * File: kern/lock.c
56 * Author: Avadis Tevanian, Jr., Michael Wayne Young
57 * Date: 1985
58 *
59 * Locking primitives implementation
60 */
61
62 #define LOCK_PRIVATE 1
63
64 #include <mach_ldebug.h>
65
66 #include <mach/machine/sdt.h>
67
68 #include <kern/locks_internal.h>
69 #include <kern/zalloc.h>
70 #include <kern/lock_stat.h>
71 #include <kern/locks.h>
72 #include <kern/misc_protos.h>
73 #include <kern/thread.h>
74 #include <kern/processor.h>
75 #include <kern/sched_hygiene.h>
76 #include <kern/sched_prim.h>
77 #include <kern/debug.h>
78 #include <kern/kcdata.h>
79 #include <kern/percpu.h>
80 #include <kern/hvg_hypercall.h>
81 #include <string.h>
82 #include <arm/cpu_internal.h>
83 #include <os/hash.h>
84 #include <arm/cpu_data.h>
85
86 #include <arm/cpu_data_internal.h>
87 #include <arm64/proc_reg.h>
88 #include <arm/smp.h>
89 #include <machine/atomic.h>
90 #include <machine/machine_cpu.h>
91
92 #include <pexpert/pexpert.h>
93
94 #include <sys/kdebug.h>
95
96 #define ANY_LOCK_DEBUG (USLOCK_DEBUG || LOCK_DEBUG || MUTEX_DEBUG)
97
98 // Panic in tests that check lock usage correctness
99 // These are undesirable when in a panic or a debugger is runnning.
100 #define LOCK_CORRECTNESS_PANIC() (kernel_debugger_entry_count == 0)
101
102 /* Forwards */
103
104 extern unsigned int not_in_kdp;
105
106 MACHINE_TIMEOUT(lock_panic_timeout, "lock-panic",
107 0xc00000 /* 12.5 m ticks ~= 524ms with 24MHz OSC */, MACHINE_TIMEOUT_UNIT_TIMEBASE, NULL);
108
109 #define NOINLINE __attribute__((noinline))
110
111 #define interrupts_disabled(mask) (mask & DAIF_IRQF)
112
113 KALLOC_TYPE_DEFINE(KT_LCK_SPIN, lck_spin_t, KT_PRIV_ACCT);
114
115 #pragma GCC visibility push(hidden)
116 /*
117 * atomic exchange API is a low level abstraction of the operations
118 * to atomically read, modify, and write a pointer. This abstraction works
119 * for both Intel and ARMv8.1 compare and exchange atomic instructions as
120 * well as the ARM exclusive instructions.
121 *
122 * atomic_exchange_begin() - begin exchange and retrieve current value
123 * atomic_exchange_complete() - conclude an exchange
124 * atomic_exchange_abort() - cancel an exchange started with atomic_exchange_begin()
125 */
126 uint32_t
load_exclusive32(uint32_t * target,enum memory_order ord)127 load_exclusive32(uint32_t *target, enum memory_order ord)
128 {
129 uint32_t value;
130
131 if (_os_atomic_mo_has_acquire(ord)) {
132 value = __builtin_arm_ldaex(target); // ldaxr
133 } else {
134 value = __builtin_arm_ldrex(target); // ldxr
135 }
136
137 return value;
138 }
139
140 boolean_t
store_exclusive32(uint32_t * target,uint32_t value,enum memory_order ord)141 store_exclusive32(uint32_t *target, uint32_t value, enum memory_order ord)
142 {
143 boolean_t err;
144
145 if (_os_atomic_mo_has_release(ord)) {
146 err = __builtin_arm_stlex(value, target); // stlxr
147 } else {
148 err = __builtin_arm_strex(value, target); // stxr
149 }
150
151 return !err;
152 }
153
154 uint32_t
atomic_exchange_begin32(uint32_t * target,uint32_t * previous,enum memory_order ord)155 atomic_exchange_begin32(uint32_t *target, uint32_t *previous, enum memory_order ord)
156 {
157 uint32_t val;
158
159 #if !OS_ATOMIC_USE_LLSC
160 ord = memory_order_relaxed;
161 #endif
162 val = load_exclusive32(target, ord);
163 *previous = val;
164 return val;
165 }
166
167 boolean_t
atomic_exchange_complete32(uint32_t * target,uint32_t previous,uint32_t newval,enum memory_order ord)168 atomic_exchange_complete32(uint32_t *target, uint32_t previous, uint32_t newval, enum memory_order ord)
169 {
170 #if !OS_ATOMIC_USE_LLSC
171 return __c11_atomic_compare_exchange_strong((_Atomic uint32_t *)target, &previous, newval, ord, memory_order_relaxed);
172 #else
173 (void)previous; // Previous not needed, monitor is held
174 return store_exclusive32(target, newval, ord);
175 #endif
176 }
177
178 void
atomic_exchange_abort(void)179 atomic_exchange_abort(void)
180 {
181 os_atomic_clear_exclusive();
182 }
183
184 boolean_t
atomic_test_and_set32(uint32_t * target,uint32_t test_mask,uint32_t set_mask,enum memory_order ord,boolean_t wait)185 atomic_test_and_set32(uint32_t *target, uint32_t test_mask, uint32_t set_mask, enum memory_order ord, boolean_t wait)
186 {
187 uint32_t value, prev;
188
189 for (;;) {
190 value = atomic_exchange_begin32(target, &prev, ord);
191 if (value & test_mask) {
192 if (wait) {
193 wait_for_event(); // Wait with monitor held
194 } else {
195 atomic_exchange_abort(); // Clear exclusive monitor
196 }
197 return FALSE;
198 }
199 value |= set_mask;
200 if (atomic_exchange_complete32(target, prev, value, ord)) {
201 return TRUE;
202 }
203 }
204 }
205
206 #pragma GCC visibility pop
207 #pragma mark preemption
208
209 #if SCHED_HYGIENE_DEBUG
210
211 uint64_t _Atomic PERCPU_DATA_HACK_78750602(preemption_disable_max_mt);
212
213 MACHINE_TIMEOUT_WRITEABLE(sched_preemption_disable_threshold_mt, "sched-preemption", 0, MACHINE_TIMEOUT_UNIT_TIMEBASE, kprintf_spam_mt_pred);
214
215 TUNABLE_DT_WRITEABLE(sched_hygiene_mode_t, sched_preemption_disable_debug_mode,
216 "machine-timeouts",
217 "sched-preemption-disable-mode", /* DT property names have to be 31 chars max */
218 "sched_preemption_disable_debug_mode",
219 SCHED_HYGIENE_MODE_OFF,
220 TUNABLE_DT_CHECK_CHOSEN);
221
222 static uint32_t const sched_preemption_disable_debug_dbgid = MACHDBG_CODE(DBG_MACH_SCHED, MACH_PREEMPTION_EXPIRED) | DBG_FUNC_NONE;
223
224 NOINLINE void
_prepare_preemption_disable_measurement(thread_t thread)225 _prepare_preemption_disable_measurement(thread_t thread)
226 {
227 if (thread->machine.inthandler_timestamp == 0) {
228 /*
229 * Only prepare a measurement if not currently in an interrupt
230 * handler.
231 *
232 * We are only interested in the net duration of disabled
233 * preemption, that is: The time in which preemption was
234 * disabled, minus the intervals in which any (likely
235 * unrelated) interrupts were handled.
236 * ml_adjust_preemption_disable_time() will remove those
237 * intervals, however we also do not even start measuring
238 * preemption disablement if we are already within handling of
239 * an interrupt when preemption was disabled (the resulting
240 * net time would be 0).
241 *
242 * Interrupt handling duration is handled separately, and any
243 * long intervals of preemption disablement are counted
244 * towards that.
245 */
246
247 bool istate = ml_set_interrupts_enabled_with_debug(false, false); // don't take int masked timestamp
248 thread->machine.preemption_disable_abandon = false;
249 thread->machine.preemption_disable_mt = ml_get_sched_hygiene_timebase();
250 thread->machine.preemption_disable_adjust = 0;
251 #if MONOTONIC
252 if (sched_hygiene_debug_pmc) {
253 mt_cur_cpu_cycles_instrs_speculative(&thread->machine.preemption_disable_cycles, &thread->machine.preemption_disable_instr);
254 }
255 #endif
256 ml_set_interrupts_enabled_with_debug(istate, false);
257 }
258 }
259
260 NOINLINE void
_collect_preemption_disable_measurement(thread_t thread)261 _collect_preemption_disable_measurement(thread_t thread)
262 {
263 bool istate = ml_set_interrupts_enabled_with_debug(false, false); // don't take int masked timestamp
264 /*
265 * Collect start time and current time with interrupts disabled.
266 * Otherwise an interrupt coming in after grabbing the timestamp
267 * could spuriously inflate the measurement, because it will
268 * adjust preemption_disable_mt only after we already grabbed
269 * it.
270 *
271 * (Even worse if we collected the current time first: Then a
272 * subsequent interrupt could adjust preemption_disable_mt to
273 * make the duration go negative after subtracting the already
274 * grabbed time. With interrupts disabled we don't care much about
275 * the order.)
276 */
277
278 uint64_t const mt = thread->machine.preemption_disable_mt;
279 uint64_t const adjust = thread->machine.preemption_disable_adjust;
280 uint64_t const now = ml_get_sched_hygiene_timebase();
281 thread->machine.preemption_disable_mt = 0;
282 thread->machine.preemption_disable_adjust = 0;
283
284 /*
285 * Don't need to reset (or even save) preemption_disable_abandon
286 * here: abandon_preemption_disable_measurement is a no-op anyway
287 * if preemption_disable_mt == 0 (which we just set), and it
288 * will stay that way until the next call to
289 * _collect_preemption_disable_measurement.
290 */
291
292 os_compiler_barrier(acq_rel);
293
294 ml_set_interrupts_enabled_with_debug(istate, false);
295
296 /*
297 * Fine to get with interrupts enabled:
298 * Above we set preemption_disable_mt to 0, which turns
299 * abandon_preemption_disable_measurement() into a no-op
300 * until the next collection starts.
301 */
302 if (thread->machine.preemption_disable_abandon) {
303 return;
304 }
305
306 int64_t const gross_duration = now - mt;
307 int64_t const net_duration = gross_duration - adjust;
308
309 uint64_t _Atomic * const max_duration = PERCPU_GET(preemption_disable_max_mt);
310
311 if (__improbable(net_duration > *max_duration)) {
312 os_atomic_store(max_duration, net_duration, relaxed);
313 }
314
315 uint64_t const threshold = os_atomic_load(&sched_preemption_disable_threshold_mt, relaxed);
316 if (__improbable(threshold > 0 && net_duration >= threshold)) {
317 uint64_t average_freq = 0;
318 uint64_t average_cpi_whole = 0;
319 uint64_t average_cpi_fractional = 0;
320
321 #if MONOTONIC
322 if (sched_hygiene_debug_pmc) {
323 uint64_t current_cycles = 0, current_instrs = 0;
324
325 /*
326 * We're getting these values a bit late, but getting them
327 * is a bit expensive, so we take the slight hit in
328 * accuracy for the reported values (which aren't very
329 * stable anyway).
330 */
331 istate = ml_set_interrupts_enabled_with_debug(false, false);
332 mt_cur_cpu_cycles_instrs_speculative(¤t_cycles, ¤t_instrs);
333 ml_set_interrupts_enabled_with_debug(istate, false);
334
335 uint64_t duration_ns;
336 absolutetime_to_nanoseconds(gross_duration, &duration_ns);
337
338 average_freq = (current_cycles - thread->machine.preemption_disable_cycles) / (duration_ns / 1000);
339 average_cpi_whole = (current_cycles - thread->machine.preemption_disable_cycles) / (current_instrs - thread->machine.preemption_disable_instr);
340 average_cpi_fractional =
341 (((current_cycles - thread->machine.preemption_disable_cycles) * 100) / (current_instrs - thread->machine.preemption_disable_instr)) % 100;
342 }
343 #endif
344
345 if (sched_preemption_disable_debug_mode == SCHED_HYGIENE_MODE_PANIC) {
346 panic("preemption disable timeout exceeded: %llu >= %llu mt ticks (start: %llu, now: %llu, gross: %llu, inttime: %llu), "
347 "freq = %llu MHz, CPI = %llu.%llu",
348 net_duration, threshold, mt, now, gross_duration, adjust,
349 average_freq, average_cpi_whole, average_cpi_fractional);
350 }
351
352 DTRACE_SCHED4(mach_preemption_expired, uint64_t, net_duration, uint64_t, gross_duration,
353 uint64_t, average_cpi_whole, uint64_t, average_cpi_fractional);
354 if (__improbable(kdebug_debugid_enabled(sched_preemption_disable_debug_dbgid))) {
355 KDBG(sched_preemption_disable_debug_dbgid, net_duration, gross_duration, average_cpi_whole, average_cpi_fractional);
356 }
357 }
358 }
359
360 #if SCHED_HYGIENE_DEBUG
361
362 /*
363 * Abandon a potential preemption disable measurement. Useful for
364 * example for the idle thread, which would just spuriously
365 * trigger the threshold while actually idling, which we don't
366 * care about.
367 */
368 void
abandon_preemption_disable_measurement(void)369 abandon_preemption_disable_measurement(void)
370 {
371 thread_t t = current_thread();
372 bool istate = ml_set_interrupts_enabled_with_debug(false, false); // don't take int masked timestamp
373
374 if (t->machine.preemption_disable_mt != 0) {
375 t->machine.preemption_disable_abandon = true;
376 }
377 ml_set_interrupts_enabled_with_debug(istate, false);
378 }
379
380 #endif
381
382
383 /*
384 * Skip predicate for sched_preemption_disable, which would trigger
385 * spuriously when kprintf spam is enabled.
386 */
387 bool
kprintf_spam_mt_pred(struct machine_timeout_spec const __unused * spec)388 kprintf_spam_mt_pred(struct machine_timeout_spec const __unused *spec)
389 {
390 bool const kprintf_spam_enabled = !(disable_kprintf_output || disable_serial_output);
391 return kprintf_spam_enabled;
392 }
393
394 #endif /* SCHED_HYGIENE_DEBUG */
395
396 /*
397 * To help _disable_preemption() inline everywhere with LTO,
398 * we keep these nice non inlineable functions as the panic()
399 * codegen setup is quite large and for weird reasons causes a frame.
400 */
401 __abortlike
402 static void
_disable_preemption_overflow(void)403 _disable_preemption_overflow(void)
404 {
405 panic("Preemption count overflow");
406 }
407
408 void
_disable_preemption(void)409 _disable_preemption(void)
410 {
411 thread_t thread = current_thread();
412 unsigned int count = thread->machine.preemption_count;
413
414 if (__improbable(++count == 0)) {
415 _disable_preemption_overflow();
416 }
417
418 os_atomic_store(&thread->machine.preemption_count, count, compiler_acq_rel);
419
420 #if SCHED_HYGIENE_DEBUG
421 /*
422 * Note that this is not the only place preemption gets disabled,
423 * it also gets modified on ISR and PPL entry/exit. Both of those
424 * events will be treated specially however, and
425 * increment/decrement being paired around their entry/exit means
426 * that collection here is not desynced otherwise.
427 */
428
429 if (count == 1 && sched_preemption_disable_debug_mode) {
430 _prepare_preemption_disable_measurement(thread);
431 }
432 #endif /* SCHED_HYGIENE_DEBUG */
433 }
434
435 /*
436 * This variant of _disable_preemption() allows disabling preemption
437 * without taking measurements (and later potentially triggering
438 * actions on those).
439 *
440 * We do this through a separate variant because we do not want to
441 * disturb inlinability of _disable_preemption(). However, in order to
442 * also avoid code duplication, instead of repeating common code we
443 * simply call _disable_preemption() and explicitly abandon any taken
444 * measurement.
445 */
446 void
_disable_preemption_without_measurements(void)447 _disable_preemption_without_measurements(void)
448 {
449 _disable_preemption();
450
451 #if SCHED_HYGIENE_DEBUG
452 abandon_preemption_disable_measurement();
453 #endif /* SCHED_HYGIENE_DEBUG */
454 }
455
456 /*
457 * This function checks whether an AST_URGENT has been pended.
458 *
459 * It is called once the preemption has been reenabled, which means the thread
460 * may have been preempted right before this was called, and when this function
461 * actually performs the check, we've changed CPU.
462 *
463 * This race is however benign: the point of AST_URGENT is to trigger a context
464 * switch, so if one happened, there's nothing left to check for, and AST_URGENT
465 * was cleared in the process.
466 *
467 * It follows that this check cannot have false negatives, which allows us
468 * to avoid fiddling with interrupt state for the vast majority of cases
469 * when the check will actually be negative.
470 */
471 static NOINLINE void
kernel_preempt_check(thread_t thread)472 kernel_preempt_check(thread_t thread)
473 {
474 uint64_t state;
475
476 /* If interrupts are masked, we can't take an AST here */
477 state = __builtin_arm_rsr64("DAIF");
478 if (state & DAIF_IRQF) {
479 return;
480 }
481
482 /* disable interrupts (IRQ FIQ ASYNCF) */
483 __builtin_arm_wsr64("DAIFSet", DAIFSC_STANDARD_DISABLE);
484
485 /*
486 * Reload cpu_pending_ast: a context switch would cause it to change.
487 * Now that interrupts are disabled, this will debounce false positives.
488 */
489 if (thread->machine.CpuDatap->cpu_pending_ast & AST_URGENT) {
490 ast_taken_kernel();
491 }
492
493 /* restore the original interrupt mask */
494 __builtin_arm_wsr64("DAIF", state);
495 }
496
497 /*
498 * To help _enable_preemption() inline everywhere with LTO,
499 * we keep these nice non inlineable functions as the panic()
500 * codegen setup is quite large and for weird reasons causes a frame.
501 */
502 __abortlike
503 static void
_enable_preemption_underflow(void)504 _enable_preemption_underflow(void)
505 {
506 panic("Preemption count underflow");
507 }
508
509 void
_enable_preemption(void)510 _enable_preemption(void)
511 {
512 thread_t thread = current_thread();
513 unsigned int count = thread->machine.preemption_count;
514
515 if (__improbable(count == 0)) {
516 _enable_preemption_underflow();
517 }
518 count -= 1;
519
520 #if SCHED_HYGIENE_DEBUG
521 if (count == 0 && thread->machine.preemption_disable_mt != 0) {
522 _collect_preemption_disable_measurement(thread);
523 }
524 #endif /* SCHED_HYGIENE_DEBUG */
525
526 os_atomic_store(&thread->machine.preemption_count, count, compiler_acq_rel);
527 if (count == 0) {
528 /*
529 * This check is racy and could load from another CPU's pending_ast mask,
530 * but as described above, this can't have false negatives.
531 */
532 if (__improbable(thread->machine.CpuDatap->cpu_pending_ast & AST_URGENT)) {
533 kernel_preempt_check(thread);
534 }
535 }
536
537 os_compiler_barrier();
538 }
539
540 int
get_preemption_level(void)541 get_preemption_level(void)
542 {
543 return current_thread()->machine.preemption_count;
544 }
545
546 #if CONFIG_PV_TICKET
547 __startup_func
548 void
lck_init_pv(void)549 lck_init_pv(void)
550 {
551 uint32_t pvtck = 1;
552 PE_parse_boot_argn("pvticket", &pvtck, sizeof(pvtck));
553 if (pvtck == 0) {
554 return;
555 }
556 has_lock_pv = hvg_is_hcall_available(HVG_HCALL_VCPU_WFK) &&
557 hvg_is_hcall_available(HVG_HCALL_VCPU_KICK);
558 }
559 STARTUP(LOCKS, STARTUP_RANK_FIRST, lck_init_pv);
560 #endif
561
562
563 #pragma mark lck_spin_t
564 #if LCK_SPIN_IS_TICKET_LOCK
565
566 lck_spin_t *
lck_spin_alloc_init(lck_grp_t * grp,lck_attr_t * attr)567 lck_spin_alloc_init(lck_grp_t *grp, lck_attr_t *attr)
568 {
569 lck_spin_t *lck;
570
571 lck = zalloc(KT_LCK_SPIN);
572 lck_spin_init(lck, grp, attr);
573 return lck;
574 }
575
576 void
lck_spin_free(lck_spin_t * lck,lck_grp_t * grp)577 lck_spin_free(lck_spin_t *lck, lck_grp_t *grp)
578 {
579 lck_spin_destroy(lck, grp);
580 zfree(KT_LCK_SPIN, lck);
581 }
582
583 void
lck_spin_init(lck_spin_t * lck,lck_grp_t * grp,__unused lck_attr_t * attr)584 lck_spin_init(lck_spin_t *lck, lck_grp_t *grp, __unused lck_attr_t *attr)
585 {
586 lck_ticket_init(lck, grp);
587 }
588
589 /*
590 * arm_usimple_lock is a lck_spin_t without a group or attributes
591 */
592 MARK_AS_HIBERNATE_TEXT void inline
arm_usimple_lock_init(simple_lock_t lck,__unused unsigned short initial_value)593 arm_usimple_lock_init(simple_lock_t lck, __unused unsigned short initial_value)
594 {
595 lck_ticket_init((lck_ticket_t *)lck, LCK_GRP_NULL);
596 }
597
598 void
lck_spin_assert(lck_spin_t * lock,unsigned int type)599 lck_spin_assert(lck_spin_t *lock, unsigned int type)
600 {
601 if (type == LCK_ASSERT_OWNED) {
602 lck_ticket_assert_owned(lock);
603 } else if (type == LCK_ASSERT_NOTOWNED) {
604 lck_ticket_assert_not_owned(lock);
605 } else {
606 panic("lck_spin_assert(): invalid arg (%u)", type);
607 }
608 }
609
610 void
lck_spin_lock(lck_spin_t * lock)611 lck_spin_lock(lck_spin_t *lock)
612 {
613 lck_ticket_lock(lock, LCK_GRP_NULL);
614 }
615
616 void
lck_spin_lock_nopreempt(lck_spin_t * lock)617 lck_spin_lock_nopreempt(lck_spin_t *lock)
618 {
619 lck_ticket_lock_nopreempt(lock, LCK_GRP_NULL);
620 }
621
622 int
lck_spin_try_lock(lck_spin_t * lock)623 lck_spin_try_lock(lck_spin_t *lock)
624 {
625 return lck_ticket_lock_try(lock, LCK_GRP_NULL);
626 }
627
628 int
lck_spin_try_lock_nopreempt(lck_spin_t * lock)629 lck_spin_try_lock_nopreempt(lck_spin_t *lock)
630 {
631 return lck_ticket_lock_try_nopreempt(lock, LCK_GRP_NULL);
632 }
633
634 void
lck_spin_unlock(lck_spin_t * lock)635 lck_spin_unlock(lck_spin_t *lock)
636 {
637 lck_ticket_unlock(lock);
638 }
639
640 void
lck_spin_destroy(lck_spin_t * lck,lck_grp_t * grp)641 lck_spin_destroy(lck_spin_t *lck, lck_grp_t *grp)
642 {
643 lck_ticket_destroy(lck, grp);
644 }
645
646 /*
647 * those really should be in an alias file instead,
648 * but you can't make that conditional.
649 *
650 * it will be good enough for perf evals for now
651 *
652 * we also can't make aliases for symbols that
653 * are in alias files like lck_spin_init and friends,
654 * so this suffers double jump penalties for kexts
655 * (LTO does the right thing for XNU).
656 */
657 #define make_alias(a, b) asm(".globl _" #a "\n" ".set _" #a ", _" #b "\n")
658 make_alias(lck_spin_lock_grp, lck_ticket_lock);
659 make_alias(lck_spin_lock_nopreempt_grp, lck_ticket_lock_nopreempt);
660 make_alias(lck_spin_try_lock_grp, lck_ticket_lock_try);
661 make_alias(lck_spin_try_lock_nopreempt_grp, lck_ticket_lock_try_nopreempt);
662 make_alias(lck_spin_unlock_nopreempt, lck_ticket_unlock_nopreempt);
663 make_alias(kdp_lck_spin_is_acquired, kdp_lck_ticket_is_acquired);
664 #undef make_alias
665
666 #else /* !LCK_SPIN_IS_TICKET_LOCK */
667
668 #if DEVELOPMENT || DEBUG
669 __abortlike
670 static void
__lck_spin_invalid_panic(lck_spin_t * lck)671 __lck_spin_invalid_panic(lck_spin_t *lck)
672 {
673 const char *how = "Invalid";
674
675 if (lck->type == LCK_SPIN_TYPE_DESTROYED ||
676 lck->lck_spin_data == LCK_SPIN_TAG_DESTROYED) {
677 how = "Destroyed";
678 }
679
680 panic("%s spinlock %p: <0x%016lx 0x%16lx>",
681 how, lck, lck->lck_spin_data, lck->type);
682 }
683
684 static inline void
lck_spin_verify(lck_spin_t * lck)685 lck_spin_verify(lck_spin_t *lck)
686 {
687 if (lck->type != LCK_SPIN_TYPE ||
688 lck->lck_spin_data == LCK_SPIN_TAG_DESTROYED) {
689 __lck_spin_invalid_panic(lck);
690 }
691 }
692 #else /* DEVELOPMENT || DEBUG */
693 #define lck_spin_verify(lck) ((void)0)
694 #endif /* DEVELOPMENT || DEBUG */
695
696 lck_spin_t *
lck_spin_alloc_init(lck_grp_t * grp,lck_attr_t * attr)697 lck_spin_alloc_init(lck_grp_t *grp, lck_attr_t *attr)
698 {
699 lck_spin_t *lck;
700
701 lck = zalloc(KT_LCK_SPIN);
702 lck_spin_init(lck, grp, attr);
703 return lck;
704 }
705
706 void
lck_spin_free(lck_spin_t * lck,lck_grp_t * grp)707 lck_spin_free(lck_spin_t *lck, lck_grp_t *grp)
708 {
709 lck_spin_destroy(lck, grp);
710 zfree(KT_LCK_SPIN, lck);
711 }
712
713 void
lck_spin_init(lck_spin_t * lck,lck_grp_t * grp,__unused lck_attr_t * attr)714 lck_spin_init(lck_spin_t *lck, lck_grp_t *grp, __unused lck_attr_t *attr)
715 {
716 lck->type = LCK_SPIN_TYPE;
717 hw_lock_init(&lck->hwlock);
718 if (grp) {
719 lck_grp_reference(grp, &grp->lck_grp_spincnt);
720 }
721 }
722
723 /*
724 * arm_usimple_lock is a lck_spin_t without a group or attributes
725 */
726 MARK_AS_HIBERNATE_TEXT void inline
arm_usimple_lock_init(simple_lock_t lck,__unused unsigned short initial_value)727 arm_usimple_lock_init(simple_lock_t lck, __unused unsigned short initial_value)
728 {
729 lck->type = LCK_SPIN_TYPE;
730 hw_lock_init(&lck->hwlock);
731 }
732
733 void
lck_spin_assert(lck_spin_t * lock,unsigned int type)734 lck_spin_assert(lck_spin_t *lock, unsigned int type)
735 {
736 thread_t thread, holder;
737
738 if (lock->type != LCK_SPIN_TYPE) {
739 panic("Invalid spinlock %p", lock);
740 }
741
742 holder = HW_LOCK_STATE_TO_THREAD(lock->lck_spin_data);
743 thread = current_thread();
744 if (type == LCK_ASSERT_OWNED) {
745 if (holder == 0) {
746 panic("Lock not owned %p = %p", lock, holder);
747 }
748 if (holder != thread) {
749 panic("Lock not owned by current thread %p = %p", lock, holder);
750 }
751 } else if (type == LCK_ASSERT_NOTOWNED) {
752 if (holder != THREAD_NULL && holder == thread) {
753 panic("Lock owned by current thread %p = %p", lock, holder);
754 }
755 } else {
756 panic("lck_spin_assert(): invalid arg (%u)", type);
757 }
758 }
759
760 void
lck_spin_lock(lck_spin_t * lock)761 lck_spin_lock(lck_spin_t *lock)
762 {
763 lck_spin_verify(lock);
764 hw_lock_lock(&lock->hwlock, LCK_GRP_NULL);
765 }
766
767 void
lck_spin_lock_grp(lck_spin_t * lock,lck_grp_t * grp)768 lck_spin_lock_grp(lck_spin_t *lock, lck_grp_t *grp)
769 {
770 #pragma unused(grp)
771 lck_spin_verify(lock);
772 hw_lock_lock(&lock->hwlock, grp);
773 }
774
775 void
lck_spin_lock_nopreempt(lck_spin_t * lock)776 lck_spin_lock_nopreempt(lck_spin_t *lock)
777 {
778 lck_spin_verify(lock);
779 hw_lock_lock_nopreempt(&lock->hwlock, LCK_GRP_NULL);
780 }
781
782 void
lck_spin_lock_nopreempt_grp(lck_spin_t * lock,lck_grp_t * grp)783 lck_spin_lock_nopreempt_grp(lck_spin_t *lock, lck_grp_t *grp)
784 {
785 #pragma unused(grp)
786 lck_spin_verify(lock);
787 hw_lock_lock_nopreempt(&lock->hwlock, grp);
788 }
789
790 int
lck_spin_try_lock(lck_spin_t * lock)791 lck_spin_try_lock(lck_spin_t *lock)
792 {
793 lck_spin_verify(lock);
794 return hw_lock_try(&lock->hwlock, LCK_GRP_NULL);
795 }
796
797 int
lck_spin_try_lock_grp(lck_spin_t * lock,lck_grp_t * grp)798 lck_spin_try_lock_grp(lck_spin_t *lock, lck_grp_t *grp)
799 {
800 #pragma unused(grp)
801 lck_spin_verify(lock);
802 return hw_lock_try(&lock->hwlock, grp);
803 }
804
805 int
lck_spin_try_lock_nopreempt(lck_spin_t * lock)806 lck_spin_try_lock_nopreempt(lck_spin_t *lock)
807 {
808 lck_spin_verify(lock);
809 return hw_lock_try_nopreempt(&lock->hwlock, LCK_GRP_NULL);
810 }
811
812 int
lck_spin_try_lock_nopreempt_grp(lck_spin_t * lock,lck_grp_t * grp)813 lck_spin_try_lock_nopreempt_grp(lck_spin_t *lock, lck_grp_t *grp)
814 {
815 #pragma unused(grp)
816 lck_spin_verify(lock);
817 return hw_lock_try_nopreempt(&lock->hwlock, grp);
818 }
819
820 void
lck_spin_unlock(lck_spin_t * lock)821 lck_spin_unlock(lck_spin_t *lock)
822 {
823 lck_spin_verify(lock);
824 hw_lock_unlock(&lock->hwlock);
825 }
826
827 void
lck_spin_unlock_nopreempt(lck_spin_t * lock)828 lck_spin_unlock_nopreempt(lck_spin_t *lock)
829 {
830 lck_spin_verify(lock);
831 hw_lock_unlock_nopreempt(&lock->hwlock);
832 }
833
834 void
lck_spin_destroy(lck_spin_t * lck,lck_grp_t * grp)835 lck_spin_destroy(lck_spin_t *lck, lck_grp_t *grp)
836 {
837 lck_spin_verify(lck);
838 *lck = (lck_spin_t){
839 .lck_spin_data = LCK_SPIN_TAG_DESTROYED,
840 .type = LCK_SPIN_TYPE_DESTROYED,
841 };
842 if (grp) {
843 lck_grp_deallocate(grp, &grp->lck_grp_spincnt);
844 }
845 }
846
847 /*
848 * Routine: kdp_lck_spin_is_acquired
849 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
850 */
851 boolean_t
kdp_lck_spin_is_acquired(lck_spin_t * lck)852 kdp_lck_spin_is_acquired(lck_spin_t *lck)
853 {
854 if (not_in_kdp) {
855 panic("panic: spinlock acquired check done outside of kernel debugger");
856 }
857 return ((lck->lck_spin_data & ~LCK_SPIN_TAG_DESTROYED) != 0) ? TRUE:FALSE;
858 }
859
860 #endif /* !LCK_SPIN_IS_TICKET_LOCK */
861
862 /*
863 * Initialize a usimple_lock.
864 *
865 * No change in preemption state.
866 */
867 void
usimple_lock_init(usimple_lock_t l,unsigned short tag)868 usimple_lock_init(
869 usimple_lock_t l,
870 unsigned short tag)
871 {
872 simple_lock_init((simple_lock_t) l, tag);
873 }
874
875
876 /*
877 * Acquire a usimple_lock.
878 *
879 * Returns with preemption disabled. Note
880 * that the hw_lock routines are responsible for
881 * maintaining preemption state.
882 */
883 void
884 (usimple_lock)(
885 usimple_lock_t l
886 LCK_GRP_ARG(lck_grp_t *grp))
887 {
888 simple_lock((simple_lock_t) l, LCK_GRP_PROBEARG(grp));
889 }
890
891
892 /*
893 * Release a usimple_lock.
894 *
895 * Returns with preemption enabled. Note
896 * that the hw_lock routines are responsible for
897 * maintaining preemption state.
898 */
899 void
900 (usimple_unlock)(
901 usimple_lock_t l)
902 {
903 simple_unlock((simple_lock_t)l);
904 }
905
906
907 /*
908 * Conditionally acquire a usimple_lock.
909 *
910 * On success, returns with preemption disabled.
911 * On failure, returns with preemption in the same state
912 * as when first invoked. Note that the hw_lock routines
913 * are responsible for maintaining preemption state.
914 *
915 * XXX No stats are gathered on a miss; I preserved this
916 * behavior from the original assembly-language code, but
917 * doesn't it make sense to log misses? XXX
918 */
919 unsigned
920 int
921 (usimple_lock_try)(
922 usimple_lock_t l
923 LCK_GRP_ARG(lck_grp_t *grp))
924 {
925 return simple_lock_try((simple_lock_t) l, grp);
926 }
927