[PATCH] rtems: Fix rate monotonic statistics

Sebastian Huber sebastian.huber at embedded-brains.de
Tue Oct 12 06:36:28 UTC 2021


The rate monotonic period statistics were affected by
rtems_cpu_usage_reset().  The logic to detect and work around a CPU
usage reset was broken.

The Thread_Contol::cpu_time_used is changed to contain the processor
time used throughout the entire lifetime of the thread.  The new member
Thread_Contol::cpu_time_used_at_last_reset is added to contain the
processor time used at the time of the last reset through
rtems_cpu_usage_reset().  This decouples the resets of the CPU usage and
the rate monotonic period statistics.

Update #4528.
---
 cpukit/include/rtems/rtems/ratemonimpl.h      |  7 +-
 cpukit/include/rtems/score/schedulerimpl.h    |  4 +-
 cpukit/include/rtems/score/thread.h           | 15 +++-
 cpukit/include/rtems/score/threadimpl.h       | 40 +++++++++--
 cpukit/libcsupport/src/__times.c              |  3 +-
 cpukit/libmisc/cpuuse/cpuusagereport.c        |  2 +-
 cpukit/libmisc/cpuuse/cpuusagereset.c         |  3 +-
 cpukit/libmisc/cpuuse/cpuusagetop.c           |  2 +-
 cpukit/libtest/testbusy.c                     |  4 +-
 cpukit/rtems/src/ratemongetstatus.c           | 28 +++-----
 cpukit/rtems/src/ratemonperiod.c              | 21 ++----
 cpukit/score/src/threadgetcputimeused.c       | 32 ++++++---
 .../src/threadgetcputimeusedafterreset.c      | 71 +++++++++++++++++++
 spec/build/cpukit/librtemscpu.yml             |  1 +
 14 files changed, 166 insertions(+), 67 deletions(-)
 create mode 100644 cpukit/score/src/threadgetcputimeusedafterreset.c

