source: rtems/cpukit/rtems/src/ratemonperiod.c @ 66cb142

5
Last change on this file since 66cb142 was ed24ed4e, checked in by Sebastian Huber <sebastian.huber@…>, on 02/08/18 at 09:47:16

Use _Thread_Dispatch_direct()

Use _Thread_Dispatch_direct() for operations that block the executing
thread. This ensures that we get a fatal error
(INTERNAL_ERROR_BAD_THREAD_DISPATCH_DISABLE_LEVEL) if we try to block in
an invalid context, e.g. during system start or an interrupt handler.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/**
2 *  @file
3 *
4 *  @brief Rate Monotonic Support
5 *  @ingroup ClassicRateMon
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-2010.
10 *  On-Line Applications Research Corporation (OAR).
11 *  Copyright (c) 2016 embedded brains GmbH.
12 *  COPYRIGHT (c) 2016 Kuan-Hsun Chen.
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#if HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <rtems/rtems/ratemonimpl.h>
24#include <rtems/score/schedulerimpl.h>
25#include <rtems/score/todimpl.h>
26
27bool _Rate_monotonic_Get_status(
28  const Rate_monotonic_Control *the_period,
29  Timestamp_Control            *wall_since_last_period,
30  Timestamp_Control            *cpu_since_last_period
31)
32{
33  Timestamp_Control        uptime;
34  Thread_Control          *owning_thread = the_period->owner;
35  Timestamp_Control        used;
36
37  /*
38   *  Determine elapsed wall time since period initiated.
39   */
40  _TOD_Get_uptime( &uptime );
41  _Timestamp_Subtract(
42    &the_period->time_period_initiated, &uptime, wall_since_last_period
43  );
44
45  /*
46   *  Determine cpu usage since period initiated.
47   */
48  _Thread_Get_CPU_time_used( owning_thread, &used );
49
50  /*
51   *  The cpu usage info was reset while executing.  Can't
52   *  determine a status.
53   */
54  if ( _Timestamp_Less_than( &used, &the_period->cpu_usage_period_initiated ) )
55    return false;
56
57   /* used = current cpu usage - cpu usage at start of period */
58  _Timestamp_Subtract(
59    &the_period->cpu_usage_period_initiated,
60    &used,
61    cpu_since_last_period
62  );
63
64  return true;
65}
66
67static void _Rate_monotonic_Release_postponed_job(
68  Rate_monotonic_Control *the_period,
69  Thread_Control         *owner,
70  rtems_interval          next_length,
71  ISR_lock_Context       *lock_context
72)
73{
74  Per_CPU_Control      *cpu_self;
75  Thread_queue_Context  queue_context;
76
77  --the_period->postponed_jobs;
78  _Scheduler_Release_job(
79    owner,
80    &the_period->Priority,
81    the_period->latest_deadline,
82    &queue_context
83  );
84
85  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
86  _Rate_monotonic_Release( the_period, lock_context );
87  _Thread_Priority_update( &queue_context );
88  _Thread_Dispatch_direct( cpu_self );
89}
90
91static void _Rate_monotonic_Release_job(
92  Rate_monotonic_Control *the_period,
93  Thread_Control         *owner,
94  rtems_interval          next_length,
95  ISR_lock_Context       *lock_context
96)
97{
98  Per_CPU_Control      *cpu_self;
99  Thread_queue_Context  queue_context;
100  uint64_t              deadline;
101
102  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
103
104  deadline = _Watchdog_Per_CPU_insert_ticks(
105    &the_period->Timer,
106    cpu_self,
107    next_length
108  );
109  _Scheduler_Release_job(
110    owner,
111    &the_period->Priority,
112    deadline,
113    &queue_context
114  );
115
116  _Rate_monotonic_Release( the_period, lock_context );
117  _Thread_Priority_update( &queue_context );
118  _Thread_Dispatch_enable( cpu_self );
119}
120
121void _Rate_monotonic_Restart(
122  Rate_monotonic_Control *the_period,
123  Thread_Control         *owner,
124  ISR_lock_Context       *lock_context
125)
126{
127  /*
128   *  Set the starting point and the CPU time used for the statistics.
129   */
130  _TOD_Get_uptime( &the_period->time_period_initiated );
131  _Thread_Get_CPU_time_used( owner, &the_period->cpu_usage_period_initiated );
132
133  _Rate_monotonic_Release_job(
134    the_period,
135    owner,
136    the_period->next_length,
137    lock_context
138  );
139}
140
141static void _Rate_monotonic_Update_statistics(
142  Rate_monotonic_Control    *the_period
143)
144{
145  Timestamp_Control          executed;
146  Timestamp_Control          since_last_period;
147  Rate_monotonic_Statistics *stats;
148  bool                       valid_status;
149
150  /*
151   *  Assume we are only called in states where it is appropriate
152   *  to update the statistics.  This should only be RATE_MONOTONIC_ACTIVE
153   *  and RATE_MONOTONIC_EXPIRED.
154   */
155
156  /*
157   *  Update the counts.
158   */
159  stats = &the_period->Statistics;
160  stats->count++;
161
162  if ( the_period->state == RATE_MONOTONIC_EXPIRED )
163    stats->missed_count++;
164
165  /*
166   *  Grab status for time statistics.
167   */
168  valid_status =
169    _Rate_monotonic_Get_status( the_period, &since_last_period, &executed );
170  if (!valid_status)
171    return;
172
173  /*
174   *  Update CPU time
175   */
176  _Timestamp_Add_to( &stats->total_cpu_time, &executed );
177
178  if ( _Timestamp_Less_than( &executed, &stats->min_cpu_time ) )
179    stats->min_cpu_time = executed;
180
181  if ( _Timestamp_Greater_than( &executed, &stats->max_cpu_time ) )
182    stats->max_cpu_time = executed;
183
184  /*
185   *  Update Wall time
186   */
187  _Timestamp_Add_to( &stats->total_wall_time, &since_last_period );
188
189  if ( _Timestamp_Less_than( &since_last_period, &stats->min_wall_time ) )
190    stats->min_wall_time = since_last_period;
191
192  if ( _Timestamp_Greater_than( &since_last_period, &stats->max_wall_time ) )
193    stats->max_wall_time = since_last_period;
194}
195
196static rtems_status_code _Rate_monotonic_Get_status_for_state(
197  rtems_rate_monotonic_period_states state
198)
199{
200  switch ( state ) {
201    case RATE_MONOTONIC_INACTIVE:
202      return RTEMS_NOT_DEFINED;
203    case RATE_MONOTONIC_EXPIRED:
204      return RTEMS_TIMEOUT;
205    default:
206      _Assert( state == RATE_MONOTONIC_ACTIVE );
207      return RTEMS_SUCCESSFUL;
208  }
209}
210
211static rtems_status_code _Rate_monotonic_Activate(
212  Rate_monotonic_Control *the_period,
213  rtems_interval          length,
214  Thread_Control         *executing,
215  ISR_lock_Context       *lock_context
216)
217{
218  the_period->postponed_jobs = 0;
219  the_period->state = RATE_MONOTONIC_ACTIVE;
220  the_period->next_length = length;
221  _Rate_monotonic_Restart( the_period, executing, lock_context );
222  return RTEMS_SUCCESSFUL;
223}
224
225static rtems_status_code _Rate_monotonic_Block_while_active(
226  Rate_monotonic_Control *the_period,
227  rtems_interval          length,
228  Thread_Control         *executing,
229  ISR_lock_Context       *lock_context
230)
231{
232  Per_CPU_Control *cpu_self;
233  bool             success;
234
235  /*
236   *  Update statistics from the concluding period.
237   */
238  _Rate_monotonic_Update_statistics( the_period );
239
240  /*
241   *  This tells the _Rate_monotonic_Timeout that this task is
242   *  in the process of blocking on the period and that we
243   *  may be changing the length of the next period.
244   */
245  the_period->next_length = length;
246  executing->Wait.return_argument = the_period;
247  _Thread_Wait_flags_set( executing, RATE_MONOTONIC_INTEND_TO_BLOCK );
248
249  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
250  _Rate_monotonic_Release( the_period, lock_context );
251
252  _Thread_Set_state( executing, STATES_WAITING_FOR_PERIOD );
253
254  success = _Thread_Wait_flags_try_change_acquire(
255    executing,
256    RATE_MONOTONIC_INTEND_TO_BLOCK,
257    RATE_MONOTONIC_BLOCKED
258  );
259  if ( !success ) {
260    _Assert(
261      _Thread_Wait_flags_get( executing ) == RATE_MONOTONIC_READY_AGAIN
262    );
263    _Thread_Unblock( executing );
264  }
265
266  _Thread_Dispatch_direct( cpu_self );
267  return RTEMS_SUCCESSFUL;
268}
269
270/*
271 * There are two possible cases: one is that the previous deadline is missed,
272 * The other is that the number of postponed jobs is not 0, but the current
273 * deadline is still not expired, i.e., state = RATE_MONOTONIC_ACTIVE.
274 */
275static rtems_status_code _Rate_monotonic_Block_while_expired(
276  Rate_monotonic_Control *the_period,
277  rtems_interval          length,
278  Thread_Control         *executing,
279  ISR_lock_Context       *lock_context
280)
281{
282  /*
283   * No matter the just finished jobs in time or not,
284   * they are actually missing their deadlines already.
285   */
286  the_period->state = RATE_MONOTONIC_EXPIRED;
287
288  /*
289   * Update statistics from the concluding period
290   */
291  _Rate_monotonic_Update_statistics( the_period );
292
293  the_period->state = RATE_MONOTONIC_ACTIVE;
294  the_period->next_length = length;
295
296  _Rate_monotonic_Release_postponed_job(
297      the_period,
298      executing,
299      length,
300      lock_context
301  );
302  return RTEMS_TIMEOUT;
303}
304
305rtems_status_code rtems_rate_monotonic_period(
306  rtems_id       id,
307  rtems_interval length
308)
309{
310  Rate_monotonic_Control            *the_period;
311  ISR_lock_Context                   lock_context;
312  Thread_Control                    *executing;
313  rtems_status_code                  status;
314  rtems_rate_monotonic_period_states state;
315
316  the_period = _Rate_monotonic_Get( id, &lock_context );
317  if ( the_period == NULL ) {
318    return RTEMS_INVALID_ID;
319  }
320
321  executing = _Thread_Executing;
322  if ( executing != the_period->owner ) {
323    _ISR_lock_ISR_enable( &lock_context );
324    return RTEMS_NOT_OWNER_OF_RESOURCE;
325  }
326
327  _Rate_monotonic_Acquire_critical( the_period, &lock_context );
328
329  state = the_period->state;
330
331  if ( length == RTEMS_PERIOD_STATUS ) {
332    status = _Rate_monotonic_Get_status_for_state( state );
333    _Rate_monotonic_Release( the_period, &lock_context );
334  } else {
335    switch ( state ) {
336      case RATE_MONOTONIC_ACTIVE:
337
338        if( the_period->postponed_jobs > 0 ){
339          /*
340           * If the number of postponed jobs is not 0, it means the
341           * previous postponed instance is finished without exceeding
342           * the current period deadline.
343           *
344           * Do nothing on the watchdog deadline assignment but release the
345           * next remaining postponed job.
346           */
347          status = _Rate_monotonic_Block_while_expired(
348            the_period,
349            length,
350            executing,
351            &lock_context
352          );
353        }else{
354          /*
355           * Normal case that no postponed jobs and no expiration, so wait for
356           * the period and update the deadline of watchdog accordingly.
357           */
358          status = _Rate_monotonic_Block_while_active(
359            the_period,
360            length,
361            executing,
362            &lock_context
363          );
364        }
365        break;
366      case RATE_MONOTONIC_INACTIVE:
367        status = _Rate_monotonic_Activate(
368          the_period,
369          length,
370          executing,
371          &lock_context
372        );
373        break;
374      default:
375        /*
376         * As now this period was already TIMEOUT, there must be at least one
377         * postponed job recorded by the watchdog. The one which exceeded
378         * the previous deadlines was just finished.
379         *
380         * Maybe there is more than one job postponed due to the preemption or
381         * the previous finished job.
382         */
383        _Assert( state == RATE_MONOTONIC_EXPIRED );
384        status = _Rate_monotonic_Block_while_expired(
385          the_period,
386          length,
387          executing,
388          &lock_context
389        );
390        break;
391    }
392  }
393
394  return status;
395}
Note: See TracBrowser for help on using the repository browser.