proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[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.2 2003/06/17 04:28:35 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 #define GLPROFILE_NOT
29
30 #ifdef CHEAP_TPR
31
32 /* we assumme that the 'reserved bits' can be written with zeros */
33
34 #else /* CHEAP_TPR */
35
36 #error HEADS UP: this code needs work
37 /*
38  * The APIC doc says that reserved bits must be written with whatever
39  * value they currently contain, ie you should: read, modify, write,
40  * instead of just writing new values to the TPR register.  Current
41  * silicon seems happy with just writing.  If the behaviour of the
42  * silicon changes, all code that access the lapic_tpr must be modified.
43  * The last version to contain such code was:
44  *   Id: mplock.s,v 1.17 1997/08/10 20:59:07 fsmp Exp
45  */
46
47 #endif /* CHEAP_TPR */
48
49 #ifdef GRAB_LOPRIO
50 /*
51  * Claim LOWest PRIOrity, ie. attempt to grab ALL INTerrupts.
52  */
53
54 /* after 1st acquire of lock we grab all hardware INTs */
55 #define GRAB_HWI        movl    $ALLHWI_LEVEL, lapic_tpr
56
57 /* after last release of lock give up LOW PRIO (ie, arbitrate INTerrupts) */
58 #define ARB_HWI         movl    $LOPRIO_LEVEL, lapic_tpr /* CHEAP_TPR */
59
60 #else /* GRAB_LOPRIO */
61
62 #define GRAB_HWI        /* nop */
63 #define ARB_HWI         /* nop */
64
65 #endif /* GRAB_LOPRIO */
66
67
68         .text
69
70 #ifdef SMP 
71
72 /***********************************************************************
73  *  void MPgetlock_edx(unsigned int *lock : %edx)
74  *  ----------------------------------
75  *  Destroys    %eax, %ecx.  %edx must hold lock argument.
76  *
77  *  Grabs hardware interrupts on first aquire.
78  *
79  *  NOTE: Serialization is not required if we already hold the lock, since
80  *  we already hold the lock, nor do we need a locked instruction if we 
81  *  already hold the lock.
82  */
83
84 NON_GPROF_ENTRY(MPgetlock_edx)
85 1:
86         movl    (%edx), %eax            /* Get current contents of lock */
87         movl    %eax, %ecx
88         andl    $CPU_FIELD,%ecx
89         cmpl    _cpu_lockid, %ecx       /* Do we already own the lock? */
90         jne     2f
91         incl    %eax                    /* yes, just bump the count */
92         movl    %eax, (%edx)            /* serialization not required */
93         ret
94 2:
95         movl    $FREE_LOCK, %eax        /* lock must be free */
96         movl    _cpu_lockid, %ecx
97         incl    %ecx
98         lock
99         cmpxchg %ecx, (%edx)            /* attempt to replace %eax<->%ecx */
100 #ifdef GLPROFILE
101         jne     3f
102         incl    _gethits2
103 #else
104         jne     1b
105 #endif /* GLPROFILE */
106         GRAB_HWI                        /* 1st acquire, grab hw INTs */
107         ret
108 #ifdef GLPROFILE
109 3:
110         incl    _gethits3
111         jmp     1b
112 #endif
113
114 /***********************************************************************
115  *  int MPtrylock(unsigned int *lock)
116  *  ---------------------------------
117  *  Destroys    %eax, %ecx and %edx.
118  *  Returns     1 if lock was successfull
119  */
120
121 NON_GPROF_ENTRY(MPtrylock)
122         movl    4(%esp), %edx           /* Get the address of the lock */
123
124         movl    $FREE_LOCK, %eax        /* Assume it's free */
125         movl    _cpu_lockid, %ecx       /* - get pre-shifted logical cpu id */
126         incl    %ecx                    /* - new count is one */
127         lock
128         cmpxchg %ecx, (%edx)            /* - try it atomically */
129         jne     1f                      /* ...do not collect $200 */
130 #ifdef GLPROFILE
131         incl    _tryhits2
132 #endif /* GLPROFILE */
133         GRAB_HWI                        /* 1st acquire, grab hw INTs */
134         movl    $1, %eax
135         ret
136 1:
137         movl    (%edx), %eax            /* Try to see if we have it already */
138         andl    $COUNT_FIELD, %eax      /* - get count */
139         movl    _cpu_lockid, %ecx       /* - get pre-shifted logical cpu id */
140         orl     %ecx, %eax              /* - combine them */
141         movl    %eax, %ecx
142         incl    %ecx                    /* - new count is one more */
143         lock
144         cmpxchg %ecx, (%edx)            /* - try it atomically */
145         jne     2f                      /* - miss */
146 #ifdef GLPROFILE
147         incl    _tryhits
148 #endif /* GLPROFILE */
149         movl    $1, %eax
150         ret
151 2:
152 #ifdef GLPROFILE
153         incl    _tryhits3
154 #endif /* GLPROFILE */
155         movl    $0, %eax
156         ret
157
158
159 /***********************************************************************
160  *  void MPrellock_edx(unsigned int *lock : %edx)
161  *  ----------------------------------
162  *  Destroys    %ecx, argument must be in %edx
163  *
164  *  SERIALIZATION NOTE!
165  *
166  *  After a lot of arguing, it turns out that there is no problem with
167  *  not having a synchronizing instruction in the MP unlock code.  There
168  *  are two things to keep in mind:  First, Intel guarentees that writes
169  *  are ordered amoungst themselves.  Second, the P6 is allowed to reorder
170  *  reads around writes.  Third, the P6 maintains cache consistency (snoops
171  *  the bus).  The second is not an issue since the one read we do is the 
172  *  basis for the conditional which determines whether the write will be 
173  *  made or not.
174  *
175  *  Therefore, no synchronizing instruction is required on unlock.  There are
176  *  three performance cases:  First, if a single cpu is getting and releasing
177  *  the lock the removal of the synchronizing instruction saves approx
178  *  200 nS (testing w/ duel cpu PIII 450).  Second, if one cpu is contending
179  *  for the lock while the other holds it, the removal of the synchronizing
180  *  instruction results in a 700nS LOSS in performance.  Third, if two cpu's
181  *  are switching off ownership of the MP lock but not contending for it (the
182  *  most common case), this results in a 400nS IMPROVEMENT in performance.
183  *
184  *  Since our goal is to reduce lock contention in the first place, we have
185  *  decided to remove the synchronizing instruction from the unlock code.
186  */
187
188 NON_GPROF_ENTRY(MPrellock_edx)
189         movl    (%edx), %ecx            /* - get the value */
190         decl    %ecx                    /* - new count is one less */
191         testl   $COUNT_FIELD, %ecx      /* - Unless it's zero... */
192         jnz     2f
193         ARB_HWI                         /* last release, arbitrate hw INTs */
194         movl    $FREE_LOCK, %ecx        /* - In which case we release it */
195 #if 0
196         lock
197         addl    $0,0(%esp)              /* see note above */
198 #endif
199 2:
200         movl    %ecx, (%edx)
201         ret
202
203 /***********************************************************************
204  *  void get_mplock()
205  *  -----------------
206  *  All registers preserved
207  *
208  *  Stack (after call to _MPgetlock):
209  *      
210  *      edx              4(%esp)
211  *      ecx              8(%esp)
212  *      eax             12(%esp)
213  *
214  * Requirements:  Interrupts should be enabled on call so we can take
215  *                IPI's and FAST INTs while we are waiting for the lock
216  *                (else the system may not be able to halt).
217  *
218  *                XXX there are still places where get_mplock() is called
219  *                with interrupts disabled, so we have to temporarily reenable
220  *                interrupts.
221  *
222  * Side effects:  The current cpu will be given ownership of the
223  *                hardware interrupts when it first aquires the lock.
224  *
225  * Costs:         Initial aquisition requires the use of a costly locked
226  *                instruction, but recursive aquisition is cheap.  Release
227  *                is very cheap.
228  */
229
230 NON_GPROF_ENTRY(get_mplock)
231         pushl   %eax
232         pushl   %ecx
233         pushl   %edx
234         movl    $_mp_lock, %edx
235         pushfl  
236         testl   $(1<<9), (%esp)
237         jz     2f           
238         call    _MPgetlock_edx
239         addl    $4,%esp
240 1:
241         popl    %edx
242         popl    %ecx
243         popl    %eax
244         ret
245 2:
246         sti
247         call    _MPgetlock_edx
248         popfl
249         jmp     1b
250
251 /*
252  * Special version of get_mplock that is used during bootstrap when we can't
253  * yet enable interrupts of any sort since the APIC isn't online yet.  We
254  * do an endrun around MPgetlock_edx to avoid enabling interrupts.
255  *
256  * XXX FIXME.. - APIC should be online from the start to simplify IPI's.
257  */
258 NON_GPROF_ENTRY(boot_get_mplock)
259         pushl   %eax
260         pushl   %ecx
261         pushl   %edx
262 #ifdef GRAB_LOPRIO      
263         pushfl
264         pushl   lapic_tpr
265         cli
266 #endif
267         
268         movl    $_mp_lock, %edx
269         call    _MPgetlock_edx
270
271 #ifdef GRAB_LOPRIO      
272         popl    lapic_tpr
273         popfl
274 #endif
275         popl    %edx
276         popl    %ecx
277         popl    %eax
278         ret
279
280 /***********************************************************************
281  *  void try_mplock()
282  *  -----------------
283  *  reg %eax == 1 if success
284  */
285
286 NON_GPROF_ENTRY(try_mplock)
287         pushl   %ecx
288         pushl   %edx
289         pushl   $_mp_lock
290         call    _MPtrylock
291         add     $4, %esp
292         popl    %edx
293         popl    %ecx
294         ret
295
296 /***********************************************************************
297  *  void rel_mplock()
298  *  -----------------
299  *  All registers preserved
300  */
301
302 NON_GPROF_ENTRY(rel_mplock)
303         pushl   %ecx
304         pushl   %edx
305         movl    $_mp_lock,%edx
306         call    _MPrellock_edx
307         popl    %edx
308         popl    %ecx
309         ret
310
311 #endif
312
313 /***********************************************************************
314  * 
315  */
316         .data
317         .p2align 2                      /* xx_lock aligned on int boundary */
318
319 #ifdef SMP
320
321         .globl _mp_lock
322 _mp_lock:       .long   0               
323
324 #ifdef GLPROFILE
325         .globl  _gethits
326 _gethits:
327         .long   0
328 _gethits2:
329         .long   0
330 _gethits3:
331         .long   0
332
333         .globl  _tryhits
334 _tryhits:
335         .long   0
336 _tryhits2:
337         .long   0
338 _tryhits3:
339         .long   0
340
341 msg:
342         .asciz  "lock hits: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n"
343 #endif /* GLPROFILE */
344 #endif /* SMP */