diff --git a/cpukit/include/rtems/rtems/ratemonimpl.h b/cpukit/include/rtems/rtems/ratemonimpl.h
index 7e42a0437c..62327c5b09 100644
--- a/cpukit/include/rtems/rtems/ratemonimpl.h
+++ b/cpukit/include/rtems/rtems/ratemonimpl.h
@@ -92,7 +92,7 @@ RTEMS_INLINE_ROUTINE Rate_monotonic_Control *_Rate_monotonic_Get(
 void _Rate_monotonic_Timeout( Watchdog_Control *watchdog );
 
 /**
- * @brief _Rate_monotonic_Get_status(
+ * @brief Gets the rate monotonic CPU usage status.
  *
  * This routine is invoked to compute the elapsed wall time and cpu
  * time for a period.
@@ -102,11 +102,8 @@ void _Rate_monotonic_Timeout( Watchdog_Control *watchdog );
  *             since the period was initiated.
  * @param[out] cpu_since_last_period is set to the cpu time used by the
  *             owning thread since the period was initiated.
- *
- * @retval This routine returns true if the status can be determined
- *         and false otherwise.
  */
-bool _Rate_monotonic_Get_status(
+void _Rate_monotonic_Get_status(
   const Rate_monotonic_Control *the_period,
   Timestamp_Control            *wall_since_last_period,
   Timestamp_Control            *cpu_since_last_period
diff --git a/cpukit/include/rtems/score/schedulerimpl.h b/cpukit/include/rtems/score/schedulerimpl.h
index 98f8e337fd..50110ea6e7 100644
--- a/cpukit/include/rtems/score/schedulerimpl.h
+++ b/cpukit/include/rtems/score/schedulerimpl.h
@@ -1270,8 +1270,8 @@ RTEMS_INLINE_ROUTINE void _Scheduler_Update_heir(
   if ( heir != new_heir && ( heir->is_preemptible || force_dispatch ) ) {
 #if defined(RTEMS_SMP)
     /*
-     * We need this state only for _Thread_Get_CPU_time_used().  Cannot use
-     * _Scheduler_Thread_change_state() since THREAD_SCHEDULER_BLOCKED to
+     * We need this state only for _Thread_Get_CPU_time_used_locked().  Cannot
+     * use _Scheduler_Thread_change_state() since THREAD_SCHEDULER_BLOCKED to
      * THREAD_SCHEDULER_BLOCKED state changes are illegal for the real SMP
      * schedulers.
      */
diff --git a/cpukit/include/rtems/score/thread.h b/cpukit/include/rtems/score/thread.h
index e23261701a..aff2f58d77 100644
--- a/cpukit/include/rtems/score/thread.h
+++ b/cpukit/include/rtems/score/thread.h
@@ -853,10 +853,19 @@ struct _Thread_Control {
   Thread_CPU_budget_algorithms          budget_algorithm;
   /** This field is the method invoked with the budgeted time is consumed. */
   Thread_CPU_budget_algorithm_callout   budget_callout;
-  /** This field is the amount of CPU time consumed by this thread
-   *  since it was created.
+
+  /**
+   * @brief This member contains the amount of CPU time consumed by this thread
+   *   since it was created.
+   */
+  Timestamp_Control cpu_time_used;
+
+  /**
+   * @brief This member contains the amount of CPU time consumed by this thread
+   *   at the time of the last reset of the CPU usage by
+   *   rtems_cpu_usage_reset().
    */
-  Timestamp_Control                     cpu_time_used;
+  Timestamp_Control cpu_time_used_at_last_reset;
 
   /** This field contains information about the starting state of
    *  this thread.
diff --git a/cpukit/include/rtems/score/threadimpl.h b/cpukit/include/rtems/score/threadimpl.h
index 8bf885c3b0..d467f12f97 100644
--- a/cpukit/include/rtems/score/threadimpl.h
+++ b/cpukit/include/rtems/score/threadimpl.h
@@ -1242,15 +1242,41 @@ RTEMS_INLINE_ROUTINE void _Thread_Dispatch_update_heir(
 #endif
 
 /**
- * @brief Gets the used cpu time of the thread and stores it in the given
- *      Timestamp_Control.
+ * @brief Gets the used processor time of the thread throughout its entire
+ *   lifetime.
  *
- * @param the_thread The thread to get the used cpu time of.
- * @param[out] cpu_time_used Stores the used cpu time of @a the_thread.
+ * @param[in, out] the_thread is the thread.
+ *
+ * @return Returns the used processor time of the thread throughout its entire
+ *   lifetime.
  */
-void _Thread_Get_CPU_time_used(
-  Thread_Control    *the_thread,
-  Timestamp_Control *cpu_time_used
+Timestamp_Control _Thread_Get_CPU_time_used( Thread_Control *the_thread );
+
+/**
+ * @brief Gets the used processor time of the thread throughout its entire
+ *   lifetime if the caller already acquired the thread state and home
+ *   scheduler locks.
+ *
+ * @param[in, out] the_thread is the thread.
+ *
+ * @return Returns the used processor time of the thread throughout its entire
+ *   lifetime.
+ */
+Timestamp_Control _Thread_Get_CPU_time_used_locked(
+  Thread_Control *the_thread
+);
+
+/**
+ * @brief Gets the used processor time of the thread after the last CPU usage
+ *   reset.
+ *
+ * @param[in, out] the_thread is the thread.
+ *
+ * @return Returns the used processor time of the thread after the last CPU usage
+ *   reset.
+ */
+Timestamp_Control _Thread_Get_CPU_time_used_after_last_reset(
+  Thread_Control *the_thread
 );
 
 /**
diff --git a/cpukit/libcsupport/src/__times.c b/cpukit/libcsupport/src/__times.c
index 7bb7e0e9ca..a37c662654 100644
--- a/cpukit/libcsupport/src/__times.c
+++ b/cpukit/libcsupport/src/__times.c
@@ -65,7 +65,8 @@ clock_t _times(
    *  of ticks since boot and the number of ticks executed by this
    *  this thread.
    */
-  _Thread_Get_CPU_time_used( _Thread_Get_executing(), &cpu_time_used );
+  cpu_time_used =
+    _Thread_Get_CPU_time_used_after_last_reset( _Thread_Get_executing() );
   ptms->tms_utime = ((clock_t) cpu_time_used) / tick_interval;
 
   return ptms->tms_stime;
diff --git a/cpukit/libmisc/cpuuse/cpuusagereport.c b/cpukit/libmisc/cpuuse/cpuusagereport.c
index 08bc5bb541..ea21e73dc1 100644
--- a/cpukit/libmisc/cpuuse/cpuusagereport.c
+++ b/cpukit/libmisc/cpuuse/cpuusagereport.c
@@ -52,7 +52,7 @@ static bool cpu_usage_visitor( Thread_Control *the_thread, void *arg )
   ctx = arg;
   _Thread_Get_name( the_thread, name, sizeof( name ) );
 
-  _Thread_Get_CPU_time_used( the_thread, &used );
+  used = _Thread_Get_CPU_time_used_after_last_reset( the_thread );
   _TOD_Get_uptime( &uptime );
   _Timestamp_Subtract( &ctx->uptime_at_last_reset, &uptime, &ctx->total );
   _Timestamp_Divide( &used, &ctx->total, &ival, &fval );
diff --git a/cpukit/libmisc/cpuuse/cpuusagereset.c b/cpukit/libmisc/cpuuse/cpuusagereset.c
index 10d8ebec48..d1f0e65180 100644
--- a/cpukit/libmisc/cpuuse/cpuusagereset.c
+++ b/cpukit/libmisc/cpuuse/cpuusagereset.c
@@ -40,7 +40,8 @@ static bool CPU_usage_Per_thread_handler(
   scheduler = _Thread_Scheduler_get_home( the_thread );
   _Scheduler_Acquire_critical( scheduler, &scheduler_lock_context );
 
-  _Timestamp_Set_to_zero( &the_thread->cpu_time_used );
+  the_thread->cpu_time_used_at_last_reset =
+    _Thread_Get_CPU_time_used_locked( the_thread );
 
   _Scheduler_Release_critical( scheduler, &scheduler_lock_context );
   _Thread_State_release( the_thread, &state_lock_context );
diff --git a/cpukit/libmisc/cpuuse/cpuusagetop.c b/cpukit/libmisc/cpuuse/cpuusagetop.c
index dad11ad748..51d049257a 100644
--- a/cpukit/libmisc/cpuuse/cpuusagetop.c
+++ b/cpukit/libmisc/cpuuse/cpuusagetop.c
@@ -178,7 +178,7 @@ task_usage(Thread_Control* thread, void* arg)
 
   data->stack_size += thread->Start.Initial_stack.size;
 
-  _Thread_Get_CPU_time_used(thread, &usage);
+  usage = _Thread_Get_CPU_time_used_after_last_reset(thread);
 
   for (j = 0; j < data->last_task_count; j++)
   {
diff --git a/cpukit/libtest/testbusy.c b/cpukit/libtest/testbusy.c
index c1d44278be..51c6a71810 100644
--- a/cpukit/libtest/testbusy.c
+++ b/cpukit/libtest/testbusy.c
@@ -28,10 +28,10 @@ void rtems_test_busy_cpu_usage( time_t seconds, long nanoseconds )
   Timestamp_Control  now;
 
   executing = _Thread_Get_executing();
-  _Thread_Get_CPU_time_used( executing, &start );
+  start = _Thread_Get_CPU_time_used( executing );
   _Timestamp_Set( &busy, seconds, nanoseconds );
 
   do {
-    _Thread_Get_CPU_time_used( executing, &now );
+    now = _Thread_Get_CPU_time_used( executing );
   } while ( now - start < busy );
 }
diff --git a/cpukit/rtems/src/ratemongetstatus.c b/cpukit/rtems/src/ratemongetstatus.c
index 745b52f026..5b46a7a435 100644
--- a/cpukit/rtems/src/ratemongetstatus.c
+++ b/cpukit/rtems/src/ratemongetstatus.c
@@ -31,7 +31,6 @@ rtems_status_code rtems_rate_monotonic_get_status(
 {
   Rate_monotonic_Control *the_period;
   ISR_lock_Context        lock_context;
-  rtems_status_code       status;
 
   if ( period_status == NULL ) {
     return RTEMS_INVALID_ADDRESS;
@@ -54,35 +53,28 @@ rtems_status_code rtems_rate_monotonic_get_status(
      */
     _Timespec_Set_to_zero( &period_status->since_last_period );
     _Timespec_Set_to_zero( &period_status->executed_since_last_period );
-    status = RTEMS_SUCCESSFUL;
   } else {
     Timestamp_Control wall_since_last_period;
     Timestamp_Control cpu_since_last_period;
-    bool              valid_status;
 
     /*
      *  Grab the current status.
      */
-    valid_status = _Rate_monotonic_Get_status(
+    _Rate_monotonic_Get_status(
       the_period,
       &wall_since_last_period,
       &cpu_since_last_period
     );
-    if ( valid_status ) {
-      _Timestamp_To_timespec(
-        &wall_since_last_period,
-        &period_status->since_last_period
-      );
-      _Timestamp_To_timespec(
-        &cpu_since_last_period,
-        &period_status->executed_since_last_period
-      );
-      status = RTEMS_SUCCESSFUL;
-    } else {
-      status = RTEMS_NOT_DEFINED;
-    }
+    _Timestamp_To_timespec(
+      &wall_since_last_period,
+      &period_status->since_last_period
+    );
+    _Timestamp_To_timespec(
+      &cpu_since_last_period,
+      &period_status->executed_since_last_period
+    );
   }
 
   _Rate_monotonic_Release( the_period, &lock_context );
-  return status;
+  return RTEMS_SUCCESSFUL;
 }
diff --git a/cpukit/rtems/src/ratemonperiod.c b/cpukit/rtems/src/ratemonperiod.c
index 7f0d302583..32ac688a7f 100644
--- a/cpukit/rtems/src/ratemonperiod.c
+++ b/cpukit/rtems/src/ratemonperiod.c
@@ -26,7 +26,7 @@
 #include <rtems/score/schedulerimpl.h>
 #include <rtems/score/todimpl.h>
 
-bool _Rate_monotonic_Get_status(
+void _Rate_monotonic_Get_status(
   const Rate_monotonic_Control *the_period,
   Timestamp_Control            *wall_since_last_period,
   Timestamp_Control            *cpu_since_last_period
@@ -47,14 +47,7 @@ bool _Rate_monotonic_Get_status(
   /*
    *  Determine cpu usage since period initiated.
    */
-  _Thread_Get_CPU_time_used( owning_thread, &used );
-
-  /*
-   *  The cpu usage info was reset while executing.  Can't
-   *  determine a status.
-   */
-  if ( _Timestamp_Less_than( &used, &the_period->cpu_usage_period_initiated ) )
-    return false;
+  used = _Thread_Get_CPU_time_used( owning_thread );
 
    /* used = current cpu usage - cpu usage at start of period */
   _Timestamp_Subtract(
@@ -62,8 +55,6 @@ bool _Rate_monotonic_Get_status(
     &used,
     cpu_since_last_period
   );
-
-  return true;
 }
 
 static void _Rate_monotonic_Release_postponed_job(
@@ -130,7 +121,7 @@ void _Rate_monotonic_Restart(
    *  Set the starting point and the CPU time used for the statistics.
    */
   _TOD_Get_uptime( &the_period->time_period_initiated );
-  _Thread_Get_CPU_time_used( owner, &the_period->cpu_usage_period_initiated );
+  the_period->cpu_usage_period_initiated = _Thread_Get_CPU_time_used( owner );
 
   _Rate_monotonic_Release_job(
     the_period,
@@ -147,7 +138,6 @@ static void _Rate_monotonic_Update_statistics(
   Timestamp_Control          executed;
   Timestamp_Control          since_last_period;
   Rate_monotonic_Statistics *stats;
-  bool                       valid_status;
 
   /*
    *  Assume we are only called in states where it is appropriate
@@ -167,10 +157,7 @@ static void _Rate_monotonic_Update_statistics(
   /*
    *  Grab status for time statistics.
    */
-  valid_status =
-    _Rate_monotonic_Get_status( the_period, &since_last_period, &executed );
-  if (!valid_status)
-    return;
+  _Rate_monotonic_Get_status( the_period, &since_last_period, &executed );
 
   /*
    *  Update CPU time
diff --git a/cpukit/score/src/threadgetcputimeused.c b/cpukit/score/src/threadgetcputimeused.c
index 7406da0bf3..f23f606411 100644
--- a/cpukit/score/src/threadgetcputimeused.c
+++ b/cpukit/score/src/threadgetcputimeused.c
@@ -4,7 +4,7 @@
  * @ingroup RTEMSScoreThread
  *
  * @brief This source file contains the implementation of
- *   _Thread_Get_CPU_time_used().
+ *   _Thread_Get_CPU_time_used() and _Thread_Get_CPU_time_used_locked().
  */
 
 /*
@@ -37,25 +37,39 @@ static bool _Thread_Is_scheduled( const Thread_Control *the_thread )
 #endif
 }
 
-void _Thread_Get_CPU_time_used(
-  Thread_Control    *the_thread,
-  Timestamp_Control *cpu_time_used
+Timestamp_Control _Thread_Get_CPU_time_used_locked(
+  Thread_Control *the_thread
 )
+{
+  _Assert( _Thread_State_is_owner( the_thread ) );
+  _Assert(
+    _ISR_lock_Is_owner(
+      &_Scheduler_Get_context( _Thread_Scheduler_get_home( the_thread ) )->Lock
+    )
+  );
+
+  if ( _Thread_Is_scheduled( the_thread ) ) {
+    _Thread_Update_CPU_time_used( the_thread, _Thread_Get_CPU( the_thread ) );
+  }
+
+  return the_thread->cpu_time_used;
+}
+
+Timestamp_Control _Thread_Get_CPU_time_used( Thread_Control *the_thread )
 {
   const Scheduler_Control *scheduler;
   ISR_lock_Context         state_lock_context;
   ISR_lock_Context         scheduler_lock_context;
+  Timestamp_Control        cpu_time_used;
 
   _Thread_State_acquire( the_thread, &state_lock_context );
   scheduler = _Thread_Scheduler_get_home( the_thread );
   _Scheduler_Acquire_critical( scheduler, &scheduler_lock_context );
 
-  if ( _Thread_Is_scheduled( the_thread ) ) {
-    _Thread_Update_CPU_time_used( the_thread, _Thread_Get_CPU( the_thread ) );
-  }
-
-  *cpu_time_used = the_thread->cpu_time_used;
+  cpu_time_used = _Thread_Get_CPU_time_used_locked( the_thread );
 
   _Scheduler_Release_critical( scheduler, &scheduler_lock_context );
   _Thread_State_release( the_thread, &state_lock_context );
+
+  return cpu_time_used;
 }
diff --git a/cpukit/score/src/threadgetcputimeusedafterreset.c b/cpukit/score/src/threadgetcputimeusedafterreset.c
new file mode 100644
index 0000000000..1ab4aa16fb
--- /dev/null
+++ b/cpukit/score/src/threadgetcputimeusedafterreset.c
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSScoreThread
+ *
+ * @brief This source file contains the implementation of
+ *   _Thread_Get_CPU_time_used_after_last_reset().
+ */
+
+/*
+ * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <rtems/score/threadimpl.h>
+#include <rtems/score/schedulerimpl.h>
+
+Timestamp_Control _Thread_Get_CPU_time_used_after_last_reset(
+  Thread_Control *the_thread
+)
+{
+  const Scheduler_Control *scheduler;
+  ISR_lock_Context         state_lock_context;
+  ISR_lock_Context         scheduler_lock_context;
+  Timestamp_Control        cpu_time_used;
+  Timestamp_Control        cpu_time_used_at_last_reset;
+
+  _Thread_State_acquire( the_thread, &state_lock_context );
+  scheduler = _Thread_Scheduler_get_home( the_thread );
+  _Scheduler_Acquire_critical( scheduler, &scheduler_lock_context );
+
+  cpu_time_used = _Thread_Get_CPU_time_used_locked( the_thread );
+  cpu_time_used_at_last_reset = the_thread->cpu_time_used_at_last_reset;
+
+  _Scheduler_Release_critical( scheduler, &scheduler_lock_context );
+  _Thread_State_release( the_thread, &state_lock_context );
+
+  _Timestamp_Subtract(
+    &cpu_time_used_at_last_reset,
+    &cpu_time_used,
+    &cpu_time_used
+  );
+
+  return cpu_time_used;
+}
diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml
index 33fc12ef97..ae572021cb 100644
--- a/spec/build/cpukit/librtemscpu.yml
+++ b/spec/build/cpukit/librtemscpu.yml
@@ -441,6 +441,7 @@ source:
 - cpukit/score/src/threadentryadaptornumeric.c
 - cpukit/score/src/threadget.c
 - cpukit/score/src/threadgetcputimeused.c
+- cpukit/score/src/threadgetcputimeusedafterreset.c
 - cpukit/score/src/threadhandler.c
 - cpukit/score/src/threadidledefault.c
 - cpukit/score/src/threadinitialize.c
-- 
2.31.1



More information about the devel mailing list