From b99820744c4c71448183d36b796c55bf577e4406 Mon Sep 17 00:00:00 2001 From: yinshengkai Date: Wed, 6 Sep 2023 22:52:59 +0800 Subject: [PATCH] timer: handle perf count overflow clock_getcycle always returns an incremented cycle value If the hardware does not support perf event it will use arch_alarm's up_perf_gettime Signed-off-by: yinshengkai --- include/nuttx/clock.h | 18 +++ sched/Kconfig | 10 ++ sched/clock/CMakeLists.txt | 1 + sched/clock/Make.defs | 1 + sched/clock/clock.h | 6 + sched/clock/clock_initialize.c | 2 + sched/clock/clock_perf.c | 203 +++++++++++++++++++++++++++++++++ 7 files changed, 241 insertions(+) create mode 100644 sched/clock/clock_perf.c diff --git a/include/nuttx/clock.h b/include/nuttx/clock.h index ace7a20a48..816126bb79 100644 --- a/include/nuttx/clock.h +++ b/include/nuttx/clock.h @@ -663,6 +663,24 @@ struct timer_lowerhalf_s; void nxsched_period_extclk(FAR struct timer_lowerhalf_s *lower); #endif +/**************************************************************************** + * perf_gettime + ****************************************************************************/ + +clock_t perf_gettime(void); + +/**************************************************************************** + * perf_convert + ****************************************************************************/ + +void perf_convert(clock_t elapsed, FAR struct timespec *ts); + +/**************************************************************************** + * perf_gettfreq + ****************************************************************************/ + +unsigned long perf_getfreq(void); + #undef EXTERN #ifdef __cplusplus } diff --git a/sched/Kconfig b/sched/Kconfig index 1fd87a54c9..98db23a2b6 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -283,6 +283,16 @@ config PREALLOC_TIMERS pool of preallocated timer structures to minimize dynamic allocations. Set to zero for all dynamic allocations. +config PERF_OVERFLOW_CORRECTION + bool "Compensate perf count overflow" + depends on SYSTEM_TIME64 && (ALARM_ARCH || TIMER_ARCH || ARCH_PERF_EVENTS) + default n + ---help--- + If this option is enabled, then the perf event will be enabled + by default. + When enabled, it will always return an increasing count value to + avoid overflow on 32-bit platforms. + endmenu # Clocks and Timers menu "Tasks and Scheduling" diff --git a/sched/clock/CMakeLists.txt b/sched/clock/CMakeLists.txt index 1547a6f410..551f1aa1b0 100644 --- a/sched/clock/CMakeLists.txt +++ b/sched/clock/CMakeLists.txt @@ -20,6 +20,7 @@ set(SRCS clock.c + clock_perf.c clock_initialize.c clock_settime.c clock_gettime.c diff --git a/sched/clock/Make.defs b/sched/clock/Make.defs index 2a05343824..66439b4603 100644 --- a/sched/clock/Make.defs +++ b/sched/clock/Make.defs @@ -20,6 +20,7 @@ CSRCS += clock.c clock_initialize.c clock_settime.c clock_gettime.c CSRCS += clock_abstime2ticks.c clock_systime_ticks.c clock_systime_timespec.c +CSRCS += clock_perf.c ifeq ($(CONFIG_CLOCK_TIMEKEEPING),y) CSRCS += clock_timekeeping.c diff --git a/sched/clock/clock.h b/sched/clock/clock.h index 0555ea2422..018bc4fc87 100644 --- a/sched/clock/clock.h +++ b/sched/clock/clock.h @@ -94,4 +94,10 @@ int clock_abstime2ticks(clockid_t clockid, FAR const struct timespec *abstime, FAR sclock_t *ticks); +/**************************************************************************** + * perf_init + ****************************************************************************/ + +void perf_init(void); + #endif /* __SCHED_CLOCK_CLOCK_H */ diff --git a/sched/clock/clock_initialize.c b/sched/clock/clock_initialize.c index db0574aa58..7e73106add 100644 --- a/sched/clock/clock_initialize.c +++ b/sched/clock/clock_initialize.c @@ -237,6 +237,8 @@ void clock_initialize(void) #endif + perf_init(); + sched_trace_end(); } diff --git a/sched/clock/clock_perf.c b/sched/clock/clock_perf.c new file mode 100644 index 0000000000..ab45d000eb --- /dev/null +++ b/sched/clock/clock_perf.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * sched/clock/clock_perf.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#if defined(CONFIG_PERF_OVERFLOW_CORRECTION) && ULONG_MAX != UINT64_MAX + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct perf_s +{ + struct wdog_s wdog; + unsigned long last; + unsigned long overflow; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct perf_s g_perf; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * perf_update + ****************************************************************************/ + +static void perf_update(wdparm_t arg) +{ + clock_t tick = (clock_t)LONG_MAX * TICK_PER_SEC / up_perf_getfreq(); + + perf_gettime(); + wd_start((FAR struct wdog_s *)arg, tick, perf_update, arg); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * perf_init + ****************************************************************************/ + +void perf_init(void) +{ + FAR struct perf_s *perf = &g_perf; + clock_t tick = (clock_t)LONG_MAX * TICK_PER_SEC / up_perf_getfreq(); + + perf->last = up_perf_gettime(); + + /* Periodic check for overflow */ + + wd_start(&perf->wdog, tick, perf_update, (wdparm_t)perf); +} + +/**************************************************************************** + * perf_gettime + ****************************************************************************/ + +clock_t perf_gettime(void) +{ + FAR struct perf_s *perf = &g_perf; + unsigned long now = up_perf_gettime(); + + /* Check if overflow */ + + if (now < perf->last) + { + perf->overflow++; + } + + perf->last = now; + return (clock_t)now | (clock_t)perf->overflow << 32; +} + +/**************************************************************************** + * perf_convert + ****************************************************************************/ + +void perf_convert(clock_t elapsed, FAR struct timespec *ts) +{ + unsigned long freq = up_perf_getfreq(); + + ts->tv_sec = elapsed / freq; + elapsed -= ts->tv_sec * freq; + ts->tv_nsec = NSEC_PER_SEC * elapsed / freq; +} + +/**************************************************************************** + * perf_getfreq + ****************************************************************************/ + +unsigned long perf_getfreq(void) +{ + return up_perf_getfreq(); +} + +#elif defined(CONFIG_ALARM_ARCH) || defined (CONFIG_TIMER_ARCH) || \ + defined(CONFIG_ARCH_PERF_EVENTS) + +/**************************************************************************** + * perf_init + ****************************************************************************/ + +void perf_init(void) +{ +} + +/**************************************************************************** + * perf_gettime + ****************************************************************************/ + +clock_t perf_gettime(void) +{ + return up_perf_gettime(); +} + +/**************************************************************************** + * perf_convert + ****************************************************************************/ + +void perf_convert(clock_t elapsed, FAR struct timespec *ts) +{ + up_perf_convert(elapsed, ts); +} + +/**************************************************************************** + * perf_getfreq + ****************************************************************************/ + +unsigned long perf_getfreq(void) +{ + return up_perf_getfreq(); +} + +#else + +/**************************************************************************** + * perf_init + ****************************************************************************/ + +void perf_init(void) +{ +} + +/**************************************************************************** + * perf_gettime + ****************************************************************************/ + +clock_t perf_gettime(void) +{ + return clock_systime_ticks(); +} + +/**************************************************************************** + * perf_convert + ****************************************************************************/ + +void perf_convert(clock_t elapsed, FAR struct timespec *ts) +{ + clock_ticks2time(elapsed, ts); +} + +/**************************************************************************** + * perf_getfreq + ****************************************************************************/ + +unsigned long perf_getfreq(void) +{ + return TICK_PER_SEC; +} + +#endif