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 <yinshengkai@xiaomi.com>
This commit is contained in:
yinshengkai 2023-09-06 22:52:59 +08:00 committed by Mateusz Szafoni
parent 244293ce5f
commit b99820744c
7 changed files with 241 additions and 0 deletions

View File

@ -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
}

View File

@ -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"

View File

@ -20,6 +20,7 @@
set(SRCS
clock.c
clock_perf.c
clock_initialize.c
clock_settime.c
clock_gettime.c

View File

@ -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

View File

@ -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 */

View File

@ -237,6 +237,8 @@ void clock_initialize(void)
#endif
perf_init();
sched_trace_end();
}

203
sched/clock/clock_perf.c Normal file
View File

@ -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 <stdint.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/wdog.h>
#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