rtems: Fix rate monotonic statistics

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.
This commit is contained in:
Sebastian Huber
2021-10-12 08:26:39 +02:00
parent 9be97a34db
commit 0221da5f56
14 changed files with 169 additions and 70 deletions

View File

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

View File

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

View File

@@ -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;
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_at_last_reset;
/** This field contains information about the starting state of
* this thread.

View File

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

View File

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

View File

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

View File

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

View File

@@ -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++)
{

View File

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

View File

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

View File

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

View File

@@ -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
)
{
const Scheduler_Control *scheduler;
ISR_lock_Context state_lock_context;
ISR_lock_Context scheduler_lock_context;
_Thread_State_acquire( the_thread, &state_lock_context );
scheduler = _Thread_Scheduler_get_home( the_thread );
_Scheduler_Acquire_critical( scheduler, &scheduler_lock_context );
_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 ) );
}
*cpu_time_used = the_thread->cpu_time_used;
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 );
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;
}

View File

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

View File

@@ -1543,6 +1543,7 @@ source:
- cpukit/score/src/threadentryadaptorpointer.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