/** * @file * * @ingroup RTEMSScoreWatchdog * * @brief This header file provides interfaces of the * @ref RTEMSScoreWatchdog which are only used by the implementation. */ /* * COPYRIGHT (c) 1989-2004. * 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. */ #ifndef _RTEMS_SCORE_WATCHDOGIMPL_H #define _RTEMS_SCORE_WATCHDOGIMPL_H #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** * @addtogroup RTEMSScoreWatchdog * * @{ */ /** * @brief Watchdog states. */ typedef enum { /** * @brief The watchdog is scheduled and a black node in the red-black tree. */ WATCHDOG_SCHEDULED_BLACK, /** * @brief The watchdog is scheduled and a red node in the red-black tree. */ WATCHDOG_SCHEDULED_RED, /** * @brief The watchdog is inactive. */ WATCHDOG_INACTIVE, /** * @brief The watchdog is on a chain of pending watchdogs. * * This state is used by the timer server for example. */ WATCHDOG_PENDING } Watchdog_State; /** * @brief Watchdog initializer for static initialization. * * The processor of this watchdog is set to processor with index zero. * * @see _Watchdog_Preinitialize(). */ #if defined(RTEMS_SMP) #define WATCHDOG_INITIALIZER( routine ) \ { \ { { { NULL, NULL, NULL, WATCHDOG_INACTIVE } } }, \ &_Per_CPU_Information[ 0 ].per_cpu, \ ( routine ), \ 0 \ } #else #define WATCHDOG_INITIALIZER( routine ) \ { \ { { { NULL, NULL, NULL, WATCHDOG_INACTIVE } } }, \ ( routine ), \ 0 \ } #endif /** * @brief Initializes the watchdog header. * * @param[out] header The header to initialize. */ RTEMS_INLINE_ROUTINE void _Watchdog_Header_initialize( Watchdog_Header *header ) { _RBTree_Initialize_empty( &header->Watchdogs ); header->first = NULL; } /** * @brief Returns the first of the watchdog header. * * @param header The watchdog header to remove the first of. * * @return The first of @a header. */ RTEMS_INLINE_ROUTINE Watchdog_Control *_Watchdog_Header_first( const Watchdog_Header *header ) { return (Watchdog_Control *) header->first; } /** * @brief Destroys the watchdog header. * * @param header The watchdog header to destroy. */ RTEMS_INLINE_ROUTINE void _Watchdog_Header_destroy( Watchdog_Header *header ) { /* Do nothing */ (void) header; } /** * @brief Performs a watchdog tick. * * @param cpu The processor for this watchdog tick. */ void _Watchdog_Tick( struct Per_CPU_Control *cpu ); /** * @brief Gets the state of the watchdog. * * @param the_watchdog The watchdog to get the state of. * * @return The RB_COLOR of @a the_watchdog. */ RTEMS_INLINE_ROUTINE Watchdog_State _Watchdog_Get_state( const Watchdog_Control *the_watchdog ) { return (Watchdog_State) RB_COLOR( &the_watchdog->Node.RBTree, Node ); } /** * @brief Sets the state of the watchdog. * * @param[out] the_watchdog The watchdog to set the state of. * @param state The state to set the watchdog to. */ RTEMS_INLINE_ROUTINE void _Watchdog_Set_state( Watchdog_Control *the_watchdog, Watchdog_State state ) { RB_COLOR( &the_watchdog->Node.RBTree, Node ) = state; } /** * @brief Gets the watchdog's cpu. * * @param the_watchdog The watchdog to get the cpu of. * * @return The cpu of the watchdog. */ RTEMS_INLINE_ROUTINE Per_CPU_Control *_Watchdog_Get_CPU( const Watchdog_Control *the_watchdog ) { #if defined(RTEMS_SMP) return the_watchdog->cpu; #else return _Per_CPU_Get_by_index( 0 ); #endif } /** * @brief Sets the cpu for the watchdog. * * @param[out] the_watchdog The watchdog to set the cpu of. * @param cpu The cpu to be set as @a the_watchdog's cpu. */ RTEMS_INLINE_ROUTINE void _Watchdog_Set_CPU( Watchdog_Control *the_watchdog, Per_CPU_Control *cpu ) { #if defined(RTEMS_SMP) the_watchdog->cpu = cpu; #else (void) cpu; #endif } /** * @brief Pre-initializes a watchdog. * * This routine must be called before a watchdog is used in any way. The * exception are statically initialized watchdogs via WATCHDOG_INITIALIZER(). * * @param[out] the_watchdog The uninitialized watchdog. */ RTEMS_INLINE_ROUTINE void _Watchdog_Preinitialize( Watchdog_Control *the_watchdog, Per_CPU_Control *cpu ) { _Watchdog_Set_CPU( the_watchdog, cpu ); _Watchdog_Set_state( the_watchdog, WATCHDOG_INACTIVE ); #if defined(RTEMS_DEBUG) the_watchdog->routine = NULL; the_watchdog->expire = 0; #endif } /** * @brief Initializes a watchdog with a new service routine. * * The watchdog must be inactive. * * @param[out] the_watchdog The watchdog to initialize. * @param routing The service routine for @a the_watchdog. */ RTEMS_INLINE_ROUTINE void _Watchdog_Initialize( Watchdog_Control *the_watchdog, Watchdog_Service_routine_entry routine ) { _Assert( _Watchdog_Get_state( the_watchdog ) == WATCHDOG_INACTIVE ); the_watchdog->routine = routine; } /** * @brief Calls the routine of each not expired watchdog control node. * * @param header The watchdog header. * @param first The first watchdog control node. * @param now The current time to check the expiration time against. * @param lock The lock that is released before calling the routine and then * acquired after the call. * @param lock_context The lock context for the release before calling the * routine and for the acquire after. */ void _Watchdog_Do_tickle( Watchdog_Header *header, Watchdog_Control *first, uint64_t now, #if defined(RTEMS_SMP) ISR_lock_Control *lock, #endif ISR_lock_Context *lock_context ); #if defined(RTEMS_SMP) #define _Watchdog_Tickle( header, first, now, lock, lock_context ) \ _Watchdog_Do_tickle( header, first, now, lock, lock_context ) #else #define _Watchdog_Tickle( header, first, now, lock, lock_context ) \ _Watchdog_Do_tickle( header, first, now, lock_context ) #endif /** * @brief Inserts a watchdog into the set of scheduled watchdogs according to * the specified expiration time. * * The watchdog must be inactive. * * @param[in, out] header The set of scheduler watchdogs to insert into. * @param[in, out] the_watchdog The watchdog to insert. * @param expire The expiration time for the watchdog. */ void _Watchdog_Insert( Watchdog_Header *header, Watchdog_Control *the_watchdog, uint64_t expire ); /** * @brief In the case the watchdog is scheduled, then it is removed from the set of * scheduled watchdogs. * * The watchdog must be initialized before this call. * * @param[in, out] header The scheduled watchdogs. * @param[in, out] the_watchdog The watchdog to remove. */ void _Watchdog_Remove( Watchdog_Header *header, Watchdog_Control *the_watchdog ); /** * @brief In the case the watchdog is scheduled, then it is removed from the set of * scheduled watchdogs. * * The watchdog must be initialized before this call. * * @param[in, out] header The scheduled watchdogs. * @param[in, out] the_watchdog The watchdog to remove. * @param now The current time. * * @retval other The difference of the now and expiration time. * @retval 0 The now time is greater than or equal to the expiration time of * the watchdog. */ RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Cancel( Watchdog_Header *header, Watchdog_Control *the_watchdog, uint64_t now ) { uint64_t expire; uint64_t remaining; expire = the_watchdog->expire; if ( now < expire ) { remaining = expire - now; } else { remaining = 0; } _Watchdog_Remove( header, the_watchdog ); return remaining; } /** * @brief Checks if the watchdog is scheduled. * * @param the_watchdog The watchdog for the verification. * * @retval true The watchdog is scheduled. * @retval false The watchdog is inactive. */ RTEMS_INLINE_ROUTINE bool _Watchdog_Is_scheduled( const Watchdog_Control *the_watchdog ) { return _Watchdog_Get_state( the_watchdog ) < WATCHDOG_INACTIVE; } /** * @brief Sets the first watchdog of the watchdog collection to the next * watchdog of the current first watchdog. * * This function may be used during watchdog removals, see _Watchdog_Remove() * and _Watchdog_Tickle(). * * @param[in, out] header is the watchdog collection header. * * @param first is the current first watchdog which should be removed * afterwards. */ RTEMS_INLINE_ROUTINE void _Watchdog_Next_first( Watchdog_Header *header, const Watchdog_Control *first ) { RBTree_Node *right; /* * This function uses the following properties of red-black trees: * * 1. Every leaf (NULL) is black. * * 2. If a node is red, then both its children are black. * * 3. Every simple path from a node to a descendant leaf contains the same * number of black nodes. * * The first node has no left child. So every path from the first node has * exactly one black node (including leafs). The first node cannot have a * non-leaf black right child. It may have a red right child. In this case * both children must be leafs. */ _Assert( header->first == &first->Node.RBTree ); _Assert( _RBTree_Left( &first->Node.RBTree ) == NULL ); right = _RBTree_Right( &first->Node.RBTree ); if ( right != NULL ) { _Assert( RB_COLOR( right, Node ) == RB_RED ); _Assert( _RBTree_Left( right ) == NULL ); _Assert( _RBTree_Right( right ) == NULL ); header->first = right; } else { header->first = _RBTree_Parent( &first->Node.RBTree ); } } /** * @brief The maximum watchdog ticks value for the far future. */ #define WATCHDOG_MAXIMUM_TICKS UINT64_MAX #define WATCHDOG_NANOSECONDS_PER_SECOND 1000000000 /** * @brief The bits necessary to store 1000000000 * (= WATCHDOG_NANOSECONDS_PER_SECOND) nanoseconds. * * The expiration time is an unsigned 64-bit integer. To store nanoseconds * timeouts we use 30 bits (2**30 == 1073741824) for the nanoseconds and 34 * bits for the seconds since UNIX Epoch. This leads to a year 2514 problem. */ #define WATCHDOG_BITS_FOR_1E9_NANOSECONDS 30 /** * @brief The maximum number of seconds representable in the nanoseconds * watchdog format. * * We have 2**34 bits for the seconds part. */ #define WATCHDOG_MAX_SECONDS 0x3ffffffff /** * @brief Checks if the timespec is a valid timespec for a watchdog. * * @param ts The timespec for the verification. * * @retval true The timespec is a valid timespec. * @retval false The timespec is invalid. */ RTEMS_INLINE_ROUTINE bool _Watchdog_Is_valid_timespec( const struct timespec *ts ) { return ts != NULL && (unsigned long) ts->tv_nsec < WATCHDOG_NANOSECONDS_PER_SECOND; } /** * @brief Checks if the timespec is a valid interval timespec for a watchdog. * * @param ts The timespec for the verification. * * @retval true The timespec is a valid interval timespec. * @retval false The timespec is invalid. */ RTEMS_INLINE_ROUTINE bool _Watchdog_Is_valid_interval_timespec( const struct timespec *ts ) { return _Watchdog_Is_valid_timespec( ts ) && ts->tv_sec >= 0; } /** * @brief Adds the delta timespec to the current time if the delta is a valid * interval timespec. * * @param[in, out] now The current time. * @param delta The delta timespec for the addition. * * @retval pointer Pointer to the now timespec. * @retval NULL @a delta is not a valid interval timespec. */ RTEMS_INLINE_ROUTINE const struct timespec * _Watchdog_Future_timespec( struct timespec *now, const struct timespec *delta ) { uint64_t sec; if ( !_Watchdog_Is_valid_interval_timespec( delta ) ) { return NULL; } sec = (uint64_t) now->tv_sec; sec += (uint64_t) delta->tv_sec; now->tv_nsec += delta->tv_nsec; /* We have 2 * (2**63 - 1) + 1 == UINT64_MAX */ if ( now->tv_nsec >= WATCHDOG_NANOSECONDS_PER_SECOND ) { now->tv_nsec -= WATCHDOG_NANOSECONDS_PER_SECOND; ++sec; } if ( sec <= INT64_MAX ) { now->tv_sec = sec; } else { now->tv_sec = INT64_MAX; } return now; } /** * @brief Checks if the timespec is too far in the future. * * @param ts The timespec for the verification. * * @retval true @a ts is too far in the future. * @retval false @a ts is not too far in the future. */ RTEMS_INLINE_ROUTINE bool _Watchdog_Is_far_future_timespec( const struct timespec *ts ) { return ts->tv_sec > WATCHDOG_MAX_SECONDS; } /** * @brief Converts the seconds to ticks. * * @param seconds The seconds to convert to ticks. * * @return @a seconds converted to ticks. */ RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Ticks_from_seconds( uint32_t seconds ) { uint64_t ticks = seconds; ticks <<= WATCHDOG_BITS_FOR_1E9_NANOSECONDS; return ticks; } /** * @brief Converts the timespec in ticks. * * @param ts The timespec to convert to ticks. * * @return @a ts converted to ticks. */ RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Ticks_from_timespec( const struct timespec *ts ) { uint64_t ticks; _Assert( _Watchdog_Is_valid_timespec( ts ) ); _Assert( ts->tv_sec >= 0 ); _Assert( !_Watchdog_Is_far_future_timespec( ts ) ); ticks = (uint64_t) ts->tv_sec; ticks <<= WATCHDOG_BITS_FOR_1E9_NANOSECONDS; ticks |= (uint32_t) ts->tv_nsec; return ticks; } /** * @brief Converts the ticks to timespec. * * @param ticks are the ticks to convert. * * @param[out] ts is the timespec to return the converted ticks. */ RTEMS_INLINE_ROUTINE void _Watchdog_Ticks_to_timespec( uint64_t ticks, struct timespec *ts ) { ts->tv_sec = ticks >> WATCHDOG_BITS_FOR_1E9_NANOSECONDS; ts->tv_nsec = ticks & ( ( 1U << WATCHDOG_BITS_FOR_1E9_NANOSECONDS ) - 1 ); } /** * @brief Converts the sbintime in ticks. * * @param sbt The sbintime to convert to ticks. * * @return @a sbt converted to ticks. */ RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Ticks_from_sbintime( int64_t sbt ) { uint64_t ticks = ( sbt >> 32 ) << WATCHDOG_BITS_FOR_1E9_NANOSECONDS; ticks |= ( (uint64_t) 1000000000 * (uint32_t) sbt ) >> 32; return ticks; } /** * @brief Acquires the per cpu watchdog lock in a critical section. * * @param cpu The cpu to acquire the watchdog lock of. * @param lock_context The lock context. */ RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_acquire_critical( Per_CPU_Control *cpu, ISR_lock_Context *lock_context ) { _ISR_lock_Acquire( &cpu->Watchdog.Lock, lock_context ); } /** * @brief Releases the per cpu watchdog lock in a critical section. * * @param cpu The cpu to release the watchdog lock of. * @param lock_context The lock context. */ RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_release_critical( Per_CPU_Control *cpu, ISR_lock_Context *lock_context ) { _ISR_lock_Release( &cpu->Watchdog.Lock, lock_context ); } /** * @brief Sets the watchdog's cpu to the given instance and sets its expiration * time to the watchdog expiration time of the cpu plus the ticks. * * @param[in, out] the_watchdog The watchdog to set the cpu and expiration time of. * @param cpu The cpu for the watchdog. * @param ticks The ticks to add to the expiration time. * * @return The new expiration time of the watchdog. */ RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Per_CPU_insert_ticks( Watchdog_Control *the_watchdog, Per_CPU_Control *cpu, Watchdog_Interval ticks ) { ISR_lock_Context lock_context; Watchdog_Header *header; uint64_t expire; header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ]; _Watchdog_Set_CPU( the_watchdog, cpu ); _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context ); expire = ticks + cpu->Watchdog.ticks; _Watchdog_Insert(header, the_watchdog, expire); _Watchdog_Per_CPU_release_critical( cpu, &lock_context ); return expire; } /** * @brief Sets the watchdog's cpu and inserts it with the given expiration time * in the scheduled watchdogs. * * @param[in, out] the_watchdog The watchdog to set cpu and expiration time of. * @param cpu The cpu for the operation. * @param[in, out] header The scheduled watchdogs. * @param expire The expiration time for the watchdog. * * @return The expiration time of the watchdog. */ RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Per_CPU_insert( Watchdog_Control *the_watchdog, Per_CPU_Control *cpu, Watchdog_Header *header, uint64_t expire ) { ISR_lock_Context lock_context; _Watchdog_Set_CPU( the_watchdog, cpu ); _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context ); _Watchdog_Insert( header, the_watchdog, expire ); _Watchdog_Per_CPU_release_critical( cpu, &lock_context ); return expire; } /** * @brief Removes the watchdog from the cpu and the scheduled watchdogs. * * @param[in, out] the_watchdog The watchdog to remove. * @param cpu The cpu to remove the watchdog from. * @param[in, out] The scheduled watchdogs. */ RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_remove( Watchdog_Control *the_watchdog, Per_CPU_Control *cpu, Watchdog_Header *header ) { ISR_lock_Context lock_context; _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context ); _Watchdog_Remove( header, the_watchdog ); _Watchdog_Per_CPU_release_critical( cpu, &lock_context ); } /** * @brief Removes the watchdog from the cpu and the scheduled watchdogs. * * @param[in, out] the_watchdog The watchdog to remove. */ RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_remove_ticks( Watchdog_Control *the_watchdog ) { Per_CPU_Control *cpu; cpu = _Watchdog_Get_CPU( the_watchdog ); _Watchdog_Per_CPU_remove( the_watchdog, cpu, &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ] ); } /** @} */ #ifdef __cplusplus } #endif #endif /* end of include file */