]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/mk_timer.c
8de9c9012cf57260872fd372b9c30fd0bc629829
[apple/xnu.git] / osfmk / kern / mk_timer.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
30 *
31 * HISTORY
32 *
33 * 29 June 2000 (debo)
34 * Created.
35 */
36
37 #include <mach/mach_types.h>
38 #include <mach/mach_traps.h>
39 #include <mach/mach_port_server.h>
40
41 #include <mach/mk_timer.h>
42
43 #include <ipc/ipc_space.h>
44
45 #include <kern/lock_group.h>
46 #include <kern/mk_timer.h>
47 #include <kern/thread_call.h>
48
49 static zone_t mk_timer_zone;
50
51 static mach_port_qos_t mk_timer_qos = {
52 .name = FALSE,
53 .prealloc = TRUE,
54 .len = sizeof(mk_timer_expire_msg_t),
55 };
56
57 static void mk_timer_expire(
58 void *p0,
59 void *p1);
60
61 mach_port_name_t
62 mk_timer_create_trap(
63 __unused struct mk_timer_create_trap_args *args)
64 {
65 mk_timer_t timer;
66 ipc_space_t myspace = current_space();
67 mach_port_name_t name = MACH_PORT_NULL;
68 ipc_port_t port;
69 kern_return_t result;
70
71 timer = (mk_timer_t)zalloc(mk_timer_zone);
72 if (timer == NULL) {
73 return MACH_PORT_NULL;
74 }
75
76 result = mach_port_allocate_internal(myspace, MACH_PORT_RIGHT_RECEIVE,
77 &mk_timer_qos, &name);
78 if (result == KERN_SUCCESS) {
79 result = ipc_port_translate_receive(myspace, name, &port);
80 }
81
82 if (result != KERN_SUCCESS) {
83 zfree(mk_timer_zone, timer);
84
85 return MACH_PORT_NULL;
86 }
87
88 simple_lock_init(&timer->lock, 0);
89 thread_call_setup(&timer->call_entry, mk_timer_expire, timer);
90 timer->is_armed = timer->is_dead = FALSE;
91 timer->active = 0;
92
93 timer->port = port;
94 ipc_kobject_set_atomically(port, (ipc_kobject_t)timer, IKOT_TIMER);
95
96 port->ip_srights++;
97 ip_reference(port);
98 ip_unlock(port);
99
100 return name;
101 }
102
103 void
104 mk_timer_port_destroy(
105 ipc_port_t port)
106 {
107 mk_timer_t timer = NULL;
108
109 ip_lock(port);
110 if (ip_kotype(port) == IKOT_TIMER) {
111 timer = (mk_timer_t)port->ip_kobject;
112 assert(timer != NULL);
113 ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE);
114 simple_lock(&timer->lock, LCK_GRP_NULL);
115 assert(timer->port == port);
116 }
117 ip_unlock(port);
118
119 if (timer != NULL) {
120 if (thread_call_cancel(&timer->call_entry)) {
121 timer->active--;
122 }
123 timer->is_armed = FALSE;
124
125 timer->is_dead = TRUE;
126 if (timer->active == 0) {
127 simple_unlock(&timer->lock);
128 zfree(mk_timer_zone, timer);
129
130 ipc_port_release_send(port);
131 return;
132 }
133
134 simple_unlock(&timer->lock);
135 }
136 }
137
138 void
139 mk_timer_init(void)
140 {
141 int s = sizeof(mk_timer_data_t);
142
143 assert(!(mk_timer_zone != NULL));
144
145 mk_timer_zone = zinit(s, (4096 * s), (16 * s), "mk_timer");
146
147 zone_change(mk_timer_zone, Z_NOENCRYPT, TRUE);
148 }
149
150 static void
151 mk_timer_expire(
152 void *p0,
153 __unused void *p1)
154 {
155 mk_timer_t timer = p0;
156 ipc_port_t port;
157
158 simple_lock(&timer->lock, LCK_GRP_NULL);
159
160 if (timer->active > 1) {
161 timer->active--;
162 simple_unlock(&timer->lock);
163 return;
164 }
165
166 port = timer->port;
167 assert(port != IP_NULL);
168 assert(timer->active == 1);
169
170 while (timer->is_armed && timer->active == 1) {
171 mk_timer_expire_msg_t msg;
172
173 timer->is_armed = FALSE;
174 simple_unlock(&timer->lock);
175
176 msg.header.msgh_bits =
177 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
178 msg.header.msgh_remote_port = port;
179 msg.header.msgh_local_port = MACH_PORT_NULL;
180 msg.header.msgh_voucher_port = MACH_PORT_NULL;
181 msg.header.msgh_id = 0;
182
183 msg.unused[0] = msg.unused[1] = msg.unused[2] = 0;
184
185 (void) mach_msg_send_from_kernel_proper(&msg.header, sizeof(msg));
186
187 simple_lock(&timer->lock, LCK_GRP_NULL);
188 }
189
190 if (--timer->active == 0 && timer->is_dead) {
191 simple_unlock(&timer->lock);
192 zfree(mk_timer_zone, timer);
193
194 ipc_port_release_send(port);
195 return;
196 }
197
198 simple_unlock(&timer->lock);
199 }
200
201 /*
202 * mk_timer_destroy_trap: Destroy the Mach port associated with a timer
203 *
204 * Parameters: args User argument descriptor (see below)
205 *
206 * Indirect: args->name Mach port name
207 *
208 *
209 * Returns: 0 Success
210 * !0 Not success
211 *
212 */
213 kern_return_t
214 mk_timer_destroy_trap(
215 struct mk_timer_destroy_trap_args *args)
216 {
217 mach_port_name_t name = args->name;
218 ipc_space_t myspace = current_space();
219 ipc_port_t port;
220 kern_return_t result;
221
222 result = ipc_port_translate_receive(myspace, name, &port);
223 if (result != KERN_SUCCESS) {
224 return result;
225 }
226
227 if (ip_kotype(port) == IKOT_TIMER) {
228 ip_unlock(port);
229 result = mach_port_destroy(myspace, name);
230 } else {
231 ip_unlock(port);
232 result = KERN_INVALID_ARGUMENT;
233 }
234
235 return result;
236 }
237
238 /*
239 * mk_timer_arm_trap: Start (arm) a timer
240 *
241 * Parameters: args User argument descriptor (see below)
242 *
243 * Indirect: args->name Mach port name
244 * args->expire_time Time when timer expires
245 *
246 *
247 * Returns: 0 Success
248 * !0 Not success
249 *
250 */
251
252 static kern_return_t
253 mk_timer_arm_trap_internal(mach_port_name_t name, uint64_t expire_time, uint64_t mk_leeway, uint64_t mk_timer_flags)
254 {
255 mk_timer_t timer;
256 ipc_space_t myspace = current_space();
257 ipc_port_t port;
258 kern_return_t result;
259
260 result = ipc_port_translate_receive(myspace, name, &port);
261 if (result != KERN_SUCCESS) {
262 return result;
263 }
264
265 if (ip_kotype(port) == IKOT_TIMER) {
266 timer = (mk_timer_t)port->ip_kobject;
267 assert(timer != NULL);
268
269 simple_lock(&timer->lock, LCK_GRP_NULL);
270 assert(timer->port == port);
271 ip_unlock(port);
272
273 if (!timer->is_dead) {
274 timer->is_armed = TRUE;
275
276 if (expire_time > mach_absolute_time()) {
277 uint32_t tcflags = THREAD_CALL_DELAY_USER_NORMAL;
278
279 if (mk_timer_flags & MK_TIMER_CRITICAL) {
280 tcflags = THREAD_CALL_DELAY_USER_CRITICAL;
281 }
282
283 if (mk_leeway != 0) {
284 tcflags |= THREAD_CALL_DELAY_LEEWAY;
285 }
286
287 if (!thread_call_enter_delayed_with_leeway(
288 &timer->call_entry, NULL,
289 expire_time, mk_leeway, tcflags)) {
290 timer->active++;
291 }
292 } else {
293 if (!thread_call_enter1(&timer->call_entry, NULL)) {
294 timer->active++;
295 }
296 }
297 }
298
299 simple_unlock(&timer->lock);
300 } else {
301 ip_unlock(port);
302 result = KERN_INVALID_ARGUMENT;
303 }
304 return result;
305 }
306
307 kern_return_t
308 mk_timer_arm_trap(struct mk_timer_arm_trap_args *args)
309 {
310 return mk_timer_arm_trap_internal(args->name, args->expire_time, 0, MK_TIMER_NORMAL);
311 }
312
313 kern_return_t
314 mk_timer_arm_leeway_trap(struct mk_timer_arm_leeway_trap_args *args)
315 {
316 return mk_timer_arm_trap_internal(args->name, args->expire_time, args->mk_leeway, args->mk_timer_flags);
317 }
318
319 /*
320 * mk_timer_cancel_trap: Cancel a timer
321 *
322 * Parameters: args User argument descriptor (see below)
323 *
324 * Indirect: args->name Mach port name
325 * args->result_time The armed time of the cancelled timer (return value)
326 *
327 *
328 * Returns: 0 Success
329 * !0 Not success
330 *
331 */
332 kern_return_t
333 mk_timer_cancel_trap(
334 struct mk_timer_cancel_trap_args *args)
335 {
336 mach_port_name_t name = args->name;
337 mach_vm_address_t result_time_addr = args->result_time;
338 uint64_t armed_time = 0;
339 mk_timer_t timer;
340 ipc_space_t myspace = current_space();
341 ipc_port_t port;
342 kern_return_t result;
343
344 result = ipc_port_translate_receive(myspace, name, &port);
345 if (result != KERN_SUCCESS) {
346 return result;
347 }
348
349 if (ip_kotype(port) == IKOT_TIMER) {
350 timer = (mk_timer_t)port->ip_kobject;
351 assert(timer != NULL);
352 simple_lock(&timer->lock, LCK_GRP_NULL);
353 assert(timer->port == port);
354 ip_unlock(port);
355
356 if (timer->is_armed) {
357 armed_time = timer->call_entry.tc_call.deadline;
358 if (thread_call_cancel(&timer->call_entry)) {
359 timer->active--;
360 }
361 timer->is_armed = FALSE;
362 }
363
364 simple_unlock(&timer->lock);
365 } else {
366 ip_unlock(port);
367 result = KERN_INVALID_ARGUMENT;
368 }
369
370 if (result == KERN_SUCCESS) {
371 if (result_time_addr != 0 &&
372 copyout((void *)&armed_time, result_time_addr,
373 sizeof(armed_time)) != 0) {
374 result = KERN_FAILURE;
375 }
376 }
377
378 return result;
379 }