Merge from vendor branch OPENSSH:
[dragonfly.git] / sys / kern / kern_systimer.c
1 /*
2  * Copyright (c) 2003,2004 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/kern_systimer.c,v 1.4 2004/07/16 05:51:10 dillon Exp $
35  */
36
37 /*
38  * WARNING!  THE SYSTIMER MODULE DOES NOT OPERATE OR DISPATCH WITH THE
39  * MP LOCK HELD.  ALL CODE USING THIS MODULE MUST BE MP-SAFE.
40  *
41  * This code implements a fine-grained per-cpu system timer which is
42  * ultimately based on a hardware timer.  The hardware timer abstraction
43  * is sufficiently disconnected from this code to support both per-cpu
44  * hardware timers or a single system-wide hardware timer.
45  *
46  * Notes on machine-dependant code (in arch/arch/systimer.c)
47  *
48  * cputimer_intr_reload()       Reload the one-shot (per-cpu basis)
49  *
50  * cputimer_count()             Get the current absolute sysclock_t value.
51  */
52
53 #include <sys/param.h>
54 #include <sys/kernel.h>
55 #include <sys/systm.h>
56 #include <sys/thread.h>
57 #include <sys/globaldata.h>
58 #include <sys/systimer.h>
59 #include <sys/thread2.h>
60
61 /*
62  * Execute ready systimers.  Called directly from the platform-specific
63  * one-shot timer clock interrupt (e.g. clkintr()).  Systimer functions are
64  * responsible for calling hardclock, statclock, and other finely-timed
65  * routines.
66  */
67 void
68 systimer_intr(sysclock_t *timep, struct intrframe *frame)
69 {
70     globaldata_t gd = mycpu;
71     sysclock_t time = *timep;
72     systimer_t info;
73
74     if (gd->gd_syst_nest)
75         return;
76
77     crit_enter();
78     ++gd->gd_syst_nest;
79     while ((info = TAILQ_FIRST(&gd->gd_systimerq)) != NULL) {
80         /*
81          * If we haven't reached the requested time, tell the cputimer
82          * how much is left and break out.
83          */
84         if ((int)(info->time - time) > 0) {
85             cputimer_intr_reload(info->time - time);
86             break;
87         }
88
89         /*
90          * Dequeue and execute
91          */
92         info->flags &= ~SYSTF_ONQUEUE;
93         TAILQ_REMOVE(info->queue, info, node);
94         crit_exit();
95         info->func(info, frame);
96         crit_enter();
97
98         /*
99          * Reinstall if periodic
100          */
101         if (info->periodic) {
102             info->time += info->periodic;
103             systimer_add(info);
104         }
105     }
106     if (info)
107         gd->gd_nextclock = info->time;
108     else
109         gd->gd_nextclock = 0;
110     --gd->gd_syst_nest;
111     crit_exit();
112 }
113
114 void
115 systimer_add(systimer_t info)
116 {
117     struct globaldata *gd = mycpu;
118
119     KKASSERT((info->flags & (SYSTF_ONQUEUE|SYSTF_IPIRUNNING)) == 0);
120     crit_enter();
121     if (info->gd == gd) {
122         systimer_t scan1;
123         systimer_t scan2;
124         scan1 = TAILQ_FIRST(&gd->gd_systimerq);
125         if (scan1 == NULL || (int)(scan1->time - info->time) > 0) {
126             gd->gd_nextclock = info->time;
127             cputimer_intr_reload(info->time - cputimer_count());
128             TAILQ_INSERT_HEAD(&gd->gd_systimerq, info, node);
129         } else {
130             scan2 = TAILQ_LAST(&gd->gd_systimerq, systimerq);
131             for (;;) {
132                 if (scan1 == NULL) {
133                     TAILQ_INSERT_TAIL(&gd->gd_systimerq, info, node);
134                     break;
135                 }
136                 if ((int)(scan1->time - info->time) > 0) {
137                     TAILQ_INSERT_BEFORE(scan1, info, node);
138                     break;
139                 }
140                 if ((int)(scan2->time - info->time) <= 0) {
141                     TAILQ_INSERT_AFTER(&gd->gd_systimerq, scan2, info, node);
142                     break;
143                 }
144                 scan1 = TAILQ_NEXT(scan1, node);
145                 scan2 = TAILQ_PREV(scan2, systimerq, node);
146             }
147         }
148         info->flags = (info->flags | SYSTF_ONQUEUE) & ~SYSTF_IPIRUNNING;
149         info->queue = &gd->gd_systimerq;
150     } else {
151         info->flags |= SYSTF_IPIRUNNING;
152         lwkt_send_ipiq(info->gd, (ipifunc_t)systimer_add, info);
153     }
154     crit_exit();
155 }
156
157 /*
158  * systimer_del()
159  *
160  *      Delete a system timer.  Only the owning cpu can delete a timer.
161  */
162 void
163 systimer_del(systimer_t info)
164 {
165     KKASSERT(info->gd == mycpu && (info->flags & SYSTF_IPIRUNNING) == 0);
166     crit_enter();
167     if (info->flags & SYSTF_ONQUEUE) {
168         TAILQ_REMOVE(info->queue, info, node);
169         info->flags &= ~SYSTF_ONQUEUE;
170     }
171     crit_exit();
172 }
173
174 /*
175  * systimer_init_periodic()
176  *
177  *      Initialize a periodic timer at the specified frequency and add
178  *      it to the system.  The frequency is uncompensated and approximate.
179  *
180  *      Try to synchronize multi registrations of the same or similar
181  *      frequencies so the hardware interrupt is able to dispatch several
182  *      at together by adjusting the phase of the initial interrupt.  This
183  *      helps SMP.  Note that we are not attempting to synchronize to 
184  *      the realtime clock.
185  */
186 void
187 systimer_init_periodic(systimer_t info, void *func, void *data, int hz)
188 {
189     sysclock_t base_count;
190
191     bzero(info, sizeof(struct systimer));
192     info->periodic = cputimer_fromhz(hz);
193     base_count = cputimer_count();
194     base_count = base_count - (base_count % info->periodic);
195     info->time = base_count + info->periodic;
196     info->func = func;
197     info->data = data;
198     info->gd = mycpu;
199     systimer_add(info);
200 }
201
202 /*
203  * systimer_init_oneshot()
204  *
205  *      Initialize a periodic timer at the specified frequency and add
206  *      it to the system.  The frequency is uncompensated and approximate.
207  */
208 void
209 systimer_init_oneshot(systimer_t info, void *func, void *data, int us)
210 {
211     bzero(info, sizeof(struct systimer));
212     info->time = cputimer_count() + cputimer_fromus(us);
213     info->func = func;
214     info->data = data;
215     info->gd = mycpu;
216     systimer_add(info);
217 }
218