This is an automated email from the ASF dual-hosted git repository.

raiden00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit b99820744c4c71448183d36b796c55bf577e4406
Author: yinshengkai <[email protected]>
AuthorDate: Wed Sep 6 22:52:59 2023 +0800

    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 <[email protected]>
---
 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(+)

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

Reply via email to