source: rtems/cpukit/rtems/src/ratemonperiod.c @ ad40220f

5
Last change on this file since ad40220f was 3a46b72, checked in by Kuan-Hsun Chen <c0066c@…>, on 12/21/16 at 16:42:39

Enhancement of the RMS manager for the overrun handling.

Three additional functions:
rtems_rate_monotonic_postponed_job_count,
_Rate_monotonic_Renew_deadline, and _Rate_monotonic_Release_postponed_job.

Four refined functions:
_Rate_monotonic_Activate, _Rate_monotonic_Block_while_expired,
rtems_rate_monotonic_period, _Rate_monotonic_Timeout.

Rate_monotonic_Control contains one counter for counting the postponed jobs
and one for recording the recent deadline.

Update #2795.

  • Property mode set to 100644
File size: 11.5 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_postponedjob(
68  Rate_monotonic_Control *the_period,
69  Thread_Control         *owner,
70  rtems_interval          next_length,
71  ISR_lock_Context       *lock_context
72)
73{
74  /* This function only releases the postponed jobs. */
75  Per_CPU_Control *cpu_self;
76  Thread_queue_Context  queue_context;
77  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
78  _Rate_monotonic_Release( owner, lock_context );
79
80  the_period->postponed_jobs -=1;
81  _Scheduler_Release_job(
82    owner,
83    &the_period->Priority,
84    the_period->latest_deadline,
85    &queue_context
86  );
87
88  _Rate_monotonic_Release( the_period, lock_context );
89  _Thread_Priority_update( &queue_context );
90  _Thread_Dispatch_enable( cpu_self );
91}
92
93static void _Rate_monotonic_Release_job(
94  Rate_monotonic_Control *the_period,
95  Thread_Control         *owner,
96  rtems_interval          next_length,
97  ISR_lock_Context       *lock_context
98)
99{
100  Per_CPU_Control      *cpu_self;
101  Thread_queue_Context  queue_context;
102  uint64_t              deadline;
103
104  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
105
106  deadline = _Watchdog_Per_CPU_insert_relative(
107    &the_period->Timer,
108    cpu_self,
109    next_length
110  );
111  _Scheduler_Release_job(
112    owner,
113    &the_period->Priority,
114    deadline,
115    &queue_context
116  );
117
118  _Rate_monotonic_Release( the_period, lock_context );
119  _Thread_Priority_update( &queue_context );
120  _Thread_Dispatch_enable( cpu_self );
121}
122
123void _Rate_monotonic_Renew_deadline(
124  Rate_monotonic_Control *the_period,
125  Thread_Control         *owner,
126  ISR_lock_Context       *lock_context
127)
128{
129  Per_CPU_Control *cpu_self;
130  uint64_t deadline;
131
132  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
133  _Rate_monotonic_Release( owner, lock_context );
134
135  _ISR_lock_ISR_disable( lock_context );
136  deadline = _Watchdog_Per_CPU_insert_relative(
137    &the_period->Timer,
138    cpu_self,
139    the_period->next_length
140  );
141  the_period->latest_deadline = deadline;
142  _ISR_lock_ISR_enable( lock_context );
143  _Thread_Dispatch_enable( cpu_self );
144
145}
146
147void _Rate_monotonic_Restart(
148  Rate_monotonic_Control *the_period,
149  Thread_Control         *owner,
150  ISR_lock_Context       *lock_context
151)
152{
153  /*
154   *  Set the starting point and the CPU time used for the statistics.
155   */
156  _TOD_Get_uptime( &the_period->time_period_initiated );
157  _Thread_Get_CPU_time_used( owner, &the_period->cpu_usage_period_initiated );
158
159  _Rate_monotonic_Release_job(
160    the_period,
161    owner,
162    the_period->next_length,
163    lock_context
164  );
165}
166
167static void _Rate_monotonic_Update_statistics(
168  Rate_monotonic_Control    *the_period
169)
170{
171  Timestamp_Control          executed;
172  Timestamp_Control          since_last_period;
173  Rate_monotonic_Statistics *stats;
174  bool                       valid_status;
175
176  /*
177   *  Assume we are only called in states where it is appropriate
178   *  to update the statistics.  This should only be RATE_MONOTONIC_ACTIVE
179   *  and RATE_MONOTONIC_EXPIRED.
180   */
181
182  /*
183   *  Update the counts.
184   */
185  stats = &the_period->Statistics;
186  stats->count++;
187
188  if ( the_period->state == RATE_MONOTONIC_EXPIRED )
189    stats->missed_count++;
190
191  /*
192   *  Grab status for time statistics.
193   */
194  valid_status =
195    _Rate_monotonic_Get_status( the_period, &since_last_period, &executed );
196  if (!valid_status)
197    return;
198
199  /*
200   *  Update CPU time
201   */
202  _Timestamp_Add_to( &stats->total_cpu_time, &executed );
203
204  if ( _Timestamp_Less_than( &executed, &stats->min_cpu_time ) )
205    stats->min_cpu_time = executed;
206
207  if ( _Timestamp_Greater_than( &executed, &stats->max_cpu_time ) )
208    stats->max_cpu_time = executed;
209
210  /*
211   *  Update Wall time
212   */
213  _Timestamp_Add_to( &stats->total_wall_time, &since_last_period );
214
215  if ( _Timestamp_Less_than( &since_last_period, &stats->min_wall_time ) )
216    stats->min_wall_time = since_last_period;
217
218  if ( _Timestamp_Greater_than( &since_last_period, &stats->max_wall_time ) )
219    stats->max_wall_time = since_last_period;
220}
221
222static rtems_status_code _Rate_monotonic_Get_status_for_state(
223  rtems_rate_monotonic_period_states state
224)
225{
226  switch ( state ) {
227    case RATE_MONOTONIC_INACTIVE:
228      return RTEMS_NOT_DEFINED;
229    case RATE_MONOTONIC_EXPIRED:
230      return RTEMS_TIMEOUT;
231    default:
232      _Assert( state == RATE_MONOTONIC_ACTIVE );
233      return RTEMS_SUCCESSFUL;
234  }
235}
236
237static rtems_status_code _Rate_monotonic_Activate(
238  Rate_monotonic_Control *the_period,
239  rtems_interval          length,
240  Thread_Control         *executing,
241  ISR_lock_Context       *lock_context
242)
243{
244  the_period->postponed_jobs = 0;
245  the_period->state = RATE_MONOTONIC_ACTIVE;
246  the_period->next_length = length;
247  _Rate_monotonic_Restart( the_period, executing, lock_context );
248  return RTEMS_SUCCESSFUL;
249}
250
251static rtems_status_code _Rate_monotonic_Block_while_active(
252  Rate_monotonic_Control *the_period,
253  rtems_interval          length,
254  Thread_Control         *executing,
255  ISR_lock_Context       *lock_context
256)
257{
258  Per_CPU_Control *cpu_self;
259  bool             success;
260
261  /*
262   *  Update statistics from the concluding period.
263   */
264  _Rate_monotonic_Update_statistics( the_period );
265
266  /*
267   *  This tells the _Rate_monotonic_Timeout that this task is
268   *  in the process of blocking on the period and that we
269   *  may be changing the length of the next period.
270   */
271  the_period->next_length = length;
272  executing->Wait.return_argument = the_period;
273  _Thread_Wait_flags_set( executing, RATE_MONOTONIC_INTEND_TO_BLOCK );
274
275  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
276  _Rate_monotonic_Release( the_period, lock_context );
277
278  _Thread_Set_state( executing, STATES_WAITING_FOR_PERIOD );
279
280  success = _Thread_Wait_flags_try_change_acquire(
281    executing,
282    RATE_MONOTONIC_INTEND_TO_BLOCK,
283    RATE_MONOTONIC_BLOCKED
284  );
285  if ( !success ) {
286    _Assert(
287      _Thread_Wait_flags_get( executing ) == RATE_MONOTONIC_READY_AGAIN
288    );
289    _Thread_Unblock( executing );
290  }
291
292  _Thread_Dispatch_enable( cpu_self );
293  return RTEMS_SUCCESSFUL;
294}
295
296/*
297 * There are two possible cases: one is that the previous deadline is missed,
298 * The other is that the number of postponed jobs is not 0, but the current
299 * deadline is still not expired, i.e., state = RATE_MONOTONIC_ACTIVE.
300 */
301static rtems_status_code _Rate_monotonic_Block_while_expired(
302  Rate_monotonic_Control *the_period,
303  rtems_interval          length,
304  Thread_Control         *executing,
305  ISR_lock_Context       *lock_context
306)
307{
308  /*
309   * No matter the just finished jobs in time or not,
310   * they are actually missing their deadlines already.
311   */
312  the_period->state = RATE_MONOTONIC_EXPIRED;
313
314  /*
315   * Update statistics from the concluding period
316   */
317  _Rate_monotonic_Update_statistics( the_period );
318
319  the_period->state = RATE_MONOTONIC_ACTIVE;
320  the_period->next_length = length;
321
322  _Rate_monotonic_Release_postponedjob( the_period, executing, length, lock_context );
323  return RTEMS_TIMEOUT;
324}
325
326/*
327 * This helper function is prepared for run-time monitoring.
328 */
329uint32_t rtems_rate_monotonic_postponed_num(
330    rtems_id   period_id
331)
332{
333  Rate_monotonic_Control             *the_period;
334  ISR_lock_Context                    lock_context;
335  Thread_Control                     *owner;
336
337  the_period = _Rate_monotonic_Get( period_id, &lock_context );
338  _Assert( the_period != NULL );
339  uint32_t jobs = the_period->postponed_jobs;
340  owner = the_period->owner;
341  _Rate_monotonic_Release( owner, &lock_context );
342  return jobs;
343}
344
345rtems_status_code rtems_rate_monotonic_period(
346  rtems_id       id,
347  rtems_interval length
348)
349{
350  Rate_monotonic_Control            *the_period;
351  ISR_lock_Context                   lock_context;
352  Thread_Control                    *executing;
353  rtems_status_code                  status;
354  rtems_rate_monotonic_period_states state;
355
356  the_period = _Rate_monotonic_Get( id, &lock_context );
357  if ( the_period == NULL ) {
358    return RTEMS_INVALID_ID;
359  }
360
361  executing = _Thread_Executing;
362  if ( executing != the_period->owner ) {
363    _ISR_lock_ISR_enable( &lock_context );
364    return RTEMS_NOT_OWNER_OF_RESOURCE;
365  }
366
367  _Rate_monotonic_Acquire_critical( the_period, &lock_context );
368
369  state = the_period->state;
370
371  if ( length == RTEMS_PERIOD_STATUS ) {
372    status = _Rate_monotonic_Get_status_for_state( state );
373    _Rate_monotonic_Release( the_period, &lock_context );
374  } else {
375    switch ( state ) {
376      case RATE_MONOTONIC_ACTIVE:
377
378        if( the_period->postponed_jobs > 0 ){
379          /*
380           * If the number of postponed jobs is not 0, it means the
381           * previous postponed instance is finished without exceeding
382           * the current period deadline.
383           *
384           * Do nothing on the watchdog deadline assignment but release the next
385           * remaining postponed job.
386           */
387          status = _Rate_monotonic_Block_while_expired(
388            the_period,
389            length,
390            executing,
391            &lock_context
392          );
393        }else{
394          /*
395           * Normal case that no postponed jobs and no expiration, so wait for the period
396           * and update the deadline of watchdog accordingly.
397           */
398          status = _Rate_monotonic_Block_while_active(
399            the_period,
400            length,
401            executing,
402            &lock_context
403          );
404        }
405        break;
406      case RATE_MONOTONIC_INACTIVE:
407        status = _Rate_monotonic_Activate(
408          the_period,
409          length,
410          executing,
411          &lock_context
412        );
413        break;
414      default:
415        /*
416         * As now this period was already TIMEOUT, there must be at least one
417         * postponed job recorded by the watchdog. The one which exceeded
418         * the previous deadlines was just finished.
419         *
420         * Maybe there is more than one job postponed due to the preemption or
421         * the previous finished job.
422         */
423        _Assert( state == RATE_MONOTONIC_EXPIRED );
424        status = _Rate_monotonic_Block_while_expired(
425          the_period,
426          length,
427          executing,
428          &lock_context
429        );
430        break;
431    }
432  }
433
434  return status;
435}
Note: See TracBrowser for help on using the repository browser.