diff options
Diffstat (limited to 'cpukit/score/src/kern_tc.c')
-rw-r--r-- | cpukit/score/src/kern_tc.c | 220 |
1 files changed, 143 insertions, 77 deletions
diff --git a/cpukit/score/src/kern_tc.c b/cpukit/score/src/kern_tc.c index ea7d9ec953..95ae01b5b4 100644 --- a/cpukit/score/src/kern_tc.c +++ b/cpukit/score/src/kern_tc.c @@ -56,12 +56,17 @@ #define timecounter _Timecounter #define time_second _Timecounter_Time_second #define time_uptime _Timecounter_Time_uptime + #include <rtems/score/timecounterimpl.h> +#include <rtems/score/assert.h> #include <rtems/score/atomic.h> #include <rtems/score/smp.h> #include <rtems/score/todimpl.h> #include <rtems/score/watchdogimpl.h> #include <rtems/rtems/clock.h> + +#define ENOIOCTL EINVAL +#define KASSERT(exp, arg) _Assert(exp) #endif /* __rtems__ */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -90,6 +95,7 @@ __FBSDID("$FreeBSD$"); #include <sys/vdso.h> #endif /* __rtems__ */ #ifdef __rtems__ +#include <errno.h> #include <limits.h> #include <string.h> #include <rtems.h> @@ -115,6 +121,13 @@ atomic_thread_fence_rel(void) } static inline u_int +atomic_load_int(Atomic_Uint *i) +{ + + return (_Atomic_Load_uint(i, ATOMIC_ORDER_RELAXED)); +} + +static inline u_int atomic_load_acq_int(Atomic_Uint *i) { @@ -135,13 +148,13 @@ atomic_load_ptr(void *ptr) return ((void *)_Atomic_Load_uintptr(ptr, ATOMIC_ORDER_RELAXED)); } -static Timecounter_NTP_update_second _Timecounter_NTP_update_second; +static Timecounter_NTP_update_second _Timecounter_NTP_update_second_handler; void _Timecounter_Set_NTP_update_second(Timecounter_NTP_update_second handler) { - _Timecounter_NTP_update_second = handler; + _Timecounter_NTP_update_second_handler = handler; } #define ntp_update_second(a, b) (*ntp_update_second_handler)(a, b) @@ -219,7 +232,6 @@ static struct timecounter *timecounters = &dummy_timecounter; /* Mutex to protect the timecounter list. */ static struct mtx tc_lock; -MTX_SYSINIT(tc_lock, &tc_lock, "tc", MTX_DEF); int tc_min_ticktock_freq = 1; #else /* __rtems__ */ @@ -390,6 +402,23 @@ tc_delta(struct timehands *th) tc->tc_counter_mask); } +static __inline void +bintime_add_tc_delta(struct bintime *bt, uint64_t scale, + uint64_t large_delta, uint64_t delta) +{ + uint64_t x; + + if (__predict_false(delta >= large_delta)) { + /* Avoid overflow for scale * delta. */ + x = (scale >> 32) * delta; + bt->sec += x >> 32; + bintime_addx(bt, x << 32); + bintime_addx(bt, (scale & 0xffffffff) * delta); + } else { + bintime_addx(bt, scale * delta); + } +} + /* * Functions for reading the time. We have to loop until we are sure that * the timehands that we operated on was not updated under our feet. See @@ -401,7 +430,7 @@ bintime_off(struct bintime *bt, u_int off) { struct timehands *th; struct bintime *btp; - uint64_t scale, x; + uint64_t scale; #ifndef __rtems__ u_int delta, gen, large_delta; #else /* __rtems__ */ @@ -424,15 +453,7 @@ bintime_off(struct bintime *bt, u_int off) } while (gen != th->th_generation); #endif - if (__predict_false(delta >= large_delta)) { - /* Avoid overflow for scale * delta. */ - x = (scale >> 32) * delta; - bt->sec += x >> 32; - bintime_addx(bt, x << 32); - bintime_addx(bt, (scale & 0xffffffff) * delta); - } else { - bintime_addx(bt, scale * delta); - } + bintime_add_tc_delta(bt, scale, large_delta, delta); } #define GETTHBINTIME(dst, member) \ do { \ @@ -1498,7 +1519,6 @@ unlock: #endif /* __rtems__ */ } -#ifndef __rtems__ /* Report the frequency of the current timecounter. */ uint64_t tc_getfrequency(void) @@ -1507,6 +1527,7 @@ tc_getfrequency(void) return (timehands->th_counter->tc_frequency); } +#ifndef __rtems__ static bool sleeping_on_old_rtc(struct thread *td) { @@ -1685,17 +1706,8 @@ _Timecounter_Windup(struct bintime *new_boottimebin, #endif th->th_offset_count += delta; th->th_offset_count &= th->th_counter->tc_counter_mask; - while (delta > th->th_counter->tc_frequency) { - /* Eat complete unadjusted seconds. */ - delta -= th->th_counter->tc_frequency; - th->th_offset.sec++; - } - if ((delta > th->th_counter->tc_frequency / 2) && - (th->th_scale * delta < ((uint64_t)1 << 63))) { - /* The product th_scale * delta just barely overflows. */ - th->th_offset.sec++; - } - bintime_addx(&th->th_offset, th->th_scale * delta); + bintime_add_tc_delta(&th->th_offset, th->th_scale, + th->th_large_delta, delta); #ifndef __rtems__ /* @@ -1722,7 +1734,7 @@ _Timecounter_Windup(struct bintime *new_boottimebin, bt = th->th_offset; bintime_add(&bt, &th->th_boottime); #ifdef __rtems__ - ntp_update_second_handler = _Timecounter_NTP_update_second; + ntp_update_second_handler = _Timecounter_NTP_update_second_handler; if (ntp_update_second_handler != NULL) { #endif /* __rtems__ */ i = bt.sec - tho->th_microtime.tv_sec; @@ -1892,7 +1904,6 @@ SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, "Timecounter hardware detected"); #endif /* __rtems__ */ -#ifndef __rtems__ /* * RFC 2783 PPS-API implementation. */ @@ -1911,9 +1922,15 @@ abi_aware(struct pps_state *pps, int vers) static int pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) { +#ifndef __rtems__ int err, timo; +#else /* __rtems__ */ + int err; +#endif /* __rtems__ */ pps_seq_t aseq, cseq; +#ifndef __rtems__ struct timeval tv; +#endif /* __rtems__ */ if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC) return (EINVAL); @@ -1926,6 +1943,7 @@ pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) * sleep a long time. */ if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec) { +#ifndef __rtems__ if (fapi->timeout.tv_sec == -1) timo = 0x7fffffff; else { @@ -1933,10 +1951,12 @@ pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) tv.tv_usec = fapi->timeout.tv_nsec / 1000; timo = tvtohz(&tv); } +#endif /* __rtems__ */ aseq = atomic_load_int(&pps->ppsinfo.assert_sequence); cseq = atomic_load_int(&pps->ppsinfo.clear_sequence); while (aseq == atomic_load_int(&pps->ppsinfo.assert_sequence) && cseq == atomic_load_int(&pps->ppsinfo.clear_sequence)) { +#ifndef __rtems__ if (abi_aware(pps, 1) && pps->driver_mtx != NULL) { if (pps->flags & PPSFLAG_MTX_SPIN) { err = msleep_spin(pps, pps->driver_mtx, @@ -1957,6 +1977,12 @@ pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) } else if (err != 0) { return (err); } +#else /* __rtems__ */ + _Assert(pps->wait != NULL); + err = (*pps->wait)(pps, fapi->timeout); + if (err != 0) + return (err); +#endif /* __rtems__ */ } } @@ -2052,9 +2078,43 @@ pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps) } } +#ifdef __rtems__ +/* + * The real implementation of hardpps() is defined in kern_ntptime.c. Use it + * only if the NTP support is needed by the application. + */ +RTEMS_WEAK void +hardpps(struct timespec *tsp, long nsec) +{ + + (void)tsp; + (void)nsec; +} + +static int +default_wait(struct pps_state *pps, struct timespec timeout) +{ + + (void)pps; + (void)timeout; + + return (ETIMEDOUT); +} + +static void +default_wakeup(struct pps_state *pps) +{ + + (void)pps; +} +#endif /* __rtems__ */ void pps_init(struct pps_state *pps) { +#ifdef __rtems__ + pps->wait = default_wait; + pps->wakeup = default_wakeup; +#endif /* __rtems__ */ pps->ppscap |= PPS_TSFMT_TSPEC | PPS_CANWAIT; if (pps->ppscap & PPS_CAPTUREASSERT) pps->ppscap |= PPS_OFFSETASSERT; @@ -2081,6 +2141,7 @@ void pps_capture(struct pps_state *pps) { struct timehands *th; + struct timecounter *tc; KASSERT(pps != NULL, ("NULL pps pointer in pps_capture")); th = timehands; @@ -2089,17 +2150,18 @@ pps_capture(struct pps_state *pps) #ifdef FFCLOCK pps->capffth = fftimehands; #endif - pps->capcount = th->th_counter->tc_get_timecount(th->th_counter); - atomic_thread_fence_acq(); - if (pps->capgen != th->th_generation) - pps->capgen = 0; + tc = th->th_counter; + pps->capcount = tc->tc_get_timecount(tc); } void pps_event(struct pps_state *pps, int event) { + struct timehands *capth; + struct timecounter *captc; + uint64_t capth_scale; struct bintime bt; - struct timespec ts, *tsp, *osp; + struct timespec *tsp, *osp; uint32_t tcount, *pcount; int foff; pps_seq_t *pseq; @@ -2116,9 +2178,21 @@ pps_event(struct pps_state *pps, int event) /* Nothing to do if not currently set to capture this event type. */ if ((event & pps->ppsparam.mode) == 0) return; + + /* Make a snapshot of the captured timehand */ + capth = pps->capth; + captc = capth->th_counter; + capth_scale = capth->th_scale; + tcount = capth->th_offset_count; + bt = capth->th_bintime; + /* If the timecounter was wound up underneath us, bail out. */ - if (pps->capgen == 0 || pps->capgen != - atomic_load_acq_int(&pps->capth->th_generation)) + atomic_thread_fence_acq(); +#if defined(RTEMS_SMP) + if (pps->capgen == 0 || pps->capgen != capth->th_generation) +#else + if (pps->capgen != capth->th_generation) +#endif return; /* Things would be easier with arrays. */ @@ -2152,32 +2226,25 @@ pps_event(struct pps_state *pps, int event) #endif } + *pcount = pps->capcount; + /* * If the timecounter changed, we cannot compare the count values, so * we have to drop the rest of the PPS-stuff until the next event. */ - if (pps->ppstc != pps->capth->th_counter) { - pps->ppstc = pps->capth->th_counter; - *pcount = pps->capcount; + if (__predict_false(pps->ppstc != captc)) { + pps->ppstc = captc; pps->ppscount[2] = pps->capcount; return; } - /* Convert the count to a timespec. */ - tcount = pps->capcount - pps->capth->th_offset_count; - tcount &= pps->capth->th_counter->tc_counter_mask; - bt = pps->capth->th_bintime; - bintime_addx(&bt, pps->capth->th_scale * tcount); - bintime2timespec(&bt, &ts); - - /* If the timecounter was wound up underneath us, bail out. */ - atomic_thread_fence_acq(); - if (pps->capgen != pps->capth->th_generation) - return; - - *pcount = pps->capcount; (*pseq)++; - *tsp = ts; + + /* Convert the count to a timespec. */ + tcount = pps->capcount - tcount; + tcount &= captc->tc_counter_mask; + bintime_addx(&bt, capth_scale * tcount); + bintime2timespec(&bt, tsp); if (foff) { timespecadd(tsp, osp, tsp); @@ -2192,14 +2259,14 @@ pps_event(struct pps_state *pps, int event) bt = pps->capffth->tick_time; ffclock_convert_delta(tcount, pps->capffth->cest.period, &bt); bintime_add(&bt, &pps->capffth->tick_time); - bintime2timespec(&bt, &ts); (*pseq_ffc)++; - *tsp_ffc = ts; + bintime2timespec(&bt, tsp_ffc); #endif #ifdef PPS_SYNC if (fhard) { - uint64_t scale; + uint64_t delta_nsec; + uint64_t freq; /* * Feed the NTP PLL/FLL. @@ -2208,24 +2275,23 @@ pps_event(struct pps_state *pps, int event) */ tcount = pps->capcount - pps->ppscount[2]; pps->ppscount[2] = pps->capcount; - tcount &= pps->capth->th_counter->tc_counter_mask; - scale = (uint64_t)1 << 63; - scale /= pps->capth->th_counter->tc_frequency; - scale *= 2; - bt.sec = 0; - bt.frac = 0; - bintime_addx(&bt, scale * tcount); - bintime2timespec(&bt, &ts); - hardpps(tsp, ts.tv_nsec + 1000000000 * ts.tv_sec); + tcount &= captc->tc_counter_mask; + delta_nsec = 1000000000; + delta_nsec *= tcount; + freq = captc->tc_frequency; + delta_nsec = (delta_nsec + freq / 2) / freq; + hardpps(tsp, (long)delta_nsec); } #endif /* Wakeup anyone sleeping in pps_fetch(). */ +#ifndef __rtems__ wakeup(pps); -} #else /* __rtems__ */ -/* FIXME: https://devel.rtems.org/ticket/2349 */ + _Assert(pps->wakeup != NULL); + (*pps->wakeup)(pps); #endif /* __rtems__ */ +} /* * Timecounters need to be updated every so often to prevent the hardware @@ -2261,9 +2327,13 @@ _Timecounter_Tick(void) { Per_CPU_Control *cpu_self = _Per_CPU_Get(); +#if defined(RTEMS_SMP) if (_Per_CPU_Is_boot_processor(cpu_self)) { +#endif tc_windup(NULL); +#if defined(RTEMS_SMP) } +#endif _Watchdog_Tick(cpu_self); } @@ -2378,6 +2448,8 @@ inittimehands(void *dummy) TUNABLE_STR_FETCH("kern.timecounter.hardware", tc_from_tunable, sizeof(tc_from_tunable)); + + mtx_init(&tc_lock, "tc", NULL, MTX_DEF); } SYSINIT(timehands, SI_SUB_TUNABLES, SI_ORDER_ANY, inittimehands, NULL); @@ -2423,7 +2495,7 @@ SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL); /* Cpu tick handling -------------------------------------------------*/ -static int cpu_tick_variable; +static bool cpu_tick_variable; static uint64_t cpu_tick_frequency; DPCPU_DEFINE_STATIC(uint64_t, tc_cpu_ticks_base); @@ -2516,14 +2588,14 @@ cpu_tick_calibrate(int reset) } void -set_cputicker(cpu_tick_f *func, uint64_t freq, unsigned var) +set_cputicker(cpu_tick_f *func, uint64_t freq, bool isvariable) { if (func == NULL) { cpu_ticks = tc_cpu_ticks; } else { cpu_tick_frequency = freq; - cpu_tick_variable = var; + cpu_tick_variable = isvariable; cpu_ticks = func; } } @@ -2543,20 +2615,14 @@ cpu_tickrate(void) * years) and in 64 bits at 4 GHz (146 years), but if we do a multiply * before divide conversion (to retain precision) we find that the * margin shrinks to 1.5 hours (one millionth of 146y). - * With a three prong approach we never lose significant bits, no - * matter what the cputick rate and length of timeinterval is. */ uint64_t cputick2usec(uint64_t tick) { - - if (tick > 18446744073709551LL) /* floor(2^64 / 1000) */ - return (tick / (cpu_tickrate() / 1000000LL)); - else if (tick > 18446744073709LL) /* floor(2^64 / 1000000) */ - return ((tick * 1000LL) / (cpu_tickrate() / 1000LL)); - else - return ((tick * 1000000LL) / cpu_tickrate()); + uint64_t tr; + tr = cpu_tickrate(); + return ((tick / tr) * 1000000ULL) + ((tick % tr) * 1000000ULL) / tr; } cpu_tick_f *cpu_ticks = tc_cpu_ticks; |