source: rtems/cpukit/include/rtems/score/watchdogimpl.h @ 9be97a3

Last change on this file since 9be97a3 was a4112bd, checked in by Sebastian Huber <sebastian.huber@…>, on 10/09/21 at 14:12:07

score: Simplify _Watchdog_Next_first()

  • Property mode set to 100644
File size: 17.5 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup RTEMSScoreWatchdog
5 *
6 * @brief This header file provides interfaces of the
7 *   @ref RTEMSScoreWatchdog which are only used by the implementation.
8 */
9
10/*
11 *  COPYRIGHT (c) 1989-2004.
12 *  On-Line Applications Research Corporation (OAR).
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#ifndef _RTEMS_SCORE_WATCHDOGIMPL_H
20#define _RTEMS_SCORE_WATCHDOGIMPL_H
21
22#include <rtems/score/watchdog.h>
23#include <rtems/score/watchdogticks.h>
24#include <rtems/score/assert.h>
25#include <rtems/score/isrlock.h>
26#include <rtems/score/percpu.h>
27#include <rtems/score/rbtreeimpl.h>
28
29#include <sys/types.h>
30#include <sys/timespec.h>
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36/**
37 * @addtogroup RTEMSScoreWatchdog
38 *
39 * @{
40 */
41
42/**
43 * @brief Watchdog states.
44 */
45typedef enum {
46  /**
47   * @brief The watchdog is scheduled and a black node in the red-black tree.
48   */
49  WATCHDOG_SCHEDULED_BLACK,
50
51  /**
52   * @brief The watchdog is scheduled and a red node in the red-black tree.
53   */
54  WATCHDOG_SCHEDULED_RED,
55
56  /**
57   * @brief The watchdog is inactive.
58   */
59  WATCHDOG_INACTIVE,
60
61  /**
62   * @brief The watchdog is on a chain of pending watchdogs.
63   *
64   * This state is used by the timer server for example.
65   */
66  WATCHDOG_PENDING
67} Watchdog_State;
68
69/**
70 * @brief Watchdog initializer for static initialization.
71 *
72 * The processor of this watchdog is set to processor with index zero.
73 *
74 * @see _Watchdog_Preinitialize().
75 */
76#if defined(RTEMS_SMP)
77  #define WATCHDOG_INITIALIZER( routine ) \
78    { \
79      { { { NULL, NULL, NULL, WATCHDOG_INACTIVE } } }, \
80      &_Per_CPU_Information[ 0 ].per_cpu, \
81      ( routine ), \
82      0 \
83    }
84#else
85  #define WATCHDOG_INITIALIZER( routine ) \
86    { \
87      { { { NULL, NULL, NULL, WATCHDOG_INACTIVE } } }, \
88      ( routine ), \
89      0 \
90    }
91#endif
92
93/**
94 * @brief Initializes the watchdog header.
95 *
96 * @param[out] header The header to initialize.
97 */
98RTEMS_INLINE_ROUTINE void _Watchdog_Header_initialize(
99  Watchdog_Header *header
100)
101{
102  _RBTree_Initialize_empty( &header->Watchdogs );
103  header->first = NULL;
104}
105
106/**
107 * @brief Returns the first of the watchdog header.
108 *
109 * @param header The watchdog header to remove the first of.
110 *
111 * @return The first of @a header.
112 */
113RTEMS_INLINE_ROUTINE Watchdog_Control *_Watchdog_Header_first(
114  const Watchdog_Header *header
115)
116{
117  return (Watchdog_Control *) header->first;
118}
119
120/**
121 * @brief Destroys the watchdog header.
122 *
123 * @param header The watchdog header to destroy.
124 */
125RTEMS_INLINE_ROUTINE void _Watchdog_Header_destroy(
126  Watchdog_Header *header
127)
128{
129  /* Do nothing */
130  (void) header;
131}
132
133/**
134 * @brief Performs a watchdog tick.
135 *
136 * @param cpu The processor for this watchdog tick.
137 */
138void _Watchdog_Tick( struct Per_CPU_Control *cpu );
139
140/**
141 * @brief Gets the state of the watchdog.
142 *
143 * @param the_watchdog The watchdog to get the state of.
144 *
145 * @return The RB_COLOR of @a the_watchdog.
146 */
147RTEMS_INLINE_ROUTINE Watchdog_State _Watchdog_Get_state(
148  const Watchdog_Control *the_watchdog
149)
150{
151  return (Watchdog_State) RB_COLOR( &the_watchdog->Node.RBTree, Node );
152}
153
154/**
155 * @brief Sets the state of the watchdog.
156 *
157 * @param[out] the_watchdog The watchdog to set the state of.
158 * @param state The state to set the watchdog to.
159 */
160RTEMS_INLINE_ROUTINE void _Watchdog_Set_state(
161  Watchdog_Control *the_watchdog,
162  Watchdog_State    state
163)
164{
165  RB_COLOR( &the_watchdog->Node.RBTree, Node ) = state;
166}
167
168/**
169 * @brief Gets the watchdog's cpu.
170 *
171 * @param the_watchdog The watchdog to get the cpu of.
172 *
173 * @return The cpu of the watchdog.
174 */
175RTEMS_INLINE_ROUTINE Per_CPU_Control *_Watchdog_Get_CPU(
176  const Watchdog_Control *the_watchdog
177)
178{
179#if defined(RTEMS_SMP)
180  return the_watchdog->cpu;
181#else
182  return _Per_CPU_Get_by_index( 0 );
183#endif
184}
185
186/**
187 * @brief Sets the cpu for the watchdog.
188 *
189 * @param[out] the_watchdog The watchdog to set the cpu of.
190 * @param cpu The cpu to be set as @a the_watchdog's cpu.
191 */
192RTEMS_INLINE_ROUTINE void _Watchdog_Set_CPU(
193  Watchdog_Control *the_watchdog,
194  Per_CPU_Control  *cpu
195)
196{
197#if defined(RTEMS_SMP)
198  the_watchdog->cpu = cpu;
199#else
200  (void) cpu;
201#endif
202}
203
204/**
205 * @brief Pre-initializes a watchdog.
206 *
207 * This routine must be called before a watchdog is used in any way.  The
208 * exception are statically initialized watchdogs via WATCHDOG_INITIALIZER().
209 *
210 * @param[out] the_watchdog The uninitialized watchdog.
211 */
212RTEMS_INLINE_ROUTINE void _Watchdog_Preinitialize(
213  Watchdog_Control *the_watchdog,
214  Per_CPU_Control  *cpu
215)
216{
217  _Watchdog_Set_CPU( the_watchdog, cpu );
218  _Watchdog_Set_state( the_watchdog, WATCHDOG_INACTIVE );
219
220#if defined(RTEMS_DEBUG)
221  the_watchdog->routine = NULL;
222  the_watchdog->expire = 0;
223#endif
224}
225
226/**
227 * @brief Initializes a watchdog with a new service routine.
228 *
229 * The watchdog must be inactive.
230 *
231 * @param[out] the_watchdog The watchdog to initialize.
232 * @param routing The service routine for @a the_watchdog.
233 */
234RTEMS_INLINE_ROUTINE void _Watchdog_Initialize(
235  Watchdog_Control               *the_watchdog,
236  Watchdog_Service_routine_entry  routine
237)
238{
239  _Assert( _Watchdog_Get_state( the_watchdog ) == WATCHDOG_INACTIVE );
240  the_watchdog->routine = routine;
241}
242
243/**
244 * @brief Calls the routine of each not expired watchdog control node.
245 *
246 * @param header The watchdog header.
247 * @param first The first watchdog control node.
248 * @param now The current time to check the expiration time against.
249 * @param lock The lock that is released before calling the routine and then
250 *      acquired after the call.
251 * @param lock_context The lock context for the release before calling the
252 *      routine and for the acquire after.
253 */
254void _Watchdog_Do_tickle(
255  Watchdog_Header  *header,
256  Watchdog_Control *first,
257  uint64_t          now,
258#if defined(RTEMS_SMP)
259  ISR_lock_Control *lock,
260#endif
261  ISR_lock_Context *lock_context
262);
263
264#if defined(RTEMS_SMP)
265  #define _Watchdog_Tickle( header, first, now, lock, lock_context ) \
266    _Watchdog_Do_tickle( header, first, now, lock, lock_context )
267#else
268  #define _Watchdog_Tickle( header, first, now, lock, lock_context ) \
269    _Watchdog_Do_tickle( header, first, now, lock_context )
270#endif
271
272/**
273 * @brief Inserts a watchdog into the set of scheduled watchdogs according to
274 * the specified expiration time.
275 *
276 * The watchdog must be inactive.
277 *
278 * @param[in, out] header The set of scheduler watchdogs to insert into.
279 * @param[in, out] the_watchdog The watchdog to insert.
280 * @param expire The expiration time for the watchdog.
281 */
282void _Watchdog_Insert(
283  Watchdog_Header  *header,
284  Watchdog_Control *the_watchdog,
285  uint64_t          expire
286);
287
288/**
289 * @brief In the case the watchdog is scheduled, then it is removed from the set of
290 * scheduled watchdogs.
291 *
292 * The watchdog must be initialized before this call.
293 *
294 * @param[in, out] header The scheduled watchdogs.
295 * @param[in, out] the_watchdog The watchdog to remove.
296 */
297void _Watchdog_Remove(
298  Watchdog_Header  *header,
299  Watchdog_Control *the_watchdog
300);
301
302/**
303 * @brief In the case the watchdog is scheduled, then it is removed from the set of
304 * scheduled watchdogs.
305 *
306 * The watchdog must be initialized before this call.
307 *
308 * @param[in, out] header The scheduled watchdogs.
309 * @param[in, out] the_watchdog The watchdog to remove.
310 * @param now The current time.
311 *
312 * @retval other The difference of the now and expiration time.
313 * @retval 0 The now time is greater than or equal to the expiration time of
314 * the watchdog.
315 */
316RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Cancel(
317  Watchdog_Header  *header,
318  Watchdog_Control *the_watchdog,
319  uint64_t          now
320)
321{
322  uint64_t expire;
323  uint64_t remaining;
324
325  expire = the_watchdog->expire;
326
327  if ( now < expire ) {
328    remaining = expire - now;
329  } else {
330    remaining = 0;
331  }
332
333  _Watchdog_Remove( header, the_watchdog );
334
335  return remaining;
336}
337
338/**
339 * @brief Checks if the watchdog is scheduled.
340 *
341 * @param the_watchdog The watchdog for the verification.
342 *
343 * @retval true The watchdog is scheduled.
344 * @retval false The watchdog is inactive.
345 */
346RTEMS_INLINE_ROUTINE bool _Watchdog_Is_scheduled(
347  const Watchdog_Control *the_watchdog
348)
349{
350  return _Watchdog_Get_state( the_watchdog ) < WATCHDOG_INACTIVE;
351}
352
353/**
354 * @brief Sets the first watchdog of the watchdog collection to the next
355 * watchdog of the current first watchdog.
356 *
357 * This function may be used during watchdog removals, see _Watchdog_Remove()
358 * and _Watchdog_Tickle().
359 *
360 * @param[in, out] header is the watchdog collection header.
361 *
362 * @param first is the current first watchdog which should be removed
363 *   afterwards.
364 */
365RTEMS_INLINE_ROUTINE void _Watchdog_Next_first(
366  Watchdog_Header        *header,
367  const Watchdog_Control *first
368)
369{
370  RBTree_Node *right;
371
372  /*
373   * This function uses the following properties of red-black trees:
374   *
375   * 1. Every leaf (NULL) is black.
376   *
377   * 2. If a node is red, then both its children are black.
378   *
379   * 3. Every simple path from a node to a descendant leaf contains the same
380   *    number of black nodes.
381   *
382   * The first node has no left child.  So every path from the first node has
383   * exactly one black node (including leafs).  The first node cannot have a
384   * non-leaf black right child.  It may have a red right child.  In this case
385   * both children must be leafs.
386   */
387  _Assert( header->first == &first->Node.RBTree );
388  _Assert( _RBTree_Left( &first->Node.RBTree ) == NULL );
389  right = _RBTree_Right( &first->Node.RBTree );
390
391  if ( right != NULL ) {
392    _Assert( RB_COLOR( right, Node ) == RB_RED );
393    _Assert( _RBTree_Left( right ) == NULL );
394    _Assert( _RBTree_Right( right ) == NULL );
395    header->first = right;
396  } else {
397    header->first = _RBTree_Parent( &first->Node.RBTree );
398  }
399}
400
401/**
402 * @brief The maximum watchdog ticks value for the far future.
403 */
404#define WATCHDOG_MAXIMUM_TICKS UINT64_MAX
405
406#define WATCHDOG_NANOSECONDS_PER_SECOND 1000000000
407
408/**
409 * @brief The bits necessary to store 1000000000
410 * (= WATCHDOG_NANOSECONDS_PER_SECOND) nanoseconds.
411 *
412 * The expiration time is an unsigned 64-bit integer.  To store nanoseconds
413 * timeouts we use 30 bits (2**30 == 1073741824) for the nanoseconds and 34
414 * bits for the seconds since UNIX Epoch.  This leads to a year 2514 problem.
415 */
416#define WATCHDOG_BITS_FOR_1E9_NANOSECONDS 30
417
418/**
419 * @brief The maximum number of seconds representable in the nanoseconds
420 * watchdog format.
421 *
422 * We have 2**34 bits for the seconds part.
423 */
424#define WATCHDOG_MAX_SECONDS 0x3ffffffff
425
426/**
427 * @brief Checks if the timespec is a valid timespec for a watchdog.
428 *
429 * @param ts The timespec for the verification.
430 *
431 * @retval true The timespec is a valid timespec.
432 * @retval false The timespec is invalid.
433 */
434RTEMS_INLINE_ROUTINE bool _Watchdog_Is_valid_timespec(
435  const struct timespec *ts
436)
437{
438  return ts != NULL
439    && (unsigned long) ts->tv_nsec < WATCHDOG_NANOSECONDS_PER_SECOND;
440}
441
442/**
443 * @brief Checks if the timespec is a valid interval timespec for a watchdog.
444 *
445 * @param ts The timespec for the verification.
446 *
447 * @retval true The timespec is a valid interval timespec.
448 * @retval false The timespec is invalid.
449 */
450RTEMS_INLINE_ROUTINE bool _Watchdog_Is_valid_interval_timespec(
451  const struct timespec *ts
452)
453{
454  return _Watchdog_Is_valid_timespec( ts ) && ts->tv_sec >= 0;
455}
456
457/**
458 * @brief Adds the delta timespec to the current time if the delta is a valid
459 * interval timespec.
460 *
461 * @param[in, out] now The current time.
462 * @param delta The delta timespec for the addition.
463 *
464 * @retval pointer Pointer to the now timespec.
465 * @retval NULL @a delta is not a valid interval timespec.
466 */
467RTEMS_INLINE_ROUTINE const struct timespec * _Watchdog_Future_timespec(
468  struct timespec       *now,
469  const struct timespec *delta
470)
471{
472  uint64_t sec;
473
474  if ( !_Watchdog_Is_valid_interval_timespec( delta ) ) {
475    return NULL;
476  }
477
478  sec = (uint64_t) now->tv_sec;
479  sec += (uint64_t) delta->tv_sec;
480  now->tv_nsec += delta->tv_nsec;
481
482  /* We have 2 * (2**63 - 1) + 1 == UINT64_MAX */
483  if ( now->tv_nsec >= WATCHDOG_NANOSECONDS_PER_SECOND ) {
484    now->tv_nsec -= WATCHDOG_NANOSECONDS_PER_SECOND;
485    ++sec;
486  }
487
488  if ( sec <= INT64_MAX ) {
489    now->tv_sec = sec;
490  } else {
491    now->tv_sec = INT64_MAX;
492  }
493
494  return now;
495}
496
497/**
498 * @brief Checks if the timespec is too far in the future.
499 *
500 * @param ts The timespec for the verification.
501 *
502 * @retval true @a ts is too far in the future.
503 * @retval false @a ts is not too far in the future.
504 */
505RTEMS_INLINE_ROUTINE bool _Watchdog_Is_far_future_timespec(
506  const struct timespec *ts
507)
508{
509  return ts->tv_sec > WATCHDOG_MAX_SECONDS;
510}
511
512/**
513 * @brief Converts the seconds to ticks.
514 *
515 * @param seconds The seconds to convert to ticks.
516 *
517 * @return @a seconds converted to ticks.
518 */
519RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Ticks_from_seconds(
520  uint32_t seconds
521)
522{
523  uint64_t ticks = seconds;
524
525  ticks <<= WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
526
527  return ticks;
528}
529
530/**
531 * @brief Converts the timespec in ticks.
532 *
533 * @param ts The timespec to convert to ticks.
534 *
535 * @return @a ts converted to ticks.
536 */
537RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Ticks_from_timespec(
538  const struct timespec *ts
539)
540{
541  uint64_t ticks;
542
543  _Assert( _Watchdog_Is_valid_timespec( ts ) );
544  _Assert( ts->tv_sec >= 0 );
545  _Assert( !_Watchdog_Is_far_future_timespec( ts ) );
546
547  ticks = (uint64_t) ts->tv_sec;
548  ticks <<= WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
549  ticks |= (uint32_t) ts->tv_nsec;
550
551  return ticks;
552}
553
554/**
555 * @brief Converts the ticks to timespec.
556 *
557 * @param ticks are the ticks to convert.
558 *
559 * @param[out] ts is the timespec to return the converted ticks.
560 */
561RTEMS_INLINE_ROUTINE void _Watchdog_Ticks_to_timespec(
562  uint64_t         ticks,
563  struct timespec *ts
564)
565{
566  ts->tv_sec = ticks >> WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
567  ts->tv_nsec = ticks & ( ( 1U << WATCHDOG_BITS_FOR_1E9_NANOSECONDS ) - 1 );
568}
569
570/**
571 * @brief Converts the sbintime in ticks.
572 *
573 * @param sbt The sbintime to convert to ticks.
574 *
575 * @return @a sbt converted to ticks.
576 */
577RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Ticks_from_sbintime( int64_t sbt )
578{
579  uint64_t ticks = ( sbt >> 32 ) << WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
580
581  ticks |= ( (uint64_t) 1000000000 * (uint32_t) sbt ) >> 32;
582
583  return ticks;
584}
585
586/**
587 * @brief Acquires the per cpu watchdog lock in a critical section.
588 *
589 * @param cpu The cpu to acquire the watchdog lock of.
590 * @param lock_context The lock context.
591 */
592RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_acquire_critical(
593  Per_CPU_Control  *cpu,
594  ISR_lock_Context *lock_context
595)
596{
597  _ISR_lock_Acquire( &cpu->Watchdog.Lock, lock_context );
598}
599
600/**
601 * @brief Releases the per cpu watchdog lock in a critical section.
602 *
603 * @param cpu The cpu to release the watchdog lock of.
604 * @param lock_context The lock context.
605 */
606RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_release_critical(
607  Per_CPU_Control  *cpu,
608  ISR_lock_Context *lock_context
609)
610{
611  _ISR_lock_Release( &cpu->Watchdog.Lock, lock_context );
612}
613
614/**
615 * @brief Sets the watchdog's cpu to the given instance and sets its expiration
616 *      time to the watchdog expiration time of the cpu plus the ticks.
617 *
618 * @param[in, out] the_watchdog The watchdog to set the cpu and expiration time of.
619 * @param cpu The cpu for the watchdog.
620 * @param ticks The ticks to add to the expiration time.
621 *
622 * @return The new expiration time of the watchdog.
623 */
624RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Per_CPU_insert_ticks(
625  Watchdog_Control  *the_watchdog,
626  Per_CPU_Control   *cpu,
627  Watchdog_Interval  ticks
628)
629{
630  ISR_lock_Context  lock_context;
631  Watchdog_Header  *header;
632  uint64_t          expire;
633
634  header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ];
635
636  _Watchdog_Set_CPU( the_watchdog, cpu );
637
638  _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context );
639  expire = ticks + cpu->Watchdog.ticks;
640  _Watchdog_Insert(header, the_watchdog, expire);
641  _Watchdog_Per_CPU_release_critical( cpu, &lock_context );
642  return expire;
643}
644
645/**
646 * @brief Sets the watchdog's cpu and inserts it with the given expiration time
647 *      in the scheduled watchdogs.
648 *
649 * @param[in, out] the_watchdog The watchdog to set cpu and expiration time of.
650 * @param cpu The cpu for the operation.
651 * @param[in, out] header The scheduled watchdogs.
652 * @param expire The expiration time for the watchdog.
653 *
654 * @return The expiration time of the watchdog.
655 */
656RTEMS_INLINE_ROUTINE uint64_t _Watchdog_Per_CPU_insert(
657  Watchdog_Control *the_watchdog,
658  Per_CPU_Control  *cpu,
659  Watchdog_Header  *header,
660  uint64_t          expire
661)
662{
663  ISR_lock_Context lock_context;
664
665  _Watchdog_Set_CPU( the_watchdog, cpu );
666
667  _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context );
668  _Watchdog_Insert( header, the_watchdog, expire );
669  _Watchdog_Per_CPU_release_critical( cpu, &lock_context );
670  return expire;
671}
672
673/**
674 * @brief Removes the watchdog from the cpu and the scheduled watchdogs.
675 *
676 * @param[in, out] the_watchdog The watchdog to remove.
677 * @param cpu The cpu to remove the watchdog from.
678 * @param[in, out] The scheduled watchdogs.
679 */
680RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_remove(
681  Watchdog_Control *the_watchdog,
682  Per_CPU_Control  *cpu,
683  Watchdog_Header  *header
684)
685{
686  ISR_lock_Context lock_context;
687
688  _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context );
689  _Watchdog_Remove(
690    header,
691    the_watchdog
692  );
693  _Watchdog_Per_CPU_release_critical( cpu, &lock_context );
694}
695
696/**
697 * @brief Removes the watchdog from the cpu and the scheduled watchdogs.
698 *
699 * @param[in, out] the_watchdog The watchdog to remove.
700 */
701RTEMS_INLINE_ROUTINE void _Watchdog_Per_CPU_remove_ticks(
702  Watchdog_Control *the_watchdog
703)
704{
705  Per_CPU_Control *cpu;
706
707  cpu = _Watchdog_Get_CPU( the_watchdog );
708  _Watchdog_Per_CPU_remove(
709    the_watchdog,
710    cpu,
711    &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ]
712  );
713}
714
715/** @} */
716
717#ifdef __cplusplus
718}
719#endif
720
721#endif
722/* end of include file */
Note: See TracBrowser for help on using the repository browser.