2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
30 * Purpose: Routines for the creation and use of kernel
31 * alarm clock services. This file and the ipc
32 * routines in kern/ipc_clock.c constitute the
33 * machine-independent clock service layer.
37 #include <mach_host.h>
39 #include <mach/boolean.h>
40 #include <mach/processor_info.h>
41 #include <mach/vm_param.h>
42 #include <machine/mach_param.h>
43 #include <kern/cpu_number.h>
44 #include <kern/misc_protos.h>
45 #include <kern/lock.h>
46 #include <kern/host.h>
48 #include <kern/thread.h>
49 #include <kern/thread_swap.h>
50 #include <kern/ipc_host.h>
51 #include <kern/clock.h>
52 #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 decl_simple_lock_data(static,calend_adjlock
)
77 static int64_t calend_adjtotal
;
78 static uint32_t calend_adjdelta
;
80 static timer_call_data_t calend_adjcall
;
81 static uint64_t calend_adjinterval
, calend_adjdeadline
;
83 /* backwards compatibility */
84 int hz
= HZ
; /* GET RID OF THIS !!! */
85 int tick
= (1000000 / HZ
); /* GET RID OF THIS !!! */
87 /* external declarations */
88 extern struct clock clock_list
[];
89 extern int clock_count
;
91 /* local clock subroutines */
103 alarm_type_t alarm_type
,
104 mach_timespec_t
*alarm_time
,
105 mach_timespec_t
*clock_time
);
108 void clock_alarm_deliver(
109 thread_call_param_t p0
,
110 thread_call_param_t p1
);
113 void clock_calend_adjust(
114 timer_call_param_t p0
,
115 timer_call_param_t p1
);
118 * Macros to lock/unlock clock system.
120 #define LOCK_CLOCK(s) \
122 simple_lock(&ClockLock);
124 #define UNLOCK_CLOCK(s) \
125 simple_unlock(&ClockLock); \
129 * Configure the clock system. (Not sure if we need this,
130 * as separate from clock_init()).
138 if (cpu_number() != master_cpu
)
139 panic("clock_config");
142 * Configure clock devices.
144 simple_lock_init(&calend_adjlock
, ETAP_MISC_CLOCK
);
145 simple_lock_init(&ClockLock
, ETAP_MISC_CLOCK
);
146 for (i
= 0; i
< clock_count
; i
++) {
147 clock
= &clock_list
[i
];
149 if ((*clock
->cl_ops
->c_config
)() == 0)
154 /* start alarm sequence numbers at 0 */
159 * Initialize the clock system.
168 * Initialize basic clock structures.
170 for (i
= 0; i
< clock_count
; i
++) {
171 clock
= &clock_list
[i
];
173 (*clock
->cl_ops
->c_init
)();
178 * Initialize the clock ipc service facility.
181 clock_service_create(void)
186 mk_timer_initialize();
189 * Initialize ipc clock services.
191 for (i
= 0; i
< clock_count
; i
++) {
192 clock
= &clock_list
[i
];
194 ipc_clock_init(clock
);
195 ipc_clock_enable(clock
);
199 timer_call_setup(&calend_adjcall
, clock_calend_adjust
, NULL
);
202 * Initialize clock service alarms.
204 i
= sizeof(struct alarm
);
205 alarm_zone
= zinit(i
, (4096/i
)*i
, 10*i
, "alarms");
207 thread_call_setup(&alarm_deliver
, clock_alarm_deliver
, NULL
);
211 * Get the service port on a clock.
214 host_get_clock_service(
217 clock_t *clock
) /* OUT */
219 if (host
== HOST_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
221 return (KERN_INVALID_ARGUMENT
);
224 *clock
= &clock_list
[clock_id
];
225 if ((*clock
)->cl_ops
== 0)
226 return (KERN_FAILURE
);
227 return (KERN_SUCCESS
);
231 * Get the control port on a clock.
234 host_get_clock_control(
235 host_priv_t host_priv
,
237 clock_t *clock
) /* OUT */
239 if (host_priv
== HOST_PRIV_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
241 return (KERN_INVALID_ARGUMENT
);
244 *clock
= &clock_list
[clock_id
];
245 if ((*clock
)->cl_ops
== 0)
246 return (KERN_FAILURE
);
247 return (KERN_SUCCESS
);
251 * Get the current clock time.
256 mach_timespec_t
*cur_time
) /* OUT */
258 if (clock
== CLOCK_NULL
)
259 return (KERN_INVALID_ARGUMENT
);
260 return ((*clock
->cl_ops
->c_gettime
)(cur_time
));
264 * Get clock attributes.
267 clock_get_attributes(
269 clock_flavor_t flavor
,
270 clock_attr_t attr
, /* OUT */
271 mach_msg_type_number_t
*count
) /* IN/OUT */
273 kern_return_t (*getattr
)(
274 clock_flavor_t flavor
,
276 mach_msg_type_number_t
*count
);
278 if (clock
== CLOCK_NULL
)
279 return (KERN_INVALID_ARGUMENT
);
280 if (getattr
= clock
->cl_ops
->c_getattr
)
281 return((*getattr
)(flavor
, attr
, count
));
283 return (KERN_FAILURE
);
287 * Set the current clock time.
292 mach_timespec_t new_time
)
294 mach_timespec_t
*clock_time
;
295 kern_return_t (*settime
)(
296 mach_timespec_t
*clock_time
);
299 mach_timespec_t
*clock_time
);
301 if (clock
== CLOCK_NULL
)
302 return (KERN_INVALID_ARGUMENT
);
303 if ((settime
= clock
->cl_ops
->c_settime
) == 0)
304 return (KERN_FAILURE
);
305 if (settime
== calend_settime
)
306 return (KERN_FAILURE
);
307 clock_time
= &new_time
;
308 if (BAD_MACH_TIMESPEC(clock_time
))
309 return (KERN_INVALID_VALUE
);
312 * Flush all outstanding alarms.
319 return ((*settime
)(clock_time
));
323 * Set the clock alarm resolution.
326 clock_set_attributes(
328 clock_flavor_t flavor
,
330 mach_msg_type_number_t count
)
332 kern_return_t (*setattr
)(
333 clock_flavor_t flavor
,
335 mach_msg_type_number_t count
);
337 if (clock
== CLOCK_NULL
)
338 return (KERN_INVALID_ARGUMENT
);
339 if (setattr
= clock
->cl_ops
->c_setattr
)
340 return ((*setattr
)(flavor
, attr
, count
));
342 return (KERN_FAILURE
);
346 * Setup a clock alarm.
351 alarm_type_t alarm_type
,
352 mach_timespec_t alarm_time
,
353 ipc_port_t alarm_port
,
354 mach_msg_type_name_t alarm_port_type
)
357 mach_timespec_t clock_time
;
359 kern_return_t reply_code
;
362 if (clock
== CLOCK_NULL
)
363 return (KERN_INVALID_ARGUMENT
);
364 if (clock
->cl_ops
->c_setalrm
== 0)
365 return (KERN_FAILURE
);
366 if (IP_VALID(alarm_port
) == 0)
367 return (KERN_INVALID_CAPABILITY
);
370 * Check alarm parameters. If parameters are invalid,
371 * send alarm message immediately.
373 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
374 chkstat
= check_time(alarm_type
, &alarm_time
, &clock_time
);
376 reply_code
= (chkstat
< 0 ? KERN_INVALID_VALUE
: KERN_SUCCESS
);
377 clock_alarm_reply(alarm_port
, alarm_port_type
,
378 reply_code
, alarm_type
, clock_time
);
379 return (KERN_SUCCESS
);
383 * Get alarm and add to clock alarm list.
387 if ((alarm
= alrmfree
) == 0) {
389 alarm
= (alarm_t
) zalloc(alarm_zone
);
391 return (KERN_RESOURCE_SHORTAGE
);
395 alrmfree
= alarm
->al_next
;
397 alarm
->al_status
= ALARM_CLOCK
;
398 alarm
->al_time
= alarm_time
;
399 alarm
->al_type
= alarm_type
;
400 alarm
->al_port
= alarm_port
;
401 alarm
->al_port_type
= alarm_port_type
;
402 alarm
->al_clock
= clock
;
403 alarm
->al_seqno
= alrm_seqno
++;
404 post_alarm(clock
, alarm
);
407 return (KERN_SUCCESS
);
411 * Sleep on a clock. System trap. User-level libmach clock_sleep
412 * interface call takes a mach_timespec_t sleep_time argument which it
413 * converts to sleep_sec and sleep_nsec arguments which are then
414 * passed to clock_sleep_trap.
418 mach_port_name_t clock_name
,
419 sleep_type_t sleep_type
,
422 mach_timespec_t
*wakeup_time
)
425 mach_timespec_t swtime
;
426 kern_return_t rvalue
;
429 * Convert the trap parameters.
431 if (clock_name
!= MACH_PORT_NULL
)
432 clock
= port_name_to_clock(clock_name
);
434 clock
= &clock_list
[SYSTEM_CLOCK
];
436 swtime
.tv_sec
= sleep_sec
;
437 swtime
.tv_nsec
= sleep_nsec
;
440 * Call the actual clock_sleep routine.
442 rvalue
= clock_sleep_internal(clock
, sleep_type
, &swtime
);
445 * Return current time as wakeup time.
447 if (rvalue
!= KERN_INVALID_ARGUMENT
&& rvalue
!= KERN_FAILURE
) {
448 copyout((char *)&swtime
, (char *)wakeup_time
,
449 sizeof(mach_timespec_t
));
455 * Kernel internally callable clock sleep routine. The calling
456 * thread is suspended until the requested sleep time is reached.
459 clock_sleep_internal(
461 sleep_type_t sleep_type
,
462 mach_timespec_t
*sleep_time
)
465 mach_timespec_t clock_time
;
466 kern_return_t rvalue
;
470 if (clock
== CLOCK_NULL
)
471 return (KERN_INVALID_ARGUMENT
);
472 if (clock
->cl_ops
->c_setalrm
== 0)
473 return (KERN_FAILURE
);
476 * Check sleep parameters. If parameters are invalid
477 * return an error, otherwise post alarm request.
479 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
481 chkstat
= check_time(sleep_type
, sleep_time
, &clock_time
);
483 return (KERN_INVALID_VALUE
);
484 rvalue
= KERN_SUCCESS
;
486 wait_result_t wait_result
;
489 * Get alarm and add to clock alarm list.
493 if ((alarm
= alrmfree
) == 0) {
495 alarm
= (alarm_t
) zalloc(alarm_zone
);
497 return (KERN_RESOURCE_SHORTAGE
);
501 alrmfree
= alarm
->al_next
;
504 * Wait for alarm to occur.
506 wait_result
= assert_wait((event_t
)alarm
, THREAD_ABORTSAFE
);
507 if (wait_result
== THREAD_WAITING
) {
508 alarm
->al_time
= *sleep_time
;
509 alarm
->al_status
= ALARM_SLEEP
;
510 post_alarm(clock
, alarm
);
513 wait_result
= thread_block(THREAD_CONTINUE_NULL
);
516 * Note if alarm expired normally or whether it
517 * was aborted. If aborted, delete alarm from
518 * clock alarm list. Return alarm to free list.
521 if (alarm
->al_status
!= ALARM_DONE
) {
522 assert(wait_result
!= THREAD_AWAKENED
);
523 if ((alarm
->al_prev
)->al_next
= alarm
->al_next
)
524 (alarm
->al_next
)->al_prev
= alarm
->al_prev
;
525 rvalue
= KERN_ABORTED
;
527 *sleep_time
= alarm
->al_time
;
528 alarm
->al_status
= ALARM_FREE
;
530 assert(wait_result
== THREAD_INTERRUPTED
);
531 assert(alarm
->al_status
== ALARM_FREE
);
532 rvalue
= KERN_ABORTED
;
534 alarm
->al_next
= alrmfree
;
539 *sleep_time
= clock_time
;
545 * CLOCK INTERRUPT SERVICE ROUTINES.
549 * Service clock alarm interrupts. Called from machine dependent
550 * layer at splclock(). The clock_id argument specifies the clock,
551 * and the clock_time argument gives that clock's current time.
556 mach_timespec_t
*clock_time
)
559 register alarm_t alrm1
;
560 register alarm_t alrm2
;
561 mach_timespec_t
*alarm_time
;
564 clock
= &clock_list
[clock_id
];
567 * Update clock alarm list. All alarms that are due are moved
568 * to the alarmdone list to be serviced by the alarm_thread.
572 alrm1
= (alarm_t
) &clock
->cl_alarm
;
573 while (alrm2
= alrm1
->al_next
) {
574 alarm_time
= &alrm2
->al_time
;
575 if (CMP_MACH_TIMESPEC(alarm_time
, clock_time
) > 0)
579 * Alarm has expired, so remove it from the
582 if (alrm1
->al_next
= alrm2
->al_next
)
583 (alrm1
->al_next
)->al_prev
= alrm1
;
586 * If a clock_sleep() alarm, wakeup the thread
587 * which issued the clock_sleep() call.
589 if (alrm2
->al_status
== ALARM_SLEEP
) {
591 alrm2
->al_status
= ALARM_DONE
;
592 alrm2
->al_time
= *clock_time
;
593 thread_wakeup((event_t
)alrm2
);
597 * If a clock_alarm() alarm, place the alarm on
598 * the alarm done list and schedule the alarm
599 * delivery mechanism.
602 assert(alrm2
->al_status
== ALARM_CLOCK
);
603 if (alrm2
->al_next
= alrmdone
)
604 alrmdone
->al_prev
= alrm2
;
606 thread_call_enter(&alarm_deliver
);
607 alrm2
->al_prev
= (alarm_t
) &alrmdone
;
609 alrm2
->al_status
= ALARM_DONE
;
610 alrm2
->al_time
= *clock_time
;
615 * Setup the clock dependent layer to deliver another
616 * interrupt for the next pending alarm.
619 (*clock
->cl_ops
->c_setalrm
)(alarm_time
);
624 * ALARM DELIVERY ROUTINES.
629 thread_call_param_t p0
,
630 thread_call_param_t p1
)
632 register alarm_t alrm
;
637 while (alrm
= alrmdone
) {
638 if (alrmdone
= alrm
->al_next
)
639 alrmdone
->al_prev
= (alarm_t
) &alrmdone
;
642 code
= (alrm
->al_status
== ALARM_DONE
? KERN_SUCCESS
: KERN_ABORTED
);
643 if (alrm
->al_port
!= IP_NULL
) {
644 /* Deliver message to designated port */
645 if (IP_VALID(alrm
->al_port
)) {
646 clock_alarm_reply(alrm
->al_port
, alrm
->al_port_type
, code
,
647 alrm
->al_type
, alrm
->al_time
);
651 alrm
->al_status
= ALARM_FREE
;
652 alrm
->al_next
= alrmfree
;
656 panic("clock_alarm_deliver");
663 * CLOCK PRIVATE SERVICING SUBROUTINES.
667 * Flush all pending alarms on a clock. All alarms
668 * are activated and timestamped correctly, so any
669 * programs waiting on alarms/threads will proceed
670 * with accurate information.
677 register alarm_t alrm1
, alrm2
;
681 * Flush all outstanding alarms.
684 alrm1
= (alarm_t
) &clock
->cl_alarm
;
685 while (alrm2
= alrm1
->al_next
) {
687 * Remove alarm from the clock alarm list.
689 if (alrm1
->al_next
= alrm2
->al_next
)
690 (alrm1
->al_next
)->al_prev
= alrm1
;
693 * If a clock_sleep() alarm, wakeup the thread
694 * which issued the clock_sleep() call.
696 if (alrm2
->al_status
== ALARM_SLEEP
) {
698 thread_wakeup((event_t
)alrm2
);
702 * If a clock_alarm() alarm, place the alarm on
703 * the alarm done list and wakeup the dedicated
704 * kernel alarm_thread to service the alarm.
706 assert(alrm2
->al_status
== ALARM_CLOCK
);
707 if (alrm2
->al_next
= alrmdone
)
708 alrmdone
->al_prev
= alrm2
;
710 thread_wakeup((event_t
)&alrmdone
);
711 alrm2
->al_prev
= (alarm_t
) &alrmdone
;
719 * Post an alarm on a clock's active alarm list. The alarm is
720 * inserted in time-order into the clock's active alarm list.
721 * Always called from within a LOCK_CLOCK() code section.
729 register alarm_t alrm1
, alrm2
;
730 mach_timespec_t
*alarm_time
;
731 mach_timespec_t
*queue_time
;
734 * Traverse alarm list until queue time is greater
735 * than alarm time, then insert alarm.
737 alarm_time
= &alarm
->al_time
;
738 alrm1
= (alarm_t
) &clock
->cl_alarm
;
739 while (alrm2
= alrm1
->al_next
) {
740 queue_time
= &alrm2
->al_time
;
741 if (CMP_MACH_TIMESPEC(queue_time
, alarm_time
) > 0)
745 alrm1
->al_next
= alarm
;
746 alarm
->al_next
= alrm2
;
747 alarm
->al_prev
= alrm1
;
749 alrm2
->al_prev
= alarm
;
752 * If the inserted alarm is the 'earliest' alarm,
753 * reset the device layer alarm time accordingly.
755 if (clock
->cl_alarm
.al_next
== alarm
)
756 (*clock
->cl_ops
->c_setalrm
)(alarm_time
);
760 * Check the validity of 'alarm_time' and 'alarm_type'. If either
761 * argument is invalid, return a negative value. If the 'alarm_time'
762 * is now, return a 0 value. If the 'alarm_time' is in the future,
763 * return a positive value.
768 alarm_type_t alarm_type
,
769 mach_timespec_t
*alarm_time
,
770 mach_timespec_t
*clock_time
)
774 if (BAD_ALRMTYPE(alarm_type
))
776 if (BAD_MACH_TIMESPEC(alarm_time
))
778 if ((alarm_type
& ALRMTYPE
) == TIME_RELATIVE
)
779 ADD_MACH_TIMESPEC(alarm_time
, clock_time
);
781 result
= CMP_MACH_TIMESPEC(alarm_time
, clock_time
);
783 return ((result
>= 0)? result
: 0);
787 clock_get_system_value(void)
789 clock_t clock
= &clock_list
[SYSTEM_CLOCK
];
790 mach_timespec_t value
;
792 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
798 clock_get_calendar_value(void)
800 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
801 mach_timespec_t value
= MACH_TIMESPEC_ZERO
;
803 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
809 clock_set_calendar_value(
810 mach_timespec_t value
)
812 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
814 (void) (*clock
->cl_ops
->c_settime
)(&value
);
818 clock_deadline_for_periodic_event(
823 assert(interval
!= 0);
825 *deadline
+= interval
;
827 if (*deadline
<= abstime
) {
829 clock_get_uptime(&abstime
);
830 *deadline
+= interval
;
832 if (*deadline
<= abstime
) {
834 *deadline
+= interval
;
842 uint32_t *abs_to_ns_numer
,
843 uint32_t *abs_to_ns_denom
,
844 uint32_t *proc_to_abs_numer
,
845 uint32_t *proc_to_abs_denom
)
847 mach_timebase_info_data_t info
;
850 clock_timebase_info(&info
);
852 copyout((void *)&one
, (void *)delta
, sizeof (uint32_t));
854 copyout((void *)&info
.numer
, (void *)abs_to_ns_numer
, sizeof (uint32_t));
855 copyout((void *)&info
.denom
, (void *)abs_to_ns_denom
, sizeof (uint32_t));
857 copyout((void *)&one
, (void *)proc_to_abs_numer
, sizeof (uint32_t));
858 copyout((void *)&one
, (void *)proc_to_abs_denom
, sizeof (uint32_t));
863 mach_timebase_info_t out_info
)
865 mach_timebase_info_data_t info
;
867 clock_timebase_info(&info
);
869 copyout((void *)&info
, (void *)out_info
, sizeof (info
));
871 return (KERN_SUCCESS
);
880 wait_result
= assert_wait((event_t
)&mach_wait_until
, THREAD_ABORTSAFE
);
881 if (wait_result
== THREAD_WAITING
) {
882 thread_set_timer_deadline(deadline
);
883 wait_result
= thread_block(THREAD_CONTINUE_NULL
);
884 if (wait_result
!= THREAD_TIMED_OUT
)
885 thread_cancel_timer();
888 return ((wait_result
== THREAD_INTERRUPTED
)? KERN_ABORTED
: KERN_SUCCESS
);
892 clock_set_calendar_adjtime(
900 simple_lock(&calend_adjlock
);
902 if (calend_adjinterval
== 0)
903 clock_interval_to_absolutetime_interval(10000, NSEC_PER_USEC
,
904 &calend_adjinterval
);
906 ototal
= calend_adjtotal
;
920 calend_adjtotal
= total
;
921 calend_adjdelta
= delta
;
923 if (calend_adjdeadline
>= calend_adjinterval
)
924 calend_adjdeadline
-= calend_adjinterval
;
925 clock_get_uptime(&abstime
);
926 clock_deadline_for_periodic_event(calend_adjinterval
, abstime
,
927 &calend_adjdeadline
);
929 timer_call_enter(&calend_adjcall
, calend_adjdeadline
);
934 timer_call_cancel(&calend_adjcall
);
937 simple_unlock(&calend_adjlock
);
945 timer_call_param_t p0
,
946 timer_call_param_t p1
)
951 simple_lock(&calend_adjlock
);
953 if (calend_adjtotal
> 0) {
954 clock_adjust_calendar((clock_res_t
)calend_adjdelta
);
955 calend_adjtotal
-= calend_adjdelta
;
957 if (calend_adjdelta
> calend_adjtotal
)
958 calend_adjdelta
= calend_adjtotal
;
961 if (calend_adjtotal
< 0) {
962 clock_adjust_calendar(-(clock_res_t
)calend_adjdelta
);
963 calend_adjtotal
+= calend_adjdelta
;
965 if (calend_adjdelta
> -calend_adjtotal
)
966 calend_adjdelta
= -calend_adjtotal
;
969 if (calend_adjtotal
!= 0) {
972 clock_get_uptime(&abstime
);
973 clock_deadline_for_periodic_event(calend_adjinterval
, abstime
,
974 &calend_adjdeadline
);
976 timer_call_enter(&calend_adjcall
, calend_adjdeadline
);
979 simple_unlock(&calend_adjlock
);