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.2 2003/06/17 04:28:35 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>
32 /* we assumme that the 'reserved bits' can be written with zeros */
36 #error HEADS UP: this code needs work
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
47 #endif /* CHEAP_TPR */
51 * Claim LOWest PRIOrity, ie. attempt to grab ALL INTerrupts.
54 /* after 1st acquire of lock we grab all hardware INTs */
55 #define GRAB_HWI movl $ALLHWI_LEVEL, lapic_tpr
57 /* after last release of lock give up LOW PRIO (ie, arbitrate INTerrupts) */
58 #define ARB_HWI movl $LOPRIO_LEVEL, lapic_tpr /* CHEAP_TPR */
60 #else /* GRAB_LOPRIO */
62 #define GRAB_HWI /* nop */
63 #define ARB_HWI /* nop */
65 #endif /* GRAB_LOPRIO */
72 /***********************************************************************
73 * void MPgetlock_edx(unsigned int *lock : %edx)
74 * ----------------------------------
75 * Destroys %eax, %ecx. %edx must hold lock argument.
77 * Grabs hardware interrupts on first aquire.
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.
84 NON_GPROF_ENTRY(MPgetlock_edx)
86 movl (%edx), %eax /* Get current contents of lock */
89 cmpl _cpu_lockid, %ecx /* Do we already own the lock? */
91 incl %eax /* yes, just bump the count */
92 movl %eax, (%edx) /* serialization not required */
95 movl $FREE_LOCK, %eax /* lock must be free */
96 movl _cpu_lockid, %ecx
99 cmpxchg %ecx, (%edx) /* attempt to replace %eax<->%ecx */
105 #endif /* GLPROFILE */
106 GRAB_HWI /* 1st acquire, grab hw INTs */
114 /***********************************************************************
115 * int MPtrylock(unsigned int *lock)
116 * ---------------------------------
117 * Destroys %eax, %ecx and %edx.
118 * Returns 1 if lock was successfull
121 NON_GPROF_ENTRY(MPtrylock)
122 movl 4(%esp), %edx /* Get the address of the lock */
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 */
128 cmpxchg %ecx, (%edx) /* - try it atomically */
129 jne 1f /* ...do not collect $200 */
132 #endif /* GLPROFILE */
133 GRAB_HWI /* 1st acquire, grab hw INTs */
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 */
142 incl %ecx /* - new count is one more */
144 cmpxchg %ecx, (%edx) /* - try it atomically */
148 #endif /* GLPROFILE */
154 #endif /* GLPROFILE */
159 /***********************************************************************
160 * void MPrellock_edx(unsigned int *lock : %edx)
161 * ----------------------------------
162 * Destroys %ecx, argument must be in %edx
164 * SERIALIZATION NOTE!
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
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.
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.
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... */
193 ARB_HWI /* last release, arbitrate hw INTs */
194 movl $FREE_LOCK, %ecx /* - In which case we release it */
197 addl $0,0(%esp) /* see note above */
203 /***********************************************************************
206 * All registers preserved
208 * Stack (after call to _MPgetlock):
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).
218 * XXX there are still places where get_mplock() is called
219 * with interrupts disabled, so we have to temporarily reenable
222 * Side effects: The current cpu will be given ownership of the
223 * hardware interrupts when it first aquires the lock.
225 * Costs: Initial aquisition requires the use of a costly locked
226 * instruction, but recursive aquisition is cheap. Release
230 NON_GPROF_ENTRY(get_mplock)
236 testl $(1<<9), (%esp)
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.
256 * XXX FIXME.. - APIC should be online from the start to simplify IPI's.
258 NON_GPROF_ENTRY(boot_get_mplock)
280 /***********************************************************************
283 * reg %eax == 1 if success
286 NON_GPROF_ENTRY(try_mplock)
296 /***********************************************************************
299 * All registers preserved
302 NON_GPROF_ENTRY(rel_mplock)
313 /***********************************************************************
317 .p2align 2 /* xx_lock aligned on int boundary */
342 .asciz "lock hits: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n"
343 #endif /* GLPROFILE */