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) 1991,1990,1989,1988,1987 Carnegie Mellon University
34 * All Rights Reserved.
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
58 * Author: Avadis Tevanian, Jr., Michael Wayne Young
61 * Locking primitives implementation
64 #include <mach_ldebug.h>
66 #include <kern/locks.h>
67 #include <kern/kalloc.h>
68 #include <kern/misc_protos.h>
69 #include <kern/thread.h>
70 #include <kern/processor.h>
71 #include <kern/cpu_data.h>
72 #include <kern/cpu_number.h>
73 #include <kern/sched_prim.h>
75 #include <kern/debug.h>
78 #include <i386/machine_routines.h> /* machine_timeout_suspended() */
79 #include <machine/machine_cpu.h>
82 #include <sys/kdebug.h>
83 #include <mach/branch_predicates.h>
86 * We need only enough declarations from the BSD-side to be able to
87 * test if our probe is active, and to call __dtrace_probe(). Setting
88 * NEED_DTRACE_DEFS gets a local copy of those definitions pulled in.
91 #define NEED_DTRACE_DEFS
92 #include <../bsd/sys/lockstat.h>
95 #define LCK_RW_LCK_EXCLUSIVE_CODE 0x100
96 #define LCK_RW_LCK_EXCLUSIVE1_CODE 0x101
97 #define LCK_RW_LCK_SHARED_CODE 0x102
98 #define LCK_RW_LCK_SH_TO_EX_CODE 0x103
99 #define LCK_RW_LCK_SH_TO_EX1_CODE 0x104
100 #define LCK_RW_LCK_EX_TO_SH_CODE 0x105
102 #define LCK_RW_LCK_EX_WRITER_SPIN_CODE 0x106
103 #define LCK_RW_LCK_EX_WRITER_WAIT_CODE 0x107
104 #define LCK_RW_LCK_EX_READER_SPIN_CODE 0x108
105 #define LCK_RW_LCK_EX_READER_WAIT_CODE 0x109
106 #define LCK_RW_LCK_SHARED_SPIN_CODE 0x110
107 #define LCK_RW_LCK_SHARED_WAIT_CODE 0x111
108 #define LCK_RW_LCK_SH_TO_EX_SPIN_CODE 0x112
109 #define LCK_RW_LCK_SH_TO_EX_WAIT_CODE 0x113
112 #define ANY_LOCK_DEBUG (USLOCK_DEBUG || LOCK_DEBUG || MUTEX_DEBUG)
114 unsigned int LcksOpts
=0;
120 * Perform simple lock checks.
122 int uslock_check
= 1;
123 int max_lock_loops
= 100000000;
124 decl_simple_lock_data(extern , printf_lock
)
125 decl_simple_lock_data(extern , panic_lock
)
126 #endif /* USLOCK_DEBUG */
128 extern unsigned int not_in_kdp
;
129 extern void kdp_lck_mtx_find_owner(
130 struct waitq
* waitq
,
132 thread_waitinfo_t
* waitinfo
);
134 extern void kdp_rwlck_find_owner(
135 struct waitq
* waitq
,
137 thread_waitinfo_t
* waitinfo
);
140 * We often want to know the addresses of the callers
141 * of the various lock routines. However, this information
142 * is only used for debugging and statistics.
145 #define INVALID_PC ((void *) VM_MAX_KERNEL_ADDRESS)
146 #define INVALID_THREAD ((void *) VM_MAX_KERNEL_ADDRESS)
148 #define OBTAIN_PC(pc) ((pc) = GET_RETURN_PC())
149 #define DECL_PC(pc) pc_t pc;
150 #else /* ANY_LOCK_DEBUG */
154 * Eliminate lint complaints about unused local pc variables.
156 #define OBTAIN_PC(pc) ++pc
158 #define OBTAIN_PC(pc)
160 #endif /* USLOCK_DEBUG */
164 * Portable lock package implementation of usimple_locks.
168 #define USLDBG(stmt) stmt
169 void usld_lock_init(usimple_lock_t
, unsigned short);
170 void usld_lock_pre(usimple_lock_t
, pc_t
);
171 void usld_lock_post(usimple_lock_t
, pc_t
);
172 void usld_unlock(usimple_lock_t
, pc_t
);
173 void usld_lock_try_pre(usimple_lock_t
, pc_t
);
174 void usld_lock_try_post(usimple_lock_t
, pc_t
);
175 int usld_lock_common_checks(usimple_lock_t
, char *);
176 #else /* USLOCK_DEBUG */
178 #endif /* USLOCK_DEBUG */
181 extern int lck_rw_grab_want(lck_rw_t
*lck
);
182 extern int lck_rw_grab_shared(lck_rw_t
*lck
);
183 extern int lck_rw_held_read_or_upgrade(lck_rw_t
*lck
);
187 * Forward definitions
190 void lck_rw_lock_shared_gen(
193 void lck_rw_lock_exclusive_gen(
196 boolean_t
lck_rw_lock_shared_to_exclusive_success(
199 boolean_t
lck_rw_lock_shared_to_exclusive_failure(
201 int prior_lock_state
);
203 void lck_rw_lock_exclusive_to_shared_gen(
205 int prior_lock_state
);
207 lck_rw_type_t
lck_rw_done_gen(
209 int prior_lock_state
);
211 void lck_rw_clear_promotions_x86(thread_t thread
);
214 * Routine: lck_spin_alloc_init
223 if ((lck
= (lck_spin_t
*)kalloc(sizeof(lck_spin_t
))) != 0)
224 lck_spin_init(lck
, grp
, attr
);
230 * Routine: lck_spin_free
237 lck_spin_destroy(lck
, grp
);
238 kfree(lck
, sizeof(lck_spin_t
));
242 * Routine: lck_spin_init
248 __unused lck_attr_t
*attr
)
250 usimple_lock_init((usimple_lock_t
) lck
, 0);
251 lck_grp_reference(grp
);
252 lck_grp_lckcnt_incr(grp
, LCK_TYPE_SPIN
);
256 * Routine: lck_spin_destroy
263 if (lck
->interlock
== LCK_SPIN_TAG_DESTROYED
)
265 lck
->interlock
= LCK_SPIN_TAG_DESTROYED
;
266 lck_grp_lckcnt_decr(grp
, LCK_TYPE_SPIN
);
267 lck_grp_deallocate(grp
);
272 * Routine: lck_spin_lock
278 usimple_lock((usimple_lock_t
) lck
);
282 * Routine: lck_spin_unlock
288 usimple_unlock((usimple_lock_t
) lck
);
293 * Routine: lck_spin_try_lock
299 boolean_t lrval
= (boolean_t
)usimple_lock_try((usimple_lock_t
) lck
);
300 #if DEVELOPMENT || DEBUG
309 * Routine: lck_spin_assert
312 lck_spin_assert(lck_spin_t
*lock
, unsigned int type
)
314 thread_t thread
, holder
;
317 if (__improbable(type
!= LCK_ASSERT_OWNED
&& type
!= LCK_ASSERT_NOTOWNED
)) {
318 panic("lck_spin_assert(): invalid arg (%u)", type
);
321 state
= lock
->interlock
;
322 holder
= (thread_t
)state
;
323 thread
= current_thread();
324 if (type
== LCK_ASSERT_OWNED
) {
325 if (__improbable(holder
== THREAD_NULL
)) {
326 panic("Lock not owned %p = %lx", lock
, state
);
328 if (__improbable(holder
!= thread
)) {
329 panic("Lock not owned by current thread %p = %lx", lock
, state
);
331 } else if (type
== LCK_ASSERT_NOTOWNED
) {
332 if (__improbable(holder
!= THREAD_NULL
)) {
333 if (holder
== thread
) {
334 panic("Lock owned by current thread %p = %lx", lock
, state
);
336 panic("Lock %p owned by thread %p", lock
, holder
);
343 * Routine: kdp_lck_spin_is_acquired
344 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
345 * Returns: TRUE if lock is acquired.
348 kdp_lck_spin_is_acquired(lck_spin_t
*lck
) {
350 panic("panic: spinlock acquired check done outside of kernel debugger");
352 return (lck
->interlock
!= 0)? TRUE
: FALSE
;
356 * Initialize a usimple_lock.
358 * No change in preemption state.
363 __unused
unsigned short tag
)
365 #ifndef MACHINE_SIMPLE_LOCK
366 USLDBG(usld_lock_init(l
, tag
));
367 hw_lock_init(&l
->interlock
);
369 simple_lock_init((simple_lock_t
)l
,tag
);
373 volatile uint32_t spinlock_owner_cpu
= ~0;
374 volatile usimple_lock_t spinlock_timed_out
;
376 uint32_t spinlock_timeout_NMI(uintptr_t thread_addr
) {
380 for (i
= 0; i
< real_ncpus
; i
++) {
381 if ((uintptr_t)cpu_data_ptr
[i
]->cpu_active_thread
== thread_addr
) {
382 spinlock_owner_cpu
= i
;
383 if ((uint32_t) cpu_number() == i
)
385 cpu_datap(i
)->cpu_NMI_acknowledged
= FALSE
;
386 cpu_NMI_interrupt(i
);
387 deadline
= mach_absolute_time() + (LockTimeOut
* 2);
388 while (mach_absolute_time() < deadline
&& cpu_datap(i
)->cpu_NMI_acknowledged
== FALSE
)
394 return spinlock_owner_cpu
;
398 * Acquire a usimple_lock.
400 * Returns with preemption disabled. Note
401 * that the hw_lock routines are responsible for
402 * maintaining preemption state.
408 #ifndef MACHINE_SIMPLE_LOCK
412 USLDBG(usld_lock_pre(l
, pc
));
414 if(__improbable(hw_lock_to(&l
->interlock
, LockTimeOutTSC
) == 0)) {
415 boolean_t uslock_acquired
= FALSE
;
416 while (machine_timeout_suspended()) {
418 if ((uslock_acquired
= hw_lock_to(&l
->interlock
, LockTimeOutTSC
)))
422 if (uslock_acquired
== FALSE
) {
424 uintptr_t lowner
= (uintptr_t)l
->interlock
.lock_data
;
425 spinlock_timed_out
= l
;
426 lock_cpu
= spinlock_timeout_NMI(lowner
);
427 panic("Spinlock acquisition timed out: lock=%p, lock owner thread=0x%lx, current_thread: %p, lock owner active on CPU 0x%x, current owner: 0x%lx", l
, lowner
, current_thread(), lock_cpu
, (uintptr_t)l
->interlock
.lock_data
);
430 #if DEVELOPMENT || DEBUG
434 USLDBG(usld_lock_post(l
, pc
));
436 simple_lock((simple_lock_t
)l
);
442 * Release a usimple_lock.
444 * Returns with preemption enabled. Note
445 * that the hw_lock routines are responsible for
446 * maintaining preemption state.
452 #ifndef MACHINE_SIMPLE_LOCK
456 USLDBG(usld_unlock(l
, pc
));
457 #if DEVELOPMENT || DEBUG
460 hw_lock_unlock(&l
->interlock
);
462 simple_unlock_rwmb((simple_lock_t
)l
);
468 * Conditionally acquire a usimple_lock.
470 * On success, returns with preemption disabled.
471 * On failure, returns with preemption in the same state
472 * as when first invoked. Note that the hw_lock routines
473 * are responsible for maintaining preemption state.
475 * XXX No stats are gathered on a miss; I preserved this
476 * behavior from the original assembly-language code, but
477 * doesn't it make sense to log misses? XXX
483 #ifndef MACHINE_SIMPLE_LOCK
484 unsigned int success
;
488 USLDBG(usld_lock_try_pre(l
, pc
));
489 if ((success
= hw_lock_try(&l
->interlock
))) {
490 #if DEVELOPMENT || DEBUG
493 USLDBG(usld_lock_try_post(l
, pc
));
497 return(simple_lock_try((simple_lock_t
)l
));
502 * Acquire a usimple_lock while polling for pending TLB flushes
503 * and spinning on a lock.
507 usimple_lock_try_lock_loop(usimple_lock_t l
)
509 boolean_t istate
= ml_get_interrupts_enabled();
510 while (!simple_lock_try((l
))) {
512 handle_pending_TLB_flushes();
519 * States of a usimple_lock. The default when initializing
520 * a usimple_lock is setting it up for debug checking.
522 #define USLOCK_CHECKED 0x0001 /* lock is being checked */
523 #define USLOCK_TAKEN 0x0002 /* lock has been taken */
524 #define USLOCK_INIT 0xBAA0 /* lock has been initialized */
525 #define USLOCK_INITIALIZED (USLOCK_INIT|USLOCK_CHECKED)
526 #define USLOCK_CHECKING(l) (uslock_check && \
527 ((l)->debug.state & USLOCK_CHECKED))
530 * Trace activities of a particularly interesting lock.
532 void usl_trace(usimple_lock_t
, int, pc_t
, const char *);
536 * Initialize the debugging information contained
542 __unused
unsigned short tag
)
544 if (l
== USIMPLE_LOCK_NULL
)
545 panic("lock initialization: null lock pointer");
546 l
->lock_type
= USLOCK_TAG
;
547 l
->debug
.state
= uslock_check
? USLOCK_INITIALIZED
: 0;
548 l
->debug
.lock_cpu
= l
->debug
.unlock_cpu
= 0;
549 l
->debug
.lock_pc
= l
->debug
.unlock_pc
= INVALID_PC
;
550 l
->debug
.lock_thread
= l
->debug
.unlock_thread
= INVALID_THREAD
;
551 l
->debug
.duration
[0] = l
->debug
.duration
[1] = 0;
552 l
->debug
.unlock_cpu
= l
->debug
.unlock_cpu
= 0;
553 l
->debug
.unlock_pc
= l
->debug
.unlock_pc
= INVALID_PC
;
554 l
->debug
.unlock_thread
= l
->debug
.unlock_thread
= INVALID_THREAD
;
559 * These checks apply to all usimple_locks, not just
560 * those with USLOCK_CHECKED turned on.
563 usld_lock_common_checks(
567 if (l
== USIMPLE_LOCK_NULL
)
568 panic("%s: null lock pointer", caller
);
569 if (l
->lock_type
!= USLOCK_TAG
)
570 panic("%s: %p is not a usimple lock, 0x%x", caller
, l
, l
->lock_type
);
571 if (!(l
->debug
.state
& USLOCK_INIT
))
572 panic("%s: %p is not an initialized lock, 0x%x", caller
, l
, l
->debug
.state
);
573 return USLOCK_CHECKING(l
);
578 * Debug checks on a usimple_lock just before attempting
587 char caller
[] = "usimple_lock";
590 if (!usld_lock_common_checks(l
, caller
))
594 * Note that we have a weird case where we are getting a lock when we are]
595 * in the process of putting the system to sleep. We are running with no
596 * current threads, therefore we can't tell if we are trying to retake a lock
597 * we have or someone on the other processor has it. Therefore we just
598 * ignore this test if the locking thread is 0.
601 if ((l
->debug
.state
& USLOCK_TAKEN
) && l
->debug
.lock_thread
&&
602 l
->debug
.lock_thread
== (void *) current_thread()) {
603 printf("%s: lock %p already locked (at %p) by",
604 caller
, l
, l
->debug
.lock_pc
);
605 printf(" current thread %p (new attempt at pc %p)\n",
606 l
->debug
.lock_thread
, pc
);
609 mp_disable_preemption();
610 usl_trace(l
, cpu_number(), pc
, caller
);
611 mp_enable_preemption();
616 * Debug checks on a usimple_lock just after acquiring it.
618 * Pre-emption has been disabled at this point,
619 * so we are safe in using cpu_number.
627 char caller
[] = "successful usimple_lock";
630 if (!usld_lock_common_checks(l
, caller
))
633 if (!((l
->debug
.state
& ~USLOCK_TAKEN
) == USLOCK_INITIALIZED
))
634 panic("%s: lock %p became uninitialized",
636 if ((l
->debug
.state
& USLOCK_TAKEN
))
637 panic("%s: lock 0x%p became TAKEN by someone else",
640 mycpu
= cpu_number();
641 l
->debug
.lock_thread
= (void *)current_thread();
642 l
->debug
.state
|= USLOCK_TAKEN
;
643 l
->debug
.lock_pc
= pc
;
644 l
->debug
.lock_cpu
= mycpu
;
646 usl_trace(l
, mycpu
, pc
, caller
);
651 * Debug checks on a usimple_lock just before
652 * releasing it. Note that the caller has not
653 * yet released the hardware lock.
655 * Preemption is still disabled, so there's
656 * no problem using cpu_number.
664 char caller
[] = "usimple_unlock";
667 if (!usld_lock_common_checks(l
, caller
))
670 mycpu
= cpu_number();
672 if (!(l
->debug
.state
& USLOCK_TAKEN
))
673 panic("%s: lock 0x%p hasn't been taken",
675 if (l
->debug
.lock_thread
!= (void *) current_thread())
676 panic("%s: unlocking lock 0x%p, owned by thread %p",
677 caller
, l
, l
->debug
.lock_thread
);
678 if (l
->debug
.lock_cpu
!= mycpu
) {
679 printf("%s: unlocking lock 0x%p on cpu 0x%x",
681 printf(" (acquired on cpu 0x%x)\n", l
->debug
.lock_cpu
);
684 usl_trace(l
, mycpu
, pc
, caller
);
686 l
->debug
.unlock_thread
= l
->debug
.lock_thread
;
687 l
->debug
.lock_thread
= INVALID_PC
;
688 l
->debug
.state
&= ~USLOCK_TAKEN
;
689 l
->debug
.unlock_pc
= pc
;
690 l
->debug
.unlock_cpu
= mycpu
;
695 * Debug checks on a usimple_lock just before
696 * attempting to acquire it.
698 * Preemption isn't guaranteed to be disabled.
705 char caller
[] = "usimple_lock_try";
707 if (!usld_lock_common_checks(l
, caller
))
709 mp_disable_preemption();
710 usl_trace(l
, cpu_number(), pc
, caller
);
711 mp_enable_preemption();
716 * Debug checks on a usimple_lock just after
717 * successfully attempting to acquire it.
719 * Preemption has been disabled by the
720 * lock acquisition attempt, so it's safe
729 char caller
[] = "successful usimple_lock_try";
731 if (!usld_lock_common_checks(l
, caller
))
734 if (!((l
->debug
.state
& ~USLOCK_TAKEN
) == USLOCK_INITIALIZED
))
735 panic("%s: lock 0x%p became uninitialized",
737 if ((l
->debug
.state
& USLOCK_TAKEN
))
738 panic("%s: lock 0x%p became TAKEN by someone else",
741 mycpu
= cpu_number();
742 l
->debug
.lock_thread
= (void *) current_thread();
743 l
->debug
.state
|= USLOCK_TAKEN
;
744 l
->debug
.lock_pc
= pc
;
745 l
->debug
.lock_cpu
= mycpu
;
747 usl_trace(l
, mycpu
, pc
, caller
);
752 * For very special cases, set traced_lock to point to a
753 * specific lock of interest. The result is a series of
754 * XPRs showing lock operations on that lock. The lock_seq
755 * value is used to show the order of those operations.
757 usimple_lock_t traced_lock
;
758 unsigned int lock_seq
;
765 const char * op_name
)
767 if (traced_lock
== l
) {
769 "seq %d, cpu %d, %s @ %x\n",
770 (uintptr_t) lock_seq
, (uintptr_t) mycpu
,
771 (uintptr_t) op_name
, (uintptr_t) pc
, 0);
777 #endif /* USLOCK_DEBUG */
780 * Routine: lck_rw_alloc_init
788 if ((lck
= (lck_rw_t
*)kalloc(sizeof(lck_rw_t
))) != 0) {
789 bzero(lck
, sizeof(lck_rw_t
));
790 lck_rw_init(lck
, grp
, attr
);
797 * Routine: lck_rw_free
803 lck_rw_destroy(lck
, grp
);
804 kfree(lck
, sizeof(lck_rw_t
));
808 * Routine: lck_rw_init
816 lck_attr_t
*lck_attr
= (attr
!= LCK_ATTR_NULL
) ?
817 attr
: &LockDefaultLckAttr
;
819 hw_lock_byte_init(&lck
->lck_rw_interlock
);
820 lck
->lck_rw_want_write
= FALSE
;
821 lck
->lck_rw_want_upgrade
= FALSE
;
822 lck
->lck_rw_shared_count
= 0;
823 lck
->lck_rw_can_sleep
= TRUE
;
824 lck
->lck_r_waiting
= lck
->lck_w_waiting
= 0;
826 lck
->lck_rw_priv_excl
= ((lck_attr
->lck_attr_val
&
827 LCK_ATTR_RW_SHARED_PRIORITY
) == 0);
829 lck_grp_reference(grp
);
830 lck_grp_lckcnt_incr(grp
, LCK_TYPE_RW
);
834 * Routine: lck_rw_destroy
841 if (lck
->lck_rw_tag
== LCK_RW_TAG_DESTROYED
)
844 lck_rw_assert(lck
, LCK_RW_ASSERT_NOTHELD
);
846 lck
->lck_rw_tag
= LCK_RW_TAG_DESTROYED
;
847 lck_grp_lckcnt_decr(grp
, LCK_TYPE_RW
);
848 lck_grp_deallocate(grp
);
853 * Sleep locks. These use the same data structure and algorithm
854 * as the spin locks, but the process sleeps while it is waiting
855 * for the lock. These work on uniprocessor systems.
858 #define DECREMENTER_TIMEOUT 1000000
861 * We disable interrupts while holding the RW interlock to prevent an
862 * interrupt from exacerbating hold time.
863 * Hence, local helper functions lck_interlock_lock()/lck_interlock_unlock().
866 lck_interlock_lock(lck_rw_t
*lck
)
870 istate
= ml_set_interrupts_enabled(FALSE
);
871 hw_lock_byte_lock(&lck
->lck_rw_interlock
);
877 lck_interlock_unlock(lck_rw_t
*lck
, boolean_t istate
)
879 hw_lock_byte_unlock(&lck
->lck_rw_interlock
);
880 ml_set_interrupts_enabled(istate
);
884 * This inline is used when busy-waiting for an rw lock.
885 * If interrupts were disabled when the lock primitive was called,
886 * we poll the IPI handler for pending tlb flushes.
887 * XXX This is a hack to avoid deadlocking on the pmap_system_lock.
890 lck_rw_lock_pause(boolean_t interrupts_enabled
)
892 if (!interrupts_enabled
)
893 handle_pending_TLB_flushes();
899 * compute the deadline to spin against when
900 * waiting for a change of state on a lck_rw_t
902 static inline uint64_t
903 lck_rw_deadline_for_spin(lck_rw_t
*lck
)
905 if (lck
->lck_rw_can_sleep
) {
906 if (lck
->lck_r_waiting
|| lck
->lck_w_waiting
|| lck
->lck_rw_shared_count
> machine_info
.max_cpus
) {
908 * there are already threads waiting on this lock... this
909 * implies that they have spun beyond their deadlines waiting for
910 * the desired state to show up so we will not bother spinning at this time...
912 * the current number of threads sharing this lock exceeds our capacity to run them
913 * concurrently and since all states we're going to spin for require the rw_shared_count
914 * to be at 0, we'll not bother spinning since the latency for this to happen is
917 return (mach_absolute_time());
919 return (mach_absolute_time() + MutexSpin
);
921 return (mach_absolute_time() + (100000LL * 1000000000LL));
926 * Routine: lck_rw_lock_exclusive
929 lck_rw_lock_exclusive_gen(
932 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lck
);
933 uint64_t deadline
= 0;
937 wait_result_t res
= 0;
938 boolean_t istate
= -1;
941 boolean_t dtrace_ls_initialized
= FALSE
;
942 boolean_t dtrace_rwl_excl_spin
, dtrace_rwl_excl_block
, dtrace_ls_enabled
= FALSE
;
943 uint64_t wait_interval
= 0;
944 int readers_at_sleep
= 0;
948 * Try to acquire the lck_rw_want_write bit.
950 while ( !lck_rw_grab_want(lck
)) {
953 if (dtrace_ls_initialized
== FALSE
) {
954 dtrace_ls_initialized
= TRUE
;
955 dtrace_rwl_excl_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_SPIN
] != 0);
956 dtrace_rwl_excl_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_BLOCK
] != 0);
957 dtrace_ls_enabled
= dtrace_rwl_excl_spin
|| dtrace_rwl_excl_block
;
958 if (dtrace_ls_enabled
) {
960 * Either sleeping or spinning is happening,
961 * start a timing of our delay interval now.
963 readers_at_sleep
= lck
->lck_rw_shared_count
;
964 wait_interval
= mach_absolute_time();
969 istate
= ml_get_interrupts_enabled();
971 deadline
= lck_rw_deadline_for_spin(lck
);
973 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_SPIN_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
975 while (((gotlock
= lck_rw_grab_want(lck
)) == 0) && mach_absolute_time() < deadline
)
976 lck_rw_lock_pause(istate
);
978 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_SPIN_CODE
) | DBG_FUNC_END
, trace_lck
, 0, 0, gotlock
, 0);
983 * if we get here, the deadline has expired w/o us
984 * being able to grab the lock exclusively
985 * check to see if we're allowed to do a thread_block
987 if (lck
->lck_rw_can_sleep
) {
989 istate
= lck_interlock_lock(lck
);
991 if (lck
->lck_rw_want_write
) {
993 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_WAIT_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
995 lck
->lck_w_waiting
= TRUE
;
997 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockWrite
);
998 res
= assert_wait(RW_LOCK_WRITER_EVENT(lck
), THREAD_UNINT
);
999 lck_interlock_unlock(lck
, istate
);
1001 if (res
== THREAD_WAITING
) {
1002 res
= thread_block(THREAD_CONTINUE_NULL
);
1005 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_WRITER_WAIT_CODE
) | DBG_FUNC_END
, trace_lck
, res
, slept
, 0, 0);
1007 lck
->lck_rw_want_write
= TRUE
;
1008 lck_interlock_unlock(lck
, istate
);
1014 * Wait for readers (and upgrades) to finish...
1015 * the test for these conditions must be done simultaneously with
1016 * a check of the interlock not being held since
1017 * the rw_shared_count will drop to 0 first and then want_upgrade
1018 * will be set to 1 in the shared_to_exclusive scenario... those
1019 * adjustments are done behind the interlock and represent an
1020 * atomic change in state and must be considered as such
1021 * however, once we see the read count at 0, the want_upgrade not set
1022 * and the interlock not held, we are safe to proceed
1024 while (lck_rw_held_read_or_upgrade(lck
)) {
1028 * Either sleeping or spinning is happening, start
1029 * a timing of our delay interval now. If we set it
1030 * to -1 we don't have accurate data so we cannot later
1031 * decide to record a dtrace spin or sleep event.
1033 if (dtrace_ls_initialized
== FALSE
) {
1034 dtrace_ls_initialized
= TRUE
;
1035 dtrace_rwl_excl_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_SPIN
] != 0);
1036 dtrace_rwl_excl_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_EXCL_BLOCK
] != 0);
1037 dtrace_ls_enabled
= dtrace_rwl_excl_spin
|| dtrace_rwl_excl_block
;
1038 if (dtrace_ls_enabled
) {
1040 * Either sleeping or spinning is happening,
1041 * start a timing of our delay interval now.
1043 readers_at_sleep
= lck
->lck_rw_shared_count
;
1044 wait_interval
= mach_absolute_time();
1049 istate
= ml_get_interrupts_enabled();
1051 deadline
= lck_rw_deadline_for_spin(lck
);
1053 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_SPIN_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
1055 while ((lockheld
= lck_rw_held_read_or_upgrade(lck
)) && mach_absolute_time() < deadline
)
1056 lck_rw_lock_pause(istate
);
1058 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_SPIN_CODE
) | DBG_FUNC_END
, trace_lck
, 0, 0, lockheld
, 0);
1063 * if we get here, the deadline has expired w/o us
1064 * being able to grab the lock exclusively
1065 * check to see if we're allowed to do a thread_block
1067 if (lck
->lck_rw_can_sleep
) {
1069 istate
= lck_interlock_lock(lck
);
1071 if (lck
->lck_rw_shared_count
!= 0 || lck
->lck_rw_want_upgrade
) {
1072 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_WAIT_CODE
) | DBG_FUNC_START
, trace_lck
, 0, 0, 0, 0);
1074 lck
->lck_w_waiting
= TRUE
;
1076 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockWrite
);
1077 res
= assert_wait(RW_LOCK_WRITER_EVENT(lck
), THREAD_UNINT
);
1078 lck_interlock_unlock(lck
, istate
);
1080 if (res
== THREAD_WAITING
) {
1081 res
= thread_block(THREAD_CONTINUE_NULL
);
1084 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_READER_WAIT_CODE
) | DBG_FUNC_END
, trace_lck
, res
, slept
, 0, 0);
1086 lck_interlock_unlock(lck
, istate
);
1088 * must own the lock now, since we checked for
1089 * readers or upgrade owner behind the interlock
1090 * no need for a call to 'lck_rw_held_read_or_upgrade'
1099 * Decide what latencies we suffered that are Dtrace events.
1100 * If we have set wait_interval, then we either spun or slept.
1101 * At least we get out from under the interlock before we record
1102 * which is the best we can do here to minimize the impact
1104 * If we have set wait_interval to -1, then dtrace was not enabled when we
1105 * started sleeping/spinning so we don't record this event.
1107 if (dtrace_ls_enabled
== TRUE
) {
1109 LOCKSTAT_RECORD2(LS_LCK_RW_LOCK_EXCL_SPIN
, lck
,
1110 mach_absolute_time() - wait_interval
, 1);
1113 * For the blocking case, we also record if when we blocked
1114 * it was held for read or write, and how many readers.
1115 * Notice that above we recorded this before we dropped
1116 * the interlock so the count is accurate.
1118 LOCKSTAT_RECORD4(LS_LCK_RW_LOCK_EXCL_BLOCK
, lck
,
1119 mach_absolute_time() - wait_interval
, 1,
1120 (readers_at_sleep
== 0 ? 1 : 0), readers_at_sleep
);
1123 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_ACQUIRE
, lck
, 1);
1129 * Routine: lck_rw_done_gen
1131 * called from the assembly language wrapper...
1132 * prior_lock_state is the value in the 1st
1133 * word of the lock at the time of a successful
1134 * atomic compare and exchange with the new value...
1135 * it represents the state of the lock before we
1136 * decremented the rw_shared_count or cleared either
1137 * rw_want_upgrade or rw_want_write and
1138 * the lck_x_waiting bits... since the wrapper
1139 * routine has already changed the state atomically,
1140 * we just need to decide if we should
1141 * wake up anyone and what value to return... we do
1142 * this by examining the state of the lock before
1148 int prior_lock_state
)
1151 lck_rw_type_t lock_type
;
1153 uint32_t rwlock_count
;
1156 * prior_lock state is a snapshot of the 1st word of the
1157 * lock in question... we'll fake up a pointer to it
1158 * and carefully not access anything beyond whats defined
1159 * in the first word of a lck_rw_t
1161 fake_lck
= (lck_rw_t
*)&prior_lock_state
;
1163 if (fake_lck
->lck_rw_shared_count
<= 1) {
1164 if (fake_lck
->lck_w_waiting
)
1165 thread_wakeup(RW_LOCK_WRITER_EVENT(lck
));
1167 if (!(fake_lck
->lck_rw_priv_excl
&& fake_lck
->lck_w_waiting
) && fake_lck
->lck_r_waiting
)
1168 thread_wakeup(RW_LOCK_READER_EVENT(lck
));
1170 if (fake_lck
->lck_rw_shared_count
)
1171 lock_type
= LCK_RW_TYPE_SHARED
;
1173 lock_type
= LCK_RW_TYPE_EXCLUSIVE
;
1175 /* Check if dropping the lock means that we need to unpromote */
1176 thread
= current_thread();
1177 rwlock_count
= thread
->rwlock_count
--;
1179 if (rwlock_count
== 0) {
1180 panic("rw lock count underflow for thread %p", thread
);
1183 if ((rwlock_count
== 1 /* field now 0 */) && (thread
->sched_flags
& TH_SFLAG_RW_PROMOTED
)) {
1184 /* sched_flags checked without lock, but will be rechecked while clearing */
1185 lck_rw_clear_promotion(thread
);
1189 LOCKSTAT_RECORD(LS_LCK_RW_DONE_RELEASE
, lck
, lock_type
== LCK_RW_TYPE_SHARED
? 0 : 1);
1197 * Routine: lck_rw_unlock
1202 lck_rw_type_t lck_rw_type
)
1204 if (lck_rw_type
== LCK_RW_TYPE_SHARED
)
1205 lck_rw_unlock_shared(lck
);
1206 else if (lck_rw_type
== LCK_RW_TYPE_EXCLUSIVE
)
1207 lck_rw_unlock_exclusive(lck
);
1209 panic("lck_rw_unlock(): Invalid RW lock type: %d\n", lck_rw_type
);
1214 * Routine: lck_rw_unlock_shared
1217 lck_rw_unlock_shared(
1222 ret
= lck_rw_done(lck
);
1224 if (ret
!= LCK_RW_TYPE_SHARED
)
1225 panic("lck_rw_unlock_shared(): lock %p held in mode: %d\n", lck
, ret
);
1230 * Routine: lck_rw_unlock_exclusive
1233 lck_rw_unlock_exclusive(
1238 ret
= lck_rw_done(lck
);
1240 if (ret
!= LCK_RW_TYPE_EXCLUSIVE
)
1241 panic("lck_rw_unlock_exclusive(): lock held in mode: %d\n", ret
);
1246 * Routine: lck_rw_lock
1251 lck_rw_type_t lck_rw_type
)
1253 if (lck_rw_type
== LCK_RW_TYPE_SHARED
)
1254 lck_rw_lock_shared(lck
);
1255 else if (lck_rw_type
== LCK_RW_TYPE_EXCLUSIVE
)
1256 lck_rw_lock_exclusive(lck
);
1258 panic("lck_rw_lock(): Invalid RW lock type: %x\n", lck_rw_type
);
1263 * Routine: lck_rw_lock_shared_gen
1265 * assembly fast path code has determined that this lock
1266 * is held exclusively... this is where we spin/block
1267 * until we can acquire the lock in the shared mode
1270 lck_rw_lock_shared_gen(
1273 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lck
);
1274 uint64_t deadline
= 0;
1277 wait_result_t res
= 0;
1278 boolean_t istate
= -1;
1281 uint64_t wait_interval
= 0;
1282 int readers_at_sleep
= 0;
1283 boolean_t dtrace_ls_initialized
= FALSE
;
1284 boolean_t dtrace_rwl_shared_spin
, dtrace_rwl_shared_block
, dtrace_ls_enabled
= FALSE
;
1287 while ( !lck_rw_grab_shared(lck
)) {
1290 if (dtrace_ls_initialized
== FALSE
) {
1291 dtrace_ls_initialized
= TRUE
;
1292 dtrace_rwl_shared_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_SPIN
] != 0);
1293 dtrace_rwl_shared_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_BLOCK
] != 0);
1294 dtrace_ls_enabled
= dtrace_rwl_shared_spin
|| dtrace_rwl_shared_block
;
1295 if (dtrace_ls_enabled
) {
1297 * Either sleeping or spinning is happening,
1298 * start a timing of our delay interval now.
1300 readers_at_sleep
= lck
->lck_rw_shared_count
;
1301 wait_interval
= mach_absolute_time();
1306 istate
= ml_get_interrupts_enabled();
1308 deadline
= lck_rw_deadline_for_spin(lck
);
1310 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_SPIN_CODE
) | DBG_FUNC_START
,
1311 trace_lck
, lck
->lck_rw_want_write
, lck
->lck_rw_want_upgrade
, 0, 0);
1313 while (((gotlock
= lck_rw_grab_shared(lck
)) == 0) && mach_absolute_time() < deadline
)
1314 lck_rw_lock_pause(istate
);
1316 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_SPIN_CODE
) | DBG_FUNC_END
,
1317 trace_lck
, lck
->lck_rw_want_write
, lck
->lck_rw_want_upgrade
, gotlock
, 0);
1322 * if we get here, the deadline has expired w/o us
1323 * being able to grab the lock for read
1324 * check to see if we're allowed to do a thread_block
1326 if (lck
->lck_rw_can_sleep
) {
1328 istate
= lck_interlock_lock(lck
);
1330 if ((lck
->lck_rw_want_write
|| lck
->lck_rw_want_upgrade
) &&
1331 ((lck
->lck_rw_shared_count
== 0) || lck
->lck_rw_priv_excl
)) {
1333 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_WAIT_CODE
) | DBG_FUNC_START
,
1334 trace_lck
, lck
->lck_rw_want_write
, lck
->lck_rw_want_upgrade
, 0, 0);
1336 lck
->lck_r_waiting
= TRUE
;
1338 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockRead
);
1339 res
= assert_wait(RW_LOCK_READER_EVENT(lck
), THREAD_UNINT
);
1340 lck_interlock_unlock(lck
, istate
);
1342 if (res
== THREAD_WAITING
) {
1343 res
= thread_block(THREAD_CONTINUE_NULL
);
1346 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SHARED_WAIT_CODE
) | DBG_FUNC_END
,
1347 trace_lck
, res
, slept
, 0, 0);
1349 lck
->lck_rw_shared_count
++;
1350 lck_interlock_unlock(lck
, istate
);
1357 if (dtrace_ls_enabled
== TRUE
) {
1359 LOCKSTAT_RECORD2(LS_LCK_RW_LOCK_SHARED_SPIN
, lck
, mach_absolute_time() - wait_interval
, 0);
1361 LOCKSTAT_RECORD4(LS_LCK_RW_LOCK_SHARED_BLOCK
, lck
,
1362 mach_absolute_time() - wait_interval
, 0,
1363 (readers_at_sleep
== 0 ? 1 : 0), readers_at_sleep
);
1366 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_ACQUIRE
, lck
, 0);
1372 * Routine: lck_rw_lock_shared_to_exclusive_failure
1374 * assembly fast path code has already dropped our read
1375 * count and determined that someone else owns 'lck_rw_want_upgrade'
1376 * if 'lck_rw_shared_count' == 0, its also already dropped 'lck_w_waiting'
1377 * all we need to do here is determine if a wakeup is needed
1380 lck_rw_lock_shared_to_exclusive_failure(
1382 int prior_lock_state
)
1385 thread_t thread
= current_thread();
1386 uint32_t rwlock_count
;
1388 /* Check if dropping the lock means that we need to unpromote */
1389 rwlock_count
= thread
->rwlock_count
--;
1391 if (rwlock_count
== 0) {
1392 panic("rw lock count underflow for thread %p", thread
);
1395 if ((rwlock_count
== 1 /* field now 0 */) && (thread
->sched_flags
& TH_SFLAG_RW_PROMOTED
)) {
1396 /* sched_flags checked without lock, but will be rechecked while clearing */
1397 lck_rw_clear_promotion(thread
);
1401 * prior_lock state is a snapshot of the 1st word of the
1402 * lock in question... we'll fake up a pointer to it
1403 * and carefully not access anything beyond whats defined
1404 * in the first word of a lck_rw_t
1406 fake_lck
= (lck_rw_t
*)&prior_lock_state
;
1408 if (fake_lck
->lck_w_waiting
&& fake_lck
->lck_rw_shared_count
== 1) {
1410 * Someone else has requested upgrade.
1411 * Since we've released the read lock, wake
1412 * him up if he's blocked waiting
1414 thread_wakeup(RW_LOCK_WRITER_EVENT(lck
));
1416 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_CODE
) | DBG_FUNC_NONE
,
1417 VM_KERNEL_UNSLIDE_OR_PERM(lck
), lck
->lck_rw_shared_count
, lck
->lck_rw_want_upgrade
, 0, 0);
1424 * Routine: lck_rw_lock_shared_to_exclusive_failure
1426 * assembly fast path code has already dropped our read
1427 * count and successfully acquired 'lck_rw_want_upgrade'
1428 * we just need to wait for the rest of the readers to drain
1429 * and then we can return as the exclusive holder of this lock
1432 lck_rw_lock_shared_to_exclusive_success(
1435 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lck
);
1436 uint64_t deadline
= 0;
1438 int still_shared
= 0;
1440 boolean_t istate
= -1;
1443 uint64_t wait_interval
= 0;
1444 int readers_at_sleep
= 0;
1445 boolean_t dtrace_ls_initialized
= FALSE
;
1446 boolean_t dtrace_rwl_shared_to_excl_spin
, dtrace_rwl_shared_to_excl_block
, dtrace_ls_enabled
= FALSE
;
1449 while (lck
->lck_rw_shared_count
!= 0) {
1452 if (dtrace_ls_initialized
== FALSE
) {
1453 dtrace_ls_initialized
= TRUE
;
1454 dtrace_rwl_shared_to_excl_spin
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_TO_EXCL_SPIN
] != 0);
1455 dtrace_rwl_shared_to_excl_block
= (lockstat_probemap
[LS_LCK_RW_LOCK_SHARED_TO_EXCL_BLOCK
] != 0);
1456 dtrace_ls_enabled
= dtrace_rwl_shared_to_excl_spin
|| dtrace_rwl_shared_to_excl_block
;
1457 if (dtrace_ls_enabled
) {
1459 * Either sleeping or spinning is happening,
1460 * start a timing of our delay interval now.
1462 readers_at_sleep
= lck
->lck_rw_shared_count
;
1463 wait_interval
= mach_absolute_time();
1468 istate
= ml_get_interrupts_enabled();
1470 deadline
= lck_rw_deadline_for_spin(lck
);
1472 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_SPIN_CODE
) | DBG_FUNC_START
,
1473 trace_lck
, lck
->lck_rw_shared_count
, 0, 0, 0);
1475 while ((still_shared
= lck
->lck_rw_shared_count
) && mach_absolute_time() < deadline
)
1476 lck_rw_lock_pause(istate
);
1478 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_SPIN_CODE
) | DBG_FUNC_END
,
1479 trace_lck
, lck
->lck_rw_shared_count
, 0, 0, 0);
1484 * if we get here, the deadline has expired w/o
1485 * the rw_shared_count having drained to 0
1486 * check to see if we're allowed to do a thread_block
1488 if (lck
->lck_rw_can_sleep
) {
1490 istate
= lck_interlock_lock(lck
);
1492 if (lck
->lck_rw_shared_count
!= 0) {
1493 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_WAIT_CODE
) | DBG_FUNC_START
,
1494 trace_lck
, lck
->lck_rw_shared_count
, 0, 0, 0);
1496 lck
->lck_w_waiting
= TRUE
;
1498 thread_set_pending_block_hint(current_thread(), kThreadWaitKernelRWLockUpgrade
);
1499 res
= assert_wait(RW_LOCK_WRITER_EVENT(lck
), THREAD_UNINT
);
1500 lck_interlock_unlock(lck
, istate
);
1502 if (res
== THREAD_WAITING
) {
1503 res
= thread_block(THREAD_CONTINUE_NULL
);
1506 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_SH_TO_EX_WAIT_CODE
) | DBG_FUNC_END
,
1507 trace_lck
, res
, slept
, 0, 0);
1509 lck_interlock_unlock(lck
, istate
);
1516 * We infer whether we took the sleep/spin path above by checking readers_at_sleep.
1518 if (dtrace_ls_enabled
== TRUE
) {
1520 LOCKSTAT_RECORD2(LS_LCK_RW_LOCK_SHARED_TO_EXCL_SPIN
, lck
, mach_absolute_time() - wait_interval
, 0);
1522 LOCKSTAT_RECORD4(LS_LCK_RW_LOCK_SHARED_TO_EXCL_BLOCK
, lck
,
1523 mach_absolute_time() - wait_interval
, 1,
1524 (readers_at_sleep
== 0 ? 1 : 0), readers_at_sleep
);
1527 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_SHARED_TO_EXCL_UPGRADE
, lck
, 1);
1534 * Routine: lck_rw_lock_exclusive_to_shared
1536 * assembly fast path has already dropped
1537 * our exclusive state and bumped lck_rw_shared_count
1538 * all we need to do here is determine if anyone
1539 * needs to be awakened.
1542 lck_rw_lock_exclusive_to_shared_gen(
1544 int prior_lock_state
)
1546 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(lck
);
1550 * prior_lock state is a snapshot of the 1st word of the
1551 * lock in question... we'll fake up a pointer to it
1552 * and carefully not access anything beyond whats defined
1553 * in the first word of a lck_rw_t
1555 fake_lck
= (lck_rw_t
*)&prior_lock_state
;
1557 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_TO_SH_CODE
) | DBG_FUNC_START
,
1558 trace_lck
, fake_lck
->lck_rw_want_write
, fake_lck
->lck_rw_want_upgrade
, 0, 0);
1561 * don't wake up anyone waiting to take the lock exclusively
1562 * since we hold a read count... when the read count drops to 0,
1563 * the writers will be woken.
1565 * wake up any waiting readers if we don't have any writers waiting,
1566 * or the lock is NOT marked as rw_priv_excl (writers have privilege)
1568 if (!(fake_lck
->lck_rw_priv_excl
&& fake_lck
->lck_w_waiting
) && fake_lck
->lck_r_waiting
)
1569 thread_wakeup(RW_LOCK_READER_EVENT(lck
));
1571 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_RW_LCK_EX_TO_SH_CODE
) | DBG_FUNC_END
,
1572 trace_lck
, lck
->lck_rw_want_write
, lck
->lck_rw_want_upgrade
, lck
->lck_rw_shared_count
, 0);
1575 LOCKSTAT_RECORD(LS_LCK_RW_LOCK_EXCL_TO_SHARED_DOWNGRADE
, lck
, 0);
1581 * Routine: lck_rw_try_lock
1586 lck_rw_type_t lck_rw_type
)
1588 if (lck_rw_type
== LCK_RW_TYPE_SHARED
)
1589 return(lck_rw_try_lock_shared(lck
));
1590 else if (lck_rw_type
== LCK_RW_TYPE_EXCLUSIVE
)
1591 return(lck_rw_try_lock_exclusive(lck
));
1593 panic("lck_rw_try_lock(): Invalid rw lock type: %x\n", lck_rw_type
);
1604 case LCK_RW_ASSERT_SHARED
:
1605 if (lck
->lck_rw_shared_count
!= 0) {
1609 case LCK_RW_ASSERT_EXCLUSIVE
:
1610 if ((lck
->lck_rw_want_write
||
1611 lck
->lck_rw_want_upgrade
) &&
1612 lck
->lck_rw_shared_count
== 0) {
1616 case LCK_RW_ASSERT_HELD
:
1617 if (lck
->lck_rw_want_write
||
1618 lck
->lck_rw_want_upgrade
||
1619 lck
->lck_rw_shared_count
!= 0) {
1623 case LCK_RW_ASSERT_NOTHELD
:
1624 if (!(lck
->lck_rw_want_write
||
1625 lck
->lck_rw_want_upgrade
||
1626 lck
->lck_rw_shared_count
!= 0)) {
1634 panic("rw lock (%p)%s held (mode=%u), first word %08x\n", lck
, (type
== LCK_RW_ASSERT_NOTHELD
? "" : " not"), type
, *(uint32_t *)lck
);
1637 /* On return to userspace, this routine is called if the rwlock_count is somehow imbalanced */
1639 lck_rw_clear_promotions_x86(thread_t thread
)
1642 /* It's fatal to leave a RW lock locked and return to userspace */
1643 panic("%u rw lock(s) held on return to userspace for thread %p", thread
->rwlock_count
, thread
);
1645 /* Paper over the issue */
1646 thread
->rwlock_count
= 0;
1647 lck_rw_clear_promotion(thread
);
1653 * Routine: kdp_lck_rw_lock_is_acquired_exclusive
1654 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
1657 kdp_lck_rw_lock_is_acquired_exclusive(lck_rw_t
*lck
) {
1659 panic("panic: rw lock exclusive check done outside of kernel debugger");
1661 return ((lck
->lck_rw_want_upgrade
|| lck
->lck_rw_want_write
) && (lck
->lck_rw_shared_count
== 0)) ? TRUE
: FALSE
;
1666 extern zone_t lck_mtx_zone
;
1669 * Routine: lck_mtx_alloc_init
1678 if ((lck
= (lck_mtx_t
*)zalloc(lck_mtx_zone
)) != 0)
1679 lck_mtx_init(lck
, grp
, attr
);
1681 if ((lck
= (lck_mtx_t
*)kalloc(sizeof(lck_mtx_t
))) != 0)
1682 lck_mtx_init(lck
, grp
, attr
);
1688 * Routine: lck_mtx_free
1695 lck_mtx_destroy(lck
, grp
);
1697 zfree(lck_mtx_zone
, lck
);
1699 kfree(lck
, sizeof(lck_mtx_t
));
1704 * Routine: lck_mtx_ext_init
1712 bzero((void *)lck
, sizeof(lck_mtx_ext_t
));
1714 if ((attr
->lck_attr_val
) & LCK_ATTR_DEBUG
) {
1715 lck
->lck_mtx_deb
.type
= MUTEX_TAG
;
1716 lck
->lck_mtx_attr
|= LCK_MTX_ATTR_DEBUG
;
1719 lck
->lck_mtx_grp
= grp
;
1721 if (grp
->lck_grp_attr
& LCK_GRP_ATTR_STAT
)
1722 lck
->lck_mtx_attr
|= LCK_MTX_ATTR_STAT
;
1724 lck
->lck_mtx
.lck_mtx_is_ext
= 1;
1725 lck
->lck_mtx
.lck_mtx_pad32
= 0xFFFFFFFF;
1729 * Routine: lck_mtx_init
1737 lck_mtx_ext_t
*lck_ext
;
1738 lck_attr_t
*lck_attr
;
1740 if (attr
!= LCK_ATTR_NULL
)
1743 lck_attr
= &LockDefaultLckAttr
;
1745 if ((lck_attr
->lck_attr_val
) & LCK_ATTR_DEBUG
) {
1746 if ((lck_ext
= (lck_mtx_ext_t
*)kalloc(sizeof(lck_mtx_ext_t
))) != 0) {
1747 lck_mtx_ext_init(lck_ext
, grp
, lck_attr
);
1748 lck
->lck_mtx_tag
= LCK_MTX_TAG_INDIRECT
;
1749 lck
->lck_mtx_ptr
= lck_ext
;
1752 lck
->lck_mtx_owner
= 0;
1753 lck
->lck_mtx_state
= 0;
1755 lck
->lck_mtx_pad32
= 0xFFFFFFFF;
1756 lck_grp_reference(grp
);
1757 lck_grp_lckcnt_incr(grp
, LCK_TYPE_MTX
);
1761 * Routine: lck_mtx_init_ext
1766 lck_mtx_ext_t
*lck_ext
,
1770 lck_attr_t
*lck_attr
;
1772 if (attr
!= LCK_ATTR_NULL
)
1775 lck_attr
= &LockDefaultLckAttr
;
1777 if ((lck_attr
->lck_attr_val
) & LCK_ATTR_DEBUG
) {
1778 lck_mtx_ext_init(lck_ext
, grp
, lck_attr
);
1779 lck
->lck_mtx_tag
= LCK_MTX_TAG_INDIRECT
;
1780 lck
->lck_mtx_ptr
= lck_ext
;
1782 lck
->lck_mtx_owner
= 0;
1783 lck
->lck_mtx_state
= 0;
1785 lck
->lck_mtx_pad32
= 0xFFFFFFFF;
1787 lck_grp_reference(grp
);
1788 lck_grp_lckcnt_incr(grp
, LCK_TYPE_MTX
);
1792 * Routine: lck_mtx_destroy
1799 boolean_t lck_is_indirect
;
1801 if (lck
->lck_mtx_tag
== LCK_MTX_TAG_DESTROYED
)
1804 lck_mtx_assert(lck
, LCK_MTX_ASSERT_NOTOWNED
);
1806 lck_is_indirect
= (lck
->lck_mtx_tag
== LCK_MTX_TAG_INDIRECT
);
1808 lck_mtx_lock_mark_destroyed(lck
);
1810 if (lck_is_indirect
)
1811 kfree(lck
->lck_mtx_ptr
, sizeof(lck_mtx_ext_t
));
1812 lck_grp_lckcnt_decr(grp
, LCK_TYPE_MTX
);
1813 lck_grp_deallocate(grp
);
1818 #define LCK_MTX_LCK_WAIT_CODE 0x20
1819 #define LCK_MTX_LCK_WAKEUP_CODE 0x21
1820 #define LCK_MTX_LCK_SPIN_CODE 0x22
1821 #define LCK_MTX_LCK_ACQUIRE_CODE 0x23
1822 #define LCK_MTX_LCK_DEMOTE_CODE 0x24
1826 * Routine: lck_mtx_unlock_wakeup_x86
1828 * Invoked on unlock when there is
1829 * contention (i.e. the assembly routine sees that
1830 * that mutex->lck_mtx_waiters != 0 or
1831 * that mutex->lck_mtx_promoted != 0...
1833 * neither the mutex or interlock is held
1836 lck_mtx_unlock_wakeup_x86 (
1838 int prior_lock_state
)
1840 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(mutex
);
1844 * prior_lock state is a snapshot of the 2nd word of the
1845 * lock in question... we'll fake up a lock with the bits
1846 * copied into place and carefully not access anything
1847 * beyond whats defined in the second word of a lck_mtx_t
1849 fake_lck
.lck_mtx_state
= prior_lock_state
;
1851 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_WAKEUP_CODE
) | DBG_FUNC_START
,
1852 trace_lck
, fake_lck
.lck_mtx_promoted
, fake_lck
.lck_mtx_waiters
, fake_lck
.lck_mtx_pri
, 0);
1854 if (__probable(fake_lck
.lck_mtx_waiters
)) {
1855 if (fake_lck
.lck_mtx_waiters
> 1)
1856 thread_wakeup_one_with_pri(LCK_MTX_EVENT(mutex
), fake_lck
.lck_mtx_pri
);
1858 thread_wakeup_one(LCK_MTX_EVENT(mutex
));
1861 if (__improbable(fake_lck
.lck_mtx_promoted
)) {
1862 thread_t thread
= current_thread();
1865 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_DEMOTE_CODE
) | DBG_FUNC_NONE
,
1866 thread_tid(thread
), thread
->promotions
, thread
->sched_flags
& TH_SFLAG_PROMOTED
, 0, 0);
1868 if (thread
->promotions
> 0) {
1869 spl_t s
= splsched();
1871 thread_lock(thread
);
1873 if (--thread
->promotions
== 0 && (thread
->sched_flags
& TH_SFLAG_PROMOTED
)) {
1875 thread
->sched_flags
&= ~TH_SFLAG_PROMOTED
;
1877 if (thread
->sched_flags
& TH_SFLAG_RW_PROMOTED
) {
1878 /* Thread still has a RW lock promotion */
1879 } else if (thread
->sched_flags
& TH_SFLAG_DEPRESSED_MASK
) {
1880 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED
, MACH_DEMOTE
) | DBG_FUNC_NONE
,
1881 thread
->sched_pri
, DEPRESSPRI
, 0, trace_lck
, 0);
1883 set_sched_pri(thread
, DEPRESSPRI
);
1886 if (thread
->base_pri
< thread
->sched_pri
) {
1887 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED
, MACH_DEMOTE
) | DBG_FUNC_NONE
,
1888 thread
->sched_pri
, thread
->base_pri
, 0, trace_lck
, 0);
1890 thread_recompute_sched_pri(thread
, FALSE
);
1894 thread_unlock(thread
);
1898 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_WAKEUP_CODE
) | DBG_FUNC_END
,
1899 trace_lck
, 0, mutex
->lck_mtx_waiters
, 0, 0);
1904 * Routine: lck_mtx_lock_acquire_x86
1906 * Invoked on acquiring the mutex when there is
1907 * contention (i.e. the assembly routine sees that
1908 * that mutex->lck_mtx_waiters != 0 or
1909 * thread->was_promoted_on_wakeup != 0)...
1911 * mutex is owned... interlock is held... preemption is disabled
1914 lck_mtx_lock_acquire_x86(
1917 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(mutex
);
1922 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_ACQUIRE_CODE
) | DBG_FUNC_START
,
1923 trace_lck
, thread
->was_promoted_on_wakeup
, mutex
->lck_mtx_waiters
, mutex
->lck_mtx_pri
, 0);
1925 if (mutex
->lck_mtx_waiters
)
1926 priority
= mutex
->lck_mtx_pri
;
1930 thread
= (thread_t
)mutex
->lck_mtx_owner
; /* faster then current_thread() */
1932 if (thread
->sched_pri
< priority
|| thread
->was_promoted_on_wakeup
) {
1934 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_SCHED
, MACH_PROMOTE
) | DBG_FUNC_NONE
,
1935 thread
->sched_pri
, priority
, thread
->was_promoted_on_wakeup
, trace_lck
, 0);
1938 thread_lock(thread
);
1940 if (thread
->sched_pri
< priority
) {
1941 /* Do not promote past promotion ceiling */
1942 assert(priority
<= MAXPRI_PROMOTE
);
1943 set_sched_pri(thread
, priority
);
1945 if (mutex
->lck_mtx_promoted
== 0) {
1946 mutex
->lck_mtx_promoted
= 1;
1948 thread
->promotions
++;
1949 thread
->sched_flags
|= TH_SFLAG_PROMOTED
;
1951 thread
->was_promoted_on_wakeup
= 0;
1953 thread_unlock(thread
);
1956 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_ACQUIRE_CODE
) | DBG_FUNC_END
,
1957 trace_lck
, 0, mutex
->lck_mtx_waiters
, 0, 0);
1962 lck_mtx_interlock_try_lock(lck_mtx_t
*mutex
, boolean_t
*istate
)
1966 *istate
= ml_set_interrupts_enabled(FALSE
);
1967 retval
= lck_mtx_ilk_try_lock(mutex
);
1970 ml_set_interrupts_enabled(*istate
);
1976 lck_mtx_interlock_unlock(lck_mtx_t
*mutex
, boolean_t istate
)
1978 lck_mtx_ilk_unlock(mutex
);
1979 ml_set_interrupts_enabled(istate
);
1984 * Routine: lck_mtx_lock_spinwait_x86
1986 * Invoked trying to acquire a mutex when there is contention but
1987 * the holder is running on another processor. We spin for up to a maximum
1988 * time waiting for the lock to be released.
1990 * Called with the interlock unlocked.
1991 * returns 0 if mutex acquired
1992 * returns 1 if we spun
1993 * returns 2 if we didn't spin due to the holder not running
1996 lck_mtx_lock_spinwait_x86(
1999 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(mutex
);
2001 uint64_t overall_deadline
;
2002 uint64_t check_owner_deadline
;
2007 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_SPIN_CODE
) | DBG_FUNC_START
,
2008 trace_lck
, VM_KERNEL_UNSLIDE_OR_PERM(mutex
->lck_mtx_owner
), mutex
->lck_mtx_waiters
, 0, 0);
2010 cur_time
= mach_absolute_time();
2011 overall_deadline
= cur_time
+ MutexSpin
;
2012 check_owner_deadline
= cur_time
;
2016 * - mutex is locked, and
2017 * - its locked as a spin lock, and
2018 * - owner is running on another processor, and
2019 * - owner (processor) is not idling, and
2020 * - we haven't spun for long enough.
2023 if (__probable(lck_mtx_lock_grab_mutex(mutex
))) {
2027 cur_time
= mach_absolute_time();
2029 if (cur_time
>= overall_deadline
)
2032 if (cur_time
>= check_owner_deadline
&& mutex
->lck_mtx_owner
) {
2035 if (lck_mtx_interlock_try_lock(mutex
, &istate
)) {
2037 if ((holder
= (thread_t
) mutex
->lck_mtx_owner
) != NULL
) {
2039 if ( !(holder
->machine
.specFlags
& OnProc
) ||
2040 (holder
->state
& TH_IDLE
)) {
2042 lck_mtx_interlock_unlock(mutex
, istate
);
2049 lck_mtx_interlock_unlock(mutex
, istate
);
2051 check_owner_deadline
= cur_time
+ (MutexSpin
/ 4);
2062 * We've already kept a count via overall_deadline of how long we spun.
2063 * If dtrace is active, then we compute backwards to decide how
2066 * Note that we record a different probe id depending on whether
2067 * this is a direct or indirect mutex. This allows us to
2068 * penalize only lock groups that have debug/stats enabled
2069 * with dtrace processing if desired.
2071 if (__probable(mutex
->lck_mtx_is_ext
== 0)) {
2072 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_SPIN
, mutex
,
2073 mach_absolute_time() - (overall_deadline
- MutexSpin
));
2075 LOCKSTAT_RECORD(LS_LCK_MTX_EXT_LOCK_SPIN
, mutex
,
2076 mach_absolute_time() - (overall_deadline
- MutexSpin
));
2078 /* The lockstat acquire event is recorded by the assembly code beneath us. */
2081 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_SPIN_CODE
) | DBG_FUNC_END
,
2082 trace_lck
, VM_KERNEL_UNSLIDE_OR_PERM(mutex
->lck_mtx_owner
), mutex
->lck_mtx_waiters
, retval
, 0);
2090 * Routine: lck_mtx_lock_wait_x86
2092 * Invoked in order to wait on contention.
2094 * Called with the interlock locked and
2095 * preemption disabled...
2096 * returns it unlocked and with preemption enabled
2099 lck_mtx_lock_wait_x86 (
2102 __kdebug_only
uintptr_t trace_lck
= VM_KERNEL_UNSLIDE_OR_PERM(mutex
);
2103 thread_t self
= current_thread();
2108 uint64_t sleep_start
= 0;
2110 if (lockstat_probemap
[LS_LCK_MTX_LOCK_BLOCK
] || lockstat_probemap
[LS_LCK_MTX_EXT_LOCK_BLOCK
]) {
2111 sleep_start
= mach_absolute_time();
2114 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_WAIT_CODE
) | DBG_FUNC_START
,
2115 trace_lck
, VM_KERNEL_UNSLIDE_OR_PERM(mutex
->lck_mtx_owner
), mutex
->lck_mtx_waiters
, mutex
->lck_mtx_pri
, 0);
2117 priority
= self
->sched_pri
;
2119 if (priority
< self
->base_pri
)
2120 priority
= self
->base_pri
;
2121 if (priority
< BASEPRI_DEFAULT
)
2122 priority
= BASEPRI_DEFAULT
;
2124 /* Do not promote past promotion ceiling */
2125 priority
= MIN(priority
, MAXPRI_PROMOTE
);
2127 if (mutex
->lck_mtx_waiters
== 0 || priority
> mutex
->lck_mtx_pri
)
2128 mutex
->lck_mtx_pri
= priority
;
2129 mutex
->lck_mtx_waiters
++;
2131 if ( (holder
= (thread_t
)mutex
->lck_mtx_owner
) &&
2132 holder
->sched_pri
< mutex
->lck_mtx_pri
) {
2134 thread_lock(holder
);
2136 /* holder priority may have been bumped by another thread
2137 * before thread_lock was taken
2139 if (holder
->sched_pri
< mutex
->lck_mtx_pri
) {
2140 KERNEL_DEBUG_CONSTANT(
2141 MACHDBG_CODE(DBG_MACH_SCHED
, MACH_PROMOTE
) | DBG_FUNC_NONE
,
2142 holder
->sched_pri
, priority
, thread_tid(holder
), trace_lck
, 0);
2143 /* Assert that we're not altering the priority of a
2144 * thread above the MAXPRI_PROMOTE band
2146 assert(holder
->sched_pri
< MAXPRI_PROMOTE
);
2147 set_sched_pri(holder
, priority
);
2149 if (mutex
->lck_mtx_promoted
== 0) {
2150 holder
->promotions
++;
2151 holder
->sched_flags
|= TH_SFLAG_PROMOTED
;
2153 mutex
->lck_mtx_promoted
= 1;
2156 thread_unlock(holder
);
2159 thread_set_pending_block_hint(self
, kThreadWaitKernelMutex
);
2160 assert_wait(LCK_MTX_EVENT(mutex
), THREAD_UNINT
);
2162 lck_mtx_ilk_unlock(mutex
);
2164 thread_block(THREAD_CONTINUE_NULL
);
2166 KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_LOCKS
, LCK_MTX_LCK_WAIT_CODE
) | DBG_FUNC_END
,
2167 trace_lck
, VM_KERNEL_UNSLIDE_OR_PERM(mutex
->lck_mtx_owner
), mutex
->lck_mtx_waiters
, mutex
->lck_mtx_pri
, 0);
2171 * Record the Dtrace lockstat probe for blocking, block time
2172 * measured from when we were entered.
2175 if (mutex
->lck_mtx_is_ext
== 0) {
2176 LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_BLOCK
, mutex
,
2177 mach_absolute_time() - sleep_start
);
2179 LOCKSTAT_RECORD(LS_LCK_MTX_EXT_LOCK_BLOCK
, mutex
,
2180 mach_absolute_time() - sleep_start
);
2187 * Routine: kdp_lck_mtx_lock_spin_is_acquired
2188 * NOT SAFE: To be used only by kernel debugger to avoid deadlock.
2189 * Returns: TRUE if lock is acquired.
2192 kdp_lck_mtx_lock_spin_is_acquired(lck_mtx_t
*lck
)
2195 panic("panic: kdp_lck_mtx_lock_spin_is_acquired called outside of kernel debugger");
2198 if (lck
->lck_mtx_ilocked
|| lck
->lck_mtx_mlocked
) {
2206 kdp_lck_mtx_find_owner(__unused
struct waitq
* waitq
, event64_t event
, thread_waitinfo_t
* waitinfo
)
2208 lck_mtx_t
* mutex
= LCK_EVENT_TO_MUTEX(event
);
2209 waitinfo
->context
= VM_KERNEL_UNSLIDE_OR_PERM(mutex
);
2210 thread_t holder
= (thread_t
)mutex
->lck_mtx_owner
;
2211 waitinfo
->owner
= thread_tid(holder
);
2215 kdp_rwlck_find_owner(__unused
struct waitq
* waitq
, event64_t event
, thread_waitinfo_t
* waitinfo
)
2217 lck_rw_t
*rwlck
= NULL
;
2218 switch(waitinfo
->wait_type
) {
2219 case kThreadWaitKernelRWLockRead
:
2220 rwlck
= READ_EVENT_TO_RWLOCK(event
);
2222 case kThreadWaitKernelRWLockWrite
:
2223 case kThreadWaitKernelRWLockUpgrade
:
2224 rwlck
= WRITE_EVENT_TO_RWLOCK(event
);
2227 panic("%s was called with an invalid blocking type", __FUNCTION__
);
2230 waitinfo
->context
= VM_KERNEL_UNSLIDE_OR_PERM(rwlck
);
2231 waitinfo
->owner
= 0;