#include <mach/mach_types.h>
-#include <kern/lock.h>
#include <kern/spl.h>
#include <kern/sched_prim.h>
#include <kern/thread.h>
uint32_t hz_tick_interval = 1;
-decl_simple_lock_data(static,clock_lock)
+decl_simple_lock_data(,clock_lock)
#define clock_lock() \
simple_lock(&clock_lock)
* where CONV converts absolute time units into seconds and a fraction.
*/
static struct clock_calend {
-
uint64_t epoch;
uint64_t offset;
extern void IOKitResetTime(void);
+void _clock_delay_until_deadline(uint64_t interval,
+ uint64_t deadline);
+
static uint64_t clock_boottime; /* Seconds boottime epoch */
#define TIME_ADD(rsecs, secs, rfrac, frac, unit) \
thread_call_setup(&calend_wakecall, (thread_call_func_t)IOKitResetTime, NULL);
clock_oldconfig();
-
- /*
- * Initialize the timer callouts.
- */
- timer_call_initialize();
}
/*
clock_get_calendar_microtime(
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;
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 = (uint32_t)(now - clock_calend.adjstart);
now += clock_calend.offset;
absolutetime_to_microtime(now, secs, nanosecs);
+
*nanosecs *= NSEC_PER_USEC;
*secs += (clock_sec_t)clock_calend.epoch;
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();
clock_lock();
* Set the new calendar epoch.
*/
clock_calend.epoch = secs;
+
nanoseconds_to_absolutetime((uint64_t)microsecs * NSEC_PER_USEC, &clock_calend.offset);
/*
/*
* Set the new value for the platform clock.
*/
- PESetGMTTimeOfDay(newsecs);
+ PESetUTCTimeOfDay(newsecs, newmicrosecs);
splx(s);
void
clock_initialize_calendar(void)
{
- clock_sec_t sys, secs = PEGetGMTTimeOfDay();
- clock_usec_t microsys, microsecs = 0;
+ clock_sec_t sys, secs;
+ clock_usec_t microsys, microsecs;
spl_t s;
+ PEGetUTCTimeOfDay(&secs, µsecs);
+
s = splclock();
clock_lock();
* Set the new calendar epoch.
*/
clock_calend.epoch = secs;
+
nanoseconds_to_absolutetime((uint64_t)microsecs * NSEC_PER_USEC, &clock_calend.offset);
/*
interval = calend_set_adjustment(secs, microsecs);
if (interval != 0) {
calend_adjdeadline = mach_absolute_time() + interval;
- if (!timer_call_enter(&calend_adjcall, calend_adjdeadline))
+ if (!timer_call_enter(&calend_adjcall, calend_adjdeadline, TIMER_CALL_SYS_CRITICAL))
calend_adjactive++;
}
else
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();
+ /*
+ * 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 = (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 = (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 = (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 = (uint32_t)t64;
}
+ /*
+ * Store the total adjustment time in ns.
+ */
calend_adjtotal = total;
+
+ /*
+ * Store the delta for this adjustment period in ns.
+ */
clock_calend.adjdelta = delta;
+ /*
+ * Set the interval in absolute time for later return.
+ */
interval = calend_adjinterval;
}
- else
+ else {
+ /*
+ * No change; clear any prior adjustment.
+ */
calend_adjtotal = clock_calend.adjdelta = 0;
+ }
+ /*
+ * If an prior correction was in progress, return the
+ * remaining uncorrected time from it.
+ */
if (ototal != 0) {
- *secs = (long)(ototal / NSEC_PER_SEC);
- *microsecs = (int)((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;
if (interval != 0) {
clock_deadline_for_periodic_event(interval, mach_absolute_time(), &calend_adjdeadline);
- if (!timer_call_enter(&calend_adjcall, calend_adjdeadline))
+ if (!timer_call_enter(&calend_adjcall, calend_adjdeadline, TIMER_CALL_SYS_CRITICAL))
calend_adjactive++;
}
}
}
}
else
- if (delta < 0) {
- clock_calend.offset -= clock_calend.adjoffset;
+ if (delta < 0) {
+ clock_calend.offset -= clock_calend.adjoffset;
- calend_adjtotal -= delta;
- if (delta < calend_adjtotal) {
- clock_calend.adjdelta = delta = (int32_t)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 = (uint32_t)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 = calend_adjinterval;
/*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)
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);
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)
+{
+
+ 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 {
+ assert_wait_deadline((event_t)clock_delay_until, THREAD_UNINT, deadline);
thread_block(THREAD_CONTINUE_NULL);
}
}
+
void
delay_for_interval(
uint32_t interval,
uint32_t scale_factor)
{
- uint64_t end;
+ uint64_t abstime;
- clock_interval_to_deadline(interval, scale_factor, &end);
+ clock_interval_to_absolutetime_interval(interval, scale_factor, &abstime);
- clock_delay_until(end);
+ _clock_delay_until_deadline(abstime, mach_absolute_time() + abstime);
}
void
}
#endif /* CONFIG_DTRACE */
+