2 * Copyright (c) 2000-2012 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
33 * Copyright (c) 1989 Carnegie-Mellon University
34 * All rights reserved. The CMU software License Agreement specifies
35 * the terms and conditions for use and redistribution.
39 #include <platforms.h>
40 #include <mach_ldebug.h>
42 #include <i386/eflags.h>
43 #include <i386/trap.h>
44 #include <config_dtrace.h>
49 #define PAUSE rep; nop
51 #include <i386/pal_lock_asm.h>
54 * When performance isn't the only concern, it's
55 * nice to build stack frames...
57 #define BUILD_STACK_FRAMES (GPROF)
59 #if BUILD_STACK_FRAMES
61 /* Stack-frame-relative: */
66 #define LEAF_ENTRY(name) \
71 #define LEAF_ENTRY2(n1,n2) \
81 #else /* BUILD_STACK_FRAMES */
83 /* Stack-pointer-relative: */
88 #define LEAF_ENTRY(name) \
91 #define LEAF_ENTRY2(n1,n2) \
98 #endif /* BUILD_STACK_FRAMES */
101 /* Non-leaf routines always have a stack frame: */
103 #define NONLEAF_ENTRY(name) \
108 #define NONLEAF_ENTRY2(n1,n2) \
114 #define NONLEAF_RET \
119 /* For x86_64, the varargs ABI requires that %al indicate
120 * how many SSE register contain arguments. In our case, 0 */
121 #define ALIGN_STACK() and $0xFFFFFFFFFFFFFFF0, %rsp ;
122 #define LOAD_STRING_ARG0(label) leaq label(%rip), %rdi ;
123 #define LOAD_ARG1(x) mov x, %esi ;
124 #define LOAD_PTR_ARG1(x) mov x, %rsi ;
125 #define CALL_PANIC() xorb %al,%al ; call EXT(panic) ;
127 #define CHECK_UNLOCK(current, owner) \
128 cmp current, owner ; \
131 LOAD_STRING_ARG0(2f) ; \
135 2: String "Mutex unlock attempted from non-owner thread"; \
141 * Routines for general lock debugging.
145 * Checks for expected lock types and calls "panic" on
146 * mismatch. Detects calls to Mutex functions with
147 * type simplelock and vice versa.
149 #define CHECK_MUTEX_TYPE() \
150 cmpl $ MUTEX_TAG,M_TYPE ; \
153 LOAD_STRING_ARG0(2f) ; \
157 2: String "not a mutex!" ; \
162 * If one or more simplelocks are currently held by a thread,
163 * an attempt to acquire a mutex will cause this check to fail
164 * (since a mutex lock may context switch, holding a simplelock
165 * is not a good thing).
168 #define CHECK_PREEMPTION_LEVEL() \
169 cmpl $0,%gs:CPU_HIBERNATE ; \
171 cmpl $0,%gs:CPU_PREEMPTION_LEVEL ; \
174 movl %gs:CPU_PREEMPTION_LEVEL, %eax ; \
176 LOAD_STRING_ARG0(2f) ; \
180 2: String "preemption_level(%d) != 0!" ; \
184 #define CHECK_PREEMPTION_LEVEL()
187 #define CHECK_MYLOCK(current, owner) \
188 cmp current, owner ; \
191 LOAD_STRING_ARG0(2f) ; \
195 2: String "Attempt to recursively lock a non-recursive lock"; \
199 #else /* MACH_LDEBUG */
200 #define CHECK_MUTEX_TYPE()
201 #define CHECK_PREEMPTION_LEVEL()
202 #define CHECK_MYLOCK(thd)
203 #endif /* MACH_LDEBUG */
205 #define PREEMPTION_DISABLE \
206 incl %gs:CPU_PREEMPTION_LEVEL
208 #define PREEMPTION_LEVEL_DEBUG 1
209 #if PREEMPTION_LEVEL_DEBUG
210 #define PREEMPTION_ENABLE \
211 decl %gs:CPU_PREEMPTION_LEVEL ; \
214 testl $AST_URGENT,%gs:CPU_PENDING_AST ; \
217 testl $EFL_IF, S_PC ; \
223 call _preemption_underflow_panic ; \
228 #define PREEMPTION_ENABLE \
229 decl %gs:CPU_PREEMPTION_LEVEL ; \
231 testl $AST_URGENT,%gs:CPU_PENDING_AST ; \
234 testl $EFL_IF, S_PC ; \
247 .globl _lockstat_probe
248 .globl _lockstat_probemap
251 * LOCKSTAT_LABEL creates a dtrace symbol which contains
252 * a pointer into the lock code function body. At that
253 * point is a "ret" instruction that can be patched into
257 #define LOCKSTAT_LABEL(lab) \
265 #define LOCKSTAT_RECORD(id, lck) \
268 movl _lockstat_probemap + (id * 4)(%rip),%eax ; \
277 call *_lockstat_probe(%rip) ; \
279 /* ret - left to subsequent code, e.g. return values */
281 #endif /* CONFIG_DTRACE */
284 * For most routines, the hw_lock_t pointer is loaded into a
285 * register initially, and then either a byte or register-sized
286 * word is loaded/stored to the pointer
289 #define HW_LOCK_REGISTER %rdi
290 #define LOAD_HW_LOCK_REGISTER
291 #define HW_LOCK_THREAD_REGISTER %rcx
292 #define LOAD_HW_LOCK_THREAD_REGISTER mov %gs:CPU_ACTIVE_THREAD, HW_LOCK_THREAD_REGISTER
293 #define HW_LOCK_MOV_WORD movq
294 #define HW_LOCK_EXAM_REGISTER %rax
297 * void hw_lock_init(hw_lock_t)
299 * Initialize a hardware lock.
301 LEAF_ENTRY(hw_lock_init)
302 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
303 HW_LOCK_MOV_WORD $0, (HW_LOCK_REGISTER) /* clear the lock */
308 * void hw_lock_byte_init(volatile uint8_t *)
310 * Initialize a hardware byte lock.
312 LEAF_ENTRY(hw_lock_byte_init)
313 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
314 movb $0, (HW_LOCK_REGISTER) /* clear the lock */
318 * void hw_lock_lock(hw_lock_t)
320 * Acquire lock, spinning until it becomes available.
321 * MACH_RT: also return with preemption disabled.
323 LEAF_ENTRY(hw_lock_lock)
324 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
325 LOAD_HW_LOCK_THREAD_REGISTER /* get thread pointer */
329 mov (HW_LOCK_REGISTER), HW_LOCK_EXAM_REGISTER
330 test HW_LOCK_EXAM_REGISTER,HW_LOCK_EXAM_REGISTER /* lock locked? */
331 jne 3f /* branch if so */
332 lock; cmpxchg HW_LOCK_THREAD_REGISTER,(HW_LOCK_REGISTER) /* try to acquire the HW lock */
334 movl $1,%eax /* In case this was a timeout call */
335 LEAF_RET /* if yes, then nothing left to do */
337 PAUSE /* pause for hyper-threading */
338 jmp 1b /* try again */
341 * void hw_lock_byte_lock(uint8_t *lock_byte)
343 * Acquire byte sized lock operand, spinning until it becomes available.
344 * MACH_RT: also return with preemption disabled.
347 LEAF_ENTRY(hw_lock_byte_lock)
348 LOAD_HW_LOCK_REGISTER /* Load lock pointer */
350 movl $1, %ecx /* Set lock value */
352 movb (HW_LOCK_REGISTER), %al /* Load byte at address */
353 testb %al,%al /* lock locked? */
354 jne 3f /* branch if so */
355 lock; cmpxchg %cl,(HW_LOCK_REGISTER) /* attempt atomic compare exchange */
357 LEAF_RET /* if yes, then nothing left to do */
359 PAUSE /* pause for hyper-threading */
360 jmp 1b /* try again */
363 * unsigned int hw_lock_to(hw_lock_t, unsigned int)
365 * Acquire lock, spinning until it becomes available or timeout.
366 * MACH_RT: also return with preemption disabled.
368 LEAF_ENTRY(hw_lock_to)
370 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
371 LOAD_HW_LOCK_THREAD_REGISTER
374 * Attempt to grab the lock immediately
375 * - fastpath without timeout nonsense.
379 mov (HW_LOCK_REGISTER), HW_LOCK_EXAM_REGISTER
380 test HW_LOCK_EXAM_REGISTER,HW_LOCK_EXAM_REGISTER /* lock locked? */
381 jne 2f /* branch if so */
382 lock; cmpxchg HW_LOCK_THREAD_REGISTER,(HW_LOCK_REGISTER) /* try to acquire the HW lock */
383 jne 2f /* branch on failure */
388 #define INNER_LOOP_COUNT 1000
390 * Failed to get the lock so set the timeout
391 * and then spin re-checking the lock but pausing
392 * every so many (INNER_LOOP_COUNT) spins to check for timeout.
396 rdtsc /* read cyclecount into %edx:%eax */
398 orq %rdx, %rax /* load 64-bit quantity into %rax */
399 addq %rax, %rsi /* %rsi is the timeout expiry */
403 * The inner-loop spin to look for the lock being freed.
405 mov $(INNER_LOOP_COUNT),%r9
407 PAUSE /* pause for hyper-threading */
408 mov (HW_LOCK_REGISTER),HW_LOCK_EXAM_REGISTER /* spin checking lock value in cache */
409 test HW_LOCK_EXAM_REGISTER,HW_LOCK_EXAM_REGISTER
410 je 6f /* zero => unlocked, try to grab it */
411 decq %r9 /* decrement inner loop count */
412 jnz 5b /* time to check for timeout? */
415 * Here after spinning INNER_LOOP_COUNT times, check for timeout
418 rdtsc /* cyclecount into %edx:%eax */
420 orq %rdx, %rax /* load 64-bit quantity into %rax */
421 cmpq %rsi, %rax /* compare to timeout */
422 jb 4b /* continue spinning if less, or */
423 xor %rax,%rax /* with 0 return value */
429 * Here to try to grab the lock that now appears to be free
432 LOAD_HW_LOCK_THREAD_REGISTER
433 lock; cmpxchg HW_LOCK_THREAD_REGISTER,(HW_LOCK_REGISTER) /* try to acquire the HW lock */
434 jne 4b /* no - spin again */
435 movl $1,%eax /* yes */
440 * void hw_lock_unlock(hw_lock_t)
442 * Unconditionally release lock.
443 * MACH_RT: release preemption level.
445 LEAF_ENTRY(hw_lock_unlock)
446 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
447 HW_LOCK_MOV_WORD $0, (HW_LOCK_REGISTER) /* clear the lock */
452 * void hw_lock_byte_unlock(uint8_t *lock_byte)
454 * Unconditionally release byte sized lock operand.
455 * MACH_RT: release preemption level.
458 LEAF_ENTRY(hw_lock_byte_unlock)
459 LOAD_HW_LOCK_REGISTER /* Load lock pointer */
460 movb $0, (HW_LOCK_REGISTER) /* Clear the lock byte */
465 * unsigned int hw_lock_try(hw_lock_t)
466 * MACH_RT: returns with preemption disabled on success.
468 LEAF_ENTRY(hw_lock_try)
469 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
470 LOAD_HW_LOCK_THREAD_REGISTER
473 mov (HW_LOCK_REGISTER),HW_LOCK_EXAM_REGISTER
474 test HW_LOCK_EXAM_REGISTER,HW_LOCK_EXAM_REGISTER
476 lock; cmpxchg HW_LOCK_THREAD_REGISTER,(HW_LOCK_REGISTER) /* try to acquire the HW lock */
479 movl $1,%eax /* success */
483 PREEMPTION_ENABLE /* failure: release preemption... */
484 xorl %eax,%eax /* ...and return failure */
488 * unsigned int hw_lock_held(hw_lock_t)
489 * MACH_RT: doesn't change preemption state.
490 * N.B. Racy, of course.
492 LEAF_ENTRY(hw_lock_held)
493 LOAD_HW_LOCK_REGISTER /* fetch lock pointer */
494 mov (HW_LOCK_REGISTER),HW_LOCK_EXAM_REGISTER /* check lock value */
495 test HW_LOCK_EXAM_REGISTER,HW_LOCK_EXAM_REGISTER
497 cmovne %ecx,%eax /* 0 => unlocked, 1 => locked */
502 * Reader-writer lock fastpaths. These currently exist for the
503 * shared lock acquire, the exclusive lock acquire, the shared to
504 * exclusive upgrade and the release paths (where they reduce overhead
505 * considerably) -- these are by far the most frequently used routines
507 * The following should reflect the layout of the bitfield embedded within
508 * the lck_rw_t structure (see i386/locks.h).
510 #define LCK_RW_INTERLOCK (0x1 << 16)
512 #define LCK_RW_PRIV_EXCL (0x1 << 24)
513 #define LCK_RW_WANT_UPGRADE (0x2 << 24)
514 #define LCK_RW_WANT_WRITE (0x4 << 24)
515 #define LCK_R_WAITING (0x8 << 24)
516 #define LCK_W_WAITING (0x10 << 24)
518 #define LCK_RW_SHARED_MASK (0xffff)
521 * For most routines, the lck_rw_t pointer is loaded into a
522 * register initially, and the flags bitfield loaded into another
523 * register and examined
526 #define LCK_RW_REGISTER %rdi
527 #define LOAD_LCK_RW_REGISTER
528 #define LCK_RW_FLAGS_REGISTER %eax
529 #define LOAD_LCK_RW_FLAGS_REGISTER mov (LCK_RW_REGISTER), LCK_RW_FLAGS_REGISTER
531 #define RW_LOCK_SHARED_MASK (LCK_RW_INTERLOCK | LCK_RW_WANT_UPGRADE | LCK_RW_WANT_WRITE)
533 * void lck_rw_lock_shared(lck_rw_t *)
536 Entry(lck_rw_lock_shared)
537 mov %gs:CPU_ACTIVE_THREAD, %rcx /* Load thread pointer */
538 incl TH_RWLOCK_COUNT(%rcx) /* Increment count before atomic CAS */
541 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield and interlock */
542 testl $(RW_LOCK_SHARED_MASK), %eax /* Eligible for fastpath? */
545 movl %eax, %ecx /* original value in %eax for cmpxchgl */
546 incl %ecx /* Increment reader refcount */
548 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
553 * Dtrace lockstat event: LS_LCK_RW_LOCK_SHARED_ACQUIRE
554 * Implemented by swapping between return and no-op instructions.
555 * See bsd/dev/dtrace/lockstat.c.
557 LOCKSTAT_LABEL(_lck_rw_lock_shared_lockstat_patch_point)
560 Fall thru when patched, counting on lock pointer in LCK_RW_REGISTER
562 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_ACQUIRE, LCK_RW_REGISTER)
569 jmp EXT(lck_rw_lock_shared_gen)
573 #define RW_TRY_LOCK_SHARED_MASK (LCK_RW_WANT_UPGRADE | LCK_RW_WANT_WRITE)
575 * void lck_rw_try_lock_shared(lck_rw_t *)
578 Entry(lck_rw_try_lock_shared)
581 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield and interlock */
582 testl $(LCK_RW_INTERLOCK), %eax
584 testl $(RW_TRY_LOCK_SHARED_MASK), %eax
585 jne 3f /* lock is busy */
587 movl %eax, %ecx /* original value in %eax for cmpxchgl */
588 incl %ecx /* Increment reader refcount */
590 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
593 mov %gs:CPU_ACTIVE_THREAD, %rcx /* Load thread pointer */
594 incl TH_RWLOCK_COUNT(%rcx) /* Increment count on success. */
595 /* There is a 3 instr window where preemption may not notice rwlock_count after cmpxchg */
600 * Dtrace lockstat event: LS_LCK_RW_TRY_LOCK_SHARED_ACQUIRE
601 * Implemented by swapping between return and no-op instructions.
602 * See bsd/dev/dtrace/lockstat.c.
604 LOCKSTAT_LABEL(_lck_rw_try_lock_shared_lockstat_patch_point)
606 /* Fall thru when patched, counting on lock pointer in LCK_RW_REGISTER */
607 LOCKSTAT_RECORD(LS_LCK_RW_TRY_LOCK_SHARED_ACQUIRE, LCK_RW_REGISTER)
609 movl $1, %eax /* return TRUE */
619 #define RW_LOCK_EXCLUSIVE_HELD (LCK_RW_WANT_WRITE | LCK_RW_WANT_UPGRADE)
621 * int lck_rw_grab_shared(lck_rw_t *)
624 Entry(lck_rw_grab_shared)
627 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield and interlock */
628 testl $(LCK_RW_INTERLOCK), %eax
630 testl $(RW_LOCK_EXCLUSIVE_HELD), %eax
633 movl %eax, %ecx /* original value in %eax for cmpxchgl */
634 incl %ecx /* Increment reader refcount */
636 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
639 movl $1, %eax /* return success */
642 testl $(LCK_RW_SHARED_MASK), %eax
644 testl $(LCK_RW_PRIV_EXCL), %eax
647 xorl %eax, %eax /* return failure */
655 #define RW_LOCK_EXCLUSIVE_MASK (LCK_RW_SHARED_MASK | LCK_RW_INTERLOCK | \
656 LCK_RW_WANT_UPGRADE | LCK_RW_WANT_WRITE)
658 * void lck_rw_lock_exclusive(lck_rw_t*)
661 Entry(lck_rw_lock_exclusive)
662 mov %gs:CPU_ACTIVE_THREAD, %rcx /* Load thread pointer */
663 incl TH_RWLOCK_COUNT(%rcx) /* Increment count before atomic CAS */
666 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and shared count */
667 testl $(RW_LOCK_EXCLUSIVE_MASK), %eax /* Eligible for fastpath? */
668 jne 3f /* no, go slow */
670 movl %eax, %ecx /* original value in %eax for cmpxchgl */
671 orl $(LCK_RW_WANT_WRITE), %ecx
673 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
678 * Dtrace lockstat event: LS_LCK_RW_LOCK_EXCL_ACQUIRE
679 * Implemented by swapping between return and no-op instructions.
680 * See bsd/dev/dtrace/lockstat.c.
682 LOCKSTAT_LABEL(_lck_rw_lock_exclusive_lockstat_patch_point)
684 /* Fall thru when patched, counting on lock pointer in LCK_RW_REGISTER */
685 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_ACQUIRE, LCK_RW_REGISTER)
692 jmp EXT(lck_rw_lock_exclusive_gen)
696 #define RW_TRY_LOCK_EXCLUSIVE_MASK (LCK_RW_SHARED_MASK | LCK_RW_WANT_UPGRADE | LCK_RW_WANT_WRITE)
698 * void lck_rw_try_lock_exclusive(lck_rw_t *)
700 * Tries to get a write lock.
702 * Returns FALSE if the lock is not held on return.
704 Entry(lck_rw_try_lock_exclusive)
707 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and shared count */
708 testl $(LCK_RW_INTERLOCK), %eax
710 testl $(RW_TRY_LOCK_EXCLUSIVE_MASK), %eax
711 jne 3f /* can't get it */
713 movl %eax, %ecx /* original value in %eax for cmpxchgl */
714 orl $(LCK_RW_WANT_WRITE), %ecx
716 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
719 mov %gs:CPU_ACTIVE_THREAD, %rcx /* Load thread pointer */
720 incl TH_RWLOCK_COUNT(%rcx) /* Increment count on success. */
721 /* There is a 3 instr window where preemption may not notice rwlock_count after cmpxchg */
726 * Dtrace lockstat event: LS_LCK_RW_TRY_LOCK_EXCL_ACQUIRE
727 * Implemented by swapping between return and no-op instructions.
728 * See bsd/dev/dtrace/lockstat.c.
730 LOCKSTAT_LABEL(_lck_rw_try_lock_exclusive_lockstat_patch_point)
732 /* Fall thru when patched, counting on lock pointer in LCK_RW_REGISTER */
733 LOCKSTAT_RECORD(LS_LCK_RW_TRY_LOCK_EXCL_ACQUIRE, LCK_RW_REGISTER)
735 movl $1, %eax /* return TRUE */
741 xorl %eax, %eax /* return FALSE */
747 * void lck_rw_lock_shared_to_exclusive(lck_rw_t*)
749 * fastpath can be taken if
750 * the current rw_shared_count == 1
751 * AND the interlock is clear
752 * AND RW_WANT_UPGRADE is not set
754 * note that RW_WANT_WRITE could be set, but will not
755 * be indicative of an exclusive hold since we have
756 * a read count on the lock that we have not yet released
757 * we can blow by that state since the lck_rw_lock_exclusive
758 * function will block until rw_shared_count == 0 and
759 * RW_WANT_UPGRADE is clear... it does this check behind
760 * the interlock which we are also checking for
762 * to make the transition we must be able to atomically
763 * set RW_WANT_UPGRADE and get rid of the read count we hold
765 Entry(lck_rw_lock_shared_to_exclusive)
768 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and shared count */
769 testl $(LCK_RW_INTERLOCK), %eax
771 testl $(LCK_RW_WANT_UPGRADE), %eax
774 movl %eax, %ecx /* original value in %eax for cmpxchgl */
775 orl $(LCK_RW_WANT_UPGRADE), %ecx /* ask for WANT_UPGRADE */
776 decl %ecx /* and shed our read count */
778 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
780 /* we now own the WANT_UPGRADE */
781 testl $(LCK_RW_SHARED_MASK), %ecx /* check to see if all of the readers are drained */
782 jne 8f /* if not, we need to go wait */
787 * Dtrace lockstat event: LS_LCK_RW_LOCK_SHARED_TO_EXCL_UPGRADE
788 * Implemented by swapping between return and no-op instructions.
789 * See bsd/dev/dtrace/lockstat.c.
791 LOCKSTAT_LABEL(_lck_rw_lock_shared_to_exclusive_lockstat_patch_point)
793 /* Fall thru when patched, counting on lock pointer in LCK_RW_REGISTER */
794 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_TO_EXCL_UPGRADE, LCK_RW_REGISTER)
796 movl $1, %eax /* return success */
799 2: /* someone else already holds WANT_UPGRADE */
800 movl %eax, %ecx /* original value in %eax for cmpxchgl */
801 decl %ecx /* shed our read count */
802 testl $(LCK_RW_SHARED_MASK), %ecx
803 jne 3f /* we were the last reader */
804 andl $(~LCK_W_WAITING), %ecx /* so clear the wait indicator */
807 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
810 mov %eax, %esi /* put old flags as second arg */
811 /* lock is alread in %rdi */
812 call EXT(lck_rw_lock_shared_to_exclusive_failure)
813 ret /* and pass the failure return along */
818 jmp EXT(lck_rw_lock_shared_to_exclusive_success)
823 rwl_release_error_str:
824 .asciz "Releasing non-exclusive RW lock without a reader refcount!"
828 * lck_rw_type_t lck_rw_done(lck_rw_t *)
834 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
835 testl $(LCK_RW_INTERLOCK), %eax
836 jne 7f /* wait for interlock to clear */
838 movl %eax, %ecx /* keep original value in %eax for cmpxchgl */
839 testl $(LCK_RW_SHARED_MASK), %ecx /* if reader count == 0, must be exclusive lock */
841 decl %ecx /* Decrement reader count */
842 testl $(LCK_RW_SHARED_MASK), %ecx /* if reader count has now gone to 0, check for waiters */
846 testl $(LCK_RW_WANT_UPGRADE), %ecx
848 andl $(~LCK_RW_WANT_UPGRADE), %ecx
851 testl $(LCK_RW_WANT_WRITE), %ecx
852 je 8f /* lock is not 'owned', go panic */
853 andl $(~LCK_RW_WANT_WRITE), %ecx
856 * test the original values to match what
857 * lck_rw_done_gen is going to do to determine
858 * which wakeups need to happen...
860 * if !(fake_lck->lck_rw_priv_excl && fake_lck->lck_w_waiting)
862 testl $(LCK_W_WAITING), %eax
864 andl $(~LCK_W_WAITING), %ecx
866 testl $(LCK_RW_PRIV_EXCL), %eax
869 andl $(~LCK_R_WAITING), %ecx
872 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
875 mov %eax,%esi /* old flags in %rsi */
876 /* lock is in %rdi already */
877 call EXT(lck_rw_done_gen)
884 LOAD_STRING_ARG0(rwl_release_error_str)
890 * lck_rw_type_t lck_rw_lock_exclusive_to_shared(lck_rw_t *)
893 Entry(lck_rw_lock_exclusive_to_shared)
896 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
897 testl $(LCK_RW_INTERLOCK), %eax
898 jne 6f /* wait for interlock to clear */
900 movl %eax, %ecx /* keep original value in %eax for cmpxchgl */
901 incl %ecx /* Increment reader count */
903 testl $(LCK_RW_WANT_UPGRADE), %ecx
905 andl $(~LCK_RW_WANT_UPGRADE), %ecx
908 andl $(~LCK_RW_WANT_WRITE), %ecx
911 * test the original values to match what
912 * lck_rw_lock_exclusive_to_shared_gen is going to do to determine
913 * which wakeups need to happen...
915 * if !(fake_lck->lck_rw_priv_excl && fake_lck->lck_w_waiting)
917 testl $(LCK_W_WAITING), %eax
919 testl $(LCK_RW_PRIV_EXCL), %eax
922 andl $(~LCK_R_WAITING), %ecx
925 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
929 call EXT(lck_rw_lock_exclusive_to_shared_gen)
938 * int lck_rw_grab_want(lck_rw_t *)
941 Entry(lck_rw_grab_want)
944 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
945 testl $(LCK_RW_INTERLOCK), %eax
946 jne 3f /* wait for interlock to clear */
947 testl $(LCK_RW_WANT_WRITE), %eax /* want_write has been grabbed by someone else */
948 jne 2f /* go return failure */
950 movl %eax, %ecx /* original value in %eax for cmpxchgl */
951 orl $(LCK_RW_WANT_WRITE), %ecx
953 cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
955 /* we now own want_write */
956 movl $1, %eax /* return success */
959 xorl %eax, %eax /* return failure */
966 #define RW_LOCK_SHARED_OR_UPGRADE_MASK (LCK_RW_SHARED_MASK | LCK_RW_INTERLOCK | LCK_RW_WANT_UPGRADE)
968 * int lck_rw_held_read_or_upgrade(lck_rw_t *)
971 Entry(lck_rw_held_read_or_upgrade)
973 LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
974 andl $(RW_LOCK_SHARED_OR_UPGRADE_MASK), %eax
980 * N.B.: On x86, statistics are currently recorded for all indirect mutexes.
981 * Also, only the acquire attempt count (GRP_MTX_STAT_UTIL) is maintained
982 * as a 64-bit quantity (this matches the existing PowerPC implementation,
983 * and the new x86 specific statistics are also maintained as 32-bit
987 * Enable this preprocessor define to record the first miss alone
988 * By default, we count every miss, hence multiple misses may be
989 * recorded for a single lock acquire attempt via lck_mtx_lock
991 #undef LOG_FIRST_MISS_ALONE
994 * This preprocessor define controls whether the R-M-W update of the
995 * per-group statistics elements are atomic (LOCK-prefixed)
996 * Enabled by default.
998 #define ATOMIC_STAT_UPDATES 1
1000 #if defined(ATOMIC_STAT_UPDATES)
1001 #define LOCK_IF_ATOMIC_STAT_UPDATES lock
1003 #define LOCK_IF_ATOMIC_STAT_UPDATES
1004 #endif /* ATOMIC_STAT_UPDATES */
1008 * For most routines, the lck_mtx_t pointer is loaded into a
1009 * register initially, and the owner field checked for indirection.
1010 * Eventually the lock owner is loaded into a register and examined.
1013 #define M_OWNER MUTEX_OWNER
1014 #define M_PTR MUTEX_PTR
1015 #define M_STATE MUTEX_STATE
1017 #define LMTX_ARG0 %rdi
1018 #define LMTX_ARG1 %rsi
1019 #define LMTX_REG_ORIG %rdi
1020 #define LMTX_REG %rdx
1021 #define LMTX_A_REG %rax
1022 #define LMTX_A_REG32 %eax
1023 #define LMTX_C_REG %rcx
1024 #define LMTX_C_REG32 %ecx
1025 #define LMTX_RET_REG %rax
1026 #define LMTX_RET_REG32 %eax
1027 #define LMTX_LGROUP_REG %r10
1028 #define LMTX_SSTATE_REG %r11
1029 #define LOAD_LMTX_REG(arg) mov %rdi, %rdx
1030 #define LMTX_CHK_EXTENDED cmp LMTX_REG, LMTX_REG_ORIG
1031 #define LMTX_ASSERT_OWNED cmp $(MUTEX_ASSERT_OWNED), LMTX_ARG1
1033 #define LMTX_ENTER_EXTENDED \
1034 mov M_PTR(LMTX_REG), LMTX_REG ; \
1035 xor LMTX_SSTATE_REG, LMTX_SSTATE_REG ; \
1036 mov MUTEX_GRP(LMTX_REG), LMTX_LGROUP_REG ; \
1037 LOCK_IF_ATOMIC_STAT_UPDATES ; \
1038 incq GRP_MTX_STAT_UTIL(LMTX_LGROUP_REG)
1040 #define LMTX_EXIT_EXTENDED
1042 #define LMTX_CHK_EXTENDED_EXIT
1045 #if LOG_FIRST_MISS_ALONE
1046 #define LMTX_UPDATE_MISS \
1047 test $1, LMTX_SSTATE_REG ; \
1049 LOCK_IF_ATOMIC_STAT_UPDATES ; \
1050 incl GRP_MTX_STAT_MISS(LMTX_LGROUP_REG) ; \
1051 or $1, LMTX_SSTATE_REG ; \
1054 #define LMTX_UPDATE_MISS \
1055 LOCK_IF_ATOMIC_STAT_UPDATES ; \
1056 incl GRP_MTX_STAT_MISS(LMTX_LGROUP_REG)
1060 #if LOG_FIRST_MISS_ALONE
1061 #define LMTX_UPDATE_WAIT \
1062 test $2, LMTX_SSTATE_REG ; \
1064 LOCK_IF_ATOMIC_STAT_UPDATES ; \
1065 incl GRP_MTX_STAT_WAIT(LMTX_LGROUP_REG) ; \
1066 or $2, LMTX_SSTATE_REG ; \
1069 #define LMTX_UPDATE_WAIT \
1070 LOCK_IF_ATOMIC_STAT_UPDATES ; \
1071 incl GRP_MTX_STAT_WAIT(LMTX_LGROUP_REG)
1076 * Record the "direct wait" statistic, which indicates if a
1077 * miss proceeded to block directly without spinning--occurs
1078 * if the owner of the mutex isn't running on another processor
1079 * at the time of the check.
1081 #define LMTX_UPDATE_DIRECT_WAIT \
1082 LOCK_IF_ATOMIC_STAT_UPDATES ; \
1083 incl GRP_MTX_STAT_DIRECT_WAIT(LMTX_LGROUP_REG)
1086 #define LMTX_CALLEXT1(func_name) \
1087 LMTX_CHK_EXTENDED ; \
1089 push LMTX_LGROUP_REG ; \
1090 push LMTX_SSTATE_REG ; \
1091 12: push LMTX_REG_ORIG ; \
1093 mov LMTX_REG, LMTX_ARG0 ; \
1094 call EXT(func_name) ; \
1096 pop LMTX_REG_ORIG ; \
1097 LMTX_CHK_EXTENDED ; \
1099 pop LMTX_SSTATE_REG ; \
1100 pop LMTX_LGROUP_REG ; \
1103 #define LMTX_CALLEXT2(func_name, reg) \
1104 LMTX_CHK_EXTENDED ; \
1106 push LMTX_LGROUP_REG ; \
1107 push LMTX_SSTATE_REG ; \
1108 12: push LMTX_REG_ORIG ; \
1110 mov reg, LMTX_ARG1 ; \
1111 mov LMTX_REG, LMTX_ARG0 ; \
1112 call EXT(func_name) ; \
1114 pop LMTX_REG_ORIG ; \
1115 LMTX_CHK_EXTENDED ; \
1117 pop LMTX_SSTATE_REG ; \
1118 pop LMTX_LGROUP_REG ; \
1122 #define M_WAITERS_MSK 0x0000ffff
1123 #define M_PRIORITY_MSK 0x00ff0000
1124 #define M_ILOCKED_MSK 0x01000000
1125 #define M_MLOCKED_MSK 0x02000000
1126 #define M_PROMOTED_MSK 0x04000000
1127 #define M_SPIN_MSK 0x08000000
1130 * void lck_mtx_assert(lck_mtx_t* l, unsigned int)
1131 * Takes the address of a lock, and an assertion type as parameters.
1132 * The assertion can take one of two forms determine by the type
1133 * parameter: either the lock is held by the current thread, and the
1134 * type is LCK_MTX_ASSERT_OWNED, or it isn't and the type is
1135 * LCK_MTX_ASSERT_NOTOWNED. Calls panic on assertion failure.
1139 NONLEAF_ENTRY(lck_mtx_assert)
1140 LOAD_LMTX_REG(B_ARG0) /* Load lock address */
1141 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG /* Load current thread */
1143 mov M_STATE(LMTX_REG), LMTX_C_REG32
1144 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1146 mov M_PTR(LMTX_REG), LMTX_REG /* If so, take indirection */
1148 mov M_OWNER(LMTX_REG), LMTX_C_REG /* Load owner */
1150 jne 2f /* Assert ownership? */
1151 cmp LMTX_A_REG, LMTX_C_REG /* Current thread match? */
1152 jne 3f /* no, go panic */
1153 testl $(M_ILOCKED_MSK | M_MLOCKED_MSK), M_STATE(LMTX_REG)
1155 1: /* yes, we own it */
1158 cmp LMTX_A_REG, LMTX_C_REG /* Current thread match? */
1159 jne 1b /* No, return */
1161 LOAD_PTR_ARG1(LMTX_REG)
1162 LOAD_STRING_ARG0(mutex_assert_owned_str)
1166 LOAD_PTR_ARG1(LMTX_REG)
1167 LOAD_STRING_ARG0(mutex_assert_not_owned_str)
1174 LOAD_PTR_ARG1(LMTX_REG)
1175 LOAD_STRING_ARG0(mutex_interlock_destroyed_str)
1180 mutex_assert_not_owned_str:
1181 .asciz "mutex (%p) not owned\n"
1182 mutex_assert_owned_str:
1183 .asciz "mutex (%p) owned\n"
1184 mutex_interlock_destroyed_str:
1185 .asciz "trying to interlock destroyed mutex (%p)"
1192 * lck_mtx_try_lock()
1194 * lck_mtx_lock_spin()
1195 * lck_mtx_lock_spin_always()
1196 * lck_mtx_try_lock_spin()
1197 * lck_mtx_try_lock_spin_always()
1198 * lck_mtx_convert_spin()
1200 NONLEAF_ENTRY(lck_mtx_lock_spin_always)
1201 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1202 jmp Llmls_avoid_check
1204 NONLEAF_ENTRY(lck_mtx_lock_spin)
1205 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1207 CHECK_PREEMPTION_LEVEL()
1209 mov M_STATE(LMTX_REG), LMTX_C_REG32
1210 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
1212 Llmls_try: /* no - can't be INDIRECT, DESTROYED or locked */
1213 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1214 or $(M_ILOCKED_MSK | M_SPIN_MSK), LMTX_C_REG32
1218 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1219 jne Llmls_busy_disabled
1221 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1222 mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of interlock */
1224 test LMTX_A_REG, LMTX_A_REG
1226 incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1228 #endif /* MACH_LDEBUG */
1230 LMTX_CHK_EXTENDED_EXIT
1231 /* return with the interlock held and preemption disabled */
1234 LOCKSTAT_LABEL(_lck_mtx_lock_spin_lockstat_patch_point)
1236 /* inherit lock pointer in LMTX_REG above */
1237 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_SPIN_ACQUIRE, LMTX_REG)
1242 test $M_ILOCKED_MSK, LMTX_C_REG32 /* is the interlock held */
1243 jz Llml_contended /* no, must have been the mutex */
1245 cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
1246 je lck_mtx_destroyed
1247 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex */
1248 jne Llmls_loop /* no... must be interlocked */
1252 mov M_STATE(LMTX_REG), LMTX_C_REG32
1253 test $(M_SPIN_MSK), LMTX_C_REG32
1256 LMTX_UPDATE_MISS /* M_SPIN_MSK was set, so M_ILOCKED_MSK must also be present */
1259 mov M_STATE(LMTX_REG), LMTX_C_REG32
1261 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
1263 test $(M_MLOCKED_MSK), LMTX_C_REG32
1264 jnz Llml_contended /* mutex owned by someone else, go contend for it */
1267 Llmls_busy_disabled:
1273 NONLEAF_ENTRY(lck_mtx_lock)
1274 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1276 CHECK_PREEMPTION_LEVEL()
1278 mov M_STATE(LMTX_REG), LMTX_C_REG32
1279 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
1281 Llml_try: /* no - can't be INDIRECT, DESTROYED or locked */
1282 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1283 or $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
1287 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1288 jne Llml_busy_disabled
1290 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1291 mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
1293 test LMTX_A_REG, LMTX_A_REG
1295 incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1297 #endif /* MACH_LDEBUG */
1299 testl $(M_WAITERS_MSK), M_STATE(LMTX_REG)
1302 LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
1305 andl $(~M_ILOCKED_MSK), M_STATE(LMTX_REG)
1308 LMTX_CHK_EXTENDED /* is this an extended mutex */
1313 LOCKSTAT_LABEL(_lck_mtx_lock_lockstat_patch_point)
1315 /* inherit lock pointer in LMTX_REG above */
1316 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_ACQUIRE, LMTX_REG)
1323 LOCKSTAT_LABEL(_lck_mtx_lock_ext_lockstat_patch_point)
1325 /* inherit lock pointer in LMTX_REG above */
1326 LOCKSTAT_RECORD(LS_LCK_MTX_EXT_LOCK_ACQUIRE, LMTX_REG)
1332 test $M_ILOCKED_MSK, LMTX_C_REG32 /* is the interlock held */
1333 jz Llml_contended /* no, must have been the mutex */
1335 cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
1336 je lck_mtx_destroyed
1337 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1338 jne Llml_loop /* no... must be interlocked */
1342 mov M_STATE(LMTX_REG), LMTX_C_REG32
1343 test $(M_SPIN_MSK), LMTX_C_REG32
1346 LMTX_UPDATE_MISS /* M_SPIN_MSK was set, so M_ILOCKED_MSK must also be present */
1349 mov M_STATE(LMTX_REG), LMTX_C_REG32
1351 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
1353 test $(M_MLOCKED_MSK), LMTX_C_REG32
1354 jnz Llml_contended /* mutex owned by someone else, go contend for it */
1363 LMTX_CHK_EXTENDED /* is this an extended mutex */
1367 LMTX_CALLEXT1(lck_mtx_lock_spinwait_x86)
1369 test LMTX_RET_REG, LMTX_RET_REG
1370 jz Llml_acquired /* acquired mutex, interlock held and preemption disabled */
1372 cmp $1, LMTX_RET_REG /* check for direct wait status */
1374 LMTX_CHK_EXTENDED /* is this an extended mutex */
1376 LMTX_UPDATE_DIRECT_WAIT
1378 mov M_STATE(LMTX_REG), LMTX_C_REG32
1379 test $(M_ILOCKED_MSK), LMTX_C_REG32
1382 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1383 or $(M_ILOCKED_MSK), LMTX_C_REG32 /* try to take the interlock */
1387 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1390 test $(M_MLOCKED_MSK), LMTX_C_REG32 /* we've got the interlock and */
1392 or $(M_MLOCKED_MSK), LMTX_C_REG32 /* the mutex is free... grab it directly */
1393 mov LMTX_C_REG32, M_STATE(LMTX_REG)
1395 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1396 mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
1398 test LMTX_A_REG, LMTX_A_REG
1400 incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1402 #endif /* MACH_LDEBUG */
1405 testl $(M_WAITERS_MSK), M_STATE(LMTX_REG)
1407 mov M_OWNER(LMTX_REG), LMTX_A_REG
1408 mov TH_WAS_PROMOTED_ON_WAKEUP(LMTX_A_REG), LMTX_A_REG32
1409 test LMTX_A_REG32, LMTX_A_REG32
1412 LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
1415 3: /* interlock held, mutex busy */
1416 LMTX_CHK_EXTENDED /* is this an extended mutex */
1420 LMTX_CALLEXT1(lck_mtx_lock_wait_x86)
1429 NONLEAF_ENTRY(lck_mtx_try_lock_spin_always)
1430 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1431 jmp Llmts_avoid_check
1433 NONLEAF_ENTRY(lck_mtx_try_lock_spin)
1434 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1437 mov M_STATE(LMTX_REG), LMTX_C_REG32
1438 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
1440 Llmts_try: /* no - can't be INDIRECT, DESTROYED or locked */
1441 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1442 or $(M_ILOCKED_MSK | M_SPIN_MSK), LMTX_C_REG
1446 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1447 jne Llmts_busy_disabled
1449 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1450 mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
1452 test LMTX_A_REG, LMTX_A_REG
1454 incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1456 #endif /* MACH_LDEBUG */
1458 LMTX_CHK_EXTENDED_EXIT
1462 mov $1, LMTX_RET_REG /* return success */
1463 LOCKSTAT_LABEL(_lck_mtx_try_lock_spin_lockstat_patch_point)
1465 /* inherit lock pointer in LMTX_REG above */
1466 LOCKSTAT_RECORD(LS_LCK_MTX_TRY_SPIN_LOCK_ACQUIRE, LMTX_REG)
1468 mov $1, LMTX_RET_REG /* return success */
1472 test $(M_ILOCKED_MSK), LMTX_C_REG32 /* is the interlock held */
1473 jz Llmts_fail /* no, must be held as a mutex */
1475 cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
1476 je lck_mtx_destroyed
1477 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1483 mov M_STATE(LMTX_REG), LMTX_C_REG32
1485 test $(M_MLOCKED_MSK | M_SPIN_MSK), LMTX_C_REG32
1487 test $(M_ILOCKED_MSK), LMTX_C_REG32
1491 Llmts_busy_disabled:
1497 NONLEAF_ENTRY(lck_mtx_try_lock)
1498 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1500 mov M_STATE(LMTX_REG), LMTX_C_REG32
1501 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
1503 Llmt_try: /* no - can't be INDIRECT, DESTROYED or locked */
1504 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1505 or $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
1509 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1510 jne Llmt_busy_disabled
1512 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1513 mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
1515 test LMTX_A_REG, LMTX_A_REG
1517 incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1519 #endif /* MACH_LDEBUG */
1521 LMTX_CHK_EXTENDED_EXIT
1523 test $(M_WAITERS_MSK), LMTX_C_REG32
1526 LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
1528 andl $(~M_ILOCKED_MSK), M_STATE(LMTX_REG)
1533 mov $1, LMTX_RET_REG /* return success */
1534 /* Dtrace probe: LS_LCK_MTX_TRY_LOCK_ACQUIRE */
1535 LOCKSTAT_LABEL(_lck_mtx_try_lock_lockstat_patch_point)
1537 /* inherit lock pointer in LMTX_REG from above */
1538 LOCKSTAT_RECORD(LS_LCK_MTX_TRY_LOCK_ACQUIRE, LMTX_REG)
1540 mov $1, LMTX_RET_REG /* return success */
1544 test $(M_ILOCKED_MSK), LMTX_C_REG32 /* is the interlock held */
1545 jz Llmt_fail /* no, must be held as a mutex */
1547 cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
1548 je lck_mtx_destroyed
1549 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1555 mov M_STATE(LMTX_REG), LMTX_C_REG32
1557 test $(M_MLOCKED_MSK | M_SPIN_MSK), LMTX_C_REG32
1559 test $(M_ILOCKED_MSK), LMTX_C_REG32
1570 LMTX_CHK_EXTENDED /* is this an extended mutex */
1575 xor LMTX_RET_REG, LMTX_RET_REG
1580 NONLEAF_ENTRY(lck_mtx_convert_spin)
1581 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1583 mov M_STATE(LMTX_REG), LMTX_C_REG32
1584 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1586 mov M_PTR(LMTX_REG), LMTX_REG /* If so, take indirection */
1587 mov M_STATE(LMTX_REG), LMTX_C_REG32
1589 test $(M_MLOCKED_MSK), LMTX_C_REG32 /* already owned as a mutex, just return */
1591 test $(M_WAITERS_MSK), LMTX_C_REG32 /* are there any waiters? */
1594 LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
1595 mov M_STATE(LMTX_REG), LMTX_C_REG32
1597 and $(~(M_ILOCKED_MSK | M_SPIN_MSK)), LMTX_C_REG32 /* convert from spin version to mutex */
1598 or $(M_MLOCKED_MSK), LMTX_C_REG32
1599 mov LMTX_C_REG32, M_STATE(LMTX_REG) /* since I own the interlock, I don't need an atomic update */
1607 NONLEAF_ENTRY(lck_mtx_unlock)
1608 LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
1610 mov M_STATE(LMTX_REG), LMTX_C_REG32
1612 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1616 test $(M_MLOCKED_MSK), LMTX_C_REG32 /* check for full mutex */
1619 test $(M_ILOCKED_MSK), LMTX_C_REG /* have to wait for interlock to clear */
1622 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1623 and $(~M_MLOCKED_MSK), LMTX_C_REG32 /* drop mutex */
1624 or $(M_ILOCKED_MSK), LMTX_C_REG32 /* pick up interlock */
1628 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1629 jne Llmu_busy_disabled /* branch on failure to spin loop */
1632 xor LMTX_A_REG, LMTX_A_REG
1633 mov LMTX_A_REG, M_OWNER(LMTX_REG)
1634 mov LMTX_C_REG, LMTX_A_REG /* keep original state in %ecx for later evaluation */
1635 and $(~(M_ILOCKED_MSK | M_SPIN_MSK | M_PROMOTED_MSK)), LMTX_A_REG
1637 test $(M_WAITERS_MSK), LMTX_A_REG32
1639 dec LMTX_A_REG32 /* decrement waiter count */
1641 mov LMTX_A_REG32, M_STATE(LMTX_REG) /* since I own the interlock, I don't need an atomic update */
1644 /* perform lock statistics after drop to prevent delay */
1645 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1646 test LMTX_A_REG, LMTX_A_REG
1648 decl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1650 #endif /* MACH_LDEBUG */
1652 test $(M_PROMOTED_MSK | M_WAITERS_MSK), LMTX_C_REG32
1655 LMTX_CALLEXT2(lck_mtx_unlock_wakeup_x86, LMTX_C_REG)
1664 /* Dtrace: LS_LCK_MTX_UNLOCK_RELEASE */
1665 LOCKSTAT_LABEL(_lck_mtx_unlock_lockstat_patch_point)
1667 /* inherit lock pointer in LMTX_REG from above */
1668 LOCKSTAT_RECORD(LS_LCK_MTX_UNLOCK_RELEASE, LMTX_REG)
1674 /* Dtrace: LS_LCK_MTX_EXT_UNLOCK_RELEASE */
1675 LOCKSTAT_LABEL(_lck_mtx_ext_unlock_lockstat_patch_point)
1677 /* inherit lock pointer in LMTX_REG from above */
1678 LOCKSTAT_RECORD(LS_LCK_MTX_EXT_UNLOCK_RELEASE, LMTX_REG)
1687 mov M_STATE(LMTX_REG), LMTX_C_REG32
1691 mov M_PTR(LMTX_REG), LMTX_REG
1692 mov M_OWNER(LMTX_REG), LMTX_A_REG
1693 mov %gs:CPU_ACTIVE_THREAD, LMTX_C_REG
1694 CHECK_UNLOCK(LMTX_C_REG, LMTX_A_REG)
1695 mov M_STATE(LMTX_REG), LMTX_C_REG32
1700 LEAF_ENTRY(lck_mtx_ilk_unlock)
1701 LOAD_LMTX_REG(L_ARG0) /* fetch lock pointer - no indirection here */
1703 andl $(~M_ILOCKED_MSK), M_STATE(LMTX_REG)
1705 PREEMPTION_ENABLE /* need to re-enable preemption */
1711 LEAF_ENTRY(lck_mtx_lock_grab_mutex)
1712 LOAD_LMTX_REG(L_ARG0) /* fetch lock pointer - no indirection here */
1714 mov M_STATE(LMTX_REG), LMTX_C_REG32
1716 test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* can't have the mutex yet */
1719 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1720 or $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
1724 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1725 jne 2f /* branch on failure to spin loop */
1727 mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
1728 mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
1730 test LMTX_A_REG, LMTX_A_REG
1732 incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
1734 #endif /* MACH_LDEBUG */
1736 mov $1, LMTX_RET_REG /* return success */
1741 xor LMTX_RET_REG, LMTX_RET_REG /* return failure */
1746 LEAF_ENTRY(lck_mtx_lock_mark_destroyed)
1747 LOAD_LMTX_REG(L_ARG0)
1749 mov M_STATE(LMTX_REG), LMTX_C_REG32
1750 cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
1753 movl $(MUTEX_DESTROYED), M_STATE(LMTX_REG) /* convert to destroyed state */
1756 test $(M_ILOCKED_MSK), LMTX_C_REG /* have to wait for interlock to clear */
1760 mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
1761 or $(M_ILOCKED_MSK), LMTX_C_REG32
1763 cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
1764 jne 4f /* branch on failure to spin loop */
1765 movl $(MUTEX_DESTROYED), M_STATE(LMTX_REG) /* convert to destroyed state */
1768 LEAF_RET /* return with M_ILOCKED set */
1775 LEAF_ENTRY(preemption_underflow_panic)
1777 incl %gs:CPU_PREEMPTION_LEVEL
1779 LOAD_STRING_ARG0(16f)
1783 16: String "Preemption level underflow, possible cause unlocking an unlocked mutex or spinlock"
1787 LEAF_ENTRY(_disable_preemption)
1790 #endif /* MACH_RT */
1793 LEAF_ENTRY(_enable_preemption)
1796 cmpl $0,%gs:CPU_PREEMPTION_LEVEL
1798 movl %gs:CPU_PREEMPTION_LEVEL,%esi
1800 LOAD_STRING_ARG0(_enable_preemption_less_than_zero)
1804 _enable_preemption_less_than_zero:
1805 .asciz "_enable_preemption: preemption_level(%d) < 0!"
1808 #endif /* MACH_ASSERT */
1810 #endif /* MACH_RT */
1813 LEAF_ENTRY(_enable_preemption_no_check)
1816 cmpl $0,%gs:CPU_PREEMPTION_LEVEL
1819 LOAD_STRING_ARG0(_enable_preemption_no_check_less_than_zero)
1823 _enable_preemption_no_check_less_than_zero:
1824 .asciz "_enable_preemption_no_check: preemption_level <= 0!"
1827 #endif /* MACH_ASSERT */
1828 _ENABLE_PREEMPTION_NO_CHECK
1829 #endif /* MACH_RT */
1833 LEAF_ENTRY(_mp_disable_preemption)
1836 #endif /* MACH_RT */
1839 LEAF_ENTRY(_mp_enable_preemption)
1842 cmpl $0,%gs:CPU_PREEMPTION_LEVEL
1844 movl %gs:CPU_PREEMPTION_LEVEL,%esi
1846 LOAD_STRING_ARG0(_mp_enable_preemption_less_than_zero)
1850 _mp_enable_preemption_less_than_zero:
1851 .asciz "_mp_enable_preemption: preemption_level (%d) <= 0!"
1854 #endif /* MACH_ASSERT */
1856 #endif /* MACH_RT */
1859 LEAF_ENTRY(_mp_enable_preemption_no_check)
1862 cmpl $0,%gs:CPU_PREEMPTION_LEVEL
1865 LOAD_STRING_ARG0(_mp_enable_preemption_no_check_less_than_zero)
1869 _mp_enable_preemption_no_check_less_than_zero:
1870 .asciz "_mp_enable_preemption_no_check: preemption_level <= 0!"
1873 #endif /* MACH_ASSERT */
1874 _ENABLE_PREEMPTION_NO_CHECK
1875 #endif /* MACH_RT */
1879 LEAF_ENTRY(i_bit_set)
1884 LEAF_ENTRY(i_bit_clear)
1890 LEAF_ENTRY(bit_lock)
1898 LEAF_ENTRY(bit_lock_try)
1908 LEAF_ENTRY(bit_unlock)
1915 * Atomic primitives, prototyped in kern/simple_lock.h
1917 LEAF_ENTRY(hw_atomic_add)
1924 movl %esi, %eax /* Load addend */
1925 lock xaddl %eax, (%rdi) /* Atomic exchange and add */
1926 addl %esi, %eax /* Calculate result */
1929 LEAF_ENTRY(hw_atomic_sub)
1938 lock xaddl %eax, (%rdi) /* Atomic exchange and add */
1939 addl %esi, %eax /* Calculate result */
1942 LEAF_ENTRY(hw_atomic_or)
1951 movl %esi, %edx /* Load mask */
1953 lock cmpxchgl %edx, (%rdi) /* Atomic CAS */
1955 movl %edx, %eax /* Result */
1958 * A variant of hw_atomic_or which doesn't return a value.
1959 * The implementation is thus comparatively more efficient.
1962 LEAF_ENTRY(hw_atomic_or_noret)
1970 orl %esi, (%rdi) /* Atomic OR */
1974 LEAF_ENTRY(hw_atomic_and)
1983 movl %esi, %edx /* Load mask */
1985 lock cmpxchgl %edx, (%rdi) /* Atomic CAS */
1987 movl %edx, %eax /* Result */
1990 * A variant of hw_atomic_and which doesn't return a value.
1991 * The implementation is thus comparatively more efficient.
1994 LEAF_ENTRY(hw_atomic_and_noret)
2001 lock andl %esi, (%rdi) /* Atomic OR */