]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/commpage/pthreads.s
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / i386 / commpage / pthreads.s
CommitLineData
1c79356b 1/*
b0d623f7 2 * Copyright (c) 2003-2009 Apple, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b 27 */
1c79356b 28
55e303ae
A
29#include <sys/appleapiopts.h>
30#include <machine/cpu_capabilities.h>
31#include <machine/commpage.h>
b0d623f7 32#include <mach/i386/syscall_sw.h>
1c79356b 33
0c530ab8
A
34#define _PTHREAD_TSD_OFFSET32 0x48
35#define _PTHREAD_TSD_OFFSET64 0x60
4452a7af 36
0c530ab8
A
37
38/* These routines do not need to be on the copmmpage on Intel. They are for now
39 * to avoid revlock, but the code should move to Libc, and we should eventually remove
40 * these.
41 */
b0d623f7 42COMMPAGE_FUNCTION_START(pthread_getspecific, 32, 4)
55e303ae 43 movl 4(%esp), %eax
0c530ab8 44 movl %gs:_PTHREAD_TSD_OFFSET32(,%eax,4), %eax
55e303ae 45 ret
b0d623f7 46COMMPAGE_DESCRIPTOR(pthread_getspecific,_COMM_PAGE_PTHREAD_GETSPECIFIC,0,0)
55e303ae 47
b0d623f7 48COMMPAGE_FUNCTION_START(pthread_self, 32, 4)
0c530ab8 49 movl %gs:_PTHREAD_TSD_OFFSET32, %eax
55e303ae 50 ret
b0d623f7 51COMMPAGE_DESCRIPTOR(pthread_self,_COMM_PAGE_PTHREAD_SELF,0,0)
0c530ab8
A
52
53/* the 64-bit versions: */
b0d623f7 54COMMPAGE_FUNCTION_START(pthread_getspecific_64, 64, 4)
0c530ab8
A
55 movq %gs:_PTHREAD_TSD_OFFSET64(,%rdi,8), %rax
56 ret
b0d623f7 57COMMPAGE_DESCRIPTOR(pthread_getspecific_64,_COMM_PAGE_PTHREAD_GETSPECIFIC,0,0)
0c530ab8 58
b0d623f7 59COMMPAGE_FUNCTION_START(pthread_self_64, 64, 4)
0c530ab8
A
60 movq %gs:_PTHREAD_TSD_OFFSET64, %rax
61 ret
b0d623f7
A
62COMMPAGE_DESCRIPTOR(pthread_self_64,_COMM_PAGE_PTHREAD_SELF,0,0)
63
64
65/* Temporary definitions. Replace by #including the correct file when available. */
66
67#define PTHRW_EBIT 0x01
68#define PTHRW_LBIT 0x02
69#define PTHRW_YBIT 0x04
70#define PTHRW_WBIT 0x08
71#define PTHRW_UBIT 0x10
72#define PTHRW_RETRYBIT 0x20
73#define PTHRW_TRYLKBIT 0x40
74
75#define PTHRW_INC 0x100
76#define PTHRW_BIT_MASK 0x000000ff;
77
78#define PTHRW_COUNT_SHIFT 8
79#define PTHRW_COUNT_MASK 0xffffff00
80#define PTHRW_MAX_READERS 0xffffff00
81
82#define KSYN_MLWAIT 301 /* mutex lock wait syscall */
83
84#define PTHRW_STATUS_ACQUIRED 0
85#define PTHRW_STATUS_SYSCALL 1
86#define PTHRW_STATUS_ERROR 2
87
88#define PTHRW_LVAL 0
89#define PTHRW_UVAL 4
90
91
92
93/* PREEMPTION FREE ZONE (PFZ)
94 *
95 * A portion of the commpage is speacial-cased by the kernel to be "preemption free",
96 * ie as if we had disabled interrupts in user mode. This facilitates writing
97 * "nearly-lockless" code, for example code that must be serialized by a spinlock but
98 * which we do not want to preempt while the spinlock is held.
99 *
100 * The PFZ is implemented by collecting all the "preemption-free" code into a single
101 * contiguous region of the commpage. Register %ebx is used as a flag register;
102 * before entering the PFZ, %ebx is cleared. If some event occurs that would normally
103 * result in a premption while in the PFZ, the kernel sets %ebx nonzero instead of
104 * preempting. Then, when the routine leaves the PFZ we check %ebx and
105 * if nonzero execute a special "pfz_exit" syscall to take the delayed preemption.
106 *
107 * PFZ code must bound the amount of time spent in the PFZ, in order to control
108 * latency. Backward branches are dangerous and must not be used in a way that
109 * could inadvertently create a long-running loop.
110 *
111 * Because we need to avoid being preempted between changing the mutex stateword
112 * and entering the kernel to relinquish, some low-level pthread mutex manipulations
113 * are located in the PFZ.
114 */
115
116
117/* int // we return 0 on acquire, 1 on syscall
118 * pthread_mutex_lock( uint32_t *lvalp, // ptr to mutex LVAL/UVAL pair
119 * int flags, // flags to pass kernel if we do syscall
120 * uint64_t mtid, // my Thread ID
121 * uint32_t mask, // bits to test in LVAL (ie, EBIT etc)
122 * uint64_t *tidp, // ptr to TID field of mutex
123 * int *syscall_return ); // if syscall, return value stored here
124 */
125COMMPAGE_FUNCTION_START(pthread_mutex_lock, 32, 4)
126 pushl %ebp // set up frame for backtrace
127 movl %esp,%ebp
128 pushl %esi
129 pushl %edi
130 pushl %ebx
131 xorl %ebx,%ebx // clear "preemption pending" flag
132 movl 20(%esp),%edi // %edi == ptr to LVAL/UVAL structure
133 lea 20(%esp),%esi // %esi == ptr to argument list
134 movl _COMM_PAGE_SPIN_COUNT, %edx
135 movl 16(%esi),%ecx // get mask (ie, PTHRW_EBIT etc)
1361:
137 testl PTHRW_LVAL(%edi),%ecx // is mutex available?
138 jz 2f // yes, it is available
139 pause
140 decl %edx // decrement max spin count
141 jnz 1b // keep spinning
1422:
143 COMMPAGE_CALL(_COMM_PAGE_PFZ_MUTEX_LOCK,_COMM_PAGE_MUTEX_LOCK,pthread_mutex_lock)
144 testl %ebx,%ebx // pending preemption?
145 jz 3f
146 pushl %eax // save return value across sysenter
147 COMMPAGE_CALL(_COMM_PAGE_PREEMPT,_COMM_PAGE_MUTEX_LOCK,pthread_mutex_lock)
148 popl %eax
1493:
150 popl %ebx
151 popl %edi
152 popl %esi
153 popl %ebp
154 ret
155COMMPAGE_DESCRIPTOR(pthread_mutex_lock,_COMM_PAGE_MUTEX_LOCK,0,0)
156
157
158/* Internal routine to handle pthread mutex lock operation. This is in the PFZ.
159 * %edi == ptr to LVAL/UVAL pair
160 * %esi == ptr to argument list on stack
161 * %ebx == preempion pending flag (kernel sets nonzero if we should preempt)
162 */
163COMMPAGE_FUNCTION_START(pfz_mutex_lock, 32, 4)
164 pushl %ebp // set up frame for backtrace
165 movl %esp,%ebp
1661:
167 movl 16(%esi),%ecx // get mask (ie, PTHRW_EBIT etc)
1682:
169 movl PTHRW_LVAL(%edi),%eax // get mutex LVAL
170 testl %eax,%ecx // is mutex available?
171 jnz 5f // no
172
173 /* lock is available (if we act fast) */
174 lea PTHRW_INC(%eax),%edx // copy original lval and bump sequence count
175 orl $PTHRW_EBIT, %edx // set EBIT
176 lock
177 cmpxchgl %edx,PTHRW_LVAL(%edi) // try to acquire lock for real
178 jz 4f // got it
1793:
180 testl %ebx,%ebx // kernel trying to preempt us?
181 jz 2b // no, so loop and try again
182 COMMPAGE_CALL(_COMM_PAGE_PREEMPT,_COMM_PAGE_PFZ_MUTEX_LOCK,pfz_mutex_lock)
183 jmp 1b // loop to try again
184
185 /* we acquired the mutex */
1864:
187 movl 20(%esi),%eax // get ptr to TID field of mutex
188 movl 8(%esi),%ecx // get 64-bit mtid
189 movl 12(%esi),%edx
190 movl %ecx,0(%eax) // store my TID in mutex structure
191 movl %edx,4(%eax)
192 movl $PTHRW_STATUS_ACQUIRED,%eax
193 popl %ebp
194 ret
195
196 /* cannot acquire mutex, so update seq count, set "W", and block in kernel */
197 /* this is where we cannot tolerate preemption or being killed */
1985:
199 lea PTHRW_INC(%eax),%edx // copy original lval and bump sequence count
200 orl $PTHRW_WBIT, %edx // set WBIT
201 lock
202 cmpxchgl %edx,PTHRW_LVAL(%edi) // try to update lock status atomically
203 jnz 3b // failed
204 movl 20(%esi),%eax // get ptr to TID field of mutex
205 pushl 4(%esi) // arg 5: flags from arg list
206 pushl 4(%eax) // arg 4: tid field from mutex
207 pushl 0(%eax)
208 pushl PTHRW_UVAL(%edi) // arg 3: uval field from mutex
209 pushl %edx // arg 2: new value of mutex lval field
210 pushl %edi // arg 1: ptr to LVAL/UVAL pair in mutex
211 call 6f // make ksyn_mlwait call
212 jc 6f // immediately reissue syscall if error
213 movl 24(%esi),%edx // get ptr to syscall_return arg
214 movl %eax,(%edx) // save syscall return value
215 movl $PTHRW_STATUS_SYSCALL,%eax // we had to make syscall
216 addl $28,%esp // pop off syscall args and return address
217 popl %ebp // pop off frame ptr
218 ret
219
220 /* subroutine to make a ksyn_mlwait syscall */
2216:
222 movl (%esp),%edx // get return address but leave on stack
223 movl %esp,%ecx // save stack ptr here
224 movl $KSYN_MLWAIT,%eax // get syscall code
225 orl $0x00180000,%eax // copy 24 bytes of arguments in trampoline
226 xorl %ebx,%ebx // clear preemption flag
227 sysenter
228COMMPAGE_DESCRIPTOR(pfz_mutex_lock,_COMM_PAGE_PFZ_MUTEX_LOCK,0,0)
229
230
231
232/************************* x86_64 versions follow **************************/
233
234
235
236/* int // we return 0 on acquire, 1 on syscall
237 * pthread_mutex_lock( uint32_t *lvalp, // ptr to mutex LVAL/UVAL pair
238 * int flags, // flags to pass kernel if we do syscall
239 * uint64_t mtid, // my Thread ID
240 * uint32_t mask, // bits to test in LVAL (ie, EBIT etc)
241 * uint64_t *tidp, // ptr to TID field of mutex
242 * int *syscall_return ); // if syscall, return value stored here
243 *
244 * %rdi = lvalp
245 * %esi = flags
246 * %rdx = mtid
247 * %ecx = mask
248 * %r8 = tidp
249 * %r9 = &syscall_return
250 */
251COMMPAGE_FUNCTION_START(pthread_mutex_lock_64, 64, 4)
252 pushq %rbp // set up frame for backtrace
253 movq %rsp,%rbp
254 pushq %rbx
255 xorl %ebx,%ebx // clear "preemption pending" flag
256 movl _COMM_PAGE_32_TO_64(_COMM_PAGE_SPIN_COUNT), %eax
2571:
258 testl PTHRW_LVAL(%rdi),%ecx // is mutex available?
259 jz 2f // yes, it is available
260 pause
261 decl %eax // decrement max spin count
262 jnz 1b // keep spinning
2632:
264 COMMPAGE_CALL(_COMM_PAGE_PFZ_MUTEX_LOCK,_COMM_PAGE_MUTEX_LOCK,pthread_mutex_lock_64)
265 testl %ebx,%ebx // pending preemption?
266 jz 1f // no
267 COMMPAGE_CALL(_COMM_PAGE_PREEMPT,_COMM_PAGE_MUTEX_LOCK,pthread_mutex_lock_64)
2681:
269 popq %rbx
270 popq %rbp
271 ret
272COMMPAGE_DESCRIPTOR(pthread_mutex_lock_64,_COMM_PAGE_MUTEX_LOCK,0,0)
273
274
275/* Internal routine to handle pthread mutex lock operation. This is in the PFZ.
276 * %rdi = lvalp
277 * %esi = flags
278 * %rdx = mtid
279 * %ecx = mask
280 * %r8 = tidp
281 * %r9 = &syscall_return
282 * %ebx = preempion pending flag (kernel sets nonzero if we should preempt)
283 */
284COMMPAGE_FUNCTION_START(pfz_mutex_lock_64, 64, 4)
285 pushq %rbp // set up frame for backtrace
286 movq %rsp,%rbp
2871:
288 movl PTHRW_LVAL(%rdi),%eax // get old lval from mutex
2892:
290 testl %eax,%ecx // can we acquire the lock?
291 jnz 5f // no
292
293 /* lock is available (if we act fast) */
294 lea PTHRW_INC(%rax),%r11 // copy original lval and bump sequence count
295 orl $PTHRW_EBIT, %r11d // set EBIT
296 lock
297 cmpxchgl %r11d,PTHRW_LVAL(%rdi) // try to acquire lock
298 jz 4f // got it
2993:
300 testl %ebx,%ebx // kernel trying to preempt us?
301 jz 2b // no, so loop and try again
302 COMMPAGE_CALL(_COMM_PAGE_PREEMPT,_COMM_PAGE_PFZ_MUTEX_LOCK,pfz_mutex_lock_64)
303 jmp 1b // loop to try again
304
305 /* we acquired the mutex */
3064:
307 movq %rdx,(%r8) // store mtid in mutex structure
308 movl $PTHRW_STATUS_ACQUIRED,%eax
309 popq %rbp
310 ret
311
312 /* cannot acquire mutex, so update seq count and block in kernel */
313 /* this is where we cannot tolerate preemption or being killed */
3145:
315 lea PTHRW_INC(%rax),%r11 // copy original lval and bump sequence count
316 orl $PTHRW_WBIT, %r11d // set WBIT
317 lock
318 cmpxchgl %r11d,PTHRW_LVAL(%rdi) // try to update lock status atomically
319 jnz 3b // failed
320 movq (%r8),%r10 // arg 4: tid field from mutex [NB: passed in R10]
321 movl %esi,%r8d // arg 5: flags from arg list
322 movl PTHRW_UVAL(%rdi),%edx // arg 3: uval field from mutex
323 movl %r11d,%esi // arg 2: new value of mutex lval field
324 // arg 1: LVAL/UVAL ptr already in %rdi
3256:
326 movl $(SYSCALL_CONSTRUCT_UNIX(KSYN_MLWAIT)),%eax
327 pushq %rdx // some syscalls destroy %rdx so save it
328 xorl %ebx,%ebx // clear preemption flag
329 syscall
330 popq %rdx // restore in case we need to re-execute syscall
331 jc 6b // immediately re-execute syscall if error
332 movl %eax,(%r9) // store kernel return value
333 movl $PTHRW_STATUS_SYSCALL,%eax // we made syscall
334 popq %rbp
335 ret
336COMMPAGE_DESCRIPTOR(pfz_mutex_lock_64,_COMM_PAGE_PFZ_MUTEX_LOCK,0,0)
0c530ab8 337