/**
* @file
*
* @ingroup RTEMSImplClassicRateMonotonic
*
* @brief This source file contains the implementation of
* rtems_rate_monotonic_period_states().
*/
/*
* COPYRIGHT (c) 1989-2010.
* On-Line Applications Research Corporation (OAR).
* Copyright (c) 2016 embedded brains GmbH.
* COPYRIGHT (c) 2016 Kuan-Hsun Chen.
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/rtems/ratemonimpl.h>
#include <rtems/score/schedulerimpl.h>
#include <rtems/score/todimpl.h>
bool _Rate_monotonic_Get_status(
const Rate_monotonic_Control *the_period,
Timestamp_Control *wall_since_last_period,
Timestamp_Control *cpu_since_last_period
)
{
Timestamp_Control uptime;
Thread_Control *owning_thread = the_period->owner;
Timestamp_Control used;
/*
* Determine elapsed wall time since period initiated.
*/
_TOD_Get_uptime( &uptime );
_Timestamp_Subtract(
&the_period->time_period_initiated, &uptime, wall_since_last_period
);
/*
* 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 = current cpu usage - cpu usage at start of period */
_Timestamp_Subtract(
&the_period->cpu_usage_period_initiated,
&used,
cpu_since_last_period
);
return true;
}
static void _Rate_monotonic_Release_postponed_job(
Rate_monotonic_Control *the_period,
Thread_Control *owner,
rtems_interval next_length,
ISR_lock_Context *lock_context
)
{
Per_CPU_Control *cpu_self;
Thread_queue_Context queue_context;
--the_period->postponed_jobs;
_Scheduler_Release_job(
owner,
&the_period->Priority,
the_period->latest_deadline,
&queue_context
);
cpu_self = _Thread_Dispatch_disable_critical( lock_context );
_Rate_monotonic_Release( the_period, lock_context );
_Thread_Priority_update( &queue_context );
_Thread_Dispatch_direct( cpu_self );
}
static void _Rate_monotonic_Release_job(
Rate_monotonic_Control *the_period,
Thread_Control *owner,
rtems_interval next_length,
ISR_lock_Context *lock_context
)
{
Per_CPU_Control *cpu_self;
Thread_queue_Context queue_context;
uint64_t deadline;
cpu_self = _Thread_Dispatch_disable_critical( lock_context );
deadline = _Watchdog_Per_CPU_insert_ticks(
&the_period->Timer,
cpu_self,
next_length
);
_Scheduler_Release_job(
owner,
&the_period->Priority,
deadline,
&queue_context
);
_Rate_monotonic_Release( the_period, lock_context );
_Thread_Priority_update( &queue_context );
_Thread_Dispatch_enable( cpu_self );
}
void _Rate_monotonic_Restart(
Rate_monotonic_Control *the_period,
Thread_Control *owner,
ISR_lock_Context *lock_context
)
{
/*
* 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 );
_Rate_monotonic_Release_job(
the_period,
owner,
the_period->next_length,
lock_context
);
}
static void _Rate_monotonic_Update_statistics(
Rate_monotonic_Control *the_period
)
{
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
* 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
*/
_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;
/*
* Update Wall time
*/
_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;
}
static rtems_status_code _Rate_monotonic_Get_status_for_state(
rtems_rate_monotonic_period_states state
)
{
switch ( state ) {
case RATE_MONOTONIC_INACTIVE:
return RTEMS_NOT_DEFINED;
case RATE_MONOTONIC_EXPIRED:
return RTEMS_TIMEOUT;
default:
_Assert( state == RATE_MONOTONIC_ACTIVE );
return RTEMS_SUCCESSFUL;
}
}
static rtems_status_code _Rate_monotonic_Activate(
Rate_monotonic_Control *the_period,
rtems_interval length,
Thread_Control *executing,
ISR_lock_Context *lock_context
)
{
_Assert( the_period->postponed_jobs == 0 );
the_period->state = RATE_MONOTONIC_ACTIVE;
the_period->next_length = length;
_Rate_monotonic_Restart( the_period, executing, lock_context );
return RTEMS_SUCCESSFUL;
}
static rtems_status_code _Rate_monotonic_Block_while_active(
Rate_monotonic_Control *the_period,
rtems_interval length,
Thread_Control *executing,
ISR_lock_Context *lock_context
)
{
Per_CPU_Control *cpu_self;
bool success;
/*
* 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->next_length = length;
executing->Wait.return_argument = the_period;
_Thread_Wait_flags_set( executing, RATE_MONOTONIC_INTEND_TO_BLOCK );
cpu_self = _Thread_Dispatch_disable_critical( lock_context );
_Rate_monotonic_Release( the_period, lock_context );
_Thread_Set_state( executing, STATES_WAITING_FOR_PERIOD );
success = _Thread_Wait_flags_try_change_acquire(
executing,
RATE_MONOTONIC_INTEND_TO_BLOCK,
RATE_MONOTONIC_BLOCKED
);
if ( !success ) {
_Assert(
_Thread_Wait_flags_get( executing ) == RATE_MONOTONIC_READY_AGAIN
);
_Thread_Unblock( executing );
}
_Thread_Dispatch_direct( cpu_self );
return RTEMS_SUCCESSFUL;
}
/*
* There are two possible cases: one is that the previous deadline is missed,
* The other is that the number of postponed jobs is not 0, but the current
* deadline is still not expired, i.e., state = RATE_MONOTONIC_ACTIVE.
*/
static rtems_status_code _Rate_monotonic_Block_while_expired(
Rate_monotonic_Control *the_period,
rtems_interval length,
Thread_Control *executing,
ISR_lock_Context *lock_context
)
{
/*
* No matter the just finished jobs in time or not,
* they are actually missing their deadlines already.
*/
the_period->state = RATE_MONOTONIC_EXPIRED;
/*
* Update statistics from the concluding period
*/
_Rate_monotonic_Update_statistics( the_period );
the_period->state = RATE_MONOTONIC_ACTIVE;
the_period->next_length = length;
_Rate_monotonic_Release_postponed_job(
the_period,
executing,
length,
lock_context
);
return RTEMS_TIMEOUT;
}
rtems_status_code rtems_rate_monotonic_period(
rtems_id id,
rtems_interval length
)
{
Rate_monotonic_Control *the_period;
ISR_lock_Context lock_context;
Thread_Control *executing;
rtems_status_code status;
rtems_rate_monotonic_period_states state;
the_period = _Rate_monotonic_Get( id, &lock_context );
if ( the_period == NULL ) {
return RTEMS_INVALID_ID;
}
executing = _Thread_Executing;
if ( executing != the_period->owner ) {
_ISR_lock_ISR_enable( &lock_context );
return RTEMS_NOT_OWNER_OF_RESOURCE;
}
_Rate_monotonic_Acquire_critical( the_period, &lock_context );
state = the_period->state;
if ( length == RTEMS_PERIOD_STATUS ) {
status = _Rate_monotonic_Get_status_for_state( state );
_Rate_monotonic_Release( the_period, &lock_context );
} else {
switch ( state ) {
case RATE_MONOTONIC_ACTIVE:
if( the_period->postponed_jobs > 0 ){
/*
* If the number of postponed jobs is not 0, it means the
* previous postponed instance is finished without exceeding
* the current period deadline.
*
* Do nothing on the watchdog deadline assignment but release the
* next remaining postponed job.
*/
status = _Rate_monotonic_Block_while_expired(
the_period,
length,
executing,
&lock_context
);
}else{
/*
* Normal case that no postponed jobs and no expiration, so wait for
* the period and update the deadline of watchdog accordingly.
*/
status = _Rate_monotonic_Block_while_active(
the_period,
length,
executing,
&lock_context
);
}
break;
case RATE_MONOTONIC_INACTIVE:
status = _Rate_monotonic_Activate(
the_period,
length,
executing,
&lock_context
);
break;
default:
/*
* As now this period was already TIMEOUT, there must be at least one
* postponed job recorded by the watchdog. The one which exceeded
* the previous deadlines was just finished.
*
* Maybe there is more than one job postponed due to the preemption or
* the previous finished job.
*/
_Assert( state == RATE_MONOTONIC_EXPIRED );
status = _Rate_monotonic_Block_while_expired(
the_period,
length,
executing,
&lock_context
);
break;
}
}
return status;
}