kernel tree reorganization stage 1: Major cvs repository work (not logged as
[dragonfly.git] / sys / i386 / i386 / mplock.s
1 /*
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  * ----------------------------------------------------------------------------
8  *
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 $
11  *
12  * Functions for locking between CPUs in a SMP system.
13  *
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).
17  *
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.
22  */
23
24 #include <machine/asmacros.h>
25 #include <machine/smptests.h>           /** GRAB_LOPRIO */
26 #include <machine/apic.h>
27
28 #include "assym.s"
29
30 /*
31  * YYY Debugging only.  Define this to be paranoid about invalidating the
32  * TLB when we get giant.
33  */
34 #undef PARANOID_INVLTLB
35
36         .data
37         ALIGN_DATA
38 #ifdef SMP
39         .globl  mp_lock
40 mp_lock:
41         .long   -1                      /* initialized to not held */
42 #endif
43
44         .text
45         SUPERALIGN_TEXT
46
47         /*
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.
50          */
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 */
55         NON_GPROF_RET
56
57         /*
58          * cpu_try_mplock() returns non-zero on success, 0 on failure.  It
59          * only adjusts mp_lock.  It does not touch td_mpcount.
60          */
61 NON_GPROF_ENTRY(cpu_try_mplock)
62         movl    PCPU(cpuid),%ecx
63         movl    $-1,%eax
64         lock cmpxchgl %ecx,mp_lock      /* ecx<->mem if eax matches */
65         jnz     1f
66 #ifdef PARANOID_INVLTLB
67         movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
68 #endif
69         movl    $1,%eax
70         NON_GPROF_RET
71 1:
72         subl    %eax,%eax
73         NON_GPROF_RET
74
75         /*
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.
84          *
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
89          * a critical section.
90          */
91 NON_GPROF_ENTRY(get_mplock)
92         movl    PCPU(cpuid),%ecx
93         movl    PCPU(curthread),%edx
94         cmpl    %ecx,mp_lock
95         jne     1f
96         incl    TD_MPCOUNT(%edx)
97         NON_GPROF_RET
98 1:
99         incl    TD_MPCOUNT(%edx)
100         movl    $-1,%eax
101         lock cmpxchgl %ecx,mp_lock
102         jnz     2f
103 #ifdef PARANOID_INVLTLB
104         movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
105 #endif
106         NON_GPROF_RET
107 2:
108         call    lwkt_switch             /* will be correct on return */
109 #ifdef INVARIANTS
110         movl    PCPU(cpuid),%eax        /* failure */
111         cmpl    %eax,mp_lock
112         jne     4f
113 #endif
114         NON_GPROF_RET
115 #ifdef INVARIANTS
116 4:
117         cmpl    $0,panicstr             /* don't double panic */
118         je      badmp_get2
119         NON_GPROF_RET
120 #endif
121
122         /*
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.
126          */
127 NON_GPROF_ENTRY(try_mplock)
128         movl    PCPU(cpuid),%ecx
129         movl    PCPU(curthread),%edx
130         cmpl    %ecx,mp_lock
131         jne     1f
132         incl    TD_MPCOUNT(%edx)
133         movl    $1,%eax
134         NON_GPROF_RET
135 1:
136         incl    TD_MPCOUNT(%edx)        /* pre-dispose */
137         movl    $-1,%eax
138         lock cmpxchgl %ecx,mp_lock
139         jnz     2f
140 #ifdef PARANOID_INVLTLB
141         movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
142 #endif
143         movl    $1,%eax
144         NON_GPROF_RET
145 2:
146         decl    TD_MPCOUNT(%edx)        /* un-dispose */
147         subl    %eax,%eax
148         NON_GPROF_RET
149
150         /*
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).
155          */
156 NON_GPROF_ENTRY(rel_mplock)
157         movl    PCPU(curthread),%edx
158         movl    TD_MPCOUNT(%edx),%eax
159         cmpl    $1,%eax
160         je      1f
161 #ifdef INVARIANTS
162         testl   %eax,%eax
163         jz      badmp_rel
164 #endif
165         subl    $1,%eax
166         movl    %eax,TD_MPCOUNT(%edx)
167         NON_GPROF_RET
168 1:
169 #ifdef INVARIANTS
170         movl    PCPU(cpuid),%ecx
171         cmpl    %ecx,mp_lock
172         jne     badmp_rel2
173 #endif
174         movl    $MP_FREE_LOCK,mp_lock
175         movl    $0,TD_MPCOUNT(%edx)
176         NON_GPROF_RET
177
178 #ifdef INVARIANTS
179
180 badmp_get:
181         pushl   $bmpsw1
182         call    panic
183 badmp_get2:
184         pushl   $bmpsw1a
185         call    panic
186 badmp_rel:
187         pushl   $bmpsw2
188         call    panic
189 badmp_rel2:
190         pushl   $bmpsw2a
191         call    panic
192
193         .data
194
195 bmpsw1:
196         .asciz  "try/get_mplock(): already have lock! %d %p"
197
198 bmpsw1a:
199         .asciz  "try/get_mplock(): failed on count or switch %d %p"
200
201 bmpsw2:
202         .asciz  "rel_mplock(): mpcount already 0 @ %p %p %p %p %p %p %p %p!"
203
204 bmpsw2a:
205         .asciz  "rel_mplock(): Releasing another cpu's MP lock! %p %p"
206
207 #endif
208
209 #if 0
210 /* after 1st acquire of lock we grab all hardware INTs */
211 #ifdef GRAB_LOPRIO
212 #define GRAB_HWI        movl    $ALLHWI_LEVEL, lapic_tpr
213
214 /* after last release of lock give up LOW PRIO (ie, arbitrate INTerrupts) */
215 #define ARB_HWI         movl    $LOPRIO_LEVEL, lapic_tpr /* CHEAP_TPR */
216 #endif
217 #endif
218