X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/2d21ac55c334faf3a56e5634905ed6987fc787d4..3e170ce000f1506b7b5d2c5c7faec85ceabb573d:/osfmk/kern/clock.c diff --git a/osfmk/kern/clock.c b/osfmk/kern/clock.c index 4a762bec5..f493edc67 100644 --- a/osfmk/kern/clock.c +++ b/osfmk/kern/clock.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2008 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -33,7 +33,6 @@ #include -#include #include #include #include @@ -47,13 +46,22 @@ #include #include +#include + uint32_t hz_tick_interval = 1; -#if CONFIG_DTRACE -static void clock_track_calend_nowait(void); -#endif -decl_simple_lock_data(static,clock_lock) +decl_simple_lock_data(,clock_lock) + +#define clock_lock() \ + simple_lock(&clock_lock) + +#define clock_unlock() \ + simple_unlock(&clock_lock) + +#define clock_lock_init() \ + simple_lock_init(&clock_lock, 0) + /* * Time of day (calendar) variables. @@ -65,20 +73,17 @@ decl_simple_lock_data(static,clock_lock) * where CONV converts absolute time units into seconds and a fraction. */ static struct clock_calend { - uint64_t epoch; uint64_t offset; - int64_t adjtotal; /* Nanosecond remaining total adjustment */ - uint64_t adjdeadline; /* Absolute time value for next adjustment period */ - uint32_t adjinterval; /* Absolute time interval of adjustment period */ + uint64_t epoch_absolute; + int32_t adjdelta; /* Nanosecond time delta for this adjustment period */ uint64_t adjstart; /* Absolute time value for start of this adjustment period */ uint32_t adjoffset; /* Absolute time offset for this adjustment period as absolute value */ - uint32_t adjactive; - timer_call_data_t adjcall; } clock_calend; -#if CONFIG_DTRACE +#if CONFIG_DTRACE + /* * Unlocked calendar flipflop; this is used to track a clock_calend such * that we can safely access a snapshot of a valid clock_calend structure @@ -93,6 +98,9 @@ static struct unlocked_clock_calend { struct clock_calend calend; /* copy of calendar */ uint32_t gen; /* generation count */ } flipflop[ 2]; + +static void clock_track_calend_nowait(void); + #endif /* @@ -102,16 +110,25 @@ static struct unlocked_clock_calend { #define calend_adjskew (40 * NSEC_PER_USEC) /* "standard" skew, ns / period */ #define calend_adjbig (NSEC_PER_SEC) /* use 10x skew above adjbig ns */ +static int64_t calend_adjtotal; /* Nanosecond remaining total adjustment */ +static uint64_t calend_adjdeadline; /* Absolute time value for next adjustment period */ +static uint32_t calend_adjinterval; /* Absolute time interval of adjustment period */ + +static timer_call_data_t calend_adjcall; +static uint32_t calend_adjactive; + static uint32_t calend_set_adjustment( - int32_t *secs, - int32_t *microsecs); + long *secs, + int *microsecs); static void calend_adjust_call(void); static uint32_t calend_adjust(void); -static thread_call_data_t calend_wakecall; - -extern void IOKitResetTime(void); +void _clock_delay_until_deadline(uint64_t interval, + uint64_t deadline); +void _clock_delay_until_deadline_with_leeway(uint64_t interval, + uint64_t deadline, + uint64_t leeway); static uint64_t clock_boottime; /* Seconds boottime epoch */ @@ -126,7 +143,7 @@ MACRO_END #define TIME_SUB(rsecs, secs, rfrac, frac, unit) \ MACRO_BEGIN \ - if ((int32_t)((rfrac) -= (frac)) < 0) { \ + if ((int)((rfrac) -= (frac)) < 0) { \ (rfrac) += (unit); \ (rsecs) -= 1; \ } \ @@ -141,17 +158,11 @@ MACRO_END void clock_config(void) { - simple_lock_init(&clock_lock, 0); + clock_lock_init(); - timer_call_setup(&clock_calend.adjcall, (timer_call_func_t)calend_adjust_call, NULL); - thread_call_setup(&calend_wakecall, (thread_call_func_t)IOKitResetTime, NULL); + timer_call_setup(&calend_adjcall, (timer_call_func_t)calend_adjust_call, NULL); clock_oldconfig(); - - /* - * Initialize the timer callouts. - */ - timer_call_initialize(); } /* @@ -179,10 +190,10 @@ clock_timebase_init(void) uint64_t abstime; nanoseconds_to_absolutetime(calend_adjperiod, &abstime); - clock_calend.adjinterval = abstime; + calend_adjinterval = (uint32_t)abstime; nanoseconds_to_absolutetime(NSEC_PER_SEC / 100, &abstime); - hz_tick_interval = abstime; + hz_tick_interval = (uint32_t)abstime; sched_timebase_init(); } @@ -218,22 +229,50 @@ mach_timebase_info_trap( */ void clock_get_calendar_microtime( - uint32_t *secs, - uint32_t *microsecs) + clock_sec_t *secs, + clock_usec_t *microsecs) +{ + clock_get_calendar_absolute_and_microtime(secs, microsecs, NULL); +} + +/* + * clock_get_calendar_absolute_and_microtime: + * + * Returns the current calendar value, + * microseconds as the fraction. Also + * returns mach_absolute_time if abstime + * is not NULL. + */ +void +clock_get_calendar_absolute_and_microtime( + clock_sec_t *secs, + clock_usec_t *microsecs, + uint64_t *abstime) { uint64_t now; spl_t s; s = splclock(); - simple_lock(&clock_lock); + clock_lock(); now = mach_absolute_time(); + if (abstime) + *abstime = now; if (clock_calend.adjdelta < 0) { uint32_t t32; + /* + * Since offset is decremented during a negative adjustment, + * ensure that time increases monotonically without going + * temporarily backwards. + * If the delta has not yet passed, now is set to the start + * of the current adjustment period; otherwise, we're between + * the expiry of the delta and the next call to calend_adjust(), + * and we offset accordingly. + */ if (now > clock_calend.adjstart) { - t32 = now - clock_calend.adjstart; + t32 = (uint32_t)(now - clock_calend.adjstart); if (t32 > clock_calend.adjoffset) now -= clock_calend.adjoffset; @@ -246,9 +285,9 @@ clock_get_calendar_microtime( absolutetime_to_microtime(now, secs, microsecs); - *secs += clock_calend.epoch; + *secs += (clock_sec_t)clock_calend.epoch; - simple_unlock(&clock_lock); + clock_unlock(); splx(s); } @@ -264,14 +303,14 @@ clock_get_calendar_microtime( */ void clock_get_calendar_nanotime( - uint32_t *secs, - uint32_t *nanosecs) + clock_sec_t *secs, + clock_nsec_t *nanosecs) { uint64_t now; spl_t s; s = splclock(); - simple_lock(&clock_lock); + clock_lock(); now = mach_absolute_time(); @@ -279,7 +318,7 @@ clock_get_calendar_nanotime( uint32_t t32; if (now > clock_calend.adjstart) { - t32 = now - clock_calend.adjstart; + t32 = (uint32_t)(now - clock_calend.adjstart); if (t32 > clock_calend.adjoffset) now -= clock_calend.adjoffset; @@ -291,11 +330,12 @@ clock_get_calendar_nanotime( now += clock_calend.offset; absolutetime_to_microtime(now, secs, nanosecs); + *nanosecs *= NSEC_PER_USEC; - *secs += clock_calend.epoch; + *secs += (clock_sec_t)clock_calend.epoch; - simple_unlock(&clock_lock); + clock_unlock(); splx(s); } @@ -312,14 +352,14 @@ clock_get_calendar_nanotime( */ void clock_gettimeofday( - uint32_t *secs, - uint32_t *microsecs) + clock_sec_t *secs, + clock_usec_t *microsecs) { uint64_t now; spl_t s; s = splclock(); - simple_lock(&clock_lock); + clock_lock(); now = mach_absolute_time(); @@ -330,7 +370,7 @@ clock_gettimeofday( uint32_t t32; if (now > clock_calend.adjstart) { - t32 = now - clock_calend.adjstart; + t32 = (uint32_t)(now - clock_calend.adjstart); if (t32 > clock_calend.adjoffset) now -= clock_calend.adjoffset; @@ -342,10 +382,10 @@ clock_gettimeofday( absolutetime_to_microtime(now, secs, microsecs); - *secs += clock_calend.epoch; + *secs += (clock_sec_t)clock_calend.epoch; } - simple_unlock(&clock_lock); + clock_unlock(); splx(s); } @@ -363,18 +403,20 @@ clock_gettimeofday( */ void clock_set_calendar_microtime( - uint32_t secs, - uint32_t microsecs) + clock_sec_t secs, + clock_usec_t microsecs) { - uint32_t sys, microsys; - uint32_t newsecs; - spl_t s; + clock_sec_t sys; + clock_usec_t microsys; + clock_sec_t newsecs; + clock_usec_t newmicrosecs; + spl_t s; - newsecs = (microsecs < 500*USEC_PER_SEC)? - secs: secs + 1; + newsecs = secs; + newmicrosecs = microsecs; s = splclock(); - simple_lock(&clock_lock); + clock_lock(); commpage_disable_timestamp(); @@ -394,19 +436,23 @@ clock_set_calendar_microtime( * Set the new calendar epoch. */ clock_calend.epoch = secs; + nanoseconds_to_absolutetime((uint64_t)microsecs * NSEC_PER_USEC, &clock_calend.offset); + clock_interval_to_absolutetime_interval((uint32_t) secs, NSEC_PER_SEC, &clock_calend.epoch_absolute); + clock_calend.epoch_absolute += clock_calend.offset; + /* * Cancel any adjustment in progress. */ - clock_calend.adjdelta = clock_calend.adjtotal = 0; + calend_adjtotal = clock_calend.adjdelta = 0; - simple_unlock(&clock_lock); + clock_unlock(); /* * Set the new value for the platform clock. */ - PESetGMTTimeOfDay(newsecs); + PESetUTCTimeOfDay(newsecs, newmicrosecs); splx(s); @@ -429,19 +475,26 @@ clock_set_calendar_microtime( * * Also sends host notifications. */ + +uint64_t mach_absolutetime_asleep; +uint64_t mach_absolutetime_last_sleep; + void clock_initialize_calendar(void) { - uint32_t sys, microsys; - uint32_t microsecs = 0, secs = PEGetGMTTimeOfDay(); - spl_t s; + clock_sec_t sys, secs; + clock_usec_t microsys, microsecs; + uint64_t new_epoch; + spl_t s; + + PEGetUTCTimeOfDay(&secs, µsecs); s = splclock(); - simple_lock(&clock_lock); + clock_lock(); commpage_disable_timestamp(); - if ((int32_t)secs >= (int32_t)clock_boottime) { + if ((long)secs >= (long)clock_boottime) { /* * Initialize the boot time based on the platform clock. */ @@ -458,16 +511,35 @@ clock_initialize_calendar(void) /* * Set the new calendar epoch. */ + clock_calend.epoch = secs; + nanoseconds_to_absolutetime((uint64_t)microsecs * NSEC_PER_USEC, &clock_calend.offset); + clock_interval_to_absolutetime_interval((uint32_t) secs, NSEC_PER_SEC, &new_epoch); + new_epoch += clock_calend.offset; + + if (clock_calend.epoch_absolute) + { + mach_absolutetime_last_sleep = new_epoch - clock_calend.epoch_absolute; + mach_absolutetime_asleep += mach_absolutetime_last_sleep; + KERNEL_DEBUG_CONSTANT( + MACHDBG_CODE(DBG_MACH_CLOCK,MACH_EPOCH_CHANGE) | DBG_FUNC_NONE, + (uintptr_t) mach_absolutetime_last_sleep, + (uintptr_t) mach_absolutetime_asleep, + (uintptr_t) (mach_absolutetime_last_sleep >> 32), + (uintptr_t) (mach_absolutetime_asleep >> 32), + 0); + } + clock_calend.epoch_absolute = new_epoch; + /* * Cancel any adjustment in progress. */ - clock_calend.adjdelta = clock_calend.adjtotal = 0; + calend_adjtotal = clock_calend.adjdelta = 0; } - simple_unlock(&clock_lock); + clock_unlock(); splx(s); /* @@ -487,11 +559,19 @@ clock_initialize_calendar(void) */ void clock_get_boottime_nanotime( - uint32_t *secs, - uint32_t *nanosecs) + clock_sec_t *secs, + clock_nsec_t *nanosecs) { - *secs = clock_boottime; + spl_t s; + + s = splclock(); + clock_lock(); + + *secs = (clock_sec_t)clock_boottime; *nanosecs = 0; + + clock_unlock(); + splx(s); } /* @@ -504,82 +584,138 @@ clock_get_boottime_nanotime( */ void clock_adjtime( - int32_t *secs, - int32_t *microsecs) + long *secs, + int *microsecs) { uint32_t interval; spl_t s; s = splclock(); - simple_lock(&clock_lock); + clock_lock(); interval = calend_set_adjustment(secs, microsecs); if (interval != 0) { - clock_calend.adjdeadline = mach_absolute_time() + interval; - if (!timer_call_enter(&clock_calend.adjcall, clock_calend.adjdeadline)) - clock_calend.adjactive++; + calend_adjdeadline = mach_absolute_time() + interval; + if (!timer_call_enter(&calend_adjcall, calend_adjdeadline, TIMER_CALL_SYS_CRITICAL)) + calend_adjactive++; } else - if (timer_call_cancel(&clock_calend.adjcall)) - clock_calend.adjactive--; + if (timer_call_cancel(&calend_adjcall)) + calend_adjactive--; - simple_unlock(&clock_lock); + clock_unlock(); splx(s); } static uint32_t calend_set_adjustment( - int32_t *secs, - int32_t *microsecs) + long *secs, + int *microsecs) { uint64_t now, t64; int64_t total, ototal; uint32_t interval = 0; - total = (int64_t)*secs * NSEC_PER_SEC + *microsecs * NSEC_PER_USEC; + /* + * Compute the total adjustment time in nanoseconds. + */ + total = ((int64_t)*secs * (int64_t)NSEC_PER_SEC) + (*microsecs * (int64_t)NSEC_PER_USEC); + /* + * Disable commpage gettimeofday(). + */ commpage_disable_timestamp(); + /* + * Get current absolute time. + */ now = mach_absolute_time(); - ototal = clock_calend.adjtotal; + /* + * Save the old adjustment total for later return. + */ + ototal = calend_adjtotal; + /* + * Is a new correction specified? + */ if (total != 0) { + /* + * Set delta to the standard, small, adjustment skew. + */ int32_t delta = calend_adjskew; if (total > 0) { - if (total > calend_adjbig) + /* + * Positive adjustment. If greater than the preset 'big' + * threshold, slew at a faster rate, capping if necessary. + */ + if (total > (int64_t) calend_adjbig) delta *= 10; if (delta > total) - delta = total; + delta = (int32_t)total; + /* + * Convert the delta back from ns to absolute time and store in adjoffset. + */ nanoseconds_to_absolutetime((uint64_t)delta, &t64); - clock_calend.adjoffset = t64; + clock_calend.adjoffset = (uint32_t)t64; } else { - if (total < -calend_adjbig) + /* + * Negative adjustment; therefore, negate the delta. If + * greater than the preset 'big' threshold, slew at a faster + * rate, capping if necessary. + */ + if (total < (int64_t) -calend_adjbig) delta *= 10; delta = -delta; if (delta < total) - delta = total; + delta = (int32_t)total; + /* + * Save the current absolute time. Subsequent time operations occuring + * during this negative correction can make use of this value to ensure + * that time increases monotonically. + */ clock_calend.adjstart = now; + /* + * Convert the delta back from ns to absolute time and store in adjoffset. + */ nanoseconds_to_absolutetime((uint64_t)-delta, &t64); - clock_calend.adjoffset = t64; + clock_calend.adjoffset = (uint32_t)t64; } - clock_calend.adjtotal = total; + /* + * Store the total adjustment time in ns. + */ + calend_adjtotal = total; + + /* + * Store the delta for this adjustment period in ns. + */ clock_calend.adjdelta = delta; - interval = clock_calend.adjinterval; + /* + * Set the interval in absolute time for later return. + */ + interval = calend_adjinterval; + } + else { + /* + * No change; clear any prior adjustment. + */ + calend_adjtotal = clock_calend.adjdelta = 0; } - else - clock_calend.adjdelta = clock_calend.adjtotal = 0; + /* + * If an prior correction was in progress, return the + * remaining uncorrected time from it. + */ if (ototal != 0) { - *secs = ototal / NSEC_PER_SEC; - *microsecs = (ototal % NSEC_PER_SEC) / NSEC_PER_USEC; + *secs = (long)(ototal / (long)NSEC_PER_SEC); + *microsecs = (int)((ototal % (int)NSEC_PER_SEC) / (int)NSEC_PER_USEC); } else *secs = *microsecs = 0; @@ -598,20 +734,19 @@ calend_adjust_call(void) spl_t s; s = splclock(); - simple_lock(&clock_lock); + clock_lock(); - if (--clock_calend.adjactive == 0) { + if (--calend_adjactive == 0) { interval = calend_adjust(); if (interval != 0) { - clock_deadline_for_periodic_event(interval, mach_absolute_time(), - &clock_calend.adjdeadline); + clock_deadline_for_periodic_event(interval, mach_absolute_time(), &calend_adjdeadline); - if (!timer_call_enter(&clock_calend.adjcall, clock_calend.adjdeadline)) - clock_calend.adjactive++; + if (!timer_call_enter(&calend_adjcall, calend_adjdeadline, TIMER_CALL_SYS_CRITICAL)) + calend_adjactive++; } } - simple_unlock(&clock_lock); + clock_unlock(); splx(s); } @@ -631,32 +766,32 @@ calend_adjust(void) if (delta > 0) { clock_calend.offset += clock_calend.adjoffset; - clock_calend.adjtotal -= delta; - if (delta > clock_calend.adjtotal) { - clock_calend.adjdelta = delta = clock_calend.adjtotal; + calend_adjtotal -= delta; + if (delta > calend_adjtotal) { + clock_calend.adjdelta = delta = (int32_t)calend_adjtotal; nanoseconds_to_absolutetime((uint64_t)delta, &t64); - clock_calend.adjoffset = t64; + clock_calend.adjoffset = (uint32_t)t64; } } else - if (delta < 0) { - clock_calend.offset -= clock_calend.adjoffset; + if (delta < 0) { + clock_calend.offset -= clock_calend.adjoffset; - clock_calend.adjtotal -= delta; - if (delta < clock_calend.adjtotal) { - clock_calend.adjdelta = delta = clock_calend.adjtotal; + calend_adjtotal -= delta; + if (delta < calend_adjtotal) { + clock_calend.adjdelta = delta = (int32_t)calend_adjtotal; - nanoseconds_to_absolutetime((uint64_t)-delta, &t64); - clock_calend.adjoffset = t64; + nanoseconds_to_absolutetime((uint64_t)-delta, &t64); + clock_calend.adjoffset = (uint32_t)t64; + } + + if (clock_calend.adjdelta != 0) + clock_calend.adjstart = now; } - if (clock_calend.adjdelta != 0) - clock_calend.adjstart = now; - } - if (clock_calend.adjdelta != 0) - interval = clock_calend.adjinterval; + interval = calend_adjinterval; #if CONFIG_DTRACE clock_track_calend_nowait(); @@ -665,19 +800,6 @@ calend_adjust(void) return (interval); } -/* - * clock_wakeup_calendar: - * - * Interface to power management, used - * to initiate the reset of the calendar - * on wake from sleep event. - */ -void -clock_wakeup_calendar(void) -{ - thread_call_enter(&calend_wakecall); -} - /* * Wait / delay routines. */ @@ -690,6 +812,15 @@ mach_wait_until_continue( /*NOTREACHED*/ } +/* + * mach_wait_until_trap: Suspend execution of calling thread until the specified time has passed + * + * Parameters: args->deadline Amount of time to wait + * + * Returns: 0 Success + * !0 Not success + * + */ kern_return_t mach_wait_until_trap( struct mach_wait_until_trap_args *args) @@ -697,7 +828,8 @@ mach_wait_until_trap( uint64_t deadline = args->deadline; wait_result_t wresult; - wresult = assert_wait_deadline((event_t)mach_wait_until_trap, THREAD_ABORTSAFE, deadline); + wresult = assert_wait_deadline_with_leeway((event_t)mach_wait_until_trap, THREAD_ABORTSAFE, + TIMEOUT_URGENCY_USER_NORMAL, deadline, 0); if (wresult == THREAD_WAITING) wresult = thread_block(mach_wait_until_continue); @@ -713,12 +845,50 @@ clock_delay_until( if (now >= deadline) return; - if ( (deadline - now) < (8 * sched_cswtime) || + _clock_delay_until_deadline(deadline - now, deadline); +} + +/* + * Preserve the original precise interval that the client + * requested for comparison to the spin threshold. + */ +void +_clock_delay_until_deadline( + uint64_t interval, + uint64_t deadline) +{ + _clock_delay_until_deadline_with_leeway(interval, deadline, 0); +} + +/* + * Like _clock_delay_until_deadline, but it accepts a + * leeway value. + */ +void +_clock_delay_until_deadline_with_leeway( + uint64_t interval, + uint64_t deadline, + uint64_t leeway) +{ + + if (interval == 0) + return; + + if ( ml_delay_should_spin(interval) || get_preemption_level() != 0 || - ml_get_interrupts_enabled() == FALSE ) - machine_delay_until(deadline); - else { - assert_wait_deadline((event_t)clock_delay_until, THREAD_UNINT, deadline - sched_cswtime); + ml_get_interrupts_enabled() == FALSE ) { + machine_delay_until(interval, deadline); + } else { + /* + * For now, assume a leeway request of 0 means the client does not want a leeway + * value. We may want to change this interpretation in the future. + */ + + if (leeway) { + assert_wait_deadline_with_leeway((event_t)clock_delay_until, THREAD_UNINT, TIMEOUT_URGENCY_LEEWAY, deadline, leeway); + } else { + assert_wait_deadline((event_t)clock_delay_until, THREAD_UNINT, deadline); + } thread_block(THREAD_CONTINUE_NULL); } @@ -729,11 +899,26 @@ delay_for_interval( uint32_t interval, uint32_t scale_factor) { - uint64_t end; + uint64_t abstime; + + clock_interval_to_absolutetime_interval(interval, scale_factor, &abstime); + + _clock_delay_until_deadline(abstime, mach_absolute_time() + abstime); +} + +void +delay_for_interval_with_leeway( + uint32_t interval, + uint32_t leeway, + uint32_t scale_factor) +{ + uint64_t abstime_interval; + uint64_t abstime_leeway; - clock_interval_to_deadline(interval, scale_factor, &end); + clock_interval_to_absolutetime_interval(interval, scale_factor, &abstime_interval); + clock_interval_to_absolutetime_interval(leeway, scale_factor, &abstime_leeway); - clock_delay_until(end); + _clock_delay_until_deadline_with_leeway(abstime_interval, mach_absolute_time() + abstime_interval, abstime_leeway); } void @@ -793,7 +978,7 @@ clock_deadline_for_periodic_event( } } -#if CONFIG_DTRACE +#if CONFIG_DTRACE /* * clock_get_calendar_nanotime_nowait @@ -809,8 +994,8 @@ clock_deadline_for_periodic_event( */ void clock_get_calendar_nanotime_nowait( - uint32_t *secs, - uint32_t *nanosecs) + clock_sec_t *secs, + clock_nsec_t *nanosecs) { int i = 0; uint64_t now; @@ -845,7 +1030,7 @@ clock_get_calendar_nanotime_nowait( uint32_t t32; if (now > stable.calend.adjstart) { - t32 = now - stable.calend.adjstart; + t32 = (uint32_t)(now - stable.calend.adjstart); if (t32 > stable.calend.adjoffset) now -= stable.calend.adjoffset; @@ -859,7 +1044,7 @@ clock_get_calendar_nanotime_nowait( absolutetime_to_microtime(now, secs, nanosecs); *nanosecs *= NSEC_PER_USEC; - *secs += stable.calend.epoch; + *secs += (clock_sec_t)stable.calend.epoch; } static void @@ -889,4 +1074,6 @@ clock_track_calend_nowait(void) (void)hw_atomic_add(&flipflop[i].gen, 1); } } -#endif /* CONFIG_DTRACE */ + +#endif /* CONFIG_DTRACE */ +