source: rtems/cpukit/rtems/src/timerserver.c @ e6b31b27

4.115
Last change on this file since e6b31b27 was 80fca28, checked in by Sebastian Huber <sebastian.huber@…>, on 06/13/15 at 13:29:04

score: Add _Watchdog_Preinitialize()

Add an assert to ensure that the watchdog is the proper state for a
_Watchdog_Initialize(). This helps to detect invalid initializations
which may lead to a corrupt watchdog chain.

  • Property mode set to 100644
File size: 11.8 KB
Line 
1/**
2 *  @file timerserver.c
3 *
4 *  Timer Manager - rtems_timer_initiate_server directive along with
5 *  the Timer Server Body and support routines
6 *
7 *  @note Data specific to the Timer Server is declared in this
8 *        file as the Timer Server so it does not have to be in the
9 *        minimum footprint.  It is only really required when
10 *        task-based timers are used.  Since task-based timers can
11 *        not be started until the server is initiated, this structure
12 *        does not have to be initialized until then.
13 */
14
15/*  COPYRIGHT (c) 1989-2008.
16 *  On-Line Applications Research Corporation (OAR).
17 *
18 *  Copyright (c) 2009-2015 embedded brains GmbH.
19 *
20 *  The license and distribution terms for this file may be
21 *  found in the file LICENSE in this distribution or at
22 *  http://www.rtems.org/license/LICENSE.
23 */
24
25#if HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <rtems.h>
30#include <rtems/rtems/timerimpl.h>
31#include <rtems/rtems/tasksimpl.h>
32#include <rtems/score/apimutex.h>
33#include <rtems/score/todimpl.h>
34
35static Timer_server_Control _Timer_server_Default;
36
37static void _Timer_server_Cancel_method(
38  Timer_server_Control *ts,
39  Timer_Control *timer
40)
41{
42  if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
43    _Watchdog_Remove( &ts->Interval_watchdogs.Header, &timer->Ticker );
44  } else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
45    _Watchdog_Remove( &ts->TOD_watchdogs.Header, &timer->Ticker );
46  }
47}
48
49static Watchdog_Interval _Timer_server_Get_ticks( void )
50{
51  return _Watchdog_Ticks_since_boot;
52}
53
54static Watchdog_Interval _Timer_server_Get_seconds( void )
55{
56  return _TOD_Seconds_since_epoch();
57}
58
59static void _Timer_server_Update_system_watchdog(
60  Timer_server_Watchdogs *watchdogs,
61  Watchdog_Header *system_header
62)
63{
64  ISR_lock_Context lock_context;
65
66  _Watchdog_Acquire( &watchdogs->Header, &lock_context );
67
68  if ( watchdogs->system_watchdog_helper == NULL ) {
69    Thread_Control *executing;
70    uint32_t my_generation;
71
72    executing = _Thread_Executing;
73    watchdogs->system_watchdog_helper = executing;
74
75    do {
76      my_generation = watchdogs->generation;
77
78      if ( !_Watchdog_Is_empty( &watchdogs->Header ) ) {
79        Watchdog_Control *first;
80        Watchdog_Interval delta;
81
82        first = _Watchdog_First( &watchdogs->Header );
83        delta = first->delta_interval;
84
85        if (
86          watchdogs->System_watchdog.state == WATCHDOG_INACTIVE
87            || delta != watchdogs->system_watchdog_delta
88        ) {
89          watchdogs->system_watchdog_delta = delta;
90          _Watchdog_Release( &watchdogs->Header, &lock_context );
91
92          _Watchdog_Remove( system_header, &watchdogs->System_watchdog );
93          watchdogs->System_watchdog.initial = delta;
94          _Watchdog_Insert( system_header, &watchdogs->System_watchdog );
95
96          _Watchdog_Acquire( &watchdogs->Header, &lock_context );
97        }
98      }
99    } while ( watchdogs->generation != my_generation );
100
101    watchdogs->system_watchdog_helper = NULL;
102  }
103
104  _Watchdog_Release( &watchdogs->Header, &lock_context );
105}
106
107static void _Timer_server_Insert_timer(
108  Timer_server_Watchdogs *watchdogs,
109  Timer_Control *timer,
110  Watchdog_Header *system_header,
111  Watchdog_Interval (*get_ticks)( void )
112)
113{
114  ISR_lock_Context lock_context;
115  Watchdog_Interval now;
116  Watchdog_Interval delta;
117
118  _Watchdog_Acquire( &watchdogs->Header, &lock_context );
119
120  now = (*get_ticks)();
121  delta = now - watchdogs->last_snapshot;
122  watchdogs->last_snapshot = now;
123  watchdogs->current_snapshot = now;
124
125  if ( watchdogs->system_watchdog_delta > delta ) {
126    watchdogs->system_watchdog_delta -= delta;
127  } else {
128    watchdogs->system_watchdog_delta = 0;
129  }
130
131  if ( !_Watchdog_Is_empty( &watchdogs->Header ) ) {
132    Watchdog_Control *first = _Watchdog_First( &watchdogs->Header );
133
134    if ( first->delta_interval > delta ) {
135      first->delta_interval -= delta;
136    } else {
137      first->delta_interval = 0;
138    }
139  }
140
141  _Watchdog_Insert_locked(
142    &watchdogs->Header,
143    &timer->Ticker,
144    &lock_context
145  );
146
147  ++watchdogs->generation;
148
149  _Watchdog_Release( &watchdogs->Header, &lock_context );
150
151  _Timer_server_Update_system_watchdog( watchdogs, system_header );
152}
153
154static void _Timer_server_Schedule_operation_method(
155  Timer_server_Control *ts,
156  Timer_Control *timer
157)
158{
159  if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
160    _Timer_server_Insert_timer(
161      &ts->Interval_watchdogs,
162      timer,
163      &_Watchdog_Ticks_header,
164      _Timer_server_Get_ticks
165    );
166  } else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
167    _Timer_server_Insert_timer(
168      &ts->TOD_watchdogs,
169      timer,
170      &_Watchdog_Seconds_header,
171      _Timer_server_Get_seconds
172    );
173  }
174}
175
176static void _Timer_server_Update_current_snapshot(
177  Timer_server_Watchdogs *watchdogs,
178  Watchdog_Interval (*get_ticks)( void )
179)
180{
181  ISR_lock_Context lock_context;
182
183  _Watchdog_Acquire( &watchdogs->Header, &lock_context );
184  watchdogs->current_snapshot = (*get_ticks)();
185  watchdogs->system_watchdog_delta = 0;
186  _Watchdog_Release( &watchdogs->Header, &lock_context );
187}
188
189static void _Timer_server_Tickle(
190  Timer_server_Watchdogs *watchdogs,
191  Watchdog_Header *system_header,
192  Watchdog_Interval (*get_ticks)( void ),
193  bool ticks
194)
195{
196  ISR_lock_Context lock_context;
197  Watchdog_Interval now;
198  Watchdog_Interval last;
199
200  _Watchdog_Acquire( &watchdogs->Header, &lock_context );
201
202  now = watchdogs->current_snapshot;
203  last = watchdogs->last_snapshot;
204  watchdogs->last_snapshot = now;
205
206  if ( ticks || now >= last ) {
207    _Watchdog_Adjust_forward_locked(
208      &watchdogs->Header,
209      now - last,
210      &lock_context
211    );
212  } else {
213    _Watchdog_Adjust_backward_locked(
214      &watchdogs->Header,
215      last - now
216    );
217  }
218
219  ++watchdogs->generation;
220
221  _Watchdog_Release( &watchdogs->Header, &lock_context );
222
223  _Timer_server_Update_system_watchdog( watchdogs, system_header );
224}
225
226/**
227 *  @brief Timer server body.
228 *
229 *  This is the server for task based timers.  This task executes whenever a
230 *  task-based timer should fire.  It services both "after" and "when" timers.
231 *  It is not created automatically but must be created explicitly by the
232 *  application before task-based timers may be initiated.  The parameter
233 *  @a arg points to the corresponding timer server control block.
234 */
235static rtems_task _Timer_server_Body(
236  rtems_task_argument arg
237)
238{
239  Timer_server_Control *ts = (Timer_server_Control *) arg;
240
241  while ( true ) {
242    rtems_event_set events;
243
244    _Timer_server_Tickle(
245      &ts->Interval_watchdogs,
246      &_Watchdog_Ticks_header,
247      _Timer_server_Get_ticks,
248      true
249    );
250
251    _Timer_server_Tickle(
252      &ts->TOD_watchdogs,
253      &_Watchdog_Seconds_header,
254      _Timer_server_Get_seconds,
255      false
256    );
257
258    (void) rtems_event_system_receive(
259      RTEMS_EVENT_SYSTEM_TIMER_SERVER,
260      RTEMS_EVENT_ALL | RTEMS_WAIT,
261      RTEMS_NO_TIMEOUT,
262      &events
263    );
264  }
265}
266
267static void _Timer_server_Wakeup(
268  Objects_Id  id,
269  void       *arg
270)
271{
272  Timer_server_Control *ts = arg;
273
274  _Timer_server_Update_current_snapshot(
275    &ts->Interval_watchdogs,
276    _Timer_server_Get_ticks
277  );
278
279  _Timer_server_Update_current_snapshot(
280    &ts->TOD_watchdogs,
281    _Timer_server_Get_seconds
282  );
283
284  (void) rtems_event_system_send( id, RTEMS_EVENT_SYSTEM_TIMER_SERVER );
285}
286
287static void _Timer_server_Initialize_watchdogs(
288  Timer_server_Control *ts,
289  rtems_id id,
290  Timer_server_Watchdogs *watchdogs,
291  Watchdog_Interval (*get_ticks)( void )
292)
293{
294  Watchdog_Interval now;
295
296  now = (*get_ticks)();
297  watchdogs->last_snapshot = now;
298  watchdogs->current_snapshot = now;
299
300  _Watchdog_Header_initialize( &watchdogs->Header );
301  _Watchdog_Preinitialize( &watchdogs->System_watchdog );
302  _Watchdog_Initialize(
303    &watchdogs->System_watchdog,
304    _Timer_server_Wakeup,
305    id,
306    ts
307  );
308}
309
310/**
311 *  @brief rtems_timer_initiate_server
312 *
313 *  This directive creates and starts the server for task-based timers.
314 *  It must be invoked before any task-based timers can be initiated.
315 *
316 *  @param[in] priority is the timer server priority
317 *  @param[in] stack_size is the stack size in bytes
318 *  @param[in] attribute_set is the timer server attributes
319 *
320 *  @return This method returns RTEMS_SUCCESSFUL if successful and an
321 *          error code otherwise.
322 */
323rtems_status_code rtems_timer_initiate_server(
324  uint32_t             priority,
325  uint32_t             stack_size,
326  rtems_attribute      attribute_set
327)
328{
329  rtems_id              id;
330  rtems_status_code     status;
331  rtems_task_priority   _priority;
332  static bool           initialized = false;
333  bool                  tmpInitialized;
334  Timer_server_Control *ts = &_Timer_server_Default;
335
336  /*
337   *  Make sure the requested priority is valid.  The if is
338   *  structured so we check it is invalid before looking for
339   *  a specific invalid value as the default.
340   */
341  _priority = priority;
342  if ( !_RTEMS_tasks_Priority_is_valid( priority ) ) {
343    if ( priority != RTEMS_TIMER_SERVER_DEFAULT_PRIORITY )
344      return RTEMS_INVALID_PRIORITY;
345    _priority = PRIORITY_PSEUDO_ISR;
346  }
347
348  /*
349   *  Just to make sure this is only called once.
350   */
351  _Once_Lock();
352    tmpInitialized  = initialized;
353    initialized = true;
354  _Once_Unlock();
355
356  if ( tmpInitialized )
357    return RTEMS_INCORRECT_STATE;
358
359  /*
360   *  Create the Timer Server with the name the name of "TIME".  The attribute
361   *  RTEMS_SYSTEM_TASK allows us to set a priority to 0 which will makes it
362   *  higher than any other task in the system.  It can be viewed as a low
363   *  priority interrupt.  It is also always NO_PREEMPT so it looks like
364   *  an interrupt to other tasks.
365   *
366   *  We allow the user to override the default priority because the Timer
367   *  Server can invoke TSRs which must adhere to language run-time or
368   *  other library rules.  For example, if using a TSR written in Ada the
369   *  Server should run at the same priority as the priority Ada task.
370   *  Otherwise, the priority ceiling for the mutex used to protect the
371   *  GNAT run-time is violated.
372   */
373  status = rtems_task_create(
374    _Objects_Build_name('T','I','M','E'),           /* "TIME" */
375    _priority,            /* create with priority 1 since 0 is illegal */
376    stack_size,           /* let user specify stack size */
377    rtems_configuration_is_smp_enabled() ?
378      RTEMS_DEFAULT_MODES : /* no preempt is not supported for SMP */
379      RTEMS_NO_PREEMPT,   /* no preempt is like an interrupt */
380                          /* user may want floating point but we need */
381                          /*   system task specified for 0 priority */
382    attribute_set | RTEMS_SYSTEM_TASK,
383    &id                   /* get the id back */
384  );
385  if (status) {
386    initialized = false;
387    return status;
388  }
389
390  /*
391   *  Do all the data structure initialization before starting the
392   *  Timer Server so we do not have to have a critical section.
393   */
394
395  _Timer_server_Initialize_watchdogs(
396    ts,
397    id,
398    &ts->Interval_watchdogs,
399    _Timer_server_Get_ticks
400  );
401
402  _Timer_server_Initialize_watchdogs(
403    ts,
404    id,
405    &ts->TOD_watchdogs,
406    _Timer_server_Get_seconds
407  );
408
409  /*
410   *  Initialize the pointer to the timer server methods so applications that
411   *  do not use the Timer Server do not have to pull it in.
412   */
413  ts->cancel = _Timer_server_Cancel_method;
414  ts->schedule_operation = _Timer_server_Schedule_operation_method;
415
416  /*
417   * The default timer server is now available.
418   */
419  _Timer_server = ts;
420
421  /*
422   *  Start the timer server
423   */
424  status = rtems_task_start(
425    id,
426    _Timer_server_Body,
427    (rtems_task_argument) ts
428  );
429
430  #if defined(RTEMS_DEBUG)
431    /*
432     *  One would expect a call to rtems_task_delete() here to clean up
433     *  but there is actually no way (in normal circumstances) that the
434     *  start can fail.  The id and starting address are known to be
435     *  be good.  If this service fails, something is weirdly wrong on the
436     *  target such as a stray write in an ISR or incorrect memory layout.
437     */
438    if (status) {
439      initialized = false;
440    }
441  #endif
442
443  return status;
444}
Note: See TracBrowser for help on using the repository browser.