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.7 2003/07/10 18:23:23 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 */
116 cmpl $0,panicstr /* don't double panic */
121 * try_mplock() attempts to obtain the MP lock and will not switch
122 * away if it cannot get it. Note that td_mpcoutn may not be
123 * synchronized with the actual state of the MP lock.
125 NON_GPROF_ENTRY(try_mplock)
126 movl PCPU(cpuid),%ecx
127 movl PCPU(curthread),%edx
130 incl TD_MPCOUNT(%edx)
134 incl TD_MPCOUNT(%edx) /* pre-dispose */
136 lock cmpxchgl %ecx,mp_lock
138 #ifdef PARANOID_INVLTLB
139 movl %cr3,%eax; movl %eax,%cr3 /* YYY check and remove */
143 decl TD_MPCOUNT(%edx) /* un-dispose */
148 * rel_mplock() release the MP lock. The MP lock MUST be held,
149 * td_mpcount must NOT be out of synch with the lock. It is allowed
150 * for the physical lock to be released prior to setting the count
151 * to 0, preemptions will deal with the case (see lwkt_thread.c).
153 NON_GPROF_ENTRY(rel_mplock)
154 movl PCPU(curthread),%edx
155 movl TD_MPCOUNT(%edx),%eax
163 movl %eax,TD_MPCOUNT(%edx)
167 movl PCPU(cpuid),%ecx
171 movl $MP_FREE_LOCK,mp_lock
172 movl $0,TD_MPCOUNT(%edx)
193 .asciz "try/get_mplock(): already have lock! %d %p"
196 .asciz "try/get_mplock(): failed on count or switch %d %p"
199 .asciz "rel_mplock(): mpcount already 0 @ %p %p %p %p %p %p %p %p!"
202 .asciz "rel_mplock(): Releasing another cpu's MP lock! %p %p"
207 /* after 1st acquire of lock we grab all hardware INTs */
209 #define GRAB_HWI movl $ALLHWI_LEVEL, lapic_tpr
211 /* after last release of lock give up LOW PRIO (ie, arbitrate INTerrupts) */
212 #define ARB_HWI movl $LOPRIO_LEVEL, lapic_tpr /* CHEAP_TPR */