2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
27 * Purpose: Routines for the creation and use of kernel
28 * alarm clock services. This file and the ipc
29 * routines in kern/ipc_clock.c constitute the
30 * machine-independent clock service layer.
34 #include <mach_host.h>
36 #include <mach/boolean.h>
37 #include <mach/processor_info.h>
38 #include <mach/vm_param.h>
39 #include <machine/mach_param.h>
40 #include <kern/cpu_number.h>
41 #include <kern/misc_protos.h>
42 #include <kern/lock.h>
43 #include <kern/host.h>
44 #include <kern/processor.h>
45 #include <kern/sched.h>
47 #include <kern/thread.h>
48 #include <kern/thread_swap.h>
49 #include <kern/ipc_host.h>
50 #include <kern/clock.h>
51 #include <kern/zalloc.h>
53 #include <ipc/ipc_port.h>
55 #include <mach/mach_syscalls.h>
56 #include <mach/clock_reply.h>
57 #include <mach/mach_time.h>
59 #include <kern/mk_timer.h>
65 #include <mach/clock_server.h>
66 #include <mach/mach_host_server.h>
68 /* local data declarations */
69 decl_simple_lock_data(static,ClockLock
) /* clock system synchronization */
70 static struct zone
*alarm_zone
; /* zone for user alarms */
71 static struct alarm
*alrmfree
; /* alarm free list pointer */
72 static struct alarm
*alrmdone
; /* alarm done list pointer */
73 static long alrm_seqno
; /* uniquely identifies alarms */
74 static thread_call_data_t alarm_deliver
;
76 /* backwards compatibility */
77 int hz
= HZ
; /* GET RID OF THIS !!! */
78 int tick
= (1000000 / HZ
); /* GET RID OF THIS !!! */
80 /* external declarations */
81 extern struct clock clock_list
[];
82 extern int clock_count
;
84 /* local clock subroutines */
96 alarm_type_t alarm_type
,
97 mach_timespec_t
*alarm_time
,
98 mach_timespec_t
*clock_time
);
101 void clock_alarm_deliver(
102 thread_call_param_t p0
,
103 thread_call_param_t p1
);
106 * Macros to lock/unlock clock system.
108 #define LOCK_CLOCK(s) \
110 simple_lock(&ClockLock);
112 #define UNLOCK_CLOCK(s) \
113 simple_unlock(&ClockLock); \
117 * Configure the clock system. (Not sure if we need this,
118 * as separate from clock_init()).
126 if (cpu_number() != master_cpu
)
127 panic("clock_config");
130 * Configure clock devices.
132 simple_lock_init(&ClockLock
, ETAP_MISC_CLOCK
);
133 for (i
= 0; i
< clock_count
; i
++) {
134 clock
= &clock_list
[i
];
136 if ((*clock
->cl_ops
->c_config
)() == 0)
141 /* start alarm sequence numbers at 0 */
146 * Initialize the clock system.
155 * Initialize basic clock structures.
157 for (i
= 0; i
< clock_count
; i
++) {
158 clock
= &clock_list
[i
];
160 (*clock
->cl_ops
->c_init
)();
165 * Initialize the clock ipc service facility.
168 clock_service_create(void)
173 mk_timer_initialize();
176 * Initialize ipc clock services.
178 for (i
= 0; i
< clock_count
; i
++) {
179 clock
= &clock_list
[i
];
181 ipc_clock_init(clock
);
182 ipc_clock_enable(clock
);
187 * Initialize clock service alarms.
189 i
= sizeof(struct alarm
);
190 alarm_zone
= zinit(i
, (4096/i
)*i
, 10*i
, "alarms");
193 * Initialize the clock alarm delivery mechanism.
195 thread_call_setup(&alarm_deliver
, clock_alarm_deliver
, NULL
);
199 * Get the service port on a clock.
202 host_get_clock_service(
205 clock_t *clock
) /* OUT */
207 if (host
== HOST_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
209 return (KERN_INVALID_ARGUMENT
);
212 *clock
= &clock_list
[clock_id
];
213 if ((*clock
)->cl_ops
== 0)
214 return (KERN_FAILURE
);
215 return (KERN_SUCCESS
);
219 * Get the control port on a clock.
222 host_get_clock_control(
223 host_priv_t host_priv
,
225 clock_t *clock
) /* OUT */
227 if (host_priv
== HOST_PRIV_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
229 return (KERN_INVALID_ARGUMENT
);
232 *clock
= &clock_list
[clock_id
];
233 if ((*clock
)->cl_ops
== 0)
234 return (KERN_FAILURE
);
235 return (KERN_SUCCESS
);
239 * Get the current clock time.
244 mach_timespec_t
*cur_time
) /* OUT */
246 if (clock
== CLOCK_NULL
)
247 return (KERN_INVALID_ARGUMENT
);
248 return ((*clock
->cl_ops
->c_gettime
)(cur_time
));
252 * Get clock attributes.
255 clock_get_attributes(
257 clock_flavor_t flavor
,
258 clock_attr_t attr
, /* OUT */
259 mach_msg_type_number_t
*count
) /* IN/OUT */
261 kern_return_t (*getattr
)(
262 clock_flavor_t flavor
,
264 mach_msg_type_number_t
*count
);
266 if (clock
== CLOCK_NULL
)
267 return (KERN_INVALID_ARGUMENT
);
268 if (getattr
= clock
->cl_ops
->c_getattr
)
269 return((*getattr
)(flavor
, attr
, count
));
271 return (KERN_FAILURE
);
275 * Set the current clock time.
280 mach_timespec_t new_time
)
282 mach_timespec_t
*clock_time
;
283 kern_return_t (*settime
)(
284 mach_timespec_t
*clock_time
);
286 if (clock
== CLOCK_NULL
)
287 return (KERN_INVALID_ARGUMENT
);
288 if ((settime
= clock
->cl_ops
->c_settime
) == 0)
289 return (KERN_FAILURE
);
290 clock_time
= &new_time
;
291 if (BAD_MACH_TIMESPEC(clock_time
))
292 return (KERN_INVALID_VALUE
);
295 * Flush all outstanding alarms.
302 return ((*settime
)(clock_time
));
306 * Set the clock alarm resolution.
309 clock_set_attributes(
311 clock_flavor_t flavor
,
313 mach_msg_type_number_t count
)
315 kern_return_t (*setattr
)(
316 clock_flavor_t flavor
,
318 mach_msg_type_number_t count
);
320 if (clock
== CLOCK_NULL
)
321 return (KERN_INVALID_ARGUMENT
);
322 if (setattr
= clock
->cl_ops
->c_setattr
)
323 return ((*setattr
)(flavor
, attr
, count
));
325 return (KERN_FAILURE
);
329 * Setup a clock alarm.
334 alarm_type_t alarm_type
,
335 mach_timespec_t alarm_time
,
336 ipc_port_t alarm_port
,
337 mach_msg_type_name_t alarm_port_type
)
340 mach_timespec_t clock_time
;
342 kern_return_t reply_code
;
345 if (clock
== CLOCK_NULL
)
346 return (KERN_INVALID_ARGUMENT
);
347 if (clock
->cl_ops
->c_setalrm
== 0)
348 return (KERN_FAILURE
);
349 if (IP_VALID(alarm_port
) == 0)
350 return (KERN_INVALID_CAPABILITY
);
353 * Check alarm parameters. If parameters are invalid,
354 * send alarm message immediately.
356 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
357 chkstat
= check_time(alarm_type
, &alarm_time
, &clock_time
);
359 reply_code
= (chkstat
< 0 ? KERN_INVALID_VALUE
: KERN_SUCCESS
);
360 clock_alarm_reply(alarm_port
, alarm_port_type
,
361 reply_code
, alarm_type
, clock_time
);
362 return (KERN_SUCCESS
);
366 * Get alarm and add to clock alarm list.
370 if ((alarm
= alrmfree
) == 0) {
372 alarm
= (alarm_t
) zalloc(alarm_zone
);
374 return (KERN_RESOURCE_SHORTAGE
);
378 alrmfree
= alarm
->al_next
;
380 alarm
->al_status
= ALARM_CLOCK
;
381 alarm
->al_time
= alarm_time
;
382 alarm
->al_type
= alarm_type
;
383 alarm
->al_port
= alarm_port
;
384 alarm
->al_port_type
= alarm_port_type
;
385 alarm
->al_clock
= clock
;
386 alarm
->al_seqno
= alrm_seqno
++;
387 post_alarm(clock
, alarm
);
390 return (KERN_SUCCESS
);
394 * Sleep on a clock. System trap. User-level libmach clock_sleep
395 * interface call takes a mach_timespec_t sleep_time argument which it
396 * converts to sleep_sec and sleep_nsec arguments which are then
397 * passed to clock_sleep_trap.
401 mach_port_name_t clock_name
,
402 sleep_type_t sleep_type
,
405 mach_timespec_t
*wakeup_time
)
408 mach_timespec_t swtime
;
409 kern_return_t rvalue
;
412 * Convert the trap parameters.
414 if (clock_name
!= MACH_PORT_NULL
)
415 clock
= port_name_to_clock(clock_name
);
417 clock
= &clock_list
[SYSTEM_CLOCK
];
419 swtime
.tv_sec
= sleep_sec
;
420 swtime
.tv_nsec
= sleep_nsec
;
423 * Call the actual clock_sleep routine.
425 rvalue
= clock_sleep_internal(clock
, sleep_type
, &swtime
);
428 * Return current time as wakeup time.
430 if (rvalue
!= KERN_INVALID_ARGUMENT
&& rvalue
!= KERN_FAILURE
) {
431 copyout((char *)&swtime
, (char *)wakeup_time
,
432 sizeof(mach_timespec_t
));
438 * Kernel internally callable clock sleep routine. The calling
439 * thread is suspended until the requested sleep time is reached.
442 clock_sleep_internal(
444 sleep_type_t sleep_type
,
445 mach_timespec_t
*sleep_time
)
448 mach_timespec_t clock_time
;
449 kern_return_t rvalue
;
453 if (clock
== CLOCK_NULL
)
454 return (KERN_INVALID_ARGUMENT
);
455 if (clock
->cl_ops
->c_setalrm
== 0)
456 return (KERN_FAILURE
);
459 * Check sleep parameters. If parameters are invalid
460 * return an error, otherwise post alarm request.
462 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
464 chkstat
= check_time(sleep_type
, sleep_time
, &clock_time
);
466 return (KERN_INVALID_VALUE
);
467 rvalue
= KERN_SUCCESS
;
470 * Get alarm and add to clock alarm list.
474 if ((alarm
= alrmfree
) == 0) {
476 alarm
= (alarm_t
) zalloc(alarm_zone
);
478 return (KERN_RESOURCE_SHORTAGE
);
482 alrmfree
= alarm
->al_next
;
484 alarm
->al_time
= *sleep_time
;
485 alarm
->al_status
= ALARM_SLEEP
;
486 post_alarm(clock
, alarm
);
489 * Wait for alarm to occur.
491 assert_wait((event_t
)alarm
, THREAD_ABORTSAFE
);
493 /* should we force spl(0) at this point? */
494 thread_block((void (*)(void)) 0);
495 /* we should return here at ipl0 */
498 * Note if alarm expired normally or whether it
499 * was aborted. If aborted, delete alarm from
500 * clock alarm list. Return alarm to free list.
503 if (alarm
->al_status
!= ALARM_DONE
) {
504 /* This means we were interrupted and that
505 thread->wait_result != THREAD_AWAKENED. */
506 if ((alarm
->al_prev
)->al_next
= alarm
->al_next
)
507 (alarm
->al_next
)->al_prev
= alarm
->al_prev
;
508 rvalue
= KERN_ABORTED
;
510 *sleep_time
= alarm
->al_time
;
511 alarm
->al_status
= ALARM_FREE
;
512 alarm
->al_next
= alrmfree
;
517 *sleep_time
= clock_time
;
523 * CLOCK INTERRUPT SERVICE ROUTINES.
527 * Service clock alarm interrupts. Called from machine dependent
528 * layer at splclock(). The clock_id argument specifies the clock,
529 * and the clock_time argument gives that clock's current time.
534 mach_timespec_t
*clock_time
)
537 register alarm_t alrm1
;
538 register alarm_t alrm2
;
539 mach_timespec_t
*alarm_time
;
542 clock
= &clock_list
[clock_id
];
545 * Update clock alarm list. All alarms that are due are moved
546 * to the alarmdone list to be serviced by the alarm_thread.
550 alrm1
= (alarm_t
) &clock
->cl_alarm
;
551 while (alrm2
= alrm1
->al_next
) {
552 alarm_time
= &alrm2
->al_time
;
553 if (CMP_MACH_TIMESPEC(alarm_time
, clock_time
) > 0)
557 * Alarm has expired, so remove it from the
560 if (alrm1
->al_next
= alrm2
->al_next
)
561 (alrm1
->al_next
)->al_prev
= alrm1
;
564 * If a clock_sleep() alarm, wakeup the thread
565 * which issued the clock_sleep() call.
567 if (alrm2
->al_status
== ALARM_SLEEP
) {
569 alrm2
->al_status
= ALARM_DONE
;
570 alrm2
->al_time
= *clock_time
;
571 thread_wakeup((event_t
)alrm2
);
575 * If a clock_alarm() alarm, place the alarm on
576 * the alarm done list and schedule the alarm
577 * delivery mechanism.
580 assert(alrm2
->al_status
== ALARM_CLOCK
);
581 if (alrm2
->al_next
= alrmdone
)
582 alrmdone
->al_prev
= alrm2
;
584 thread_call_enter(&alarm_deliver
);
585 alrm2
->al_prev
= (alarm_t
) &alrmdone
;
587 alrm2
->al_status
= ALARM_DONE
;
588 alrm2
->al_time
= *clock_time
;
593 * Setup the clock dependent layer to deliver another
594 * interrupt for the next pending alarm.
597 (*clock
->cl_ops
->c_setalrm
)(alarm_time
);
602 * ALARM DELIVERY ROUTINES.
607 thread_call_param_t p0
,
608 thread_call_param_t p1
)
610 register alarm_t alrm
;
615 while (alrm
= alrmdone
) {
616 if (alrmdone
= alrm
->al_next
)
617 alrmdone
->al_prev
= (alarm_t
) &alrmdone
;
620 code
= (alrm
->al_status
== ALARM_DONE
? KERN_SUCCESS
: KERN_ABORTED
);
621 if (alrm
->al_port
!= IP_NULL
) {
622 /* Deliver message to designated port */
623 if (IP_VALID(alrm
->al_port
)) {
624 clock_alarm_reply(alrm
->al_port
, alrm
->al_port_type
, code
,
625 alrm
->al_type
, alrm
->al_time
);
629 alrm
->al_status
= ALARM_FREE
;
630 alrm
->al_next
= alrmfree
;
634 panic("clock_alarm_deliver");
641 * CLOCK PRIVATE SERVICING SUBROUTINES.
645 * Flush all pending alarms on a clock. All alarms
646 * are activated and timestamped correctly, so any
647 * programs waiting on alarms/threads will proceed
648 * with accurate information.
655 register alarm_t alrm1
, alrm2
;
659 * Flush all outstanding alarms.
662 alrm1
= (alarm_t
) &clock
->cl_alarm
;
663 while (alrm2
= alrm1
->al_next
) {
665 * Remove alarm from the clock alarm list.
667 if (alrm1
->al_next
= alrm2
->al_next
)
668 (alrm1
->al_next
)->al_prev
= alrm1
;
671 * If a clock_sleep() alarm, wakeup the thread
672 * which issued the clock_sleep() call.
674 if (alrm2
->al_status
== ALARM_SLEEP
) {
676 thread_wakeup((event_t
)alrm2
);
680 * If a clock_alarm() alarm, place the alarm on
681 * the alarm done list and wakeup the dedicated
682 * kernel alarm_thread to service the alarm.
684 assert(alrm2
->al_status
== ALARM_CLOCK
);
685 if (alrm2
->al_next
= alrmdone
)
686 alrmdone
->al_prev
= alrm2
;
688 thread_wakeup((event_t
)&alrmdone
);
689 alrm2
->al_prev
= (alarm_t
) &alrmdone
;
697 * Post an alarm on a clock's active alarm list. The alarm is
698 * inserted in time-order into the clock's active alarm list.
699 * Always called from within a LOCK_CLOCK() code section.
707 register alarm_t alrm1
, alrm2
;
708 mach_timespec_t
*alarm_time
;
709 mach_timespec_t
*queue_time
;
712 * Traverse alarm list until queue time is greater
713 * than alarm time, then insert alarm.
715 alarm_time
= &alarm
->al_time
;
716 alrm1
= (alarm_t
) &clock
->cl_alarm
;
717 while (alrm2
= alrm1
->al_next
) {
718 queue_time
= &alrm2
->al_time
;
719 if (CMP_MACH_TIMESPEC(queue_time
, alarm_time
) > 0)
723 alrm1
->al_next
= alarm
;
724 alarm
->al_next
= alrm2
;
725 alarm
->al_prev
= alrm1
;
727 alrm2
->al_prev
= alarm
;
730 * If the inserted alarm is the 'earliest' alarm,
731 * reset the device layer alarm time accordingly.
733 if (clock
->cl_alarm
.al_next
== alarm
)
734 (*clock
->cl_ops
->c_setalrm
)(alarm_time
);
738 * Check the validity of 'alarm_time' and 'alarm_type'. If either
739 * argument is invalid, return a negative value. If the 'alarm_time'
740 * is now, return a 0 value. If the 'alarm_time' is in the future,
741 * return a positive value.
746 alarm_type_t alarm_type
,
747 mach_timespec_t
*alarm_time
,
748 mach_timespec_t
*clock_time
)
752 if (BAD_ALRMTYPE(alarm_type
))
754 if (BAD_MACH_TIMESPEC(alarm_time
))
756 if ((alarm_type
& ALRMTYPE
) == TIME_RELATIVE
)
757 ADD_MACH_TIMESPEC(alarm_time
, clock_time
);
759 result
= CMP_MACH_TIMESPEC(alarm_time
, clock_time
);
761 return ((result
>= 0)? result
: 0);
765 clock_get_system_value(void)
767 clock_t clock
= &clock_list
[SYSTEM_CLOCK
];
768 mach_timespec_t value
;
770 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
776 clock_get_calendar_value(void)
778 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
779 mach_timespec_t value
= MACH_TIMESPEC_ZERO
;
781 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
787 clock_set_calendar_value(
788 mach_timespec_t value
)
790 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
792 (void) (*clock
->cl_ops
->c_settime
)(&value
);
796 clock_deadline_for_periodic_event(
797 AbsoluteTime interval
,
798 AbsoluteTime abstime
,
799 AbsoluteTime
*deadline
)
801 assert(AbsoluteTime_to_scalar(&interval
) != 0);
803 ADD_ABSOLUTETIME(deadline
, &interval
);
805 if ( AbsoluteTime_to_scalar(deadline
) <=
806 AbsoluteTime_to_scalar(&abstime
) ) {
808 clock_get_uptime(&abstime
);
809 ADD_ABSOLUTETIME(deadline
, &interval
);
811 if ( AbsoluteTime_to_scalar(deadline
) <=
812 AbsoluteTime_to_scalar(&abstime
) ) {
814 ADD_ABSOLUTETIME(deadline
, &interval
);
822 uint32_t *abs_to_ns_numer
,
823 uint32_t *abs_to_ns_denom
,
824 uint32_t *proc_to_abs_numer
,
825 uint32_t *proc_to_abs_denom
)
827 mach_timebase_info_data_t info
;
830 clock_timebase_info(&info
);
832 copyout((void *)&one
, (void *)delta
, sizeof (uint32_t));
834 copyout((void *)&info
.numer
, (void *)abs_to_ns_numer
, sizeof (uint32_t));
835 copyout((void *)&info
.denom
, (void *)abs_to_ns_denom
, sizeof (uint32_t));
837 copyout((void *)&one
, (void *)proc_to_abs_numer
, sizeof (uint32_t));
838 copyout((void *)&one
, (void *)proc_to_abs_denom
, sizeof (uint32_t));
843 mach_timebase_info_t out_info
)
845 mach_timebase_info_data_t info
;
847 clock_timebase_info(&info
);
849 copyout((void *)&info
, (void *)out_info
, sizeof (info
));
851 return (KERN_SUCCESS
);
860 assert_wait((event_t
)&mach_wait_until
, THREAD_ABORTSAFE
);
861 thread_set_timer_deadline(scalar_to_AbsoluteTime(&deadline
));
862 wait_result
= thread_block((void (*)) 0);
863 if (wait_result
!= THREAD_TIMED_OUT
)
864 thread_cancel_timer();
866 return ((wait_result
== THREAD_INTERRUPTED
)? KERN_ABORTED
: KERN_SUCCESS
);