Further isolate the user process scheduler data by moving more variables
[dragonfly.git] / sys / sys / spinlock2.h
1 /*
2  * Copyright (c) 2005 Jeffrey M. Hsu.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Jeffrey M. Hsu.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of The DragonFly Project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific, prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $DragonFly: src/sys/sys/spinlock2.h,v 1.9 2006/05/21 20:23:27 dillon Exp $
33  */
34
35 #ifndef _SYS_SPINLOCK2_H_
36 #define _SYS_SPINLOCK2_H_
37
38 #ifndef _KERNEL
39
40 #error "This file should not be included by userland programs."
41
42 #else
43
44 #ifndef _SYS_THREAD2_H_
45 #include <sys/thread2.h>
46 #endif
47 #ifndef _SYS_GLOBALDATA_H_
48 #include <sys/globaldata.h>
49 #endif
50 #ifndef _MACHINE_ATOMIC_H_
51 #include <machine/atomic.h>
52 #endif
53 #ifndef _MACHINE_CPUFUNC_H_
54 #include <machine/cpufunc.h>
55 #endif
56
57 /*
58  * SPECIAL NOTE!  Obtaining a spinlock does not enter a critical section
59  * or protect against FAST interrupts but it will prevent thread preemption.
60  * Because the spinlock code path is ultra critical, we do not check for
61  * LWKT reschedule requests (due to an interrupt thread not being able to
62  * preempt).
63  */
64
65 #ifdef SMP
66
67 extern int spin_trylock_wr_contested(struct spinlock *mtx, int value);
68 extern void spin_lock_wr_contested(struct spinlock *mtx, int value);
69 extern void spin_lock_rd_contested(struct spinlock *mtx);
70
71 #endif
72
73 #ifdef SMP
74
75 /*
76  * Attempt to obtain an exclusive spinlock.  Returns FALSE on failure,
77  * TRUE on success.  Since the caller assumes that spinlocks must actually
78  * work when using this function, it is only made available to SMP builds.
79  */
80 static __inline boolean_t
81 spin_trylock_wr(struct spinlock *mtx)
82 {
83         globaldata_t gd = mycpu;
84         int value;
85
86         ++gd->gd_spinlocks_wr;
87         if ((value = atomic_swap_int(&mtx->lock, SPINLOCK_EXCLUSIVE)) != 0)
88                 return (spin_trylock_wr_contested(mtx, value));
89         return (TRUE);
90 }
91
92 #endif
93
94 /*
95  * Obtain an exclusive spinlock and return.
96  */
97 static __inline void
98 spin_lock_wr_quick(globaldata_t gd, struct spinlock *mtx)
99 {
100 #ifdef SMP
101         int value;
102 #endif
103
104         ++gd->gd_spinlocks_wr;
105 #ifdef SMP
106         if ((value = atomic_swap_int(&mtx->lock, SPINLOCK_EXCLUSIVE)) != 0)
107                 spin_lock_wr_contested(mtx, value);
108 #endif
109 }
110
111 static __inline void
112 spin_lock_wr(struct spinlock *mtx)
113 {
114         spin_lock_wr_quick(mycpu, mtx);
115 }
116
117 #if 0
118
119 /*
120  * Upgrade a shared spinlock to exclusive.  Return TRUE if we were
121  * able to upgrade without another exclusive holder getting in before
122  * us, FALSE otherwise.
123  */
124 static __inline int
125 spin_lock_upgrade(struct spinlock *mtx)
126 {
127         globaldata_t gd = mycpu;
128 #ifdef SMP
129         int value;
130 #endif
131
132         ++gd->gd_spinlocks_wr;
133 #ifdef SMP
134         value = atomic_swap_int(&mtx->lock, SPINLOCK_EXCLUSIVE);
135         cpu_sfence();
136 #endif
137         --gd->gd_spinlocks_rd;
138 #ifdef SMP
139         value &= ~gd->gd_cpumask;
140         if (value) {
141                 spin_lock_wr_contested(mtx, value);
142                 if (value & SPINLOCK_EXCLUSIVE)
143                         return (FALSE);
144                 XXX regain original shared lock?
145         }
146         return (TRUE);
147 #endif
148 }
149
150 #endif
151
152 /*
153  * Obtain a shared spinlock and return.  This is a critical code path.
154  *
155  * The vast majority of the overhead is in the cpu_mfence() (5ns vs 1ns for
156  * the entire rest of the procedure).  Unfortunately we have to ensure that
157  * spinlock count is written out before we check the cpumask to interlock
158  * against an exclusive spinlock that clears the cpumask and then checks
159  * the spinlock count.
160  *
161  * But what is EXTREMELY important here is that we do not have to perform
162  * a locked bus cycle on the spinlock itself if the shared bit for our cpu
163  * is already found to be set.  We only need the mfence, and the mfence is
164  * local to the cpu and never conflicts with other cpu's.
165  *
166  * This means that multiple parallel shared acessors (e.g. filedescriptor
167  * table lookups, namecache lookups) run at full speed and incur NO cache
168  * contention at all.  Its the difference between 10ns and 40-100ns.
169  */
170 static __inline void
171 spin_lock_rd_quick(globaldata_t gd, struct spinlock *mtx)
172 {
173         ++gd->gd_spinlocks_rd;
174 #ifdef SMP
175         cpu_mfence();
176         if ((mtx->lock & gd->gd_cpumask) == 0)
177                 spin_lock_rd_contested(mtx);
178 #endif
179 }
180
181 static __inline void
182 spin_lock_rd(struct spinlock *mtx)
183 {
184         spin_lock_rd_quick(mycpu,mtx);
185 }
186
187 /*
188  * Release an exclusive spinlock.  We can just do this passively, only
189  * ensuring that our spinlock count is left intact until the mutex is
190  * cleared.
191  */
192 static __inline void
193 spin_unlock_wr_quick(globaldata_t gd, struct spinlock *mtx)
194 {
195 #ifdef SMP
196         mtx->lock = 0;
197 #endif
198         --gd->gd_spinlocks_wr;
199 }
200
201 static __inline void
202 spin_unlock_wr(struct spinlock *mtx)
203 {
204         spin_unlock_wr_quick(mycpu, mtx);
205 }
206
207 /*
208  * Release a shared spinlock.  We leave the shared bit set in the spinlock
209  * as a cache and simply decrement the spinlock count for the cpu.  This
210  * fast-paths another shared lock later at the cost of an exclusive lock
211  * having to check per-cpu spinlock counts to determine when there are no
212  * shared holders remaining.
213  */
214 static __inline void
215 spin_unlock_rd_quick(globaldata_t gd, struct spinlock *mtx)
216 {
217         --gd->gd_spinlocks_rd;
218 }
219
220 static __inline void
221 spin_unlock_rd(struct spinlock *mtx)
222 {
223         spin_unlock_rd_quick(mycpu, mtx);
224 }
225
226 static __inline void
227 spin_init(struct spinlock *mtx)
228 {
229         mtx->lock = 0;
230 }
231
232 static __inline void
233 spin_uninit(struct spinlock *mtx)
234 {
235         /* unused */
236 }
237
238 #endif  /* _KERNEL */
239 #endif  /* _SYS_SPINLOCK2_H_ */
240