- Defer deep copyout which happens in bridge_ioctl_{gifs,rts}(), so that
[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  * $DragonFly: src/sys/kern/lwkt_serialize.c,v 1.18 2008/10/04 14:22:44 swildner Exp $
35  */
36 /*
37  * This API provides a fast locked-bus-cycle-based serializer.  It's
38  * basically a low level NON-RECURSIVE exclusive lock that can be held across
39  * a blocking condition.  It is NOT a mutex.
40  *
41  * This serializer is primarily designed for low level situations and
42  * interrupt/device interaction.  There are two primary facilities.  First,
43  * the serializer facility itself.  Second, an integrated interrupt handler 
44  * disablement facility.
45  */
46
47 #include "opt_serializer.h"
48
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/proc.h>
53 #include <sys/rtprio.h>
54 #include <sys/queue.h>
55 #include <sys/thread2.h>
56 #include <sys/serialize.h>
57 #include <sys/sysctl.h>
58 #include <sys/ktr.h>
59 #include <sys/kthread.h>
60 #include <machine/cpu.h>
61 #include <machine/cpufunc.h>
62 #include <machine/specialreg.h>
63 #include <sys/lock.h>
64 #include <sys/caps.h>
65
66 struct exp_backoff {
67         int backoff;
68         int round;
69         lwkt_serialize_t s;
70 };
71
72 #define SLZ_KTR_STRING          "slz=%p"
73 #define SLZ_KTR_ARG_SIZE        (sizeof(void *))
74
75 #ifndef KTR_SERIALIZER
76 #define KTR_SERIALIZER  KTR_ALL
77 #endif
78
79 KTR_INFO_MASTER(slz);
80 KTR_INFO(KTR_SERIALIZER, slz, enter_beg, 0, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
81 KTR_INFO(KTR_SERIALIZER, slz, sleep_beg, 1, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
82 KTR_INFO(KTR_SERIALIZER, slz, sleep_end, 2, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
83 KTR_INFO(KTR_SERIALIZER, slz, exit_end, 3, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
84 KTR_INFO(KTR_SERIALIZER, slz, wakeup_beg, 4, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
85 KTR_INFO(KTR_SERIALIZER, slz, wakeup_end, 5, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
86 KTR_INFO(KTR_SERIALIZER, slz, try, 6, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
87 KTR_INFO(KTR_SERIALIZER, slz, tryfail, 7, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
88 KTR_INFO(KTR_SERIALIZER, slz, tryok, 8, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
89 #ifdef SMP
90 KTR_INFO(KTR_SERIALIZER, slz, spinbo, 9,
91          "slz=%p bo1=%d bo=%d", (sizeof(void *) + (2 * sizeof(int))));
92 #endif
93 KTR_INFO(KTR_SERIALIZER, slz, enter_end, 10, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
94 KTR_INFO(KTR_SERIALIZER, slz, exit_beg, 11, SLZ_KTR_STRING, SLZ_KTR_ARG_SIZE);
95
96 #define logslz(name, slz)               KTR_LOG(slz_ ## name, slz)
97 #ifdef SMP
98 #define logslz_spinbo(slz, bo1, bo)     KTR_LOG(slz_spinbo, slz, bo1, bo)
99 #endif
100
101 static void lwkt_serialize_sleep(void *info);
102 static void lwkt_serialize_wakeup(void *info);
103
104 #ifdef SMP
105 static void lwkt_serialize_adaptive_sleep(void *bo);
106
107 static int slz_backoff_limit = 128;
108 SYSCTL_INT(_debug, OID_AUTO, serialize_bolimit, CTLFLAG_RW,
109            &slz_backoff_limit, 0, "");
110
111 static int slz_backoff_shift = 1;
112 SYSCTL_INT(_debug, OID_AUTO, serialize_boshift, CTLFLAG_RW,
113            &slz_backoff_shift, 0, "");
114
115 static int slz_backoff_round;
116 TUNABLE_INT("debug.serialize_boround", &slz_backoff_round);
117 SYSCTL_INT(_debug, OID_AUTO, serialize_boround, CTLFLAG_RW,
118            &slz_backoff_round, 0, "");
119 #endif  /* SMP */
120
121 void
122 lwkt_serialize_init(lwkt_serialize_t s)
123 {
124     atomic_intr_init(&s->interlock);
125     s->last_td = (void *)-4;
126
127     s->sleep_cnt = 0;
128     s->tryfail_cnt = 0;
129     s->enter_cnt = 0;
130     s->try_cnt = 0;
131 }
132
133 #ifdef SMP
134 void
135 lwkt_serialize_adaptive_enter(lwkt_serialize_t s)
136 {
137     struct exp_backoff bo;
138
139     bo.backoff = 1;
140     bo.round = 0;
141     bo.s = s;
142
143     ASSERT_NOT_SERIALIZED(s);
144
145     logslz(enter_beg, s);
146     atomic_intr_cond_enter(&s->interlock, lwkt_serialize_adaptive_sleep, &bo);
147     logslz(enter_end, s);
148
149     s->last_td = curthread;
150 #ifdef PROFILE_SERIALIZER
151     s->enter_cnt++;
152 #endif
153 }
154 #endif  /* SMP */
155
156 void
157 lwkt_serialize_enter(lwkt_serialize_t s)
158 {
159     ASSERT_NOT_SERIALIZED(s);
160
161     logslz(enter_beg, s);
162     atomic_intr_cond_enter(&s->interlock, lwkt_serialize_sleep, s);
163     logslz(enter_end, s);
164
165     s->last_td = curthread;
166 #ifdef PROFILE_SERIALIZER
167     s->enter_cnt++;
168 #endif
169 }
170
171 /*
172  * Returns non-zero on success
173  */
174 int
175 lwkt_serialize_try(lwkt_serialize_t s)
176 {
177     int error;
178
179     ASSERT_NOT_SERIALIZED(s);
180
181 #ifdef PROFILE_SERIALIZER
182     s->try_cnt++;
183 #endif
184     logslz(try, s);
185     if ((error = atomic_intr_cond_try(&s->interlock)) == 0) {
186         s->last_td = curthread;
187         logslz(tryok, s);
188         return(1);
189     }
190 #ifdef PROFILE_SERIALIZER
191     s->tryfail_cnt++;
192 #endif
193     logslz(tryfail, s);
194     return (0);
195 }
196
197 void
198 lwkt_serialize_exit(lwkt_serialize_t s)
199 {
200     ASSERT_SERIALIZED(s);
201     s->last_td = (void *)-2;
202
203     logslz(exit_beg, s);
204     atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
205     logslz(exit_end, s);
206 }
207
208 /*
209  * Interrupt handler disablement support, used by drivers.  Non-stackable
210  * (uses bit 30).
211  */
212 void
213 lwkt_serialize_handler_disable(lwkt_serialize_t s)
214 {
215     atomic_intr_handler_disable(&s->interlock);
216 }
217
218 void
219 lwkt_serialize_handler_enable(lwkt_serialize_t s)
220 {
221     atomic_intr_handler_enable(&s->interlock);
222 }
223
224 void
225 lwkt_serialize_handler_call(lwkt_serialize_t s, void (*func)(void *, void *), 
226                             void *arg, void *frame)
227 {
228     /*
229      * note: a return value of 0 indicates that the interrupt handler is 
230      * enabled.
231      */
232     if (atomic_intr_handler_is_enabled(&s->interlock) == 0) {
233         logslz(enter_beg, s);
234         atomic_intr_cond_enter(&s->interlock, lwkt_serialize_sleep, s);
235         logslz(enter_end, s);
236
237         s->last_td = curthread;
238 #ifdef PROFILE_SERIALIZER
239         s->enter_cnt++;
240 #endif
241         if (atomic_intr_handler_is_enabled(&s->interlock) == 0)
242             func(arg, frame);
243
244         ASSERT_SERIALIZED(s);
245         s->last_td = (void *)-2;
246
247         logslz(exit_beg, s);
248         atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
249         logslz(exit_end, s);
250     }
251 }
252
253 /*
254  * Similar to handler_call but does not block.  Returns 0 on success, 
255  * and 1 on failure.
256  */
257 int
258 lwkt_serialize_handler_try(lwkt_serialize_t s, void (*func)(void *, void *),
259                            void *arg, void *frame)
260 {
261     /*
262      * note: a return value of 0 indicates that the interrupt handler is 
263      * enabled.
264      */
265     if (atomic_intr_handler_is_enabled(&s->interlock) == 0) {
266 #ifdef PROFILE_SERIALIZER
267         s->try_cnt++;
268 #endif
269         logslz(try, s);
270         if (atomic_intr_cond_try(&s->interlock) == 0) {
271             s->last_td = curthread;
272             logslz(tryok, s);
273
274             func(arg, frame);
275
276             ASSERT_SERIALIZED(s);
277             s->last_td = (void *)-2;
278
279             logslz(exit_beg, s);
280             atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
281             logslz(exit_end, s);
282             return(0);
283         }
284     }
285 #ifdef PROFILE_SERIALIZER
286     s->tryfail_cnt++;
287 #endif
288     logslz(tryfail, s);
289     return(1);
290 }
291
292
293 /*
294  * Helper functions
295  *
296  * It is possible to race an interrupt which acquires and releases the
297  * bit, then calls wakeup before we actually go to sleep, so we
298  * need to check that the interlock is still acquired from within
299  * a critical section prior to sleeping.
300  */
301 static void
302 lwkt_serialize_sleep(void *info)
303 {
304     lwkt_serialize_t s = info;
305     crit_enter();
306     tsleep_interlock(s);
307     if (atomic_intr_cond_test(&s->interlock) != 0) {
308 #ifdef PROFILE_SERIALIZER
309         s->sleep_cnt++;
310 #endif
311         logslz(sleep_beg, s);
312         tsleep(s, 0, "slize", 0);
313         logslz(sleep_end, s);
314     }
315     crit_exit();
316 }
317
318 #ifdef SMP
319
320 static void
321 lwkt_serialize_adaptive_sleep(void *arg)
322 {
323     struct exp_backoff *bo = arg;
324     lwkt_serialize_t s = bo->s;
325     int backoff;
326
327     /*
328      * Randomize backoff value
329      */
330 #ifdef _RDTSC_SUPPORTED_
331     if (cpu_feature & CPUID_TSC) {
332         backoff =
333         (((u_long)rdtsc() ^ (((u_long)curthread) >> 5)) &
334          (bo->backoff - 1)) + 1;
335     } else
336 #endif
337         backoff = bo->backoff;
338
339     logslz_spinbo(s, bo->backoff, backoff);
340
341     /*
342      * Quick backoff
343      */
344     for (; backoff; --backoff)
345         cpu_pause();
346     if (bo->backoff < slz_backoff_limit) {
347         bo->backoff <<= slz_backoff_shift;
348         return;
349     } else {
350         bo->backoff = 1;
351         bo->round++;
352         if (bo->round >= slz_backoff_round)
353             bo->round = 0;
354         else
355             return;
356     }
357
358     crit_enter();
359     tsleep_interlock(s);
360     if (atomic_intr_cond_test(&s->interlock) != 0) {
361 #ifdef PROFILE_SERIALIZER
362         s->sleep_cnt++;
363 #endif
364         logslz(sleep_beg, s);
365         tsleep(s, 0, "slize", 0);
366         logslz(sleep_end, s);
367     }
368     crit_exit();
369 }
370
371 #endif  /* SMP */
372
373 static void
374 lwkt_serialize_wakeup(void *info)
375 {
376     logslz(wakeup_beg, info);
377     wakeup(info);
378     logslz(wakeup_end, info);
379 }
380
381 #ifdef SMP
382 static void
383 lwkt_serialize_sysinit(void *dummy __unused)
384 {
385         if (slz_backoff_round <= 0)
386                 slz_backoff_round = ncpus * 2;
387 }
388 SYSINIT(lwkt_serialize, SI_SUB_PRE_DRIVERS, SI_ORDER_SECOND,
389         lwkt_serialize_sysinit, NULL);
390 #endif