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

Last change on this file since d37adfe5 was d37adfe5, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 3, 2016 at 6:02:03 AM

score: Fix CPU time used by executing threads

The CPU time used of a thread was previously maintained per-processor
mostly during _Thread_Dispatch(). However, on SMP configurations the
actual processor of a thread is difficult to figure out since thread
dispatching is a highly asynchronous process (e.g. via inter-processor
interrupts). Only the intended processor of a thread is known to the
scheduler easily. Do the CPU usage accounting during thread heir
updates in the context of the scheduler operations. Provide the
function _Thread_Get_CPU_time_used() to get the CPU usage of a thread
using proper locks to get a consistent value.

Close #2627.

  • Property mode set to 100644
File size: 8.1 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 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.org/license/LICENSE.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <rtems/rtems/ratemonimpl.h>
22#include <rtems/score/schedulerimpl.h>
23#include <rtems/score/threadimpl.h>
24#include <rtems/score/todimpl.h>
25#include <rtems/score/watchdogimpl.h>
26
27bool _Rate_monotonic_Get_status(
28  Rate_monotonic_Control        *the_period,
29  Rate_monotonic_Period_time_t  *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
67void _Rate_monotonic_Restart( Rate_monotonic_Control *the_period )
68{
69  ISR_Level level;
70
71  /*
72   *  Set the starting point and the CPU time used for the statistics.
73   */
74  _TOD_Get_uptime( &the_period->time_period_initiated );
75  _Thread_Get_CPU_time_used(
76    the_period->owner,
77    &the_period->cpu_usage_period_initiated
78  );
79
80  _Scheduler_Release_job( the_period->owner, the_period->next_length );
81
82  _ISR_Disable( level );
83  _Watchdog_Per_CPU_insert_relative(
84    &the_period->Timer,
85    _Per_CPU_Get(),
86    the_period->next_length
87  );
88  _ISR_Enable( level );
89}
90
91static void _Rate_monotonic_Update_statistics(
92  Rate_monotonic_Control    *the_period
93)
94{
95  Timestamp_Control               executed;
96  Rate_monotonic_Period_time_t    since_last_period;
97  Rate_monotonic_Statistics      *stats;
98  bool                            valid_status;
99
100  /*
101   *  Assume we are only called in states where it is appropriate
102   *  to update the statistics.  This should only be RATE_MONOTONIC_ACTIVE
103   *  and RATE_MONOTONIC_EXPIRED.
104   */
105
106  /*
107   *  Update the counts.
108   */
109  stats = &the_period->Statistics;
110  stats->count++;
111
112  if ( the_period->state == RATE_MONOTONIC_EXPIRED )
113    stats->missed_count++;
114
115  /*
116   *  Grab status for time statistics.
117   */
118  valid_status =
119    _Rate_monotonic_Get_status( the_period, &since_last_period, &executed );
120  if (!valid_status)
121    return;
122
123  /*
124   *  Update CPU time
125   */
126  #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
127    _Timestamp_Add_to( &stats->total_cpu_time, &executed );
128
129    if ( _Timestamp_Less_than( &executed, &stats->min_cpu_time ) )
130      stats->min_cpu_time = executed;
131
132    if ( _Timestamp_Greater_than( &executed, &stats->max_cpu_time ) )
133      stats->max_cpu_time = executed;
134  #else
135    stats->total_cpu_time  += executed;
136
137    if ( executed < stats->min_cpu_time )
138      stats->min_cpu_time = executed;
139
140    if ( executed > stats->max_cpu_time )
141      stats->max_cpu_time = executed;
142  #endif
143
144  /*
145   *  Update Wall time
146   */
147  #ifndef __RTEMS_USE_TICKS_FOR_STATISTICS__
148    _Timestamp_Add_to( &stats->total_wall_time, &since_last_period );
149
150    if ( _Timestamp_Less_than( &since_last_period, &stats->min_wall_time ) )
151      stats->min_wall_time = since_last_period;
152
153    if ( _Timestamp_Greater_than( &since_last_period, &stats->max_wall_time ) )
154      stats->max_wall_time = since_last_period;
155  #else
156
157    /* Sanity check wall time */
158    if ( since_last_period < executed )
159      since_last_period = executed;
160
161    stats->total_wall_time += since_last_period;
162
163    if ( since_last_period < stats->min_wall_time )
164      stats->min_wall_time = since_last_period;
165
166    if ( since_last_period > stats->max_wall_time )
167      stats->max_wall_time = since_last_period;
168  #endif
169}
170
171rtems_status_code rtems_rate_monotonic_period(
172  rtems_id       id,
173  rtems_interval length
174)
175{
176  Rate_monotonic_Control              *the_period;
177  Objects_Locations                    location;
178  rtems_status_code                    return_value;
179  rtems_rate_monotonic_period_states   local_state;
180  ISR_Level                            level;
181
182  the_period = _Rate_monotonic_Get( id, &location );
183
184  switch ( location ) {
185    case OBJECTS_LOCAL:
186      if ( !_Thread_Is_executing( the_period->owner ) ) {
187        _Objects_Put( &the_period->Object );
188        return RTEMS_NOT_OWNER_OF_RESOURCE;
189      }
190
191      if ( length == RTEMS_PERIOD_STATUS ) {
192        switch ( the_period->state ) {
193          case RATE_MONOTONIC_INACTIVE:
194            return_value = RTEMS_NOT_DEFINED;
195            break;
196          case RATE_MONOTONIC_EXPIRED:
197          case RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING:
198            return_value = RTEMS_TIMEOUT;
199            break;
200          case RATE_MONOTONIC_ACTIVE:
201          default:              /* unreached -- only to remove warnings */
202            return_value = RTEMS_SUCCESSFUL;
203            break;
204        }
205        _Objects_Put( &the_period->Object );
206        return( return_value );
207      }
208
209      _ISR_Disable( level );
210      if ( the_period->state == RATE_MONOTONIC_INACTIVE ) {
211        _ISR_Enable( level );
212
213        the_period->state = RATE_MONOTONIC_ACTIVE;
214        the_period->next_length = length;
215        _Rate_monotonic_Restart( the_period );
216        _Objects_Put( &the_period->Object );
217        return RTEMS_SUCCESSFUL;
218      }
219
220      if ( the_period->state == RATE_MONOTONIC_ACTIVE ) {
221        /*
222         *  Update statistics from the concluding period.
223         */
224        _Rate_monotonic_Update_statistics( the_period );
225
226        /*
227         *  This tells the _Rate_monotonic_Timeout that this task is
228         *  in the process of blocking on the period and that we
229         *  may be changing the length of the next period.
230         */
231        the_period->state = RATE_MONOTONIC_OWNER_IS_BLOCKING;
232        the_period->next_length = length;
233
234        _ISR_Enable( level );
235
236        _Thread_Executing->Wait.id = the_period->Object.id;
237        _Thread_Set_state( _Thread_Executing, STATES_WAITING_FOR_PERIOD );
238
239        /*
240         *  Did the watchdog timer expire while we were actually blocking
241         *  on it?
242         */
243        _ISR_Disable( level );
244          local_state = the_period->state;
245          the_period->state = RATE_MONOTONIC_ACTIVE;
246        _ISR_Enable( level );
247
248        /*
249         *  If it did, then we want to unblock ourself and continue as
250         *  if nothing happen.  The period was reset in the timeout routine.
251         */
252        if ( local_state == RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING )
253          _Thread_Clear_state( _Thread_Executing, STATES_WAITING_FOR_PERIOD );
254
255        _Objects_Put( &the_period->Object );
256        return RTEMS_SUCCESSFUL;
257      }
258
259      if ( the_period->state == RATE_MONOTONIC_EXPIRED ) {
260        /*
261         *  Update statistics from the concluding period
262         */
263        _Rate_monotonic_Update_statistics( the_period );
264
265        _ISR_Enable( level );
266
267        the_period->state = RATE_MONOTONIC_ACTIVE;
268        the_period->next_length = length;
269
270        _Watchdog_Per_CPU_insert_relative(
271          &the_period->Timer,
272          _Per_CPU_Get(),
273          length
274        );
275        _Scheduler_Release_job( the_period->owner, the_period->next_length );
276        _Objects_Put( &the_period->Object );
277        return RTEMS_TIMEOUT;
278      }
279
280      /*
281       *  These should never happen so just return invalid Id.
282       *    - RATE_MONOTONIC_OWNER_IS_BLOCKING:
283       *    - RATE_MONOTONIC_EXPIRED_WHILE_BLOCKING:
284       */
285#if defined(RTEMS_MULTIPROCESSING)
286    case OBJECTS_REMOTE:            /* should never return this */
287#endif
288    case OBJECTS_ERROR:
289      break;
290  }
291
292  return RTEMS_INVALID_ID;
293}
Note: See TracBrowser for help on using the repository browser.