2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
9 * $FreeBSD: src/sys/i386/i386/mplock.s,v 1.29.2.2 2000/05/16 06:58:06 dillon Exp $
10 * $DragonFly: src/sys/i386/i386/Attic/mplock.s,v 1.9 2003/07/20 07:46:19 dillon Exp $
12 * Functions for locking between CPUs in a SMP system.
14 * This is an "exclusive counting semaphore". This means that it can be
15 * free (0xffffffff) or be owned by a CPU (0xXXYYYYYY where XX is CPU-id
16 * and YYYYYY is the count).
18 * Contrary to most implementations around, this one is entirely atomic:
19 * The attempt to seize/release the semaphore and the increment/decrement
20 * is done in one atomic operation. This way we are safe from all kinds
21 * of weird reentrancy situations.
24 #include <machine/asmacros.h>
25 #include <machine/smptests.h> /** GRAB_LOPRIO */
26 #include <machine/apic.h>
31 * YYY Debugging only. Define this to be paranoid about invalidating the
32 * TLB when we get giant.
34 #undef PARANOID_INVLTLB
41 .long -1 /* initialized to not held */
48 * Note on cmpxchgl... exchanges ecx with mem if mem matches eax.
49 * Z=1 (jz) on success. A lock prefix is required for MP.
51 NON_GPROF_ENTRY(cpu_get_initial_mplock)
52 movl PCPU(curthread),%ecx
53 movl $1,TD_MPCOUNT(%ecx) /* curthread has mpcount of 1 */
54 movl $0,mp_lock /* owned by cpu 0 */
58 * cpu_try_mplock() returns non-zero on success, 0 on failure. It
59 * only adjusts mp_lock. It does not touch td_mpcount.
61 NON_GPROF_ENTRY(cpu_try_mplock)
64 lock cmpxchgl %ecx,mp_lock /* ecx<->mem if eax matches */
66 #ifdef PARANOID_INVLTLB
67 movl %cr3,%eax; movl %eax,%cr3 /* YYY check and remove */
76 * get_mplock() Obtains the MP lock and may switch away if it cannot
77 * get it. Note that td_mpcount may not be synchronized with the
78 * actual state of the MP lock. This situation occurs when
79 * get_mplock() or try_mplock() is indirectly called from the
80 * lwkt_switch() code, or from a preemption (though, truthfully,
81 * only try_mplock() should ever be called in this fashion). If
82 * we cannot get the MP lock we pre-dispose TD_MPCOUNT and call
83 * lwkt_swich(). The MP lock will be held on return.
85 * Note that both get_mplock() and try_mplock() must pre-dispose
86 * mpcount before attempting to get the lock, in case we get
87 * preempted. This allows us to avoid expensive interrupt
88 * disablement instructions and allows us to be called from outside
91 NON_GPROF_ENTRY(get_mplock)
93 movl PCPU(curthread),%edx
101 lock cmpxchgl %ecx,mp_lock
103 #ifdef PARANOID_INVLTLB
104 movl %cr3,%eax; movl %eax,%cr3 /* YYY check and remove */
108 call lwkt_switch /* will be correct on return */
110 movl PCPU(cpuid),%eax /* failure */
117 cmpl $0,panicstr /* don't double panic */
123 * try_mplock() attempts to obtain the MP lock and will not switch
124 * away if it cannot get it. Note that td_mpcoutn may not be
125 * synchronized with the actual state of the MP lock.
127 NON_GPROF_ENTRY(try_mplock)
128 movl PCPU(cpuid),%ecx
129 movl PCPU(curthread),%edx
132 incl TD_MPCOUNT(%edx)
136 incl TD_MPCOUNT(%edx) /* pre-dispose */
138 lock cmpxchgl %ecx,mp_lock
140 #ifdef PARANOID_INVLTLB
141 movl %cr3,%eax; movl %eax,%cr3 /* YYY check and remove */
146 decl TD_MPCOUNT(%edx) /* un-dispose */
151 * rel_mplock() release the MP lock. The MP lock MUST be held,
152 * td_mpcount must NOT be out of synch with the lock. It is allowed
153 * for the physical lock to be released prior to setting the count
154 * to 0, preemptions will deal with the case (see lwkt_thread.c).
156 NON_GPROF_ENTRY(rel_mplock)
157 movl PCPU(curthread),%edx
158 movl TD_MPCOUNT(%edx),%eax
166 movl %eax,TD_MPCOUNT(%edx)
170 movl PCPU(cpuid),%ecx
174 movl $MP_FREE_LOCK,mp_lock
175 movl $0,TD_MPCOUNT(%edx)
196 .asciz "try/get_mplock(): already have lock! %d %p"
199 .asciz "try/get_mplock(): failed on count or switch %d %p"
202 .asciz "rel_mplock(): mpcount already 0 @ %p %p %p %p %p %p %p %p!"
205 .asciz "rel_mplock(): Releasing another cpu's MP lock! %p %p"
210 /* after 1st acquire of lock we grab all hardware INTs */
212 #define GRAB_HWI movl $ALLHWI_LEVEL, lapic_tpr
214 /* after last release of lock give up LOW PRIO (ie, arbitrate INTerrupts) */
215 #define ARB_HWI movl $LOPRIO_LEVEL, lapic_tpr /* CHEAP_TPR */