]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/etimer.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / i386 / etimer.c
1 /*
2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
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
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @OSF_COPYRIGHT@
30 */
31 /*
32 * @APPLE_FREE_COPYRIGHT@
33 */
34 /*
35 * File: etimer.c
36 * Purpose: Routines for handling the machine independent
37 * event timer.
38 */
39
40 #include <mach/mach_types.h>
41
42 #include <kern/timer_queue.h>
43 #include <kern/clock.h>
44 #include <kern/thread.h>
45 #include <kern/processor.h>
46 #include <kern/macro_help.h>
47 #include <kern/spl.h>
48 #include <kern/etimer.h>
49 #include <kern/pms.h>
50
51 #include <machine/commpage.h>
52 #include <machine/machine_routines.h>
53
54 #include <sys/kdebug.h>
55 #include <i386/cpu_data.h>
56 #include <i386/cpu_topology.h>
57 #include <i386/cpu_threads.h>
58
59 /*
60 * Event timer interrupt.
61 *
62 * XXX a drawback of this implementation is that events serviced earlier must not set deadlines
63 * that occur before the entire chain completes.
64 *
65 * XXX a better implementation would use a set of generic callouts and iterate over them
66 */
67 void
68 etimer_intr(
69 __unused int inuser,
70 __unused uint64_t iaddr)
71 {
72 uint64_t abstime;
73 rtclock_timer_t *mytimer;
74 cpu_data_t *pp;
75 x86_lcpu_t *lcpu;
76
77 pp = current_cpu_datap();
78 lcpu = x86_lcpu();
79
80 mytimer = &pp->rtclock_timer; /* Point to the event timer */
81 abstime = mach_absolute_time(); /* Get the time now */
82
83 /* is it time for power management state change? */
84 if (pmCPUGetDeadline(pp) <= abstime) {
85 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_DECI, 3) | DBG_FUNC_START, 0, 0, 0, 0, 0);
86 pmCPUDeadline(pp);
87 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_DECI, 3) | DBG_FUNC_END, 0, 0, 0, 0, 0);
88
89 abstime = mach_absolute_time(); /* Get the time again since we ran a bit */
90 }
91
92 /* has a pending clock timer expired? */
93 if (mytimer->deadline <= abstime) { /* Have we expired the deadline? */
94 mytimer->has_expired = TRUE; /* Remember that we popped */
95 mytimer->deadline = timer_queue_expire(&mytimer->queue, abstime);
96 mytimer->has_expired = FALSE;
97 }
98
99 /* schedule our next deadline */
100 lcpu->rtcPop = EndOfAllTime; /* any real deadline will be earlier */
101 etimer_resync_deadlines();
102 }
103
104 /*
105 * Set the clock deadline.
106 */
107 void etimer_set_deadline(uint64_t deadline)
108 {
109 rtclock_timer_t *mytimer;
110 spl_t s;
111 cpu_data_t *pp;
112
113 s = splclock(); /* no interruptions */
114 pp = current_cpu_datap();
115
116 mytimer = &pp->rtclock_timer; /* Point to the timer itself */
117 mytimer->deadline = deadline; /* Set the new expiration time */
118
119 etimer_resync_deadlines();
120
121 splx(s);
122 }
123
124 /*
125 * Re-evaluate the outstanding deadlines and select the most proximate.
126 *
127 * Should be called at splclock.
128 */
129 void
130 etimer_resync_deadlines(void)
131 {
132 uint64_t deadline;
133 uint64_t pmdeadline;
134 rtclock_timer_t *mytimer;
135 spl_t s = splclock();
136 cpu_data_t *pp;
137 x86_lcpu_t *lcpu;
138
139 pp = current_cpu_datap();
140 lcpu = x86_lcpu();
141 deadline = ~0ULL;
142
143 /*
144 * If we have a clock timer set sooner, pop on that.
145 */
146 mytimer = &pp->rtclock_timer;
147 if (!mytimer->has_expired && mytimer->deadline > 0)
148 deadline = mytimer->deadline;
149
150 /*
151 * If we have a power management deadline, see if that's earlier.
152 */
153 pmdeadline = pmCPUGetDeadline(pp);
154 if (pmdeadline > 0 && pmdeadline < deadline)
155 deadline = pmdeadline;
156
157 /*
158 * Go and set the "pop" event.
159 */
160 if (deadline > 0) {
161 int decr;
162 uint64_t now;
163
164 now = mach_absolute_time();
165 decr = setPop(deadline);
166
167 if (deadline < now)
168 lcpu->rtcPop = now + decr;
169 else
170 lcpu->rtcPop = deadline;
171
172 lcpu->rtcDeadline = lcpu->rtcPop;
173
174 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_EXCP_DECI, 1) | DBG_FUNC_NONE, decr, 2, 0, 0, 0);
175 }
176 splx(s);
177 }
178
179 void etimer_timer_expire(void *arg);
180
181 void
182 etimer_timer_expire(
183 __unused void *arg)
184 {
185 rtclock_timer_t *mytimer;
186 uint64_t abstime;
187 cpu_data_t *pp;
188 x86_lcpu_t *lcpu;
189
190 pp = current_cpu_datap();
191 lcpu = x86_lcpu();
192
193 mytimer = &pp->rtclock_timer;
194 abstime = mach_absolute_time();
195
196 mytimer->has_expired = TRUE;
197 mytimer->deadline = timer_queue_expire(&mytimer->queue, abstime);
198 mytimer->has_expired = FALSE;
199
200 lcpu->rtcPop = EndOfAllTime;
201 etimer_resync_deadlines();
202 }
203
204 queue_t
205 timer_queue_assign(
206 uint64_t deadline)
207 {
208 cpu_data_t *cdp = current_cpu_datap();
209 rtclock_timer_t *timer;
210
211 if (cdp->cpu_running) {
212 timer = &cdp->rtclock_timer;
213
214 if (deadline < timer->deadline)
215 etimer_set_deadline(deadline);
216 }
217 else
218 timer = &cpu_datap(master_cpu)->rtclock_timer;
219
220 return (&timer->queue);
221 }
222
223 void
224 timer_queue_cancel(
225 queue_t queue,
226 uint64_t deadline,
227 uint64_t new_deadline)
228 {
229 if (queue == &current_cpu_datap()->rtclock_timer.queue) {
230 if (deadline < new_deadline)
231 etimer_set_deadline(new_deadline);
232 }
233 }