2 * Copyright (c) 2007-2018 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System Copyright (c) 1991,1990,1989,1988,1987 Carnegie
33 * Mellon University All Rights Reserved.
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.
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.
45 * Carnegie Mellon requests users of this software to return to
47 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
48 * School of Computer Science Carnegie Mellon University Pittsburgh PA
51 * any improvements or extensions that they make and grant Carnegie Mellon the
52 * rights to redistribute these changes.
56 * Author: Avadis Tevanian, Jr., Michael Wayne Young
59 * Locking primitives implementation
62 #define LOCK_PRIVATE 1
64 #include <mach_ldebug.h>
66 #include <kern/kalloc.h>
67 #include <kern/lock_stat.h>
68 #include <kern/locks.h>
69 #include <kern/misc_protos.h>
70 #include <kern/thread.h>
71 #include <kern/processor.h>
72 #include <kern/sched_prim.h>
73 #include <kern/debug.h>
74 #include <kern/kcdata.h>
76 #include <arm/cpu_internal.h>
78 #include <arm/cpu_data.h>
80 #include <arm/cpu_data_internal.h>
81 #include <arm/proc_reg.h>
83 #include <machine/atomic.h>
84 #include <machine/machine_cpu.h>
86 #include <sys/kdebug.h>
89 #define DTRACE_RW_SHARED 0x0 //reader
90 #define DTRACE_RW_EXCL 0x1 //writer
91 #define DTRACE_NO_FLAG 0x0 //not applicable
92 #endif /* CONFIG_DTRACE */
94 #define LCK_RW_LCK_EXCLUSIVE_CODE 0x100
95 #define LCK_RW_LCK_EXCLUSIVE1_CODE 0x101
96 #define LCK_RW_LCK_SHARED_CODE 0x102
97 #define LCK_RW_LCK_SH_TO_EX_CODE 0x103
98 #define LCK_RW_LCK_SH_TO_EX1_CODE 0x104
99 #define LCK_RW_LCK_EX_TO_SH_CODE 0x105
102 #define ANY_LOCK_DEBUG (USLOCK_DEBUG || LOCK_DEBUG || MUTEX_DEBUG)
104 // Panic in tests that check lock usage correctness
105 // These are undesirable when in a panic or a debugger is runnning.
106 #define LOCK_CORRECTNESS_PANIC() (kernel_debugger_entry_count == 0)
108 unsigned int LcksOpts
= 0;
110 #define ADAPTIVE_SPIN_ENABLE 0x1
113 int lck_mtx_adaptive_spin_mode
= ADAPTIVE_SPIN_ENABLE
;
115 int lck_mtx_adaptive_spin_mode
= 0;
118 #define SPINWAIT_OWNER_CHECK_COUNT 4
121 SPINWAIT_ACQUIRED
, /* Got the lock. */
122 SPINWAIT_INTERLOCK
, /* Got the interlock, no owner, but caller must finish acquiring the lock. */
123 SPINWAIT_DID_SPIN_HIGH_THR
, /* Got the interlock, spun, but failed to get the lock. */
124 SPINWAIT_DID_SPIN_OWNER_NOT_CORE
, /* Got the interlock, spun, but failed to get the lock. */
125 SPINWAIT_DID_SPIN_NO_WINDOW_CONTENTION
, /* Got the interlock, spun, but failed to get the lock. */
126 SPINWAIT_DID_SPIN_SLIDING_THR
,/* Got the interlock, spun, but failed to get the lock. */
127 SPINWAIT_DID_NOT_SPIN
, /* Got the interlock, did not spin. */
130 #if CONFIG_DTRACE && __SMP__
131 extern uint64_t dtrace_spin_threshold
;
136 extern unsigned int not_in_kdp
;
139 * We often want to know the addresses of the callers
140 * of the various lock routines. However, this information
141 * is only used for debugging and statistics.
144 #define INVALID_PC ((void *) VM_MAX_KERNEL_ADDRESS)
145 #define INVALID_THREAD ((void *) VM_MAX_KERNEL_ADDRESS)
149 * Eliminate lint complaints about unused local pc variables.
151 #define OBTAIN_PC(pc, l) ++pc
153 #define OBTAIN_PC(pc, l)
158 * Portable lock package implementation of usimple_locks.
162 * Owner thread pointer when lock held in spin mode
164 #define LCK_MTX_SPIN_TAG 0xfffffff0
167 #define interlock_lock(lock) hw_lock_bit ((hw_lock_bit_t*)(&(lock)->lck_mtx_data), LCK_ILOCK_BIT, LCK_GRP_NULL)
168 #define interlock_try(lock) hw_lock_bit_try((hw_lock_bit_t*)(&(lock)->lck_mtx_data), LCK_ILOCK_BIT, LCK_GRP_NULL)
169 #define interlock_unlock(lock) hw_unlock_bit ((hw_lock_bit_t*)(&(lock)->lck_mtx_data), LCK_ILOCK_BIT)
170 #define lck_rw_ilk_lock(lock) hw_lock_bit ((hw_lock_bit_t*)(&(lock)->lck_rw_tag), LCK_RW_INTERLOCK_BIT, LCK_GRP_NULL)
171 #define lck_rw_ilk_unlock(lock) hw_unlock_bit((hw_lock_bit_t*)(&(lock)->lck_rw_tag), LCK_RW_INTERLOCK_BIT)
173 #define load_memory_barrier() os_atomic_thread_fence(acquire)
175 // Enforce program order of loads and stores.
176 #define ordered_load(target) \
177 os_atomic_load(target, compiler_acq_rel)
178 #define ordered_store(target, value) \
179 os_atomic_store(target, value, compiler_acq_rel)
181 #define ordered_load_mtx(lock) ordered_load(&(lock)->lck_mtx_data)
182 #define ordered_store_mtx(lock, value) ordered_store(&(lock)->lck_mtx_data, (value))
183 #define ordered_load_rw(lock) ordered_load(&(lock)->lck_rw_data)
184 #define ordered_store_rw(lock, value) ordered_store(&(lock)->lck_rw_data, (value))
185 #define ordered_load_rw_owner(lock) ordered_load(&(lock)->lck_rw_owner)
186 #define ordered_store_rw_owner(lock, value) ordered_store(&(lock)->lck_rw_owner, (value))
187 #define ordered_load_hw(lock) ordered_load(&(lock)->lock_data)
188 #define ordered_store_hw(lock, value) ordered_store(&(lock)->lock_data, (value))
189 #define ordered_load_bit(lock) ordered_load((lock))
190 #define ordered_store_bit(lock, value) ordered_store((lock), (value))
193 // Prevent the compiler from reordering memory operations around this
194 #define compiler_memory_fence() __asm__ volatile ("" ::: "memory")
196 #define LOCK_PANIC_TIMEOUT 0xc00000
197 #define NOINLINE __attribute__((noinline))
201 #define interrupts_disabled(mask) (mask & PSR_INTMASK)
203 #define interrupts_disabled(mask) (mask & DAIF_IRQF)
208 #define enable_fiq() __asm__ volatile ("cpsie f" ::: "memory");
209 #define enable_interrupts() __asm__ volatile ("cpsie if" ::: "memory");
213 * Forward declarations
216 static void lck_rw_lock_shared_gen(lck_rw_t
*lck
);
217 static void lck_rw_lock_exclusive_gen(lck_rw_t
*lck
);
218 static boolean_t
lck_rw_lock_shared_to_exclusive_success(lck_rw_t
*lck
);
219 static boolean_t
lck_rw_lock_shared_to_exclusive_failure(lck_rw_t
*lck
, uint32_t prior_lock_state
);
220 static void lck_rw_lock_exclusive_to_shared_gen(lck_rw_t
*lck
, uint32_t prior_lock_state
);
221 static lck_rw_type_t
lck_rw_done_gen(lck_rw_t
*lck
, uint32_t prior_lock_state
);
222 static boolean_t
lck_rw_grab(lck_rw_t
*lock
, int mode
, boolean_t wait
);
225 * atomic exchange API is a low level abstraction of the operations
226 * to atomically read, modify, and write a pointer. This abstraction works
227 * for both Intel and ARMv8.1 compare and exchange atomic instructions as
228 * well as the ARM exclusive instructions.
230 * atomic_exchange_begin() - begin exchange and retrieve current value
231 * atomic_exchange_complete() - conclude an exchange
232 * atomic_exchange_abort() - cancel an exchange started with atomic_exchange_begin()
234 __unused
static uint32_t
235 load_exclusive32(uint32_t *target
, enum memory_order ord
)
240 if (memory_order_has_release(ord
)) {
241 // Pre-load release barrier
242 atomic_thread_fence(memory_order_release
);
244 value
= __builtin_arm_ldrex(target
);
246 if (memory_order_has_acquire(ord
)) {
247 value
= __builtin_arm_ldaex(target
); // ldaxr
249 value
= __builtin_arm_ldrex(target
); // ldxr
255 __unused
static boolean_t
256 store_exclusive32(uint32_t *target
, uint32_t value
, enum memory_order ord
)
261 err
= __builtin_arm_strex(value
, target
);
262 if (memory_order_has_acquire(ord
)) {
263 // Post-store acquire barrier
264 atomic_thread_fence(memory_order_acquire
);
267 if (memory_order_has_release(ord
)) {
268 err
= __builtin_arm_stlex(value
, target
); // stlxr
270 err
= __builtin_arm_strex(value
, target
); // stxr
277 atomic_exchange_begin32(uint32_t *target
, uint32_t *previous
, enum memory_order ord
)
281 #if __ARM_ATOMICS_8_1
282 ord
= memory_order_relaxed
;
284 val
= load_exclusive32(target
, ord
);
290 atomic_exchange_complete32(uint32_t *target
, uint32_t previous
, uint32_t newval
, enum memory_order ord
)
292 #if __ARM_ATOMICS_8_1
293 return __c11_atomic_compare_exchange_strong((_Atomic
uint32_t *)target
, &previous
, newval
, ord
, memory_order_relaxed
);
295 (void)previous
; // Previous not needed, monitor is held
296 return store_exclusive32(target
, newval
, ord
);
301 atomic_exchange_abort(void)
303 os_atomic_clear_exclusive();
307 atomic_test_and_set32(uint32_t *target
, uint32_t test_mask
, uint32_t set_mask
, enum memory_order ord
, boolean_t wait
)
309 uint32_t value
, prev
;
312 value
= atomic_exchange_begin32(target
, &prev
, ord
);
313 if (value
& test_mask
) {
315 wait_for_event(); // Wait with monitor held
317 atomic_exchange_abort(); // Clear exclusive monitor
322 if (atomic_exchange_complete32(target
, prev
, value
, ord
)) {
329 hw_atomic_test_and_set32(uint32_t *target
, uint32_t test_mask
, uint32_t set_mask
, enum memory_order ord
, boolean_t wait
)
331 return atomic_test_and_set32(target
, test_mask
, set_mask
, ord
, wait
);
335 _disable_preemption(void)
337 thread_t thread
= current_thread();
338 unsigned int count
= thread
->machine
.preemption_count
;
341 if (__improbable(count
== 0)) {
342 panic("Preemption count overflow");
345 os_atomic_store(&thread
->machine
.preemption_count
, count
, compiler_acq_rel
);
349 * This function checks whether an AST_URGENT has been pended.
351 * It is called once the preemption has been reenabled, which means the thread
352 * may have been preempted right before this was called, and when this function
353 * actually performs the check, we've changed CPU.
355 * This race is however benign: the point of AST_URGENT is to trigger a context
356 * switch, so if one happened, there's nothing left to check for, and AST_URGENT
357 * was cleared in the process.
359 * It follows that this check cannot have false negatives, which allows us
360 * to avoid fiddling with interrupt state for the vast majority of cases
361 * when the check will actually be negative.
364 kernel_preempt_check(thread_t thread
)
366 cpu_data_t
*cpu_data_ptr
;
370 #define INTERRUPT_MASK PSR_IRQF
372 #define INTERRUPT_MASK DAIF_IRQF
376 * This check is racy and could load from another CPU's pending_ast mask,
377 * but as described above, this can't have false negatives.
379 cpu_data_ptr
= os_atomic_load(&thread
->machine
.CpuDatap
, compiler_acq_rel
);
380 if (__probable((cpu_data_ptr
->cpu_pending_ast
& AST_URGENT
) == 0)) {
384 /* If interrupts are masked, we can't take an AST here */
385 state
= get_interrupts();
386 if ((state
& INTERRUPT_MASK
) == 0) {
387 disable_interrupts_noread(); // Disable interrupts
390 * Reload cpu_data_ptr: a context switch would cause it to change.
391 * Now that interrupts are disabled, this will debounce false positives.
393 cpu_data_ptr
= os_atomic_load(&thread
->machine
.CpuDatap
, compiler_acq_rel
);
394 if (thread
->machine
.CpuDatap
->cpu_pending_ast
& AST_URGENT
) {
396 #if __ARM_USER_PROTECT__
397 uintptr_t up
= arm_user_protect_begin(thread
);
398 #endif // __ARM_USER_PROTECT__
401 ast_taken_kernel(); // Handle urgent AST
403 #if __ARM_USER_PROTECT__
404 arm_user_protect_end(thread
, up
, TRUE
);
405 #endif // __ARM_USER_PROTECT__
407 return; // Return early on arm only due to FIQ enabling
410 restore_interrupts(state
); // Enable interrupts
415 _enable_preemption(void)
417 thread_t thread
= current_thread();
418 unsigned int count
= thread
->machine
.preemption_count
;
420 if (__improbable(count
== 0)) {
421 panic("Preemption count underflow");
425 os_atomic_store(&thread
->machine
.preemption_count
, count
, compiler_acq_rel
);
427 kernel_preempt_check(thread
);
432 get_preemption_level(void)
434 return current_thread()->machine
.preemption_count
;
438 * Routine: lck_spin_alloc_init
447 if ((lck
= (lck_spin_t
*) kalloc(sizeof(lck_spin_t
))) != 0) {
448 lck_spin_init(lck
, grp
, attr
);
455 * Routine: lck_spin_free
462 lck_spin_destroy(lck
, grp
);
463 kfree(lck
, sizeof(lck_spin_t
));
467 * Routine: lck_spin_init
473 __unused lck_attr_t
* attr
)
475 lck
->type
= LCK_SPIN_TYPE
;
476 hw_lock_init(&lck
->hwlock
);
478 lck_grp_reference(grp
);
479 lck_grp_lckcnt_incr(grp
, LCK_TYPE_SPIN
);
484 * arm_usimple_lock is a lck_spin_t without a group or attributes
487 arm_usimple_lock_init(simple_lock_t lck
, __unused
unsigned short initial_value
)
489 lck
->type
= LCK_SPIN_TYPE
;
490 hw_lock_init(&lck
->hwlock
);
495 * Routine: lck_spin_lock
498 lck_spin_lock(lck_spin_t
*lock
)
500 #if DEVELOPMENT || DEBUG
501 if (lock
->type
!= LCK_SPIN_TYPE
) {
502 panic("Invalid spinlock %p", lock
);
504 #endif // DEVELOPMENT || DEBUG
505 hw_lock_lock(&lock
->hwlock
, LCK_GRP_NULL
);
509 lck_spin_lock_grp(lck_spin_t
*lock
, lck_grp_t
*grp
)
512 #if DEVELOPMENT || DEBUG
513 if (lock
->type
!= LCK_SPIN_TYPE
) {
514 panic("Invalid spinlock %p", lock
);
516 #endif // DEVELOPMENT || DEBUG
517 hw_lock_lock(&lock
->hwlock
, grp
);
521 * Routine: lck_spin_lock_nopreempt
524 lck_spin_lock_nopreempt(lck_spin_t
*lock
)
526 #if DEVELOPMENT || DEBUG
527 if (lock
->type
!= LCK_SPIN_TYPE
) {
528 panic("Invalid spinlock %p", lock
);
530 #endif // DEVELOPMENT || DEBUG
531 hw_lock_lock_nopreempt(&lock
->hwlock
, LCK_GRP_NULL
);
535 lck_spin_lock_nopreempt_grp(lck_spin_t
*lock
, lck_grp_t
*grp
)
538 #if DEVELOPMENT || DEBUG
539 if (lock
->type
!= LCK_SPIN_TYPE
) {
540 panic("Invalid spinlock %p", lock
);
542 #endif // DEVELOPMENT || DEBUG
543 hw_lock_lock_nopreempt(&lock
->hwlock
, grp
);
547 * Routine: lck_spin_try_lock
550 lck_spin_try_lock(lck_spin_t
*lock
)
552 return hw_lock_try(&lock
->hwlock
, LCK_GRP_NULL
);
556 lck_spin_try_lock_grp(lck_spin_t
*lock
, lck_grp_t
*grp
)
559 return hw_lock_try(&lock
->hwlock
, grp
);
563 * Routine: lck_spin_try_lock_nopreempt
566 lck_spin_try_lock_nopreempt(lck_spin_t
*lock
)
568 return hw_lock_try_nopreempt(&lock
->hwlock
, LCK_GRP_NULL
);
572 lck_spin_try_lock_nopreempt_grp(lck_spin_t
*lock
, lck_grp_t
*grp
)
575 return hw_lock_try_nopreempt(&lock
->hwlock
, grp
);
579 * Routine: lck_spin_unlock
582 lck_spin_unlock(lck_spin_t
*lock
)
584 #if DEVELOPMENT || DEBUG
585 if ((LCK_MTX_STATE_TO_THREAD(lock
->lck_spin_data
) != current_thread()) && LOCK_CORRECTNESS_PANIC()) {
586 panic("Spinlock not owned by thread %p = %lx", lock
, lock
->lck_spin_data
);
588 if (lock
->type
!= LCK_SPIN_TYPE
) {
589 panic("Invalid spinlock type %p", lock
);
591 #endif // DEVELOPMENT || DEBUG
592 hw_lock_unlock(&lock
->hwlock
);
596 * Routine: lck_spin_unlock_nopreempt
599 lck_spin_unlock_nopreempt(lck_spin_t
*lock
)
601 #if DEVELOPMENT || DEBUG
602 if ((LCK_MTX_STATE_TO_THREAD(lock
->lck_spin_data
) != current_thread()) && LOCK_CORRECTNESS_PANIC()) {
603 panic("Spinlock not owned by thread %p = %lx", lock
, lock
->lck_spin_data
);
605 if (lock
->type
!= LCK_SPIN_TYPE
) {
606 panic("Invalid spinlock type %p", lock
);
608 #endif // DEVELOPMENT || DEBUG
609 hw_lock_unlock_nopreempt(&lock
->hwlock
);
613 * Routine: lck_spin_destroy
620 if (lck
->lck_spin_data
== LCK_SPIN_TAG_DESTROYED
) {
623 lck
->lck_spin_data
= LCK_SPIN_TAG_DESTROYED
;
625 lck_grp_lckcnt_decr(grp
, LCK_TYPE_SPIN
);
626 lck_grp_deallocate(grp
);
631 * Routine: kdp_lck_spin_is_acquired
632 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
635 kdp_lck_spin_is_acquired(lck_spin_t
*lck
)
638 panic("panic: spinlock acquired check done outside of kernel debugger");
640 return ((lck
->lck_spin_data
& ~LCK_SPIN_TAG_DESTROYED
) != 0) ? TRUE
:FALSE
;
644 * Initialize a usimple_lock.
646 * No change in preemption state.
653 simple_lock_init((simple_lock_t
) l
, tag
);
658 * Acquire a usimple_lock.
660 * Returns with preemption disabled. Note
661 * that the hw_lock routines are responsible for
662 * maintaining preemption state.
667 LCK_GRP_ARG(lck_grp_t
*grp
))
669 simple_lock((simple_lock_t
) l
, LCK_GRP_PROBEARG(grp
));
673 extern void sync(void);
676 * Release a usimple_lock.
678 * Returns with preemption enabled. Note
679 * that the hw_lock routines are responsible for
680 * maintaining preemption state.
686 simple_unlock((simple_lock_t
)l
);
691 * Conditionally acquire a usimple_lock.
693 * On success, returns with preemption disabled.
694 * On failure, returns with preemption in the same state
695 * as when first invoked. Note that the hw_lock routines
696 * are responsible for maintaining preemption state.
698 * XXX No stats are gathered on a miss; I preserved this
699 * behavior from the original assembly-language code, but
700 * doesn't it make sense to log misses? XXX
706 LCK_GRP_ARG(lck_grp_t
*grp
))
708 return simple_lock_try((simple_lock_t
) l
, grp
);
712 * The C portion of the shared/exclusive locks package.
716 * compute the deadline to spin against when
717 * waiting for a change of state on a lck_rw_t
720 static inline uint64_t
721 lck_rw_deadline_for_spin(lck_rw_t
*lck
)
725 word
.data
= ordered_load_rw(lck
);
726 if (word
.can_sleep
) {
727 if (word
.r_waiting
|| word
.w_waiting
|| (word
.shared_count
> machine_info
.max_cpus
)) {
729 * there are already threads waiting on this lock... this
730 * implies that they have spun beyond their deadlines waiting for
731 * the desired state to show up so we will not bother spinning at this time...
733 * the current number of threads sharing this lock exceeds our capacity to run them
734 * concurrently and since all states we're going to spin for require the rw_shared_count
735 * to be at 0, we'll not bother spinning since the latency for this to happen is
738 return mach_absolute_time();
740 return mach_absolute_time() + MutexSpin
;
742 return mach_absolute_time() + (100000LL * 1000000000LL);
748 lck_rw_drain_status(lck_rw_t
*lock
, uint32_t status_mask
, boolean_t wait __unused
)
751 uint64_t deadline
= 0;
755 deadline
= lck_rw_deadline_for_spin(lock
);
759 data
= load_exclusive32(&lock
->lck_rw_data
, memory_order_acquire_smp
);
760 if ((data
& status_mask
) == 0) {
766 os_atomic_clear_exclusive();
768 if (!wait
|| (mach_absolute_time() >= deadline
)) {
772 os_atomic_clear_exclusive();
777 data
= ordered_load_rw(lock
);
778 if ((data
& status_mask
) == 0) {
787 * Spin while interlock is held.
790 lck_rw_interlock_spin(lck_rw_t
*lock
)
796 data
= load_exclusive32(&lock
->lck_rw_data
, memory_order_relaxed
);
797 if (data
& LCK_RW_INTERLOCK
) {
800 os_atomic_clear_exclusive();
805 panic("lck_rw_interlock_spin(): Interlock locked %p %x", lock
, lock
->lck_rw_data
);
810 * We disable interrupts while holding the RW interlock to prevent an
811 * interrupt from exacerbating hold time.
812 * Hence, local helper functions lck_interlock_lock()/lck_interlock_unlock().
814 static inline boolean_t
815 lck_interlock_lock(lck_rw_t
*lck
)
819 istate
= ml_set_interrupts_enabled(FALSE
);
820 lck_rw_ilk_lock(lck
);
825 lck_interlock_unlock(lck_rw_t
*lck
, boolean_t istate
)
827 lck_rw_ilk_unlock(lck
);
828 ml_set_interrupts_enabled(istate
);
832 #define LCK_RW_GRAB_WANT 0
833 #define LCK_RW_GRAB_SHARED 1
836 lck_rw_grab(lck_rw_t
*lock
, int mode
, boolean_t wait
)
838 uint64_t deadline
= 0;
844 deadline
= lck_rw_deadline_for_spin(lock
);
847 wait
= FALSE
; // Don't spin on UP systems
851 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_acquire_smp
);
852 if (data
& LCK_RW_INTERLOCK
) {
853 atomic_exchange_abort();
854 lck_rw_interlock_spin(lock
);
858 if (mode
== LCK_RW_GRAB_WANT
) {
859 if ((data
& LCK_RW_WANT_EXCL
) == 0) {
860 data
|= LCK_RW_WANT_EXCL
;
863 } else { // LCK_RW_GRAB_SHARED
864 if (((data
& (LCK_RW_WANT_EXCL
| LCK_RW_WANT_UPGRADE
)) == 0) ||
865 (((data
& LCK_RW_SHARED_MASK
)) && ((data
& LCK_RW_PRIV_EXCL
) == 0))) {
866 data
+= LCK_RW_SHARED_READER
;
871 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_acquire_smp
)) {
875 if (wait
) { // Non-waiting
878 atomic_exchange_abort();
880 if (!wait
|| (mach_absolute_time() >= deadline
)) {
889 * Routine: lck_rw_alloc_init
898 if ((lck
= (lck_rw_t
*)kalloc(sizeof(lck_rw_t
))) != 0) {
899 lck_rw_init(lck
, grp
, attr
);
906 * Routine: lck_rw_free
913 lck_rw_destroy(lck
, grp
);
914 kfree(lck
, sizeof(lck_rw_t
));
918 * Routine: lck_rw_init
926 if (attr
== LCK_ATTR_NULL
) {
927 attr
= &LockDefaultLckAttr
;
929 memset(lck
, 0, sizeof(lck_rw_t
));
930 lck
->lck_rw_can_sleep
= TRUE
;
931 if ((attr
->lck_attr_val
& LCK_ATTR_RW_SHARED_PRIORITY
) == 0) {
932 lck
->lck_rw_priv_excl
= TRUE
;
935 lck_grp_reference(grp
);
936 lck_grp_lckcnt_incr(grp
, LCK_TYPE_RW
);
941 * Routine: lck_rw_destroy
948 if (lck
->lck_rw_tag
== LCK_RW_TAG_DESTROYED
) {
952 lck_rw_assert(lck
, LCK_RW_ASSERT_NOTHELD
);
954 lck
->lck_rw_tag
= LCK_RW_TAG_DESTROYED
;
955 lck_grp_lckcnt_decr(grp
, LCK_TYPE_RW
);
956 lck_grp_deallocate(grp
);
961 * Routine: lck_rw_lock
966 lck_rw_type_t lck_rw_type
)
968 if (lck_rw_type
== LCK_RW_TYPE_SHARED
) {
969 lck_rw_lock_shared(lck
);
970 } else if (lck_rw_type
== LCK_RW_TYPE_EXCLUSIVE
) {
971 lck_rw_lock_exclusive(lck
);
973 panic("lck_rw_lock(): Invalid RW lock type: %x", lck_rw_type
);
978 * Routine: lck_rw_lock_exclusive
981 lck_rw_lock_exclusive(lck_rw_t
*lock
)
983 thread_t thread
= current_thread();
985 thread
->rwlock_count
++;
986 if (atomic_test_and_set32(&lock
->lck_rw_data
,
987 (LCK_RW_SHARED_MASK
| LCK_RW_WANT_EXCL
| LCK_RW_WANT_UPGRADE
| LCK_RW_INTERLOCK
),
988 LCK_RW_WANT_EXCL
, memory_order_acquire_smp
, FALSE
)) {
990 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_ACQUIRE
, lock
, DTRACE_RW_EXCL
);
991 #endif /* CONFIG_DTRACE */
993 lck_rw_lock_exclusive_gen(lock
);
996 thread_t owner
= ordered_load_rw_owner(lock
);
997 assertf(owner
== THREAD_NULL
, "state=0x%x, owner=%p", ordered_load_rw(lock
), owner
);
999 ordered_store_rw_owner(lock
, thread
);
1003 * Routine: lck_rw_lock_shared
1006 lck_rw_lock_shared(lck_rw_t
*lock
)
1008 uint32_t data
, prev
;
1010 current_thread()->rwlock_count
++;
1012 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_acquire_smp
);
1013 if (data
& (LCK_RW_WANT_EXCL
| LCK_RW_WANT_UPGRADE
| LCK_RW_INTERLOCK
)) {
1014 atomic_exchange_abort();
1015 lck_rw_lock_shared_gen(lock
);
1018 data
+= LCK_RW_SHARED_READER
;
1019 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_acquire_smp
)) {
1025 thread_t owner
= ordered_load_rw_owner(lock
);
1026 assertf(owner
== THREAD_NULL
, "state=0x%x, owner=%p", ordered_load_rw(lock
), owner
);
1029 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_ACQUIRE
, lock
, DTRACE_RW_SHARED
);
1030 #endif /* CONFIG_DTRACE */
1035 * Routine: lck_rw_lock_shared_to_exclusive
1037 * False returned upon failure, in this case the shared lock is dropped.
1040 lck_rw_lock_shared_to_exclusive(lck_rw_t
*lock
)
1042 uint32_t data
, prev
;
1045 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_acquire_smp
);
1046 if (data
& LCK_RW_INTERLOCK
) {
1047 atomic_exchange_abort();
1048 lck_rw_interlock_spin(lock
);
1051 if (data
& LCK_RW_WANT_UPGRADE
) {
1052 data
-= LCK_RW_SHARED_READER
;
1053 if ((data
& LCK_RW_SHARED_MASK
) == 0) { /* we were the last reader */
1054 data
&= ~(LCK_RW_W_WAITING
); /* so clear the wait indicator */
1056 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_acquire_smp
)) {
1057 return lck_rw_lock_shared_to_exclusive_failure(lock
, prev
);
1060 data
|= LCK_RW_WANT_UPGRADE
; /* ask for WANT_UPGRADE */
1061 data
-= LCK_RW_SHARED_READER
; /* and shed our read count */
1062 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_acquire_smp
)) {
1068 /* we now own the WANT_UPGRADE */
1069 if (data
& LCK_RW_SHARED_MASK
) { /* check to see if all of the readers are drained */
1070 lck_rw_lock_shared_to_exclusive_success(lock
); /* if not, we need to go wait */
1073 thread_t owner
= ordered_load_rw_owner(lock
);
1074 assertf(owner
== THREAD_NULL
, "state=0x%x, owner=%p", ordered_load_rw(lock
), owner
);
1076 ordered_store_rw_owner(lock
, current_thread());
1078 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_TO_EXCL_UPGRADE
, lock
, 0);
1079 #endif /* CONFIG_DTRACE */
1085 * Routine: lck_rw_lock_shared_to_exclusive_failure
1087 * Fast path code has already dropped our read
1088 * count and determined that someone else owns 'lck_rw_want_upgrade'
1089 * if 'lck_rw_shared_count' == 0, its also already dropped 'lck_w_waiting'
1090 * all we need to do here is determine if a wakeup is needed
1093 lck_rw_lock_shared_to_exclusive_failure(
1095 uint32_t prior_lock_state
)
1097 thread_t thread
= current_thread();
1098 uint32_t rwlock_count
;
1100 /* Check if dropping the lock means that we need to unpromote */
1101 rwlock_count
= thread
->rwlock_count
--;
1103 if (rwlock_count
== 0) {
1104 panic("rw lock count underflow for thread %p", thread
);
1107 if ((prior_lock_state
& LCK_RW_W_WAITING
) &&
1108 ((prior_lock_state
& LCK_RW_SHARED_MASK
) == LCK_RW_SHARED_READER
)) {
1110 * Someone else has requested upgrade.
1111 * Since we've released the read lock, wake
1112 * him up if he's blocked waiting
1114 thread_wakeup(LCK_RW_WRITER_EVENT(lck
));
1117 if ((rwlock_count
== 1 /* field now 0 */) && (thread
->sched_flags
& TH_SFLAG_RW_PROMOTED
)) {
1118 /* sched_flags checked without lock, but will be rechecked while clearing */
1119 lck_rw_clear_promotion(thread
, unslide_for_kdebug(lck
));
1122 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_CODE
) | DBG_FUNC_NONE
,
1123 VM_KERNEL_UNSLIDE_OR_PERM(lck
), lck
->lck_rw_shared_count
, lck
->lck_rw_want_upgrade
, 0, 0);
1129 * Routine: lck_rw_lock_shared_to_exclusive_success
1131 * assembly fast path code has already dropped our read
1132 * count and successfully acquired 'lck_rw_want_upgrade'
1133 * we just need to wait for the rest of the readers to drain
1134 * and then we can return as the exclusive holder of this lock
1137 lck_rw_lock_shared_to_exclusive_success(
1140 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lock
);
1145 boolean_t not_shared
;
1148 uint64_t wait_interval
= 0;
1149 int readers_at_sleep
= 0;
1150 boolean_t dtrace_ls_initialized
= FALSE
;
1151 boolean_t dtrace_rwl_shared_to_excl_spin
, dtrace_rwl_shared_to_excl_block
, dtrace_ls_enabled
= FALSE
;
1154 while (!lck_rw_drain_status(lock
, LCK_RW_SHARED_MASK
, FALSE
)) {
1155 word
.data
= ordered_load_rw(lock
);
1157 if (dtrace_ls_initialized
== FALSE
) {
1158 dtrace_ls_initialized
= TRUE
;
1159 dtrace_rwl_shared_to_excl_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_TO_EXCL_SPIN
] != 0);
1160 dtrace_rwl_shared_to_excl_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_TO_EXCL_BLOCK
] != 0);
1161 dtrace_ls_enabled
= dtrace_rwl_shared_to_excl_spin
|| dtrace_rwl_shared_to_excl_block
;
1162 if (dtrace_ls_enabled
) {
1164 * Either sleeping or spinning is happening,
1165 * start a timing of our delay interval now.
1167 readers_at_sleep
= word
.shared_count
;
1168 wait_interval
= mach_absolute_time();
1173 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_SPIN_CODE
) | DBG_FUNC_START
,
1174 trace_lck
, word
.shared_count
, 0, 0, 0);
1176 not_shared
= lck_rw_drain_status(lock
, LCK_RW_SHARED_MASK
, TRUE
);
1178 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_SPIN_CODE
) | DBG_FUNC_END
,
1179 trace_lck
, lock
->lck_rw_shared_count
, 0, 0, 0);
1186 * if we get here, the spin deadline in lck_rw_wait_on_status()
1187 * has expired w/o the rw_shared_count having drained to 0
1188 * check to see if we're allowed to do a thread_block
1190 if (word
.can_sleep
) {
1191 istate
= lck_interlock_lock(lock
);
1193 word
.data
= ordered_load_rw(lock
);
1194 if (word
.shared_count
!= 0) {
1195 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_WAIT_CODE
) | DBG_FUNC_START
,
1196 trace_lck
, word
.shared_count
, 0, 0, 0);
1199 ordered_store_rw(lock
, word
.data
);
1201 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockUpgrade
);
1202 res
= assert_wait(LCK_RW_WRITER_EVENT(lock
),
1203 THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
);
1204 lck_interlock_unlock(lock
, istate
);
1206 if (res
== THREAD_WAITING
) {
1207 res
= thread_block(THREAD_CONTINUE_NULL
);
1210 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_WAIT_CODE
) | DBG_FUNC_END
,
1211 trace_lck
, res
, slept
, 0, 0);
1213 lck_interlock_unlock(lock
, istate
);
1220 * We infer whether we took the sleep/spin path above by checking readers_at_sleep.
1222 if (dtrace_ls_enabled
== TRUE
) {
1224 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_TO_EXCL_SPIN
, lock
, mach_absolute_time() - wait_interval
, 0);
1226 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_TO_EXCL_BLOCK
, lock
,
1227 mach_absolute_time() - wait_interval
, 1,
1228 (readers_at_sleep
== 0 ? 1 : 0), readers_at_sleep
);
1231 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_TO_EXCL_UPGRADE
, lock
, 1);
1238 * Routine: lck_rw_lock_exclusive_to_shared
1242 lck_rw_lock_exclusive_to_shared(lck_rw_t
*lock
)
1244 uint32_t data
, prev
;
1246 assertf(lock
->lck_rw_owner
== current_thread(), "state=0x%x, owner=%p", lock
->lck_rw_data
, lock
->lck_rw_owner
);
1247 ordered_store_rw_owner(lock
, THREAD_NULL
);
1249 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_release_smp
);
1250 if (data
& LCK_RW_INTERLOCK
) {
1252 atomic_exchange_abort();
1253 lck_rw_interlock_spin(lock
); /* wait for interlock to clear */
1256 panic("lck_rw_lock_exclusive_to_shared(): Interlock locked (%p): %x", lock
, data
);
1259 data
+= LCK_RW_SHARED_READER
;
1260 if (data
& LCK_RW_WANT_UPGRADE
) {
1261 data
&= ~(LCK_RW_WANT_UPGRADE
);
1263 data
&= ~(LCK_RW_WANT_EXCL
);
1265 if (!((prev
& LCK_RW_W_WAITING
) && (prev
& LCK_RW_PRIV_EXCL
))) {
1266 data
&= ~(LCK_RW_W_WAITING
);
1268 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_release_smp
)) {
1273 return lck_rw_lock_exclusive_to_shared_gen(lock
, prev
);
1277 * Routine: lck_rw_lock_exclusive_to_shared_gen
1279 * Fast path has already dropped
1280 * our exclusive state and bumped lck_rw_shared_count
1281 * all we need to do here is determine if anyone
1282 * needs to be awakened.
1285 lck_rw_lock_exclusive_to_shared_gen(
1287 uint32_t prior_lock_state
)
1289 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lck
);
1290 lck_rw_word_t fake_lck
;
1293 * prior_lock state is a snapshot of the 1st word of the
1294 * lock in question... we'll fake up a pointer to it
1295 * and carefully not access anything beyond whats defined
1296 * in the first word of a lck_rw_t
1298 fake_lck
.data
= prior_lock_state
;
1300 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_TO_SH_CODE
) | DBG_FUNC_START
,
1301 trace_lck
, fake_lck
->want_excl
, fake_lck
->want_upgrade
, 0, 0);
1304 * don't wake up anyone waiting to take the lock exclusively
1305 * since we hold a read count... when the read count drops to 0,
1306 * the writers will be woken.
1308 * wake up any waiting readers if we don't have any writers waiting,
1309 * or the lock is NOT marked as rw_priv_excl (writers have privilege)
1311 if (!(fake_lck
.priv_excl
&& fake_lck
.w_waiting
) && fake_lck
.r_waiting
) {
1312 thread_wakeup(LCK_RW_READER_EVENT(lck
));
1315 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_TO_SH_CODE
) | DBG_FUNC_END
,
1316 trace_lck
, lck
->lck_rw_want_excl
, lck
->lck_rw_want_upgrade
, lck
->lck_rw_shared_count
, 0);
1319 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_TO_SHARED_DOWNGRADE
, lck
, 0);
1325 * Routine: lck_rw_try_lock
1330 lck_rw_type_t lck_rw_type
)
1332 if (lck_rw_type
== LCK_RW_TYPE_SHARED
) {
1333 return lck_rw_try_lock_shared(lck
);
1334 } else if (lck_rw_type
== LCK_RW_TYPE_EXCLUSIVE
) {
1335 return lck_rw_try_lock_exclusive(lck
);
1337 panic("lck_rw_try_lock(): Invalid rw lock type: %x", lck_rw_type
);
1343 * Routine: lck_rw_try_lock_shared
1347 lck_rw_try_lock_shared(lck_rw_t
*lock
)
1349 uint32_t data
, prev
;
1352 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_acquire_smp
);
1353 if (data
& LCK_RW_INTERLOCK
) {
1355 atomic_exchange_abort();
1356 lck_rw_interlock_spin(lock
);
1359 panic("lck_rw_try_lock_shared(): Interlock locked (%p): %x", lock
, data
);
1362 if (data
& (LCK_RW_WANT_EXCL
| LCK_RW_WANT_UPGRADE
)) {
1363 atomic_exchange_abort();
1364 return FALSE
; /* lock is busy */
1366 data
+= LCK_RW_SHARED_READER
; /* Increment reader refcount */
1367 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_acquire_smp
)) {
1373 thread_t owner
= ordered_load_rw_owner(lock
);
1374 assertf(owner
== THREAD_NULL
, "state=0x%x, owner=%p", ordered_load_rw(lock
), owner
);
1376 current_thread()->rwlock_count
++;
1378 LOCKSTAT_RECORD(LS_LCK_RW_TRY_LOCK_SHARED_ACQUIRE
, lock
, DTRACE_RW_SHARED
);
1379 #endif /* CONFIG_DTRACE */
1385 * Routine: lck_rw_try_lock_exclusive
1389 lck_rw_try_lock_exclusive(lck_rw_t
*lock
)
1391 uint32_t data
, prev
;
1395 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_acquire_smp
);
1396 if (data
& LCK_RW_INTERLOCK
) {
1398 atomic_exchange_abort();
1399 lck_rw_interlock_spin(lock
);
1402 panic("lck_rw_try_lock_exclusive(): Interlock locked (%p): %x", lock
, data
);
1405 if (data
& (LCK_RW_SHARED_MASK
| LCK_RW_WANT_EXCL
| LCK_RW_WANT_UPGRADE
)) {
1406 atomic_exchange_abort();
1409 data
|= LCK_RW_WANT_EXCL
;
1410 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_acquire_smp
)) {
1415 thread
= current_thread();
1416 thread
->rwlock_count
++;
1418 thread_t owner
= ordered_load_rw_owner(lock
);
1419 assertf(owner
== THREAD_NULL
, "state=0x%x, owner=%p", ordered_load_rw(lock
), owner
);
1421 ordered_store_rw_owner(lock
, thread
);
1423 LOCKSTAT_RECORD(LS_LCK_RW_TRY_LOCK_EXCL_ACQUIRE
, lock
, DTRACE_RW_EXCL
);
1424 #endif /* CONFIG_DTRACE */
1430 * Routine: lck_rw_unlock
1435 lck_rw_type_t lck_rw_type
)
1437 if (lck_rw_type
== LCK_RW_TYPE_SHARED
) {
1438 lck_rw_unlock_shared(lck
);
1439 } else if (lck_rw_type
== LCK_RW_TYPE_EXCLUSIVE
) {
1440 lck_rw_unlock_exclusive(lck
);
1442 panic("lck_rw_unlock(): Invalid RW lock type: %d", lck_rw_type
);
1448 * Routine: lck_rw_unlock_shared
1451 lck_rw_unlock_shared(
1456 assertf(lck
->lck_rw_owner
== THREAD_NULL
, "state=0x%x, owner=%p", lck
->lck_rw_data
, lck
->lck_rw_owner
);
1457 assertf(lck
->lck_rw_shared_count
> 0, "shared_count=0x%x", lck
->lck_rw_shared_count
);
1458 ret
= lck_rw_done(lck
);
1460 if (ret
!= LCK_RW_TYPE_SHARED
) {
1461 panic("lck_rw_unlock_shared(): lock %p held in mode: %d", lck
, ret
);
1467 * Routine: lck_rw_unlock_exclusive
1470 lck_rw_unlock_exclusive(
1475 assertf(lck
->lck_rw_owner
== current_thread(), "state=0x%x, owner=%p", lck
->lck_rw_data
, lck
->lck_rw_owner
);
1476 ret
= lck_rw_done(lck
);
1478 if (ret
!= LCK_RW_TYPE_EXCLUSIVE
) {
1479 panic("lck_rw_unlock_exclusive(): lock %p held in mode: %d", lck
, ret
);
1485 * Routine: lck_rw_lock_exclusive_gen
1488 lck_rw_lock_exclusive_gen(
1491 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lock
);
1494 boolean_t gotlock
= 0;
1495 boolean_t not_shared_or_upgrade
= 0;
1496 wait_result_t res
= 0;
1500 boolean_t dtrace_ls_initialized
= FALSE
;
1501 boolean_t dtrace_rwl_excl_spin
, dtrace_rwl_excl_block
, dtrace_ls_enabled
= FALSE
;
1502 uint64_t wait_interval
= 0;
1503 int readers_at_sleep
= 0;
1507 * Try to acquire the lck_rw_want_excl bit.
1509 while (!lck_rw_grab(lock
, LCK_RW_GRAB_WANT
, FALSE
)) {
1511 if (dtrace_ls_initialized
== FALSE
) {
1512 dtrace_ls_initialized
= TRUE
;
1513 dtrace_rwl_excl_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_SPIN
] != 0);
1514 dtrace_rwl_excl_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_BLOCK
] != 0);
1515 dtrace_ls_enabled
= dtrace_rwl_excl_spin
|| dtrace_rwl_excl_block
;
1516 if (dtrace_ls_enabled
) {
1518 * Either sleeping or spinning is happening,
1519 * start a timing of our delay interval now.
1521 readers_at_sleep
= lock
->lck_rw_shared_count
;
1522 wait_interval
= mach_absolute_time();
1527 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_SPIN_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
1529 gotlock
= lck_rw_grab(lock
, LCK_RW_GRAB_WANT
, TRUE
);
1531 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_SPIN_CODE
) | DBG_FUNC_END
, trace_lck
, 0, 0, gotlock
, 0);
1537 * if we get here, the deadline has expired w/o us
1538 * being able to grab the lock exclusively
1539 * check to see if we're allowed to do a thread_block
1541 word
.data
= ordered_load_rw(lock
);
1542 if (word
.can_sleep
) {
1543 istate
= lck_interlock_lock(lock
);
1544 word
.data
= ordered_load_rw(lock
);
1546 if (word
.want_excl
) {
1547 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_WAIT_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
1550 ordered_store_rw(lock
, word
.data
);
1552 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockWrite
);
1553 res
= assert_wait(LCK_RW_WRITER_EVENT(lock
),
1554 THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
);
1555 lck_interlock_unlock(lock
, istate
);
1557 if (res
== THREAD_WAITING
) {
1558 res
= thread_block(THREAD_CONTINUE_NULL
);
1561 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_WAIT_CODE
) | DBG_FUNC_END
, trace_lck
, res
, slept
, 0, 0);
1564 ordered_store_rw(lock
, word
.data
);
1565 lck_interlock_unlock(lock
, istate
);
1571 * Wait for readers (and upgrades) to finish...
1573 while (!lck_rw_drain_status(lock
, LCK_RW_SHARED_MASK
| LCK_RW_WANT_UPGRADE
, FALSE
)) {
1576 * Either sleeping or spinning is happening, start
1577 * a timing of our delay interval now. If we set it
1578 * to -1 we don't have accurate data so we cannot later
1579 * decide to record a dtrace spin or sleep event.
1581 if (dtrace_ls_initialized
== FALSE
) {
1582 dtrace_ls_initialized
= TRUE
;
1583 dtrace_rwl_excl_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_SPIN
] != 0);
1584 dtrace_rwl_excl_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_BLOCK
] != 0);
1585 dtrace_ls_enabled
= dtrace_rwl_excl_spin
|| dtrace_rwl_excl_block
;
1586 if (dtrace_ls_enabled
) {
1588 * Either sleeping or spinning is happening,
1589 * start a timing of our delay interval now.
1591 readers_at_sleep
= lock
->lck_rw_shared_count
;
1592 wait_interval
= mach_absolute_time();
1597 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_SPIN_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
1599 not_shared_or_upgrade
= lck_rw_drain_status(lock
, LCK_RW_SHARED_MASK
| LCK_RW_WANT_UPGRADE
, TRUE
);
1601 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_SPIN_CODE
) | DBG_FUNC_END
, trace_lck
, 0, 0, not_shared_or_upgrade
, 0);
1603 if (not_shared_or_upgrade
) {
1607 * if we get here, the deadline has expired w/o us
1608 * being able to grab the lock exclusively
1609 * check to see if we're allowed to do a thread_block
1611 word
.data
= ordered_load_rw(lock
);
1612 if (word
.can_sleep
) {
1613 istate
= lck_interlock_lock(lock
);
1614 word
.data
= ordered_load_rw(lock
);
1616 if (word
.shared_count
!= 0 || word
.want_upgrade
) {
1617 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_WAIT_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
1620 ordered_store_rw(lock
, word
.data
);
1622 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockWrite
);
1623 res
= assert_wait(LCK_RW_WRITER_EVENT(lock
),
1624 THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
);
1625 lck_interlock_unlock(lock
, istate
);
1627 if (res
== THREAD_WAITING
) {
1628 res
= thread_block(THREAD_CONTINUE_NULL
);
1631 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_WAIT_CODE
) | DBG_FUNC_END
, trace_lck
, res
, slept
, 0, 0);
1633 lck_interlock_unlock(lock
, istate
);
1635 * must own the lock now, since we checked for
1636 * readers or upgrade owner behind the interlock
1637 * no need for a call to 'lck_rw_drain_status'
1646 * Decide what latencies we suffered that are Dtrace events.
1647 * If we have set wait_interval, then we either spun or slept.
1648 * At least we get out from under the interlock before we record
1649 * which is the best we can do here to minimize the impact
1651 * If we have set wait_interval to -1, then dtrace was not enabled when we
1652 * started sleeping/spinning so we don't record this event.
1654 if (dtrace_ls_enabled
== TRUE
) {
1656 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_SPIN
, lock
,
1657 mach_absolute_time() - wait_interval
, 1);
1660 * For the blocking case, we also record if when we blocked
1661 * it was held for read or write, and how many readers.
1662 * Notice that above we recorded this before we dropped
1663 * the interlock so the count is accurate.
1665 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_BLOCK
, lock
,
1666 mach_absolute_time() - wait_interval
, 1,
1667 (readers_at_sleep
== 0 ? 1 : 0), readers_at_sleep
);
1670 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_ACQUIRE
, lock
, 1);
1671 #endif /* CONFIG_DTRACE */
1675 * Routine: lck_rw_done
1679 lck_rw_done(lck_rw_t
*lock
)
1681 uint32_t data
, prev
;
1682 boolean_t once
= FALSE
;
1685 data
= atomic_exchange_begin32(&lock
->lck_rw_data
, &prev
, memory_order_release_smp
);
1686 if (data
& LCK_RW_INTERLOCK
) { /* wait for interlock to clear */
1688 atomic_exchange_abort();
1689 lck_rw_interlock_spin(lock
);
1692 panic("lck_rw_done(): Interlock locked (%p): %x", lock
, data
);
1695 if (data
& LCK_RW_SHARED_MASK
) { /* lock is held shared */
1696 assertf(lock
->lck_rw_owner
== THREAD_NULL
, "state=0x%x, owner=%p", lock
->lck_rw_data
, lock
->lck_rw_owner
);
1697 data
-= LCK_RW_SHARED_READER
;
1698 if ((data
& LCK_RW_SHARED_MASK
) == 0) { /* if reader count has now gone to 0, check for waiters */
1701 } else { /* if reader count == 0, must be exclusive lock */
1702 if (data
& LCK_RW_WANT_UPGRADE
) {
1703 data
&= ~(LCK_RW_WANT_UPGRADE
);
1705 if (data
& LCK_RW_WANT_EXCL
) {
1706 data
&= ~(LCK_RW_WANT_EXCL
);
1707 } else { /* lock is not 'owned', panic */
1708 panic("Releasing non-exclusive RW lock without a reader refcount!");
1712 // Only check for holder and clear it once
1713 assertf(lock
->lck_rw_owner
== current_thread(), "state=0x%x, owner=%p", lock
->lck_rw_data
, lock
->lck_rw_owner
);
1714 ordered_store_rw_owner(lock
, THREAD_NULL
);
1719 * test the original values to match what
1720 * lck_rw_done_gen is going to do to determine
1721 * which wakeups need to happen...
1723 * if !(fake_lck->lck_rw_priv_excl && fake_lck->lck_w_waiting)
1725 if (prev
& LCK_RW_W_WAITING
) {
1726 data
&= ~(LCK_RW_W_WAITING
);
1727 if ((prev
& LCK_RW_PRIV_EXCL
) == 0) {
1728 data
&= ~(LCK_RW_R_WAITING
);
1731 data
&= ~(LCK_RW_R_WAITING
);
1734 if (atomic_exchange_complete32(&lock
->lck_rw_data
, prev
, data
, memory_order_release_smp
)) {
1739 return lck_rw_done_gen(lock
, prev
);
1743 * Routine: lck_rw_done_gen
1745 * called from the assembly language wrapper...
1746 * prior_lock_state is the value in the 1st
1747 * word of the lock at the time of a successful
1748 * atomic compare and exchange with the new value...
1749 * it represents the state of the lock before we
1750 * decremented the rw_shared_count or cleared either
1751 * rw_want_upgrade or rw_want_write and
1752 * the lck_x_waiting bits... since the wrapper
1753 * routine has already changed the state atomically,
1754 * we just need to decide if we should
1755 * wake up anyone and what value to return... we do
1756 * this by examining the state of the lock before
1759 static lck_rw_type_t
1762 uint32_t prior_lock_state
)
1764 lck_rw_word_t fake_lck
;
1765 lck_rw_type_t lock_type
;
1767 uint32_t rwlock_count
;
1770 * prior_lock state is a snapshot of the 1st word of the
1771 * lock in question... we'll fake up a pointer to it
1772 * and carefully not access anything beyond whats defined
1773 * in the first word of a lck_rw_t
1775 fake_lck
.data
= prior_lock_state
;
1777 if (fake_lck
.shared_count
<= 1) {
1778 if (fake_lck
.w_waiting
) {
1779 thread_wakeup(LCK_RW_WRITER_EVENT(lck
));
1782 if (!(fake_lck
.priv_excl
&& fake_lck
.w_waiting
) && fake_lck
.r_waiting
) {
1783 thread_wakeup(LCK_RW_READER_EVENT(lck
));
1786 if (fake_lck
.shared_count
) {
1787 lock_type
= LCK_RW_TYPE_SHARED
;
1789 lock_type
= LCK_RW_TYPE_EXCLUSIVE
;
1792 /* Check if dropping the lock means that we need to unpromote */
1793 thread
= current_thread();
1794 rwlock_count
= thread
->rwlock_count
--;
1796 if (rwlock_count
== 0) {
1797 panic("rw lock count underflow for thread %p", thread
);
1800 if ((rwlock_count
== 1 /* field now 0 */) && (thread
->sched_flags
& TH_SFLAG_RW_PROMOTED
)) {
1801 /* sched_flags checked without lock, but will be rechecked while clearing */
1802 lck_rw_clear_promotion(thread
, unslide_for_kdebug(lck
));
1805 LOCKSTAT_RECORD(LS_LCK_RW_DONE_RELEASE
, lck
, lock_type
== LCK_RW_TYPE_SHARED
? 0 : 1);
1811 * Routine: lck_rw_lock_shared_gen
1813 * Fast path code has determined that this lock
1814 * is held exclusively... this is where we spin/block
1815 * until we can acquire the lock in the shared mode
1818 lck_rw_lock_shared_gen(
1821 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lck
);
1823 boolean_t gotlock
= 0;
1825 wait_result_t res
= 0;
1829 uint64_t wait_interval
= 0;
1830 int readers_at_sleep
= 0;
1831 boolean_t dtrace_ls_initialized
= FALSE
;
1832 boolean_t dtrace_rwl_shared_spin
, dtrace_rwl_shared_block
, dtrace_ls_enabled
= FALSE
;
1833 #endif /* CONFIG_DTRACE */
1835 while (!lck_rw_grab(lck
, LCK_RW_GRAB_SHARED
, FALSE
)) {
1837 if (dtrace_ls_initialized
== FALSE
) {
1838 dtrace_ls_initialized
= TRUE
;
1839 dtrace_rwl_shared_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_SPIN
] != 0);
1840 dtrace_rwl_shared_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_BLOCK
] != 0);
1841 dtrace_ls_enabled
= dtrace_rwl_shared_spin
|| dtrace_rwl_shared_block
;
1842 if (dtrace_ls_enabled
) {
1844 * Either sleeping or spinning is happening,
1845 * start a timing of our delay interval now.
1847 readers_at_sleep
= lck
->lck_rw_shared_count
;
1848 wait_interval
= mach_absolute_time();
1853 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_SPIN_CODE
) | DBG_FUNC_START
,
1854 trace_lck
, lck
->lck_rw_want_excl
, lck
->lck_rw_want_upgrade
, 0, 0);
1856 gotlock
= lck_rw_grab(lck
, LCK_RW_GRAB_SHARED
, TRUE
);
1858 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_SPIN_CODE
) | DBG_FUNC_END
,
1859 trace_lck
, lck
->lck_rw_want_excl
, lck
->lck_rw_want_upgrade
, gotlock
, 0);
1865 * if we get here, the deadline has expired w/o us
1866 * being able to grab the lock for read
1867 * check to see if we're allowed to do a thread_block
1869 if (lck
->lck_rw_can_sleep
) {
1870 istate
= lck_interlock_lock(lck
);
1872 word
.data
= ordered_load_rw(lck
);
1873 if ((word
.want_excl
|| word
.want_upgrade
) &&
1874 ((word
.shared_count
== 0) || word
.priv_excl
)) {
1875 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_WAIT_CODE
) | DBG_FUNC_START
,
1876 trace_lck
, word
.want_excl
, word
.want_upgrade
, 0, 0);
1879 ordered_store_rw(lck
, word
.data
);
1881 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockRead
);
1882 res
= assert_wait(LCK_RW_READER_EVENT(lck
),
1883 THREAD_UNINT
| THREAD_WAIT_NOREPORT_USER
);
1884 lck_interlock_unlock(lck
, istate
);
1886 if (res
== THREAD_WAITING
) {
1887 res
= thread_block(THREAD_CONTINUE_NULL
);
1890 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_WAIT_CODE
) | DBG_FUNC_END
,
1891 trace_lck
, res
, slept
, 0, 0);
1893 word
.shared_count
++;
1894 ordered_store_rw(lck
, word
.data
);
1895 lck_interlock_unlock(lck
, istate
);
1902 if (dtrace_ls_enabled
== TRUE
) {
1904 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_SPIN
, lck
, mach_absolute_time() - wait_interval
, 0);
1906 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_BLOCK
, lck
,
1907 mach_absolute_time() - wait_interval
, 0,
1908 (readers_at_sleep
== 0 ? 1 : 0), readers_at_sleep
);
1911 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_ACQUIRE
, lck
, 0);
1912 #endif /* CONFIG_DTRACE */
1922 case LCK_RW_ASSERT_SHARED
:
1923 if ((lck
->lck_rw_shared_count
!= 0) &&
1924 (lck
->lck_rw_owner
== THREAD_NULL
)) {
1928 case LCK_RW_ASSERT_EXCLUSIVE
:
1929 if ((lck
->lck_rw_want_excl
|| lck
->lck_rw_want_upgrade
) &&
1930 (lck
->lck_rw_shared_count
== 0) &&
1931 (lck
->lck_rw_owner
== current_thread())) {
1935 case LCK_RW_ASSERT_HELD
:
1936 if (lck
->lck_rw_shared_count
!= 0) {
1937 return; // Held shared
1939 if ((lck
->lck_rw_want_excl
|| lck
->lck_rw_want_upgrade
) &&
1940 (lck
->lck_rw_owner
== current_thread())) {
1941 return; // Held exclusive
1944 case LCK_RW_ASSERT_NOTHELD
:
1945 if ((lck
->lck_rw_shared_count
== 0) &&
1946 !(lck
->lck_rw_want_excl
|| lck
->lck_rw_want_upgrade
) &&
1947 (lck
->lck_rw_owner
== THREAD_NULL
)) {
1954 panic("rw lock (%p)%s held (mode=%u)", lck
, (type
== LCK_RW_ASSERT_NOTHELD
? "" : " not"), type
);
1959 * Routine: kdp_lck_rw_lock_is_acquired_exclusive
1960 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
1963 kdp_lck_rw_lock_is_acquired_exclusive(lck_rw_t
*lck
)
1966 panic("panic: rw lock exclusive check done outside of kernel debugger");
1968 return ((lck
->lck_rw_want_upgrade
|| lck
->lck_rw_want_excl
) && (lck
->lck_rw_shared_count
== 0)) ? TRUE
: FALSE
;
1972 * The C portion of the mutex package. These routines are only invoked
1973 * if the optimized assembler routines can't do the work.
1977 * Forward declaration
1982 lck_mtx_ext_t
* lck
,
1987 * Routine: lck_mtx_alloc_init
1996 if ((lck
= (lck_mtx_t
*) kalloc(sizeof(lck_mtx_t
))) != 0) {
1997 lck_mtx_init(lck
, grp
, attr
);
2004 * Routine: lck_mtx_free
2011 lck_mtx_destroy(lck
, grp
);
2012 kfree(lck
, sizeof(lck_mtx_t
));
2016 * Routine: lck_mtx_init
2025 lck_mtx_ext_t
*lck_ext
;
2027 lck_attr_t
*lck_attr
;
2029 if (attr
!= LCK_ATTR_NULL
) {
2032 lck_attr
= &LockDefaultLckAttr
;
2036 if ((lck_attr
->lck_attr_val
) & LCK_ATTR_DEBUG
) {
2037 if ((lck_ext
= (lck_mtx_ext_t
*) kalloc(sizeof(lck_mtx_ext_t
))) != 0) {
2038 lck_mtx_ext_init(lck_ext
, grp
, lck_attr
);
2039 lck
->lck_mtx_tag
= LCK_MTX_TAG_INDIRECT
;
2040 lck
->lck_mtx_ptr
= lck_ext
;
2041 lck
->lck_mtx_type
= LCK_MTX_TYPE
;
2046 lck
->lck_mtx_ptr
= NULL
; // Clear any padding in the union fields below
2047 lck
->lck_mtx_waiters
= 0;
2048 lck
->lck_mtx_type
= LCK_MTX_TYPE
;
2049 ordered_store_mtx(lck
, 0);
2051 lck_grp_reference(grp
);
2052 lck_grp_lckcnt_incr(grp
, LCK_TYPE_MTX
);
2056 * Routine: lck_mtx_init_ext
2061 lck_mtx_ext_t
* lck_ext
,
2065 lck_attr_t
*lck_attr
;
2067 if (attr
!= LCK_ATTR_NULL
) {
2070 lck_attr
= &LockDefaultLckAttr
;
2073 if ((lck_attr
->lck_attr_val
) & LCK_ATTR_DEBUG
) {
2074 lck_mtx_ext_init(lck_ext
, grp
, lck_attr
);
2075 lck
->lck_mtx_tag
= LCK_MTX_TAG_INDIRECT
;
2076 lck
->lck_mtx_ptr
= lck_ext
;
2077 lck
->lck_mtx_type
= LCK_MTX_TYPE
;
2079 lck
->lck_mtx_waiters
= 0;
2080 lck
->lck_mtx_type
= LCK_MTX_TYPE
;
2081 ordered_store_mtx(lck
, 0);
2083 lck_grp_reference(grp
);
2084 lck_grp_lckcnt_incr(grp
, LCK_TYPE_MTX
);
2088 * Routine: lck_mtx_ext_init
2092 lck_mtx_ext_t
* lck
,
2096 bzero((void *) lck
, sizeof(lck_mtx_ext_t
));
2098 lck
->lck_mtx
.lck_mtx_type
= LCK_MTX_TYPE
;
2100 if ((attr
->lck_attr_val
) & LCK_ATTR_DEBUG
) {
2101 lck
->lck_mtx_deb
.type
= MUTEX_TAG
;
2102 lck
->lck_mtx_attr
|= LCK_MTX_ATTR_DEBUG
;
2104 lck
->lck_mtx_grp
= grp
;
2106 if (grp
->lck_grp_attr
& LCK_GRP_ATTR_STAT
) {
2107 lck
->lck_mtx_attr
|= LCK_MTX_ATTR_STAT
;
2111 /* The slow versions */
2112 static void lck_mtx_lock_contended(lck_mtx_t
*lock
, thread_t thread
, boolean_t interlocked
);
2113 static boolean_t
lck_mtx_try_lock_contended(lck_mtx_t
*lock
, thread_t thread
);
2114 static void lck_mtx_unlock_contended(lck_mtx_t
*lock
, thread_t thread
, boolean_t interlocked
);
2116 /* The adaptive spin function */
2117 static spinwait_result_t
lck_mtx_lock_contended_spinwait_arm(lck_mtx_t
*lock
, thread_t thread
, boolean_t interlocked
);
2120 * Routine: lck_mtx_verify
2122 * Verify if a mutex is valid
2125 lck_mtx_verify(lck_mtx_t
*lock
)
2127 if (lock
->lck_mtx_type
!= LCK_MTX_TYPE
) {
2128 panic("Invalid mutex %p", lock
);
2130 #if DEVELOPMENT || DEBUG
2131 if (lock
->lck_mtx_tag
== LCK_MTX_TAG_DESTROYED
) {
2132 panic("Mutex destroyed %p", lock
);
2134 #endif /* DEVELOPMENT || DEBUG */
2138 * Routine: lck_mtx_check_preemption
2140 * Verify preemption is enabled when attempting to acquire a mutex.
2144 lck_mtx_check_preemption(lck_mtx_t
*lock
)
2146 #if DEVELOPMENT || DEBUG
2147 int pl
= get_preemption_level();
2150 panic("Attempt to take mutex with preemption disabled. Lock=%p, level=%d", lock
, pl
);
2158 * Routine: lck_mtx_lock
2161 lck_mtx_lock(lck_mtx_t
*lock
)
2165 lck_mtx_verify(lock
);
2166 lck_mtx_check_preemption(lock
);
2167 thread
= current_thread();
2168 if (os_atomic_cmpxchg(&lock
->lck_mtx_data
,
2169 0, LCK_MTX_THREAD_TO_STATE(thread
), acquire
)) {
2171 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_ACQUIRE
, lock
, 0);
2172 #endif /* CONFIG_DTRACE */
2175 lck_mtx_lock_contended(lock
, thread
, FALSE
);
2179 * This is the slow version of mutex locking.
2181 static void NOINLINE
2182 lck_mtx_lock_contended(lck_mtx_t
*lock
, thread_t thread
, boolean_t interlocked
)
2184 thread_t holding_thread
;
2187 spinwait_result_t sw_res
;
2188 struct turnstile
*ts
= NULL
;
2190 /* Loop waiting until I see that the mutex is unowned */
2192 sw_res
= lck_mtx_lock_contended_spinwait_arm(lock
, thread
, interlocked
);
2193 interlocked
= FALSE
;
2196 case SPINWAIT_ACQUIRED
:
2198 interlock_lock(lock
);
2199 turnstile_complete((uintptr_t)lock
, NULL
, NULL
, TURNSTILE_KERNEL_MUTEX
);
2200 interlock_unlock(lock
);
2203 case SPINWAIT_INTERLOCK
:
2209 state
= ordered_load_mtx(lock
);
2210 holding_thread
= LCK_MTX_STATE_TO_THREAD(state
);
2211 if (holding_thread
== NULL
) {
2214 ordered_store_mtx(lock
, (state
| LCK_ILOCK
| ARM_LCK_WAITERS
)); // Set waiters bit and wait
2215 lck_mtx_lock_wait(lock
, holding_thread
, &ts
);
2216 /* returns interlock unlocked */
2220 /* Hooray, I'm the new owner! */
2221 state
= ordered_load_mtx(lock
);
2223 if (state
& ARM_LCK_WAITERS
) {
2224 /* Skip lck_mtx_lock_acquire if there are no waiters. */
2225 waiters
= lck_mtx_lock_acquire(lock
, ts
);
2227 * lck_mtx_lock_acquire will call
2228 * turnstile_complete
2232 turnstile_complete((uintptr_t)lock
, NULL
, NULL
, TURNSTILE_KERNEL_MUTEX
);
2236 state
= LCK_MTX_THREAD_TO_STATE(thread
);
2238 state
|= ARM_LCK_WAITERS
;
2241 state
|= LCK_ILOCK
; // Preserve interlock
2242 ordered_store_mtx(lock
, state
); // Set ownership
2243 interlock_unlock(lock
); // Release interlock, enable preemption
2245 ordered_store_mtx(lock
, state
); // Set ownership
2246 enable_preemption();
2250 load_memory_barrier();
2252 assert(thread
->turnstile
!= NULL
);
2255 turnstile_cleanup();
2259 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_ACQUIRE
, lock
, 0);
2260 #endif /* CONFIG_DTRACE */
2264 * Routine: lck_mtx_lock_spinwait_arm
2266 * Invoked trying to acquire a mutex when there is contention but
2267 * the holder is running on another processor. We spin for up to a maximum
2268 * time waiting for the lock to be released.
2270 static spinwait_result_t
2271 lck_mtx_lock_contended_spinwait_arm(lck_mtx_t
*lock
, thread_t thread
, boolean_t interlocked
)
2273 int has_interlock
= (int)interlocked
;
2275 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lock
);
2276 thread_t owner
, prev_owner
;
2277 uint64_t window_deadline
, sliding_deadline
, high_deadline
;
2278 uint64_t start_time
, cur_time
, avg_hold_time
, bias
, delta
;
2280 uint i
, prev_owner_cpu
;
2281 int total_hold_time_samples
, window_hold_time_samples
, unfairness
;
2282 bool owner_on_core
, adjust
;
2283 uintptr_t state
, new_state
, waiters
;
2284 spinwait_result_t retval
= SPINWAIT_DID_SPIN_HIGH_THR
;
2286 if (__improbable(!(lck_mtx_adaptive_spin_mode
& ADAPTIVE_SPIN_ENABLE
))) {
2287 if (!has_interlock
) {
2288 interlock_lock(lock
);
2291 return SPINWAIT_DID_NOT_SPIN
;
2294 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_SPIN_CODE
) | DBG_FUNC_START
,
2295 trace_lck
, VM_KERNEL_UNSLIDE_OR_PERM(LCK_MTX_STATE_TO_THREAD(state
)), lock
->lck_mtx_waiters
, 0, 0);
2297 start_time
= mach_absolute_time();
2299 * window_deadline represents the "learning" phase.
2300 * The thread collects statistics about the lock during
2301 * window_deadline and then it makes a decision on whether to spin more
2302 * or block according to the concurrency behavior
2305 * Every thread can spin at least low_MutexSpin.
2307 window_deadline
= start_time
+ low_MutexSpin
;
2309 * Sliding_deadline is the adjusted spin deadline
2310 * computed after the "learning" phase.
2312 sliding_deadline
= window_deadline
;
2314 * High_deadline is a hard deadline. No thread
2315 * can spin more than this deadline.
2317 if (high_MutexSpin
>= 0) {
2318 high_deadline
= start_time
+ high_MutexSpin
;
2320 high_deadline
= start_time
+ low_MutexSpin
* real_ncpus
;
2324 * Do not know yet which is the owner cpu.
2325 * Initialize prev_owner_cpu with next cpu.
2327 prev_owner_cpu
= (cpu_number() + 1) % real_ncpus
;
2328 total_hold_time_samples
= 0;
2329 window_hold_time_samples
= 0;
2332 bias
= (os_hash_kernel_pointer(lock
) + cpu_number()) % real_ncpus
;
2334 /* Snoop the lock state */
2335 state
= ordered_load_mtx(lock
);
2336 owner
= LCK_MTX_STATE_TO_THREAD(state
);
2339 if (has_interlock
) {
2340 if (owner
== NULL
) {
2341 retval
= SPINWAIT_INTERLOCK
;
2345 * We are holding the interlock, so
2346 * we can safely dereference owner.
2348 if (!(owner
->machine
.machine_thread_flags
& MACHINE_THREAD_FLAGS_ON_CPU
) ||
2349 (owner
->state
& TH_IDLE
)) {
2350 retval
= SPINWAIT_DID_NOT_SPIN
;
2354 interlock_unlock(lock
);
2360 * - mutex is locked, and
2361 * - it's locked as a spin lock, and
2362 * - owner is running on another processor, and
2363 * - we haven't spun for long enough.
2367 * Try to acquire the lock.
2369 owner
= LCK_MTX_STATE_TO_THREAD(state
);
2370 if (owner
== NULL
) {
2371 waiters
= state
& ARM_LCK_WAITERS
;
2374 * preserve the waiter bit
2375 * and try acquire the interlock.
2376 * Note: we will successfully acquire
2377 * the interlock only if we can also
2380 new_state
= ARM_LCK_WAITERS
| LCK_ILOCK
;
2382 retval
= SPINWAIT_INTERLOCK
;
2383 disable_preemption();
2385 new_state
= LCK_MTX_THREAD_TO_STATE(thread
);
2386 retval
= SPINWAIT_ACQUIRED
;
2390 * The cmpxchg will succed only if the lock
2391 * is not owned (doesn't have an owner set)
2392 * and it is not interlocked.
2393 * It will not fail if there are waiters.
2395 if (os_atomic_cmpxchgv(&lock
->lck_mtx_data
,
2396 waiters
, new_state
, &state
, acquire
)) {
2401 enable_preemption();
2406 cur_time
= mach_absolute_time();
2409 * Never spin past high_deadline.
2411 if (cur_time
>= high_deadline
) {
2412 retval
= SPINWAIT_DID_SPIN_HIGH_THR
;
2417 * Check if owner is on core. If not block.
2419 owner
= LCK_MTX_STATE_TO_THREAD(state
);
2422 owner_on_core
= FALSE
;
2424 disable_preemption();
2425 state
= ordered_load_mtx(lock
);
2426 owner
= LCK_MTX_STATE_TO_THREAD(state
);
2429 * For scalability we want to check if the owner is on core
2430 * without locking the mutex interlock.
2431 * If we do not lock the mutex interlock, the owner that we see might be
2432 * invalid, so we cannot dereference it. Therefore we cannot check
2433 * any field of the thread to tell us if it is on core.
2434 * Check if the thread that is running on the other cpus matches the owner.
2438 cpu_data_t
*cpu_data_ptr
= CpuDataEntries
[i
].cpu_data_vaddr
;
2439 if ((cpu_data_ptr
!= NULL
) && (cpu_data_ptr
->cpu_active_thread
== owner
)) {
2440 owner_on_core
= TRUE
;
2443 if (++i
>= real_ncpus
) {
2446 } while (i
!= prev_owner_cpu
);
2447 enable_preemption();
2449 if (owner_on_core
) {
2453 state
= ordered_load_mtx(lock
);
2454 owner
= LCK_MTX_STATE_TO_THREAD(state
);
2455 if (owner
== prev_owner
) {
2457 * Owner is not on core.
2460 if (loopcount
== 0) {
2461 retval
= SPINWAIT_DID_NOT_SPIN
;
2463 retval
= SPINWAIT_DID_SPIN_OWNER_NOT_CORE
;
2468 * Fall through if the owner changed while we were scanning.
2469 * The new owner could potentially be on core, so loop
2474 enable_preemption();
2479 * Save how many times we see the owner changing.
2480 * We can roughly estimate the the mutex hold
2481 * time and the fairness with that.
2483 if (owner
!= prev_owner
) {
2485 total_hold_time_samples
++;
2486 window_hold_time_samples
++;
2490 * Learning window expired.
2491 * Try to adjust the sliding_deadline.
2493 if (cur_time
>= window_deadline
) {
2495 * If there was not contention during the window
2498 if (window_hold_time_samples
< 1) {
2499 retval
= SPINWAIT_DID_SPIN_NO_WINDOW_CONTENTION
;
2505 * For a fair lock, we'd wait for at most (NCPU-1) periods,
2506 * but the lock is unfair, so let's try to estimate by how much.
2508 unfairness
= total_hold_time_samples
/ real_ncpus
;
2510 if (unfairness
== 0) {
2512 * We observed the owner changing `total_hold_time_samples` times which
2513 * let us estimate the average hold time of this mutex for the duration
2515 * avg_hold_time = (cur_time - start_time) / total_hold_time_samples;
2517 * In this case spin at max avg_hold_time * (real_ncpus - 1)
2519 delta
= cur_time
- start_time
;
2520 sliding_deadline
= start_time
+ (delta
* (real_ncpus
- 1)) / total_hold_time_samples
;
2523 * In this case at least one of the other cpus was able to get the lock twice
2524 * while I was spinning.
2525 * We could spin longer but it won't necessarily help if the system is unfair.
2526 * Try to randomize the wait to reduce contention.
2528 * We compute how much time we could potentially spin
2529 * and distribute it over the cpus.
2531 * bias is an integer between 0 and real_ncpus.
2532 * distributed_increment = ((high_deadline - cur_time) / real_ncpus) * bias
2534 delta
= high_deadline
- cur_time
;
2535 sliding_deadline
= cur_time
+ ((delta
* bias
) / real_ncpus
);
2540 window_deadline
+= low_MutexSpin
;
2541 window_hold_time_samples
= 0;
2545 * Stop spinning if we past
2546 * the adjusted deadline.
2548 if (cur_time
>= sliding_deadline
) {
2549 retval
= SPINWAIT_DID_SPIN_SLIDING_THR
;
2554 * We want to arm the monitor for wfe,
2555 * so load exclusively the lock.
2558 * we rely on the fact that wfe will
2559 * eventually return even if the cache line
2560 * is not modified. This way we will keep
2561 * looping and checking if the deadlines expired.
2563 state
= os_atomic_load_exclusive(&lock
->lck_mtx_data
, relaxed
);
2564 owner
= LCK_MTX_STATE_TO_THREAD(state
);
2565 if (owner
!= NULL
) {
2567 state
= ordered_load_mtx(lock
);
2569 atomic_exchange_abort();
2578 * Note that we record a different probe id depending on whether
2579 * this is a direct or indirect mutex. This allows us to
2580 * penalize only lock groups that have debug/stats enabled
2581 * with dtrace processing if desired.
2583 if (__probable(lock
->lck_mtx_tag
!= LCK_MTX_TAG_INDIRECT
)) {
2584 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_SPIN
, lock
,
2585 mach_absolute_time() - start_time
);
2587 LOCKSTAT_RECORD(LS_LCK_MTX_EXT_LOCK_SPIN
, lock
,
2588 mach_absolute_time() - start_time
);
2590 /* The lockstat acquire event is recorded by the caller. */
2593 state
= ordered_load_mtx(lock
);
2595 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_SPIN_CODE
) | DBG_FUNC_END
,
2596 trace_lck
, VM_KERNEL_UNSLIDE_OR_PERM(LCK_MTX_STATE_TO_THREAD(state
)), lock
->lck_mtx_waiters
, retval
, 0);
2598 /* Spinwaiting is not useful on UP systems. */
2599 #pragma unused(lock, thread)
2600 int retval
= SPINWAIT_DID_NOT_SPIN
;
2601 #endif /* __SMP__ */
2602 if ((!has_interlock
) && (retval
!= SPINWAIT_ACQUIRED
)) {
2603 /* We must own either the lock or the interlock on return. */
2604 interlock_lock(lock
);
2612 * Common code for mutex locking as spinlock
2615 lck_mtx_lock_spin_internal(lck_mtx_t
*lock
, boolean_t allow_held_as_mutex
)
2619 interlock_lock(lock
);
2620 state
= ordered_load_mtx(lock
);
2621 if (LCK_MTX_STATE_TO_THREAD(state
)) {
2622 if (allow_held_as_mutex
) {
2623 lck_mtx_lock_contended(lock
, current_thread(), TRUE
);
2625 // "Always" variants can never block. If the lock is held and blocking is not allowed
2626 // then someone is mixing always and non-always calls on the same lock, which is
2628 panic("Attempting to block on a lock taken as spin-always %p", lock
);
2632 state
&= ARM_LCK_WAITERS
; // Preserve waiters bit
2633 state
|= (LCK_MTX_SPIN_TAG
| LCK_ILOCK
); // Add spin tag and maintain interlock
2634 ordered_store_mtx(lock
, state
);
2635 load_memory_barrier();
2638 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_SPIN_ACQUIRE
, lock
, 0);
2639 #endif /* CONFIG_DTRACE */
2643 * Routine: lck_mtx_lock_spin
2646 lck_mtx_lock_spin(lck_mtx_t
*lock
)
2648 lck_mtx_check_preemption(lock
);
2649 lck_mtx_lock_spin_internal(lock
, TRUE
);
2653 * Routine: lck_mtx_lock_spin_always
2656 lck_mtx_lock_spin_always(lck_mtx_t
*lock
)
2658 lck_mtx_lock_spin_internal(lock
, FALSE
);
2662 * Routine: lck_mtx_try_lock
2665 lck_mtx_try_lock(lck_mtx_t
*lock
)
2667 thread_t thread
= current_thread();
2669 lck_mtx_verify(lock
);
2670 if (os_atomic_cmpxchg(&lock
->lck_mtx_data
,
2671 0, LCK_MTX_THREAD_TO_STATE(thread
), acquire
)) {
2673 LOCKSTAT_RECORD(LS_LCK_MTX_TRY_LOCK_ACQUIRE
, lock
, 0);
2674 #endif /* CONFIG_DTRACE */
2677 return lck_mtx_try_lock_contended(lock
, thread
);
2680 static boolean_t NOINLINE
2681 lck_mtx_try_lock_contended(lck_mtx_t
*lock
, thread_t thread
)
2683 thread_t holding_thread
;
2688 interlock_lock(lock
);
2689 state
= ordered_load_mtx(lock
);
2690 holding_thread
= LCK_MTX_STATE_TO_THREAD(state
);
2691 if (holding_thread
) {
2692 interlock_unlock(lock
);
2696 disable_preemption_for_thread(thread
);
2697 state
= ordered_load_mtx(lock
);
2698 if (state
& LCK_ILOCK
) {
2699 panic("Unexpected interlock set (%p)", lock
);
2701 holding_thread
= LCK_MTX_STATE_TO_THREAD(state
);
2702 if (holding_thread
) {
2703 enable_preemption();
2707 ordered_store_mtx(lock
, state
);
2709 waiters
= lck_mtx_lock_acquire(lock
, NULL
);
2710 state
= LCK_MTX_THREAD_TO_STATE(thread
);
2712 state
|= ARM_LCK_WAITERS
;
2715 state
|= LCK_ILOCK
; // Preserve interlock
2716 ordered_store_mtx(lock
, state
); // Set ownership
2717 interlock_unlock(lock
); // Release interlock, enable preemption
2719 ordered_store_mtx(lock
, state
); // Set ownership
2720 enable_preemption();
2722 load_memory_barrier();
2724 turnstile_cleanup();
2729 static inline boolean_t
2730 lck_mtx_try_lock_spin_internal(lck_mtx_t
*lock
, boolean_t allow_held_as_mutex
)
2734 if (!interlock_try(lock
)) {
2737 state
= ordered_load_mtx(lock
);
2738 if (LCK_MTX_STATE_TO_THREAD(state
)) {
2739 // Lock is held as mutex
2740 if (allow_held_as_mutex
) {
2741 interlock_unlock(lock
);
2743 // "Always" variants can never block. If the lock is held as a normal mutex
2744 // then someone is mixing always and non-always calls on the same lock, which is
2746 panic("Spin-mutex held as full mutex %p", lock
);
2750 state
&= ARM_LCK_WAITERS
; // Preserve waiters bit
2751 state
|= (LCK_MTX_SPIN_TAG
| LCK_ILOCK
); // Add spin tag and maintain interlock
2752 ordered_store_mtx(lock
, state
);
2753 load_memory_barrier();
2756 LOCKSTAT_RECORD(LS_LCK_MTX_TRY_SPIN_LOCK_ACQUIRE
, lock
, 0);
2757 #endif /* CONFIG_DTRACE */
2762 * Routine: lck_mtx_try_lock_spin
2765 lck_mtx_try_lock_spin(lck_mtx_t
*lock
)
2767 return lck_mtx_try_lock_spin_internal(lock
, TRUE
);
2771 * Routine: lck_mtx_try_lock_spin_always
2774 lck_mtx_try_lock_spin_always(lck_mtx_t
*lock
)
2776 return lck_mtx_try_lock_spin_internal(lock
, FALSE
);
2782 * Routine: lck_mtx_unlock
2785 lck_mtx_unlock(lck_mtx_t
*lock
)
2787 thread_t thread
= current_thread();
2789 boolean_t ilk_held
= FALSE
;
2791 lck_mtx_verify(lock
);
2793 state
= ordered_load_mtx(lock
);
2794 if (state
& LCK_ILOCK
) {
2795 if (LCK_MTX_STATE_TO_THREAD(state
) == (thread_t
)LCK_MTX_SPIN_TAG
) {
2796 ilk_held
= TRUE
; // Interlock is held by (presumably) this thread
2800 // Locked as a mutex
2801 if (os_atomic_cmpxchg(&lock
->lck_mtx_data
,
2802 LCK_MTX_THREAD_TO_STATE(thread
), 0, release
)) {
2804 LOCKSTAT_RECORD(LS_LCK_MTX_UNLOCK_RELEASE
, lock
, 0);
2805 #endif /* CONFIG_DTRACE */
2809 lck_mtx_unlock_contended(lock
, thread
, ilk_held
);
2812 static void NOINLINE
2813 lck_mtx_unlock_contended(lck_mtx_t
*lock
, thread_t thread
, boolean_t ilk_held
)
2816 boolean_t cleanup
= FALSE
;
2819 state
= ordered_load_mtx(lock
);
2822 interlock_lock(lock
);
2823 state
= ordered_load_mtx(lock
);
2824 if (thread
!= LCK_MTX_STATE_TO_THREAD(state
)) {
2825 panic("lck_mtx_unlock(): Attempt to release lock not owned by thread (%p)", lock
);
2828 disable_preemption_for_thread(thread
);
2829 state
= ordered_load_mtx(lock
);
2830 if (state
& LCK_ILOCK
) {
2831 panic("lck_mtx_unlock(): Unexpected interlock set (%p)", lock
);
2833 if (thread
!= LCK_MTX_STATE_TO_THREAD(state
)) {
2834 panic("lck_mtx_unlock(): Attempt to release lock not owned by thread (%p)", lock
);
2837 ordered_store_mtx(lock
, state
);
2839 if (state
& ARM_LCK_WAITERS
) {
2840 if (lck_mtx_unlock_wakeup(lock
, thread
)) {
2841 state
= ARM_LCK_WAITERS
;
2849 state
&= ARM_LCK_WAITERS
; /* Clear state, retain waiters bit */
2853 ordered_store_mtx(lock
, state
);
2854 interlock_unlock(lock
);
2856 ordered_store_mtx(lock
, state
);
2857 enable_preemption();
2861 * Do not do any turnstile operations outside of this block.
2862 * lock/unlock is called at early stage of boot with single thread,
2863 * when turnstile is not yet initialized.
2864 * Even without contention we can come throught the slow path
2865 * if the mutex is acquired as a spin lock.
2867 turnstile_cleanup();
2871 LOCKSTAT_RECORD(LS_LCK_MTX_UNLOCK_RELEASE
, lock
, 0);
2872 #endif /* CONFIG_DTRACE */
2876 * Routine: lck_mtx_assert
2879 lck_mtx_assert(lck_mtx_t
*lock
, unsigned int type
)
2881 thread_t thread
, holder
;
2884 state
= ordered_load_mtx(lock
);
2885 holder
= LCK_MTX_STATE_TO_THREAD(state
);
2886 if (holder
== (thread_t
)LCK_MTX_SPIN_TAG
) {
2887 // Lock is held in spin mode, owner is unknown.
2890 thread
= current_thread();
2891 if (type
== LCK_MTX_ASSERT_OWNED
) {
2892 if (thread
!= holder
) {
2893 panic("lck_mtx_assert(): mutex (%p) owned", lock
);
2895 } else if (type
== LCK_MTX_ASSERT_NOTOWNED
) {
2896 if (thread
== holder
) {
2897 panic("lck_mtx_assert(): mutex (%p) not owned", lock
);
2900 panic("lck_mtx_assert(): invalid arg (%u)", type
);
2905 * Routine: lck_mtx_ilk_unlock
2908 lck_mtx_ilk_unlock(lck_mtx_t
*lock
)
2910 interlock_unlock(lock
);
2915 * Routine: lck_mtx_convert_spin
2917 * Convert a mutex held for spin into a held full mutex
2920 lck_mtx_convert_spin(lck_mtx_t
*lock
)
2922 thread_t thread
= current_thread();
2926 state
= ordered_load_mtx(lock
);
2927 if (LCK_MTX_STATE_TO_THREAD(state
) == thread
) {
2928 return; // Already owned as mutex, return
2930 if ((state
& LCK_ILOCK
) == 0 || (LCK_MTX_STATE_TO_THREAD(state
) != (thread_t
)LCK_MTX_SPIN_TAG
)) {
2931 panic("lck_mtx_convert_spin: Not held as spinlock (%p)", lock
);
2933 state
&= ~(LCK_MTX_THREAD_MASK
); // Clear the spin tag
2934 ordered_store_mtx(lock
, state
);
2935 waiters
= lck_mtx_lock_acquire(lock
, NULL
); // Acquire to manage priority boosts
2936 state
= LCK_MTX_THREAD_TO_STATE(thread
);
2938 state
|= ARM_LCK_WAITERS
;
2942 ordered_store_mtx(lock
, state
); // Set ownership
2943 interlock_unlock(lock
); // Release interlock, enable preemption
2945 ordered_store_mtx(lock
, state
); // Set ownership
2946 enable_preemption();
2948 turnstile_cleanup();
2953 * Routine: lck_mtx_destroy
2960 if (lck
->lck_mtx_type
!= LCK_MTX_TYPE
) {
2961 panic("Destroying invalid mutex %p", lck
);
2963 if (lck
->lck_mtx_tag
== LCK_MTX_TAG_DESTROYED
) {
2964 panic("Destroying previously destroyed lock %p", lck
);
2966 lck_mtx_assert(lck
, LCK_MTX_ASSERT_NOTOWNED
);
2967 lck
->lck_mtx_tag
= LCK_MTX_TAG_DESTROYED
;
2968 lck_grp_lckcnt_decr(grp
, LCK_TYPE_MTX
);
2969 lck_grp_deallocate(grp
);
2974 * Routine: lck_spin_assert
2977 lck_spin_assert(lck_spin_t
*lock
, unsigned int type
)
2979 thread_t thread
, holder
;
2982 if (lock
->type
!= LCK_SPIN_TYPE
) {
2983 panic("Invalid spinlock %p", lock
);
2986 state
= lock
->lck_spin_data
;
2987 holder
= (thread_t
)(state
& ~LCK_ILOCK
);
2988 thread
= current_thread();
2989 if (type
== LCK_ASSERT_OWNED
) {
2991 panic("Lock not owned %p = %lx", lock
, state
);
2993 if (holder
!= thread
) {
2994 panic("Lock not owned by current thread %p = %lx", lock
, state
);
2996 if ((state
& LCK_ILOCK
) == 0) {
2997 panic("Lock bit not set %p = %lx", lock
, state
);
2999 } else if (type
== LCK_ASSERT_NOTOWNED
) {
3001 if (holder
== thread
) {
3002 panic("Lock owned by current thread %p = %lx", lock
, state
);
3006 panic("lck_spin_assert(): invalid arg (%u)", type
);
3011 lck_rw_lock_yield_shared(lck_rw_t
*lck
, boolean_t force_yield
)
3015 lck_rw_assert(lck
, LCK_RW_ASSERT_SHARED
);
3017 word
.data
= ordered_load_rw(lck
);
3018 if (word
.want_excl
|| word
.want_upgrade
|| force_yield
) {
3019 lck_rw_unlock_shared(lck
);
3021 lck_rw_lock_shared(lck
);
3029 * Routine: kdp_lck_mtx_lock_spin_is_acquired
3030 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
3033 kdp_lck_mtx_lock_spin_is_acquired(lck_mtx_t
*lck
)
3038 panic("panic: spinlock acquired check done outside of kernel debugger");
3040 state
= ordered_load_mtx(lck
);
3041 if (state
== LCK_MTX_TAG_DESTROYED
) {
3044 if (LCK_MTX_STATE_TO_THREAD(state
) || (state
& LCK_ILOCK
)) {
3051 kdp_lck_mtx_find_owner(__unused
struct waitq
* waitq
, event64_t event
, thread_waitinfo_t
* waitinfo
)
3053 lck_mtx_t
* mutex
= LCK_EVENT_TO_MUTEX(event
);
3054 waitinfo
->context
= VM_KERNEL_UNSLIDE_OR_PERM(mutex
);
3055 uintptr_t state
= ordered_load_mtx(mutex
);
3056 thread_t holder
= LCK_MTX_STATE_TO_THREAD(state
);
3057 if ((uintptr_t)holder
== (uintptr_t)LCK_MTX_SPIN_TAG
) {
3058 waitinfo
->owner
= STACKSHOT_WAITOWNER_MTXSPIN
;
3060 assertf(state
!= (uintptr_t)LCK_MTX_TAG_DESTROYED
, "state=0x%llx", (uint64_t)state
);
3061 assertf(state
!= (uintptr_t)LCK_MTX_TAG_INDIRECT
, "state=0x%llx", (uint64_t)state
);
3062 waitinfo
->owner
= thread_tid(holder
);
3067 kdp_rwlck_find_owner(__unused
struct waitq
* waitq
, event64_t event
, thread_waitinfo_t
* waitinfo
)
3069 lck_rw_t
*rwlck
= NULL
;
3070 switch (waitinfo
->wait_type
) {
3071 case kThreadWaitKernelRWLockRead
:
3072 rwlck
= READ_EVENT_TO_RWLOCK(event
);
3074 case kThreadWaitKernelRWLockWrite
:
3075 case kThreadWaitKernelRWLockUpgrade
:
3076 rwlck
= WRITE_EVENT_TO_RWLOCK(event
);
3079 panic("%s was called with an invalid blocking type", __FUNCTION__
);
3082 waitinfo
->context
= VM_KERNEL_UNSLIDE_OR_PERM(rwlck
);
3083 waitinfo
->owner
= thread_tid(rwlck
->lck_rw_owner
);