Remove CAPS.
[dragonfly.git] / sys / kern / lwkt_serialize.c
1 /*
2  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
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  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * This API provides a fast locked-bus-cycle-based serializer.  It's
36  * basically a low level NON-RECURSIVE exclusive lock that can be held across
37  * a blocking condition.  It is NOT a mutex.
38  *
39  * This serializer is primarily designed for low level situations and
40  * interrupt/device interaction.  There are two primary facilities.  First,
41  * the serializer facility itself.  Second, an integrated interrupt handler 
42  * disablement facility.
43  */
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/proc.h>
49 #include <sys/rtprio.h>
50 #include <sys/queue.h>
51 #include <sys/thread2.h>
52 #include <sys/serialize.h>
53 #include <sys/sysctl.h>
54 #include <sys/ktr.h>
55 #include <sys/kthread.h>
56 #include <machine/cpu.h>
57 #include <machine/cpufunc.h>
58 #include <machine/specialreg.h>
59 #include <sys/lock.h>
60
61 struct exp_backoff {
62         int backoff;
63         int round;
64         lwkt_serialize_t s;
65 };
66
67 #define SLZ_KTR_STRING          "slz=%p"
68 #define SLZ_KTR_ARGS            lwkt_serialize_t slz
69
70 #ifndef KTR_SERIALIZER
71 #define KTR_SERIALIZER  KTR_ALL
72 #endif
73
74 KTR_INFO_MASTER(slz);
75 KTR_INFO(KTR_SERIALIZER, slz, enter_beg, 0, SLZ_KTR_STRING, SLZ_KTR_ARGS);
76 KTR_INFO(KTR_SERIALIZER, slz, sleep_beg, 1, SLZ_KTR_STRING, SLZ_KTR_ARGS);
77 KTR_INFO(KTR_SERIALIZER, slz, sleep_end, 2, SLZ_KTR_STRING, SLZ_KTR_ARGS);
78 KTR_INFO(KTR_SERIALIZER, slz, exit_end, 3, SLZ_KTR_STRING, SLZ_KTR_ARGS);
79 KTR_INFO(KTR_SERIALIZER, slz, wakeup_beg, 4, SLZ_KTR_STRING, SLZ_KTR_ARGS);
80 KTR_INFO(KTR_SERIALIZER, slz, wakeup_end, 5, SLZ_KTR_STRING, SLZ_KTR_ARGS);
81 KTR_INFO(KTR_SERIALIZER, slz, try, 6, SLZ_KTR_STRING, SLZ_KTR_ARGS);
82 KTR_INFO(KTR_SERIALIZER, slz, tryfail, 7, SLZ_KTR_STRING, SLZ_KTR_ARGS);
83 KTR_INFO(KTR_SERIALIZER, slz, tryok, 8, SLZ_KTR_STRING, SLZ_KTR_ARGS);
84 #ifdef SMP
85 KTR_INFO(KTR_SERIALIZER, slz, spinbo, 9,
86          "slz=%p bo1=%d bo=%d", lwkt_serialize_t slz, int backoff1, int backoff);
87 #endif
88 KTR_INFO(KTR_SERIALIZER, slz, enter_end, 10, SLZ_KTR_STRING, SLZ_KTR_ARGS);
89 KTR_INFO(KTR_SERIALIZER, slz, exit_beg, 11, SLZ_KTR_STRING, SLZ_KTR_ARGS);
90
91 #define logslz(name, slz)               KTR_LOG(slz_ ## name, slz)
92 #ifdef SMP
93 #define logslz_spinbo(slz, bo1, bo)     KTR_LOG(slz_spinbo, slz, bo1, bo)
94 #endif
95
96 static void lwkt_serialize_sleep(void *info);
97 static void lwkt_serialize_wakeup(void *info);
98
99 #ifdef SMP
100 static void lwkt_serialize_adaptive_sleep(void *bo);
101
102 static int slz_backoff_limit = 128;
103 SYSCTL_INT(_debug, OID_AUTO, serialize_bolimit, CTLFLAG_RW,
104     &slz_backoff_limit, 0, "Backoff limit");
105
106 static int slz_backoff_shift = 1;
107 SYSCTL_INT(_debug, OID_AUTO, serialize_boshift, CTLFLAG_RW,
108     &slz_backoff_shift, 0, "Backoff shift");
109
110 static int slz_backoff_round;
111 TUNABLE_INT("debug.serialize_boround", &slz_backoff_round);
112 SYSCTL_INT(_debug, OID_AUTO, serialize_boround, CTLFLAG_RW,
113     &slz_backoff_round, 0,
114     "Backoff rounding");
115 #endif  /* SMP */
116
117 void
118 lwkt_serialize_init(lwkt_serialize_t s)
119 {
120     atomic_intr_init(&s->interlock);
121 #ifdef INVARIANTS
122     s->last_td = (void *)-4;
123 #endif
124 }
125
126 #ifdef SMP
127 void
128 lwkt_serialize_adaptive_enter(lwkt_serialize_t s)
129 {
130     struct exp_backoff bo;
131
132     bo.backoff = 1;
133     bo.round = 0;
134     bo.s = s;
135
136     ASSERT_NOT_SERIALIZED(s);
137
138     logslz(enter_beg, s);
139     atomic_intr_cond_enter(&s->interlock, lwkt_serialize_adaptive_sleep, &bo);
140     logslz(enter_end, s);
141 #ifdef INVARIANTS
142     s->last_td = curthread;
143 #endif
144 }
145 #endif  /* SMP */
146
147 void
148 lwkt_serialize_enter(lwkt_serialize_t s)
149 {
150     ASSERT_NOT_SERIALIZED(s);
151
152     logslz(enter_beg, s);
153     atomic_intr_cond_enter(&s->interlock, lwkt_serialize_sleep, s);
154     logslz(enter_end, s);
155 #ifdef INVARIANTS
156     s->last_td = curthread;
157 #endif
158 }
159
160 /*
161  * Returns non-zero on success
162  */
163 int
164 lwkt_serialize_try(lwkt_serialize_t s)
165 {
166     int error;
167
168     ASSERT_NOT_SERIALIZED(s);
169
170     logslz(try, s);
171     if ((error = atomic_intr_cond_try(&s->interlock)) == 0) {
172 #ifdef INVARIANTS
173         s->last_td = curthread;
174 #endif
175         logslz(tryok, s);
176         return(1);
177     }
178     logslz(tryfail, s);
179     return (0);
180 }
181
182 void
183 lwkt_serialize_exit(lwkt_serialize_t s)
184 {
185     ASSERT_SERIALIZED(s);
186 #ifdef INVARIANTS
187     s->last_td = (void *)-2;
188 #endif
189     logslz(exit_beg, s);
190     atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
191     logslz(exit_end, s);
192 }
193
194 /*
195  * Interrupt handler disablement support, used by drivers.  Non-stackable
196  * (uses bit 30).
197  */
198 void
199 lwkt_serialize_handler_disable(lwkt_serialize_t s)
200 {
201     atomic_intr_handler_disable(&s->interlock);
202 }
203
204 void
205 lwkt_serialize_handler_enable(lwkt_serialize_t s)
206 {
207     atomic_intr_handler_enable(&s->interlock);
208 }
209
210 void
211 lwkt_serialize_handler_call(lwkt_serialize_t s, void (*func)(void *, void *), 
212                             void *arg, void *frame)
213 {
214     /*
215      * note: a return value of 0 indicates that the interrupt handler is 
216      * enabled.
217      */
218     if (atomic_intr_handler_is_enabled(&s->interlock) == 0) {
219         logslz(enter_beg, s);
220         atomic_intr_cond_enter(&s->interlock, lwkt_serialize_sleep, s);
221         logslz(enter_end, s);
222 #ifdef INVARIANTS
223         s->last_td = curthread;
224 #endif
225         if (atomic_intr_handler_is_enabled(&s->interlock) == 0)
226             func(arg, frame);
227
228         ASSERT_SERIALIZED(s);
229 #ifdef INVARIANTS
230         s->last_td = (void *)-2;
231 #endif
232         logslz(exit_beg, s);
233         atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
234         logslz(exit_end, s);
235     }
236 }
237
238 /*
239  * Similar to handler_call but does not block.  Returns 0 on success, 
240  * and 1 on failure.
241  */
242 int
243 lwkt_serialize_handler_try(lwkt_serialize_t s, void (*func)(void *, void *),
244                            void *arg, void *frame)
245 {
246     /*
247      * note: a return value of 0 indicates that the interrupt handler is 
248      * enabled.
249      */
250     if (atomic_intr_handler_is_enabled(&s->interlock) == 0) {
251         logslz(try, s);
252         if (atomic_intr_cond_try(&s->interlock) == 0) {
253 #ifdef INVARIANTS
254             s->last_td = curthread;
255 #endif
256             logslz(tryok, s);
257
258             func(arg, frame);
259
260             ASSERT_SERIALIZED(s);
261 #ifdef INVARIANTS
262             s->last_td = (void *)-2;
263 #endif
264             logslz(exit_beg, s);
265             atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
266             logslz(exit_end, s);
267             return(0);
268         }
269     }
270     logslz(tryfail, s);
271     return(1);
272 }
273
274
275 /*
276  * Helper functions
277  *
278  * It is possible to race an interrupt which acquires and releases the
279  * bit, then calls wakeup before we actually go to sleep, so we
280  * need to check that the interlock is still acquired from within
281  * a critical section prior to sleeping.
282  */
283 static void
284 lwkt_serialize_sleep(void *info)
285 {
286     lwkt_serialize_t s = info;
287
288     tsleep_interlock(s, 0);
289     if (atomic_intr_cond_test(&s->interlock) != 0) {
290         logslz(sleep_beg, s);
291         tsleep(s, PINTERLOCKED, "slize", 0);
292         logslz(sleep_end, s);
293     }
294 }
295
296 #ifdef SMP
297
298 static void
299 lwkt_serialize_adaptive_sleep(void *arg)
300 {
301     struct exp_backoff *bo = arg;
302     lwkt_serialize_t s = bo->s;
303     int backoff;
304
305     /*
306      * Randomize backoff value
307      */
308 #ifdef _RDTSC_SUPPORTED_
309     if (cpu_feature & CPUID_TSC) {
310         backoff =
311         (((u_long)rdtsc() ^ (((u_long)curthread) >> 5)) &
312          (bo->backoff - 1)) + 1;
313     } else
314 #endif
315         backoff = bo->backoff;
316
317     logslz_spinbo(s, bo->backoff, backoff);
318
319     /*
320      * Quick backoff
321      */
322     for (; backoff; --backoff)
323         cpu_pause();
324     if (bo->backoff < slz_backoff_limit) {
325         bo->backoff <<= slz_backoff_shift;
326         return;
327     } else {
328         bo->backoff = 1;
329         bo->round++;
330         if (bo->round >= slz_backoff_round)
331             bo->round = 0;
332         else
333             return;
334     }
335
336     tsleep_interlock(s, 0);
337     if (atomic_intr_cond_test(&s->interlock) != 0) {
338         logslz(sleep_beg, s);
339         tsleep(s, PINTERLOCKED, "slize", 0);
340         logslz(sleep_end, s);
341     }
342 }
343
344 #endif  /* SMP */
345
346 static void
347 lwkt_serialize_wakeup(void *info)
348 {
349     logslz(wakeup_beg, info);
350     wakeup(info);
351     logslz(wakeup_end, info);
352 }
353
354 #ifdef SMP
355 static void
356 lwkt_serialize_sysinit(void *dummy __unused)
357 {
358         if (slz_backoff_round <= 0)
359                 slz_backoff_round = ncpus * 2;
360 }
361 SYSINIT(lwkt_serialize, SI_SUB_PRE_DRIVERS, SI_ORDER_SECOND,
362         lwkt_serialize_sysinit, NULL);
363 #endif