source: rtems/cpukit/rtems/src/ratemonperiod.c @ 1240aade

Last change on this file since 1240aade was 1240aade, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 24, 2017 at 1:37:42 PM

rtems: Fix _Rate_monotonic_Renew_deadline()

Make _Rate_monotonic_Renew_deadline() static and use proper locking in SMP
configurations.

Update #2795.

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