diff options
Diffstat (limited to 'cpukit/rtems/src/ratemonperiod.c')
-rw-r--r-- | cpukit/rtems/src/ratemonperiod.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/cpukit/rtems/src/ratemonperiod.c b/cpukit/rtems/src/ratemonperiod.c new file mode 100644 index 0000000000..e244fd5778 --- /dev/null +++ b/cpukit/rtems/src/ratemonperiod.c @@ -0,0 +1,374 @@ +/* + * Rate Monotonic Manager - Period Blocking and Status + * + * 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.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/system.h> +#include <rtems/rtems/status.h> +#include <rtems/rtems/support.h> +#include <rtems/score/isr.h> +#include <rtems/score/object.h> +#include <rtems/rtems/ratemon.h> +#include <rtems/score/thread.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) { + + rtems_thread_cpu_usage_t ran; + + /* + * Adjust the CPU time used to account for the time since last + * context switch. + */ + _Timespec_Subtract( + &_Thread_Time_of_last_context_switch, &uptime, &ran + ); + + _Timespec_Add_to( &the_period->cpu_usage_period_initiated, &ran ); + } + #endif +} + +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 +} + + +/*PAGE + * + * rtems_rate_monotonic_period + * + * This directive allows a thread to manipulate a rate monotonic timer. + * + * Input parameters: + * id - rate monotonic id + * length - length of period (in ticks) + * + * Output parameters: + * RTEMS_SUCCESSFUL - if successful + * error code - if unsuccessful + */ + +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 ) ) { + _Thread_Enable_dispatch(); + 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; + } + _Thread_Enable_dispatch(); + return( return_value ); + } + + _ISR_Disable( level ); + if ( the_period->state == RATE_MONOTONIC_INACTIVE ) { + _ISR_Enable( level ); + + /* + * 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 + ); + + the_period->next_length = length; + + _Watchdog_Insert_ticks( &the_period->Timer, length ); + _Thread_Enable_dispatch(); + 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 ); + + _Thread_Enable_dispatch(); + 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 ); + _Thread_Enable_dispatch(); + 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; +} |