source: rtems/cpukit/score/src/watchdogremove.c @ a48b7c44

Last change on this file since a48b7c44 was a48b7c44, checked in by Sebastian Huber <sebastian.huber@…>, on Dec 20, 2015 at 8:47:12 PM

score: Fix watchdog removal

Under certain conditions a new watchdog was inserted with a wrong and
very large delta interval due to an incomplete iterator update.

Bug was introduced by 1ccbd052910ed16131c74b0d5595c8a94066942d.

Close #2501.

  • Property mode set to 100644
File size: 4.6 KB
Line 
1/**
2 * @file
3 *
4 * @brief Remove Watchdog from List
5 * @ingroup ScoreWatchdog
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-1999.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.org/license/LICENSE.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <rtems/score/watchdogimpl.h>
22#include <rtems/score/assert.h>
23
24static void _Watchdog_Remove_it(
25  Watchdog_Header   *header,
26  Watchdog_Control  *the_watchdog
27)
28{
29  Chain_Node        *next;
30  Watchdog_Interval  delta;
31  const Chain_Node  *iterator_tail;
32  Chain_Node        *iterator_node;
33
34  _Assert( the_watchdog->state == WATCHDOG_ACTIVE );
35
36  the_watchdog->state = WATCHDOG_INACTIVE;
37  the_watchdog->stop_time = _Watchdog_Ticks_since_boot;
38
39  next = _Chain_Next( &the_watchdog->Node );
40  delta = the_watchdog->delta_interval;
41
42  if ( next != _Chain_Tail( &header->Watchdogs ) ) {
43    Watchdog_Control *next_watchdog;
44
45    next_watchdog = (Watchdog_Control *) next;
46    next_watchdog->delta_interval += delta;
47  }
48
49  _Chain_Extract_unprotected( &the_watchdog->Node );
50
51  iterator_node = _Chain_First( &header->Iterators );
52  iterator_tail = _Chain_Immutable_tail( &header->Iterators );
53
54  while ( iterator_node != iterator_tail ) {
55    Watchdog_Iterator *iterator;
56
57    iterator = (Watchdog_Iterator *) iterator_node;
58
59    if ( iterator->current == next ) {
60      iterator->delta_interval += delta;
61    }
62
63    if ( iterator->current == &the_watchdog->Node ) {
64      Chain_Node *previous = _Chain_Previous( &the_watchdog->Node );
65
66      iterator->current = previous;
67
68      if ( previous != _Chain_Head( &header->Watchdogs ) ) {
69        Watchdog_Control *previous_watchdog;
70
71        previous_watchdog = (Watchdog_Control *) previous;
72        iterator->delta_interval += previous_watchdog->delta_interval;
73      }
74    }
75
76    iterator_node = _Chain_Next( iterator_node );
77  }
78}
79
80Watchdog_States _Watchdog_Remove(
81  Watchdog_Header  *header,
82  Watchdog_Control *the_watchdog
83)
84{
85  ISR_lock_Context  lock_context;
86  Watchdog_States   previous_state;
87  Watchdog_Interval now;
88
89  _Watchdog_Acquire( header, &lock_context );
90  previous_state = the_watchdog->state;
91  switch ( previous_state ) {
92    case WATCHDOG_INACTIVE:
93      break;
94
95    case WATCHDOG_BEING_INSERTED:
96
97      /*
98       *  It is not actually on the chain so just change the state and
99       *  the Insert operation we interrupted will be aborted.
100       */
101      the_watchdog->state = WATCHDOG_INACTIVE;
102      now = _Watchdog_Ticks_since_boot;
103      the_watchdog->start_time = now;
104      the_watchdog->stop_time = now;
105      break;
106
107    case WATCHDOG_ACTIVE:
108      _Watchdog_Remove_it( header, the_watchdog );
109      break;
110  }
111
112  _Watchdog_Release( header, &lock_context );
113  return( previous_state );
114}
115
116void _Watchdog_Tickle(
117  Watchdog_Header *header
118)
119{
120  ISR_lock_Context lock_context;
121
122  _Watchdog_Acquire( header, &lock_context );
123
124  if ( !_Watchdog_Is_empty( header ) ) {
125    Watchdog_Control  *first;
126    Watchdog_Interval  delta;
127
128    first = _Watchdog_First( header );
129    delta = first->delta_interval;
130
131    /*
132     * Although it is forbidden to insert watchdogs with a delta interval of
133     * zero it is possible to observe watchdogs with a delta interval of zero
134     * at this point.  For example lets have a watchdog chain of one watchdog
135     * with a delta interval of one and insert a new one with an initial value
136     * of one.  At the start of the insert procedure it will advance one step
137     * and reduce its delta interval by one yielding zero.  Now a tick happens.
138     * This will remove the watchdog on the chain and update the insert
139     * iterator.  Now the insert operation continues and will insert the new
140     * watchdog with a delta interval of zero.
141     */
142    if ( delta > 0 ) {
143      --delta;
144      first->delta_interval = delta;
145    }
146
147    while ( delta == 0 ) {
148      bool                            run;
149      Watchdog_Service_routine_entry  routine;
150      Objects_Id                      id;
151      void                           *user_data;
152
153      run = ( first->state == WATCHDOG_ACTIVE );
154
155      _Watchdog_Remove_it( header, first );
156
157      routine = first->routine;
158      id = first->id;
159      user_data = first->user_data;
160
161      _Watchdog_Release( header, &lock_context );
162
163      if ( run ) {
164        (*routine)( id, user_data );
165      }
166
167      _Watchdog_Acquire( header, &lock_context );
168
169      if ( _Watchdog_Is_empty( header ) ) {
170        break;
171      }
172
173      first = _Watchdog_First( header );
174      delta = first->delta_interval;
175    }
176  }
177
178  _Watchdog_Release( header, &lock_context );
179}
Note: See TracBrowser for help on using the repository browser.