score: New timer server implementation

Use mostly the standard watchdog operations.  Use a system event for
synchronization.  This implementation is simpler and offers better SMP
performance.

Close #2131.
This commit is contained in:
Sebastian Huber
2015-04-10 15:31:31 +02:00
parent fd53d2514e
commit a382010c62
10 changed files with 409 additions and 656 deletions

View File

@@ -318,6 +318,11 @@ rtems_status_code rtems_event_receive (
*/
#define RTEMS_EVENT_SYSTEM_NETWORK_CLOSE RTEMS_EVENT_26
/**
* @brief Reserved system event for the timer server.
*/
#define RTEMS_EVENT_SYSTEM_TIMER_SERVER RTEMS_EVENT_30
/**
* @brief Reserved system event for transient usage.
*/

View File

@@ -64,25 +64,44 @@ typedef struct {
*/
Watchdog_Control System_watchdog;
/**
* @brief Remaining delta of the system watchdog.
*/
Watchdog_Interval system_watchdog_delta;
/**
* @brief Unique identifier of the context which deals currently with the
* system watchdog.
*/
Thread_Control *system_watchdog_helper;
/**
* @brief Each insert and tickle operation increases the generation count so
* that the system watchdog dealer notices updates of the watchdog chain.
*/
uint32_t generation;
/**
* @brief Watchdog header managed by the timer server.
*/
Watchdog_Header Header;
/**
* @brief Last known time snapshot of the timer server.
* @brief Last time snapshot of the timer server.
*
* The units may be ticks or seconds.
*/
Watchdog_Interval volatile last_snapshot;
Watchdog_Interval last_snapshot;
/**
* @brief Current time snapshot of the timer server.
*
* The units may be ticks or seconds.
*/
Watchdog_Interval current_snapshot;
} Timer_server_Watchdogs;
struct Timer_server_Control {
/**
* @brief Timer server thread.
*/
Thread_Control *thread;
/**
* @brief The cancel method of the timer server.
*/
@@ -102,26 +121,6 @@ struct Timer_server_Control {
* @brief TOD watchdogs triggered by the timer server.
*/
Timer_server_Watchdogs TOD_watchdogs;
/**
* @brief Chain of timers scheduled for insert.
*
* This pointer is not @c NULL whenever the interval and TOD chains are
* processed. After the processing this list will be checked and if
* necessary the processing will be restarted. Processing of these chains
* can be only interrupted through interrupts.
*/
Chain_Control *volatile insert_chain;
/**
* @brief Indicates that the timer server is active or not.
*
* The server is active after the delay on a system watchdog. The activity
* period of the server ends when no more watchdogs managed by the server
* fire. The system watchdogs must not be manipulated when the server is
* active.
*/
bool volatile active;
};
/**

View File

@@ -15,7 +15,7 @@
/* COPYRIGHT (c) 1989-2008.
* On-Line Applications Research Corporation (OAR).
*
* Copyright (c) 2009 embedded brains GmbH.
* Copyright (c) 2009-2015 embedded brains GmbH.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
@@ -26,190 +26,13 @@
#include "config.h"
#endif
#include <rtems.h>
#include <rtems/rtems/timerimpl.h>
#include <rtems/rtems/tasksimpl.h>
#include <rtems/score/isrlevel.h>
#include <rtems/score/threadimpl.h>
#include <rtems/score/todimpl.h>
static Timer_server_Control _Timer_server_Default;
static void _Timer_server_Stop_interval_system_watchdog(
Timer_server_Control *ts
)
{
_Watchdog_Remove_ticks( &ts->Interval_watchdogs.System_watchdog );
}
static void _Timer_server_Reset_interval_system_watchdog(
Timer_server_Control *ts
)
{
ISR_Level level;
_Timer_server_Stop_interval_system_watchdog( ts );
_ISR_Disable( level );
if ( !_Watchdog_Is_empty( &ts->Interval_watchdogs.Header ) ) {
Watchdog_Interval delta_interval =
_Watchdog_First( &ts->Interval_watchdogs.Header )->delta_interval;
_ISR_Enable( level );
/*
* The unit is TICKS here.
*/
_Watchdog_Insert_ticks(
&ts->Interval_watchdogs.System_watchdog,
delta_interval
);
} else {
_ISR_Enable( level );
}
}
static void _Timer_server_Stop_tod_system_watchdog(
Timer_server_Control *ts
)
{
_Watchdog_Remove_seconds( &ts->TOD_watchdogs.System_watchdog );
}
static void _Timer_server_Reset_tod_system_watchdog(
Timer_server_Control *ts
)
{
ISR_Level level;
_Timer_server_Stop_tod_system_watchdog( ts );
_ISR_Disable( level );
if ( !_Watchdog_Is_empty( &ts->TOD_watchdogs.Header ) ) {
Watchdog_Interval delta_interval =
_Watchdog_First( &ts->TOD_watchdogs.Header )->delta_interval;
_ISR_Enable( level );
/*
* The unit is SECONDS here.
*/
_Watchdog_Insert_seconds(
&ts->TOD_watchdogs.System_watchdog,
delta_interval
);
} else {
_ISR_Enable( level );
}
}
static void _Timer_server_Insert_timer(
Timer_server_Control *ts,
Timer_Control *timer
)
{
if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
_Watchdog_Insert( &ts->Interval_watchdogs.Header, &timer->Ticker );
} else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
_Watchdog_Insert( &ts->TOD_watchdogs.Header, &timer->Ticker );
}
}
static void _Timer_server_Insert_timer_and_make_snapshot(
Timer_server_Control *ts,
Timer_Control *timer
)
{
Watchdog_Control *first_watchdog;
Watchdog_Interval delta_interval;
Watchdog_Interval last_snapshot;
Watchdog_Interval snapshot;
Watchdog_Interval delta;
ISR_Level level;
/*
* We have to update the time snapshots here, because otherwise we may have
* problems with the integer range of the delta values. The time delta DT
* from the last snapshot to now may be arbitrarily long. The last snapshot
* is the reference point for the delta chain. Thus if we do not update the
* reference point we have to add DT to the initial delta of the watchdog
* being inserted. This could result in an integer overflow.
*/
_Thread_Disable_dispatch();
if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
/*
* We have to advance the last known ticks value of the server and update
* the watchdog chain accordingly.
*/
_ISR_Disable( level );
snapshot = _Watchdog_Ticks_since_boot;
last_snapshot = ts->Interval_watchdogs.last_snapshot;
if ( !_Watchdog_Is_empty( &ts->Interval_watchdogs.Header ) ) {
first_watchdog = _Watchdog_First( &ts->Interval_watchdogs.Header );
/*
* We assume adequate unsigned arithmetic here.
*/
delta = snapshot - last_snapshot;
delta_interval = first_watchdog->delta_interval;
if (delta_interval > delta) {
delta_interval -= delta;
} else {
delta_interval = 0;
}
first_watchdog->delta_interval = delta_interval;
}
ts->Interval_watchdogs.last_snapshot = snapshot;
_ISR_Enable( level );
_Watchdog_Insert( &ts->Interval_watchdogs.Header, &timer->Ticker );
if ( !ts->active ) {
_Timer_server_Reset_interval_system_watchdog( ts );
}
} else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
/*
* We have to advance the last known seconds value of the server and update
* the watchdog chain accordingly.
*/
_ISR_Disable( level );
snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch();
last_snapshot = ts->TOD_watchdogs.last_snapshot;
if ( !_Watchdog_Is_empty( &ts->TOD_watchdogs.Header ) ) {
first_watchdog = _Watchdog_First( &ts->TOD_watchdogs.Header );
delta_interval = first_watchdog->delta_interval;
if ( snapshot > last_snapshot ) {
/*
* We advanced in time.
*/
delta = snapshot - last_snapshot;
if (delta_interval > delta) {
delta_interval -= delta;
} else {
delta_interval = 0;
}
} else {
/*
* Someone put us in the past.
*/
delta = last_snapshot - snapshot;
delta_interval += delta;
}
first_watchdog->delta_interval = delta_interval;
}
ts->TOD_watchdogs.last_snapshot = snapshot;
_ISR_Enable( level );
_Watchdog_Insert( &ts->TOD_watchdogs.Header, &timer->Ticker );
if ( !ts->active ) {
_Timer_server_Reset_tod_system_watchdog( ts );
}
}
_Thread_Enable_dispatch();
}
static void _Timer_server_Cancel_method(
Timer_server_Control *ts,
Timer_Control *timer
@@ -222,148 +45,181 @@ static void _Timer_server_Cancel_method(
}
}
static Watchdog_Interval _Timer_server_Get_ticks( void )
{
return _Watchdog_Ticks_since_boot;
}
static Watchdog_Interval _Timer_server_Get_seconds( void )
{
return _TOD_Seconds_since_epoch();
}
static void _Timer_server_Update_system_watchdog(
Timer_server_Watchdogs *watchdogs,
Watchdog_Header *system_header
)
{
ISR_lock_Context lock_context;
_Watchdog_Acquire( &watchdogs->Header, &lock_context );
if ( watchdogs->system_watchdog_helper == NULL ) {
Thread_Control *executing;
uint32_t my_generation;
executing = _Thread_Executing;
watchdogs->system_watchdog_helper = executing;
do {
my_generation = watchdogs->generation;
if ( !_Watchdog_Is_empty( &watchdogs->Header ) ) {
Watchdog_Control *first;
Watchdog_Interval delta;
first = _Watchdog_First( &watchdogs->Header );
delta = first->delta_interval;
if (
watchdogs->System_watchdog.state == WATCHDOG_INACTIVE
|| delta != watchdogs->system_watchdog_delta
) {
watchdogs->system_watchdog_delta = delta;
_Watchdog_Release( &watchdogs->Header, &lock_context );
_Watchdog_Remove( system_header, &watchdogs->System_watchdog );
watchdogs->System_watchdog.initial = delta;
_Watchdog_Insert( system_header, &watchdogs->System_watchdog );
_Watchdog_Acquire( &watchdogs->Header, &lock_context );
}
}
} while ( watchdogs->generation != my_generation );
watchdogs->system_watchdog_helper = NULL;
}
_Watchdog_Release( &watchdogs->Header, &lock_context );
}
static void _Timer_server_Insert_timer(
Timer_server_Watchdogs *watchdogs,
Timer_Control *timer,
Watchdog_Header *system_header,
Watchdog_Interval (*get_ticks)( void )
)
{
ISR_lock_Context lock_context;
Watchdog_Interval now;
Watchdog_Interval delta;
_Watchdog_Acquire( &watchdogs->Header, &lock_context );
now = (*get_ticks)();
delta = now - watchdogs->last_snapshot;
watchdogs->last_snapshot = now;
watchdogs->current_snapshot = now;
if ( watchdogs->system_watchdog_delta > delta ) {
watchdogs->system_watchdog_delta -= delta;
} else {
watchdogs->system_watchdog_delta = 0;
}
if ( !_Watchdog_Is_empty( &watchdogs->Header ) ) {
Watchdog_Control *first = _Watchdog_First( &watchdogs->Header );
if ( first->delta_interval > delta ) {
first->delta_interval -= delta;
} else {
first->delta_interval = 0;
}
}
_Watchdog_Insert_locked(
&watchdogs->Header,
&timer->Ticker,
&lock_context
);
++watchdogs->generation;
_Watchdog_Release( &watchdogs->Header, &lock_context );
_Timer_server_Update_system_watchdog( watchdogs, system_header );
}
static void _Timer_server_Schedule_operation_method(
Timer_server_Control *ts,
Timer_Control *timer
)
{
if ( ts->insert_chain == NULL ) {
_Timer_server_Insert_timer_and_make_snapshot( ts, timer );
} else {
/*
* We interrupted a critical section of the timer server. The timer
* server is not preemptible, so we must be in interrupt context here. No
* thread dispatch will happen until the timer server finishes its
* critical section. We have to use the protected chain methods because
* we may be interrupted by a higher priority interrupt.
*/
_Chain_Append( ts->insert_chain, &timer->Object.Node );
}
}
static void _Timer_server_Process_interval_watchdogs(
Timer_server_Watchdogs *watchdogs,
Chain_Control *fire_chain
)
{
Watchdog_Interval snapshot = _Watchdog_Ticks_since_boot;
/*
* We assume adequate unsigned arithmetic here.
*/
Watchdog_Interval delta = snapshot - watchdogs->last_snapshot;
watchdogs->last_snapshot = snapshot;
_Watchdog_Adjust_to_chain( &watchdogs->Header, delta, fire_chain );
}
static void _Timer_server_Process_tod_watchdogs(
Timer_server_Watchdogs *watchdogs,
Chain_Control *fire_chain
)
{
Watchdog_Interval snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch();
Watchdog_Interval last_snapshot = watchdogs->last_snapshot;
Watchdog_Interval delta;
/*
* Process the seconds chain. Start by checking that the Time
* of Day (TOD) has not been set backwards. If it has then
* we want to adjust the watchdogs->Header to indicate this.
*/
if ( snapshot > last_snapshot ) {
/*
* This path is for normal forward movement and cases where the
* TOD has been set forward.
*/
delta = snapshot - last_snapshot;
_Watchdog_Adjust_to_chain( &watchdogs->Header, delta, fire_chain );
} else if ( snapshot < last_snapshot ) {
/*
* The current TOD is before the last TOD which indicates that
* TOD has been set backwards.
*/
delta = last_snapshot - snapshot;
_Watchdog_Adjust_backward( &watchdogs->Header, delta );
}
watchdogs->last_snapshot = snapshot;
}
static void _Timer_server_Process_insertions( Timer_server_Control *ts )
{
while ( true ) {
Timer_Control *timer = (Timer_Control *) _Chain_Get( ts->insert_chain );
if ( timer == NULL ) {
break;
}
_Timer_server_Insert_timer( ts, timer );
}
}
static void _Timer_server_Get_watchdogs_that_fire_now(
Timer_server_Control *ts,
Chain_Control *insert_chain,
Chain_Control *fire_chain
)
{
/*
* Afterwards all timer inserts are directed to this chain and the interval
* and TOD chains will be no more modified by other parties.
*/
ts->insert_chain = insert_chain;
while ( true ) {
ISR_Level level;
/*
* Remove all the watchdogs that need to fire so we can invoke them.
*/
_Timer_server_Process_interval_watchdogs(
if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
_Timer_server_Insert_timer(
&ts->Interval_watchdogs,
fire_chain
timer,
&_Watchdog_Ticks_header,
_Timer_server_Get_ticks
);
} else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
_Timer_server_Insert_timer(
&ts->TOD_watchdogs,
timer,
&_Watchdog_Seconds_header,
_Timer_server_Get_seconds
);
_Timer_server_Process_tod_watchdogs( &ts->TOD_watchdogs, fire_chain );
/*
* The insertions have to take place here, because they reference the
* current time. The previous process methods take a snapshot of the
* current time. In case someone inserts a watchdog with an initial value
* of zero it will be processed in the next iteration of the timer server
* body loop.
*/
_Timer_server_Process_insertions( ts );
_ISR_Disable( level );
if ( _Chain_Is_empty( insert_chain ) ) {
ts->insert_chain = NULL;
_ISR_Enable( level );
break;
} else {
_ISR_Enable( level );
}
}
}
/* FIXME: This locking approach for SMP is improvable! */
static void _Timer_server_SMP_lock_aquire( void )
static void _Timer_server_Update_current_snapshot(
Timer_server_Watchdogs *watchdogs,
Watchdog_Interval (*get_ticks)( void )
)
{
#if defined( RTEMS_SMP )
_Thread_Disable_dispatch();
#endif
ISR_lock_Context lock_context;
_Watchdog_Acquire( &watchdogs->Header, &lock_context );
watchdogs->current_snapshot = (*get_ticks)();
watchdogs->system_watchdog_delta = 0;
_Watchdog_Release( &watchdogs->Header, &lock_context );
}
static void _Timer_server_SMP_lock_release( void )
static void _Timer_server_Tickle(
Timer_server_Watchdogs *watchdogs,
Watchdog_Header *system_header,
Watchdog_Interval (*get_ticks)( void ),
bool ticks
)
{
#if defined( RTEMS_SMP )
_Thread_Enable_dispatch();
#endif
ISR_lock_Context lock_context;
Watchdog_Interval now;
Watchdog_Interval last;
_Watchdog_Acquire( &watchdogs->Header, &lock_context );
now = watchdogs->current_snapshot;
last = watchdogs->last_snapshot;
watchdogs->last_snapshot = now;
if ( ticks || now >= last ) {
_Watchdog_Adjust_forward_locked(
&watchdogs->Header,
now - last,
&lock_context
);
} else {
_Watchdog_Adjust_backward_locked(
&watchdogs->Header,
last - now
);
}
++watchdogs->generation;
_Watchdog_Release( &watchdogs->Header, &lock_context );
_Timer_server_Update_system_watchdog( watchdogs, system_header );
}
/**
@@ -380,83 +236,75 @@ static rtems_task _Timer_server_Body(
)
{
Timer_server_Control *ts = (Timer_server_Control *) arg;
Chain_Control insert_chain;
Chain_Control fire_chain;
_Chain_Initialize_empty( &insert_chain );
_Chain_Initialize_empty( &fire_chain );
_Timer_server_SMP_lock_aquire();
while ( true ) {
_Timer_server_Get_watchdogs_that_fire_now( ts, &insert_chain, &fire_chain );
rtems_event_set events;
if ( !_Chain_Is_empty( &fire_chain ) ) {
/*
* Fire the watchdogs.
*/
while ( true ) {
Watchdog_Control *watchdog;
ISR_Level level;
_Timer_server_Tickle(
&ts->Interval_watchdogs,
&_Watchdog_Ticks_header,
_Timer_server_Get_ticks,
true
);
/*
* It is essential that interrupts are disable here since an interrupt
* service routine may remove a watchdog from the chain.
*/
_ISR_Disable( level );
watchdog = (Watchdog_Control *) _Chain_Get_unprotected( &fire_chain );
if ( watchdog != NULL ) {
watchdog->state = WATCHDOG_INACTIVE;
_ISR_Enable( level );
} else {
_ISR_Enable( level );
_Timer_server_Tickle(
&ts->TOD_watchdogs,
&_Watchdog_Seconds_header,
_Timer_server_Get_seconds,
false
);
break;
}
_Timer_server_SMP_lock_release();
/*
* The timer server may block here and wait for resources or time.
* The system watchdogs are inactive and will remain inactive since
* the active flag of the timer server is true.
*/
(*watchdog->routine)( watchdog->id, watchdog->user_data );
_Timer_server_SMP_lock_aquire();
}
} else {
ts->active = false;
/*
* Block until there is something to do.
*/
#if !defined( RTEMS_SMP )
_Thread_Disable_dispatch();
#endif
_Thread_Set_state( ts->thread, STATES_DELAYING );
_Timer_server_Reset_interval_system_watchdog( ts );
_Timer_server_Reset_tod_system_watchdog( ts );
#if !defined( RTEMS_SMP )
_Thread_Enable_dispatch();
#endif
_Timer_server_SMP_lock_release();
_Timer_server_SMP_lock_aquire();
ts->active = true;
/*
* Maybe an interrupt did reset the system timers, so we have to stop
* them here. Since we are active now, there will be no more resets
* until we are inactive again.
*/
_Timer_server_Stop_interval_system_watchdog( ts );
_Timer_server_Stop_tod_system_watchdog( ts );
}
(void) rtems_event_system_receive(
RTEMS_EVENT_SYSTEM_TIMER_SERVER,
RTEMS_EVENT_ALL | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
}
}
static void _Timer_server_Wakeup(
Objects_Id id,
void *arg
)
{
Timer_server_Control *ts = arg;
_Timer_server_Update_current_snapshot(
&ts->Interval_watchdogs,
_Timer_server_Get_ticks
);
_Timer_server_Update_current_snapshot(
&ts->TOD_watchdogs,
_Timer_server_Get_seconds
);
(void) rtems_event_system_send( id, RTEMS_EVENT_SYSTEM_TIMER_SERVER );
}
static void _Timer_server_Initialize_watchdogs(
Timer_server_Control *ts,
rtems_id id,
Timer_server_Watchdogs *watchdogs,
Watchdog_Interval (*get_ticks)( void )
)
{
Watchdog_Interval now;
now = (*get_ticks)();
watchdogs->last_snapshot = now;
watchdogs->current_snapshot = now;
_Watchdog_Header_initialize( &watchdogs->Header );
_Watchdog_Initialize(
&watchdogs->System_watchdog,
_Timer_server_Wakeup,
id,
ts
);
}
/**
* @brief rtems_timer_initiate_server
*
@@ -542,36 +390,18 @@ rtems_status_code rtems_timer_initiate_server(
* Timer Server so we do not have to have a critical section.
*/
/*
* We work with the TCB pointer, not the ID, so we need to convert
* to a TCB pointer from here out.
*/
ts->thread = (Thread_Control *)_Objects_Get_local_object(
&_RTEMS_tasks_Information,
_Objects_Get_index(id)
_Timer_server_Initialize_watchdogs(
ts,
id,
&ts->Interval_watchdogs,
_Timer_server_Get_ticks
);
/*
* Initialize the timer lists that the server will manage.
*/
_Watchdog_Header_initialize( &ts->Interval_watchdogs.Header );
_Watchdog_Header_initialize( &ts->TOD_watchdogs.Header );
/*
* Initialize the timers that will be used to control when the
* Timer Server wakes up and services the task-based timers.
*/
_Watchdog_Initialize(
&ts->Interval_watchdogs.System_watchdog,
_Thread_Delay_ended,
0,
ts->thread
);
_Watchdog_Initialize(
&ts->TOD_watchdogs.System_watchdog,
_Thread_Delay_ended,
0,
ts->thread
_Timer_server_Initialize_watchdogs(
ts,
id,
&ts->TOD_watchdogs,
_Timer_server_Get_seconds
);
/*
@@ -581,12 +411,6 @@ rtems_status_code rtems_timer_initiate_server(
ts->cancel = _Timer_server_Cancel_method;
ts->schedule_operation = _Timer_server_Schedule_operation_method;
ts->Interval_watchdogs.last_snapshot = _Watchdog_Ticks_since_boot;
ts->TOD_watchdogs.last_snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch();
ts->insert_chain = NULL;
ts->active = false;
/*
* The default timer server is now available.
*/

View File

@@ -324,7 +324,7 @@ libscore_a_SOURCES += src/coretod.c src/coretodset.c src/coretodget.c \
## WATCHDOG_C_FILES
libscore_a_SOURCES += src/watchdog.c src/watchdogadjust.c \
src/watchdogadjusttochain.c src/watchdoginsert.c src/watchdogremove.c
src/watchdoginsert.c src/watchdogremove.c
libscore_a_SOURCES += src/watchdogtickssinceboot.c
## USEREXT_C_FILES

View File

@@ -164,6 +164,22 @@ void _Watchdog_Adjust_backward(
Watchdog_Interval units
);
/**
* @brief Adjusts the watchdogs in backward direction in a locked context.
*
* The caller must be the owner of the watchdog lock and will be the owner
* after the call.
*
* @param[in] header The watchdog header.
* @param[in] units The units of ticks to adjust.
*
* @see _Watchdog_Adjust_forward().
*/
void _Watchdog_Adjust_backward_locked(
Watchdog_Header *header,
Watchdog_Interval units
);
/**
* @brief Adjusts the header watchdog chain in the forward direction for units
* ticks.
@@ -179,24 +195,22 @@ void _Watchdog_Adjust_forward(
);
/**
* @brief Adjusts the @a header watchdog chain in the forward
* @a direction for @a units_arg ticks.
* @brief Adjusts the watchdogs in forward direction in a locked context.
*
* This routine adjusts the @a header watchdog chain in the forward
* @a direction for @a units_arg ticks.
* The caller must be the owner of the watchdog lock and will be the owner
* after the call. This function may release and acquire the watchdog lock
* internally.
*
* @param[in] header is the watchdog chain to adjust
* @param[in] units_arg is the number of units to adjust @a header
* @param[in] to_fire is a pointer to an initialized Chain_Control to which
* all watchdog instances that are to be fired will be placed.
* @param[in] header The watchdog header.
* @param[in] units The units of ticks to adjust.
* @param[in] lock_context The lock context.
*
* @note This always adjusts forward.
* @see _Watchdog_Adjust_forward().
*/
void _Watchdog_Adjust_to_chain(
void _Watchdog_Adjust_forward_locked(
Watchdog_Header *header,
Watchdog_Interval units_arg,
Chain_Control *to_fire
Watchdog_Interval units,
ISR_lock_Context *lock_context
);
/**
@@ -215,6 +229,25 @@ void _Watchdog_Insert (
Watchdog_Control *the_watchdog
);
/**
* @brief Inserts the watchdog in a locked context.
*
* The caller must be the owner of the watchdog lock and will be the owner
* after the call. This function may release and acquire the watchdog lock
* internally.
*
* @param[in] header The watchdog header.
* @param[in] the_watchdog The watchdog.
* @param[in] lock_context The lock context.
*
* @see _Watchdog_Insert().
*/
void _Watchdog_Insert_locked(
Watchdog_Header *header,
Watchdog_Control *the_watchdog,
ISR_lock_Context *lock_context
);
/**
* @brief This routine is invoked at appropriate intervals to update
* the @a header watchdog chain.

View File

@@ -19,8 +19,16 @@
#endif
#include <rtems/score/watchdogimpl.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/isrlevel.h>
void _Watchdog_Adjust_backward_locked(
Watchdog_Header *header,
Watchdog_Interval units
)
{
if ( !_Watchdog_Is_empty( header ) ) {
_Watchdog_First( header )->delta_interval += units;
}
}
void _Watchdog_Adjust_backward(
Watchdog_Header *header,
@@ -30,23 +38,16 @@ void _Watchdog_Adjust_backward(
ISR_lock_Context lock_context;
_Watchdog_Acquire( header, &lock_context );
if ( !_Watchdog_Is_empty( header ) ) {
_Watchdog_First( header )->delta_interval += units;
}
_Watchdog_Adjust_backward_locked( header, units );
_Watchdog_Release( header, &lock_context );
}
void _Watchdog_Adjust_forward(
void _Watchdog_Adjust_forward_locked(
Watchdog_Header *header,
Watchdog_Interval units
Watchdog_Interval units,
ISR_lock_Context *lock_context
)
{
ISR_lock_Context lock_context;
_Watchdog_Acquire( header, &lock_context );
while ( !_Watchdog_Is_empty( header ) && units > 0 ) {
Watchdog_Control *first = _Watchdog_First( header );
@@ -57,13 +58,23 @@ void _Watchdog_Adjust_forward(
units -= first->delta_interval;
first->delta_interval = 1;
_Watchdog_Release( header, &lock_context );
_Watchdog_Release( header, lock_context );
_Watchdog_Tickle( header );
_Watchdog_Acquire( header, &lock_context );
_Watchdog_Acquire( header, lock_context );
}
}
}
void _Watchdog_Adjust_forward(
Watchdog_Header *header,
Watchdog_Interval units
)
{
ISR_lock_Context lock_context;
_Watchdog_Acquire( header, &lock_context );
_Watchdog_Adjust_forward_locked( header, units, &lock_context );
_Watchdog_Release( header, &lock_context );
}

View File

@@ -1,75 +0,0 @@
/**
* @file
*
* @brief Watchdog Adjust to Chain
* @ingroup ScoreWatchdog
*/
/*
* COPYRIGHT (c) 1989-2009.
* 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/score/watchdogimpl.h>
#include <rtems/score/isrlevel.h>
void _Watchdog_Adjust_to_chain(
Watchdog_Header *header,
Watchdog_Interval units_arg,
Chain_Control *to_fire
)
{
Watchdog_Interval units = units_arg;
ISR_lock_Context lock_context;
Watchdog_Control *first;
_Watchdog_Acquire( header, &lock_context );
while ( 1 ) {
if ( _Watchdog_Is_empty( header ) ) {
break;
}
first = _Watchdog_First( header );
/*
* If it is longer than "units" until the first element on the chain
* fires, then bump it and quit.
*/
if ( units < first->delta_interval ) {
first->delta_interval -= units;
break;
}
/*
* The first set happens in less than units, so take all of them
* off the chain and adjust units to reflect this.
*/
units -= first->delta_interval;
first->delta_interval = 0;
while ( 1 ) {
_Chain_Extract_unprotected( &first->Node );
_Chain_Append_unprotected( to_fire, &first->Node );
_Watchdog_Flash( header, &lock_context );
if ( _Watchdog_Is_empty( header ) )
break;
first = _Watchdog_First( header );
if ( first->delta_interval != 0 )
break;
}
}
_Watchdog_Release( header, &lock_context );
}

View File

@@ -47,15 +47,12 @@ static void _Watchdog_Insert_fixup(
}
}
void _Watchdog_Insert(
void _Watchdog_Insert_locked(
Watchdog_Header *header,
Watchdog_Control *the_watchdog
Watchdog_Control *the_watchdog,
ISR_lock_Context *lock_context
)
{
ISR_lock_Context lock_context;
_Watchdog_Acquire( header, &lock_context );
if ( the_watchdog->state == WATCHDOG_INACTIVE ) {
Watchdog_Iterator iterator;
Chain_Node *current;
@@ -86,7 +83,7 @@ void _Watchdog_Insert(
iterator.delta_interval = delta - delta_next;
iterator.current = next;
_Watchdog_Flash( header, &lock_context );
_Watchdog_Flash( header, lock_context );
if ( the_watchdog->state != WATCHDOG_BEING_INSERTED ) {
goto abort_insert;
@@ -105,6 +102,16 @@ abort_insert:
_Chain_Extract_unprotected( &iterator.Node );
}
}
void _Watchdog_Insert(
Watchdog_Header *header,
Watchdog_Control *the_watchdog
)
{
ISR_lock_Context lock_context;
_Watchdog_Acquire( header, &lock_context );
_Watchdog_Insert_locked( header, the_watchdog, &lock_context );
_Watchdog_Release( header, &lock_context );
}

View File

@@ -1,10 +1,11 @@
/*
* Copyright (c) 2009
* embedded brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
* Copyright (c) 2009-2014 embedded brains GmbH.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
@@ -22,141 +23,91 @@
const char rtems_test_name[] = "SPINTRCRITICAL 17";
/* forward declarations to avoid warnings */
rtems_task Init(rtems_task_argument argument);
typedef struct {
rtems_id timer1;
rtems_id timer2;
bool done;
} test_context;
#define TIMER_COUNT 4
static test_context ctx_instance;
#define TIMER_TRIGGER 0
#define TIMER_RESET 1
#define TIMER_NEVER_INTERVAL 2
#define TIMER_NEVER_TOD 3
static rtems_id timer [TIMER_COUNT];
static rtems_time_of_day tod;
static volatile bool case_hit;
static void never_callback(rtems_id timer, void *arg)
static void never(rtems_id timer_id, void *arg)
{
rtems_test_assert(false);
rtems_test_assert(0);
}
static void reset_tod_timer(void)
static void fire(rtems_id timer_id, void *arg)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
/* The arg is NULL */
test_context *ctx = &ctx_instance;
rtems_status_code sc;
sc = rtems_timer_server_fire_when(
timer [TIMER_NEVER_TOD],
&tod,
never_callback,
NULL
);
directive_failed_with_level(sc, "rtems_timer_server_fire_after", -1);
}
if (!ctx->done) {
ctx->done =
_Timer_server->Interval_watchdogs.system_watchdog_helper != NULL;
static void reset_callback(rtems_id timer_id, void *arg)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
sc = rtems_timer_reset(timer [TIMER_RESET]);
directive_failed_with_level(sc, "rtems_timer_reset", -1);
sc = rtems_timer_reset(timer [TIMER_NEVER_INTERVAL]);
directive_failed_with_level(sc, "rtems_timer_reset", -1);
reset_tod_timer();
if (!case_hit) {
case_hit = _Timer_server->insert_chain != NULL;
if (ctx->done) {
sc = rtems_timer_server_fire_after(ctx->timer2, 100, never, NULL);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
}
}
static void trigger_callback(rtems_id timer_id, void *arg)
static bool test_body(void *arg)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
test_context *ctx = arg;
rtems_status_code sc;
if (case_hit) {
TEST_END();
sc = rtems_timer_reset(ctx->timer1);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
rtems_test_exit(0);
} else if (interrupt_critical_section_test_support_delay()) {
puts("test case not hit, give up");
rtems_test_exit(0);
}
sc = rtems_timer_reset(timer [TIMER_TRIGGER]);
directive_failed(sc, "rtems_timer_reset");
return ctx->done;
}
rtems_task Init( rtems_task_argument ignored )
static void Init(rtems_task_argument ignored)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
size_t i = 0;
test_context *ctx = &ctx_instance;
rtems_status_code sc;
TEST_BEGIN();
build_time(&tod, 4, 12, 2009, 9, 34, 11, 0);
sc = rtems_clock_set(&tod);
directive_failed(sc, "rtems_clock_set");
sc = rtems_timer_create(
rtems_build_name('T', 'I', 'M', '1'),
&ctx->timer1
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
++tod.year;
for (i = 0; i < TIMER_COUNT; ++i) {
sc = rtems_timer_create(
rtems_build_name('T', 'I', 'M', '0' + i),
&timer [i]
);
directive_failed(sc, "rtems_timer_create");
}
sc = rtems_timer_create(
rtems_build_name('T', 'I', 'M', '2'),
&ctx->timer2
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_timer_initiate_server(
RTEMS_MINIMUM_PRIORITY,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_ATTRIBUTES
);
directive_failed(sc, "rtems_timer_initiate_server");
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_timer_server_fire_after(
timer [TIMER_NEVER_INTERVAL],
2,
never_callback,
NULL
);
directive_failed(sc, "rtems_timer_server_fire_after");
sc = rtems_timer_server_fire_after(ctx->timer1, 1000, never, NULL);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
reset_tod_timer();
interrupt_critical_section_test(test_body, ctx, fire);
rtems_test_assert(ctx->done);
sc = rtems_timer_fire_after(
timer [TIMER_RESET],
1,
reset_callback,
NULL
);
directive_failed(sc, "rtems_timer_fire_after");
sc = rtems_timer_server_fire_after(
timer [TIMER_TRIGGER],
1,
trigger_callback,
NULL
);
directive_failed(sc, "rtems_timer_server_fire_after");
interrupt_critical_section_test_support_initialize(NULL);
rtems_task_delete(RTEMS_SELF);
TEST_END();
rtems_test_exit(0);
}
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_MICROSECONDS_PER_TICK 2000
#define CONFIGURE_MICROSECONDS_PER_TICK 1000
#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_MAXIMUM_TIMERS 4
#define CONFIGURE_MAXIMUM_TIMERS 3
#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009 embedded brains GmbH.
# Copyright (c) 2009-2015 embedded brains GmbH.
#
# The license and distribution terms for this file may be
# found in the file LICENSE in this distribution or at
@@ -11,9 +11,7 @@ test set name: spintrcritical17
directives:
_Timer_server_Get_watchdogs_that_fire_now
_Timer_server_Schedule_operation_method
_Timer_server_Process_insertions
_Timer_server_Update_system_watchdog
concepts: