void _Watchdog_Adjust( Chain_Control *header, Watchdog_Adjust_directions direction, Watchdog_Interval units ) { ISR_Level level; _ISR_Disable( level ); /* * NOTE: It is safe NOT to make 'header' a pointer * to volatile data (contrast this with watchdoginsert.c) * because we call _Watchdog_Tickle() below and * hence the compiler must not assume *header to remain * unmodified across that call. * * Till Straumann, 7/2003 */ if ( !_Chain_Is_empty( header ) ) { switch ( direction ) { case WATCHDOG_BACKWARD: _Watchdog_First( header )->delta_interval += units; break; case WATCHDOG_FORWARD: while ( units ) { if ( units < _Watchdog_First( header )->delta_interval ) { _Watchdog_First( header )->delta_interval -= units; break; } else { units -= _Watchdog_First( header )->delta_interval; _Watchdog_First( header )->delta_interval = 1; _ISR_Enable( level ); _Watchdog_Tickle( header ); _ISR_Disable( level ); if ( _Chain_Is_empty( header ) ) break; } } break; } } _ISR_Enable( level ); }
static rtems_timer_service_routine test_release_from_isr( rtems_id timer, void *arg ) { Watchdog_Header *header = &_Watchdog_Ticks_header; if ( !_Watchdog_Is_empty( header ) ) { Watchdog_Control *watchdog = _Watchdog_First( header ); if ( watchdog->delta_interval == 0 && watchdog->routine == _Rate_monotonic_Timeout ) { Watchdog_States state = _Watchdog_Remove_ticks( watchdog ); rtems_test_assert( state == WATCHDOG_ACTIVE ); (*watchdog->routine)( watchdog->id, watchdog->user_data ); if ( getState() == RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING ) { case_hit = true; } } } }
static rtems_timer_service_routine test_release_from_isr( rtems_id timer, void *arg ) { Watchdog_Header *header = &_Watchdog_Ticks_header; if ( !_Watchdog_Is_empty( header ) ) { Watchdog_Control *watchdog = _Watchdog_First( header ); if ( watchdog->delta_interval == 0 && watchdog->routine == _Thread_queue_Timeout ) { Watchdog_States state = _Watchdog_Remove( watchdog ); rtems_test_assert( state == WATCHDOG_ACTIVE ); (*watchdog->routine)( watchdog->id, watchdog->user_data ); if ( getState() == THREAD_BLOCKING_OPERATION_TIMEOUT ) { case_hit = true; } } } }
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 ( !_Chain_Is_empty( &ts->TOD_watchdogs.Chain ) ) { Watchdog_Interval delta_interval = _Watchdog_First( &ts->TOD_watchdogs.Chain )->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 rtems_timer_service_routine test_release_from_isr( rtems_id timer, void *arg ) { Watchdog_Header *header = &_Watchdog_Ticks_header; if ( !_Watchdog_Is_empty( header ) ) { Watchdog_Control *watchdog = _Watchdog_First( header ); if ( watchdog->delta_interval == 0 && watchdog->routine == _Thread_Timeout ) { Watchdog_States state = _Watchdog_Remove_ticks( watchdog ); rtems_test_assert( state == WATCHDOG_ACTIVE ); (*watchdog->routine)( watchdog->id, watchdog->user_data ); if ( is_interrupt_timeout() ) { case_hit = true; } } } }
void _Watchdog_Tickle( Chain_Control *header ) { ISR_Level level; Watchdog_Control *the_watchdog; Watchdog_States watchdog_state; /* * See the comment in watchdoginsert.c and watchdogadjust.c * about why it's safe not to declare header a pointer to * volatile data - till, 2003/7 */ _ISR_Disable( level ); if ( _Chain_Is_empty( header ) ) goto leave; the_watchdog = _Watchdog_First( header ); /* * For some reason, on rare occasions the_watchdog->delta_interval * of the head of the watchdog chain is 0. Before this test was * added, on these occasions an event (which usually was supposed * to have a timeout of 1 tick would have a delta_interval of 0, which * would be decremented to 0xFFFFFFFF by the unprotected * "the_watchdog->delta_interval--;" operation. * This would mean the event would not timeout, and also the chain would * be blocked, because a timeout with a very high number would be at the * head, rather than at the end. * The test "if (the_watchdog->delta_interval != 0)" * here prevents this from occuring. * * We were not able to categorically identify the situation that causes * this, but proved it to be true empirically. So this check causes * correct behaviour in this circumstance. * * The belief is that a race condition exists whereby an event at the head * of the chain is removed (by a pending ISR or higher priority task) * during the _ISR_Flash( level ); in _Watchdog_Insert, but the watchdog * to be inserted has already had its delta_interval adjusted to 0, and * so is added to the head of the chain with a delta_interval of 0. * * Steven Johnson - 12/2005 (gcc-3.2.3 -O3 on powerpc) */ if (the_watchdog->delta_interval != 0) { the_watchdog->delta_interval--; if ( the_watchdog->delta_interval != 0 ) goto leave; } do { watchdog_state = _Watchdog_Remove( the_watchdog ); _ISR_Enable( level ); switch( watchdog_state ) { case WATCHDOG_ACTIVE: (*the_watchdog->routine)( the_watchdog->id, the_watchdog->user_data ); break; case WATCHDOG_INACTIVE: /* * This state indicates that the watchdog is not on any chain. * Thus, it is NOT on a chain being tickled. This case should * never occur. */ break; case WATCHDOG_BEING_INSERTED: /* * This state indicates that the watchdog is in the process of * BEING inserted on the chain. Thus, it can NOT be on a chain * being tickled. This case should never occur. */ break; case WATCHDOG_REMOVE_IT: break; } _ISR_Disable( level ); the_watchdog = _Watchdog_First( header ); } while ( !_Chain_Is_empty( header ) && (the_watchdog->delta_interval == 0) ); leave: _ISR_Enable(level); }
void _Watchdog_Tickle( Watchdog_Header *header ) { ISR_lock_Context lock_context; _Watchdog_Acquire( header, &lock_context ); if ( !_Watchdog_Is_empty( header ) ) { Watchdog_Control *first; Watchdog_Interval delta; first = _Watchdog_First( header ); delta = first->delta_interval; /* * Although it is forbidden to insert watchdogs with a delta interval of * zero it is possible to observe watchdogs with a delta interval of zero * at this point. For example lets have a watchdog chain of one watchdog * with a delta interval of one and insert a new one with an initial value * of one. At the start of the insert procedure it will advance one step * and reduce its delta interval by one yielding zero. Now a tick happens. * This will remove the watchdog on the chain and update the insert * iterator. Now the insert operation continues and will insert the new * watchdog with a delta interval of zero. */ if ( delta > 0 ) { --delta; first->delta_interval = delta; } while ( delta == 0 ) { bool run; Watchdog_Service_routine_entry routine; Objects_Id id; void *user_data; run = ( first->state == WATCHDOG_ACTIVE ); _Watchdog_Remove_it( header, first ); routine = first->routine; id = first->id; user_data = first->user_data; _Watchdog_Release( header, &lock_context ); if ( run ) { (*routine)( id, user_data ); } _Watchdog_Acquire( header, &lock_context ); if ( _Watchdog_Is_empty( header ) ) { break; } first = _Watchdog_First( header ); delta = first->delta_interval; } } _Watchdog_Release( header, &lock_context ); }
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 ( !_Chain_Is_empty( &ts->Interval_watchdogs.Chain ) ) { first_watchdog = _Watchdog_First( &ts->Interval_watchdogs.Chain ); /* * 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.Chain, &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 ( !_Chain_Is_empty( &ts->TOD_watchdogs.Chain ) ) { first_watchdog = _Watchdog_First( &ts->TOD_watchdogs.Chain ); 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.Chain, &timer->Ticker ); if ( !ts->active ) { _Timer_server_Reset_tod_system_watchdog( ts ); } } _Thread_Enable_dispatch(); }
void _Watchdog_Insert( Watchdog_Header *header, Watchdog_Control *the_watchdog ) { ISR_Level level; Watchdog_Control *after; uint32_t insert_isr_nest_level; Watchdog_Interval delta_interval; insert_isr_nest_level = _ISR_Nest_level; _ISR_Disable( level ); /* * Check to see if the watchdog has just been inserted by a * higher priority interrupt. If so, abandon this insert. */ if ( the_watchdog->state != WATCHDOG_INACTIVE ) { _ISR_Enable( level ); return; } the_watchdog->state = WATCHDOG_BEING_INSERTED; _Watchdog_Sync_count++; restart: delta_interval = the_watchdog->initial; for ( after = _Watchdog_First( header ) ; ; after = _Watchdog_Next( after ) ) { if ( delta_interval == 0 || !_Watchdog_Next( after ) ) break; if ( delta_interval < after->delta_interval ) { after->delta_interval -= delta_interval; break; } delta_interval -= after->delta_interval; _ISR_Flash( level ); if ( the_watchdog->state != WATCHDOG_BEING_INSERTED ) { goto exit_insert; } if ( _Watchdog_Sync_level > insert_isr_nest_level ) { _Watchdog_Sync_level = insert_isr_nest_level; goto restart; } } _Watchdog_Activate( the_watchdog ); the_watchdog->delta_interval = delta_interval; _Chain_Insert_unprotected( after->Node.previous, &the_watchdog->Node ); the_watchdog->start_time = _Watchdog_Ticks_since_boot; exit_insert: _Watchdog_Sync_level = insert_isr_nest_level; _Watchdog_Sync_count--; _ISR_Enable( level ); }