/**
* @file
*
* @brief Rate Monotonic Support
* @ingroup ClassicRateMon
*/
/*
* COPYRIGHT (c) 1989-2010.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/rtems/ratemonimpl.h>
#include <rtems/score/schedulerimpl.h>
#include <rtems/score/threadimpl.h>
#include <rtems/score/todimpl.h>
#include <rtems/score/watchdogimpl.h>
bool _Rate_monotonic_Get_status(
Rate_monotonic_Control *the_period,
Rate_monotonic_Period_time_t *wall_since_last_period,
Thread_CPU_usage_t *cpu_since_last_period
)
{
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
Timestamp_Control uptime;
#endif
Thread_Control *owning_thread = the_period->owner;
Thread_CPU_usage_t used;
/*
* Determine elapsed wall time since period initiated.
*/
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
_TOD_Get_uptime( &uptime );
_Timestamp_Subtract(
&the_period->time_period_initiated, &uptime, wall_since_last_period
);
#else
*wall_since_last_period =
_Watchdog_Ticks_since_boot - the_period->time_period_initiated;
#endif
/*
* Determine cpu usage since period initiated.
*/
used = owning_thread->cpu_time_used;
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
if (owning_thread == _Thread_Executing) {
Thread_CPU_usage_t ran;
/* How much time time since last context switch */
_Timestamp_Subtract(
&_Thread_Time_of_last_context_switch, &uptime, &ran
);
/* cpu usage += ran */
_Timestamp_Add_to( &used, &ran );
/*
* 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 = current cpu usage - cpu usage at start of period */
_Timestamp_Subtract(
&the_period->cpu_usage_period_initiated,
&used,
cpu_since_last_period
);
}
#else
/*
* The cpu usage info was reset while executing. Can't
* determine a status.
*/
if (used < the_period->cpu_usage_period_initiated)
return false;
*cpu_since_last_period = used - the_period->cpu_usage_period_initiated;
#endif
return true;
}
void _Rate_monotonic_Initiate_statistics(
Rate_monotonic_Control *the_period
)
{
Thread_Control *owning_thread = the_period->owner;
/*
* If using nanosecond statistics, we need to obtain the uptime.
*/
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
Timestamp_Control uptime;
_TOD_Get_uptime( &uptime );
#endif
/*
* Set the starting point and the CPU time used for the statistics.
*/
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
the_period->time_period_initiated = uptime;
#else
the_period->time_period_initiated = _Watchdog_Ticks_since_boot;
#endif
the_period->cpu_usage_period_initiated = owning_thread->cpu_time_used;
/*
* If using nanosecond statistics and the period's thread is currently
* executing, then we need to take into account how much time the
* executing thread has run since the last context switch. When this
* routine is invoked from rtems_rate_monotonic_period, the owner will
* be the executing thread. When this routine is invoked from
* _Rate_monotonic_Timeout, it will not.
*/
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
if (owning_thread == _Thread_Executing) {
Timestamp_Control ran;
/*
* Adjust the CPU time used to account for the time since last
* context switch.
*/
_Timestamp_Subtract(
&_Thread_Time_of_last_context_switch, &uptime, &ran
);
_Timestamp_Add_to( &the_period->cpu_usage_period_initiated, &ran );
}
#endif
_Scheduler_Release_job( the_period->owner, the_period->next_length );
}
static void _Rate_monotonic_Update_statistics(
Rate_monotonic_Control *the_period
)
{
Thread_CPU_usage_t executed;
Rate_monotonic_Period_time_t since_last_period;
Rate_monotonic_Statistics *stats;
bool valid_status;
/*
* Assume we are only called in states where it is appropriate
* to update the statistics. This should only be RATE_MONOTONIC_ACTIVE
* and RATE_MONOTONIC_EXPIRED.
*/
/*
* Update the counts.
*/
stats = &the_period->Statistics;
stats->count++;
if ( the_period->state == RATE_MONOTONIC_EXPIRED )
stats->missed_count++;
/*
* Grab status for time statistics.
*/
valid_status =
_Rate_monotonic_Get_status( the_period, &since_last_period, &executed );
if (!valid_status)
return;
/*
* Update CPU time
*/
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
_Timestamp_Add_to( &stats->total_cpu_time, &executed );
if ( _Timestamp_Less_than( &executed, &stats->min_cpu_time ) )
stats->min_cpu_time = executed;
if ( _Timestamp_Greater_than( &executed, &stats->max_cpu_time ) )
stats->max_cpu_time = executed;
#else
stats->total_cpu_time += executed;
if ( executed < stats->min_cpu_time )
stats->min_cpu_time = executed;
if ( executed > stats->max_cpu_time )
stats->max_cpu_time = executed;
#endif
/*
* Update Wall time
*/
#ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
_Timestamp_Add_to( &stats->total_wall_time, &since_last_period );
if ( _Timestamp_Less_than( &since_last_period, &stats->min_wall_time ) )
stats->min_wall_time = since_last_period;
if ( _Timestamp_Greater_than( &since_last_period, &stats->max_wall_time ) )
stats->max_wall_time = since_last_period;
#else
/* Sanity check wall time */
if ( since_last_period < executed )
since_last_period = executed;
stats->total_wall_time += since_last_period;
if ( since_last_period < stats->min_wall_time )
stats->min_wall_time = since_last_period;
if ( since_last_period > stats->max_wall_time )
stats->max_wall_time = since_last_period;
#endif
}
rtems_status_code rtems_rate_monotonic_period(
rtems_id id,
rtems_interval length
)
{
Rate_monotonic_Control *the_period;
Objects_Locations location;
rtems_status_code return_value;
rtems_rate_monotonic_period_states local_state;
ISR_Level level;
the_period = _Rate_monotonic_Get( id, &location );
switch ( location ) {
case OBJECTS_LOCAL:
if ( !_Thread_Is_executing( the_period->owner ) ) {
_Objects_Put( &the_period->Object );
return RTEMS_NOT_OWNER_OF_RESOURCE;
}
if ( length == RTEMS_PERIOD_STATUS ) {
switch ( the_period->state ) {
case RATE_MONOTONIC_INACTIVE:
return_value = RTEMS_NOT_DEFINED;
break;
case RATE_MONOTONIC_EXPIRED:
case RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING:
return_value = RTEMS_TIMEOUT;
break;
case RATE_MONOTONIC_ACTIVE:
default: /* unreached -- only to remove warnings */
return_value = RTEMS_SUCCESSFUL;
break;
}
_Objects_Put( &the_period->Object );
return( return_value );
}
_ISR_Disable( level );
if ( the_period->state == RATE_MONOTONIC_INACTIVE ) {
_ISR_Enable( level );
the_period->next_length = length;
/*
* Baseline statistics information for the beginning of a period.
*/
_Rate_monotonic_Initiate_statistics( the_period );
the_period->state = RATE_MONOTONIC_ACTIVE;
_Watchdog_Initialize(
&the_period->Timer,
_Rate_monotonic_Timeout,
id,
NULL
);
_Watchdog_Insert_ticks( &the_period->Timer, length );
_Objects_Put( &the_period->Object );
return RTEMS_SUCCESSFUL;
}
if ( the_period->state == RATE_MONOTONIC_ACTIVE ) {
/*
* Update statistics from the concluding period.
*/
_Rate_monotonic_Update_statistics( the_period );
/*
* This tells the _Rate_monotonic_Timeout that this task is
* in the process of blocking on the period and that we
* may be changing the length of the next period.
*/
the_period->state = RATE_MONOTONIC_OWNER_IS_BLOCKING;
the_period->next_length = length;
_ISR_Enable( level );
_Thread_Executing->Wait.id = the_period->Object.id;
_Thread_Set_state( _Thread_Executing, STATES_WAITING_FOR_PERIOD );
/*
* Did the watchdog timer expire while we were actually blocking
* on it?
*/
_ISR_Disable( level );
local_state = the_period->state;
the_period->state = RATE_MONOTONIC_ACTIVE;
_ISR_Enable( level );
/*
* If it did, then we want to unblock ourself and continue as
* if nothing happen. The period was reset in the timeout routine.
*/
if ( local_state == RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING )
_Thread_Clear_state( _Thread_Executing, STATES_WAITING_FOR_PERIOD );
_Objects_Put( &the_period->Object );
return RTEMS_SUCCESSFUL;
}
if ( the_period->state == RATE_MONOTONIC_EXPIRED ) {
/*
* Update statistics from the concluding period
*/
_Rate_monotonic_Update_statistics( the_period );
_ISR_Enable( level );
the_period->state = RATE_MONOTONIC_ACTIVE;
the_period->next_length = length;
_Watchdog_Insert_ticks( &the_period->Timer, length );
_Scheduler_Release_job( the_period->owner, the_period->next_length );
_Objects_Put( &the_period->Object );
return RTEMS_TIMEOUT;
}
/*
* These should never happen so just return invalid Id.
* - RATE_MONOTONIC_OWNER_IS_BLOCKING:
* - RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING:
*/
#if defined(RTEMS_MULTIPROCESSING)
case OBJECTS_REMOTE: /* should never return this */
#endif
case OBJECTS_ERROR:
break;
}
return RTEMS_INVALID_ID;
}