[c05d7502] | 1 | /** |
---|
| 2 | * @file |
---|
[5f9b3db] | 3 | * |
---|
[c05d7502] | 4 | * @brief Rate Monotonic Support |
---|
| 5 | * @ingroup ClassicRateMon |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | /* |
---|
[7eef54e] | 9 | * COPYRIGHT (c) 1989-2010. |
---|
[5f9b3db] | 10 | * On-Line Applications Research Corporation (OAR). |
---|
[90960bd] | 11 | * Copyright (c) 2016 embedded brains GmbH. |
---|
[3a46b72] | 12 | * COPYRIGHT (c) 2016 Kuan-Hsun Chen. |
---|
[5f9b3db] | 13 | * |
---|
| 14 | * The license and distribution terms for this file may be |
---|
| 15 | * found in the file LICENSE in this distribution or at |
---|
[c499856] | 16 | * http://www.rtems.org/license/LICENSE. |
---|
[5f9b3db] | 17 | */ |
---|
| 18 | |
---|
[1095ec1] | 19 | #if HAVE_CONFIG_H |
---|
| 20 | #include "config.h" |
---|
| 21 | #endif |
---|
| 22 | |
---|
[ecdcf01] | 23 | #include <rtems/rtems/ratemonimpl.h> |
---|
[c6e21ee1] | 24 | #include <rtems/score/schedulerimpl.h> |
---|
[f031df0e] | 25 | #include <rtems/score/todimpl.h> |
---|
[5f9b3db] | 26 | |
---|
[eb37f9d] | 27 | bool _Rate_monotonic_Get_status( |
---|
[300eaad] | 28 | const Rate_monotonic_Control *the_period, |
---|
| 29 | Timestamp_Control *wall_since_last_period, |
---|
| 30 | Timestamp_Control *cpu_since_last_period |
---|
[eb37f9d] | 31 | ) |
---|
| 32 | { |
---|
[e6b31b27] | 33 | Timestamp_Control uptime; |
---|
| 34 | Thread_Control *owning_thread = the_period->owner; |
---|
[d297c81d] | 35 | Timestamp_Control used; |
---|
[eb37f9d] | 36 | |
---|
| 37 | /* |
---|
| 38 | * Determine elapsed wall time since period initiated. |
---|
| 39 | */ |
---|
[e6b31b27] | 40 | _TOD_Get_uptime( &uptime ); |
---|
| 41 | _Timestamp_Subtract( |
---|
| 42 | &the_period->time_period_initiated, &uptime, wall_since_last_period |
---|
| 43 | ); |
---|
[eb37f9d] | 44 | |
---|
| 45 | /* |
---|
| 46 | * Determine cpu usage since period initiated. |
---|
| 47 | */ |
---|
[d37adfe5] | 48 | _Thread_Get_CPU_time_used( owning_thread, &used ); |
---|
[eb37f9d] | 49 | |
---|
[d37adfe5] | 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; |
---|
[e6b31b27] | 56 | |
---|
[d37adfe5] | 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 | ); |
---|
[e6b31b27] | 63 | |
---|
[eb37f9d] | 64 | return true; |
---|
| 65 | } |
---|
| 66 | |
---|
[cb2cbec] | 67 | static void _Rate_monotonic_Release_postponed_job( |
---|
[3a46b72] | 68 | Rate_monotonic_Control *the_period, |
---|
| 69 | Thread_Control *owner, |
---|
| 70 | rtems_interval next_length, |
---|
| 71 | ISR_lock_Context *lock_context |
---|
| 72 | ) |
---|
| 73 | { |
---|
[625bd6a] | 74 | Per_CPU_Control *cpu_self; |
---|
[3a46b72] | 75 | Thread_queue_Context queue_context; |
---|
| 76 | |
---|
[625bd6a] | 77 | --the_period->postponed_jobs; |
---|
[3a46b72] | 78 | _Scheduler_Release_job( |
---|
| 79 | owner, |
---|
| 80 | &the_period->Priority, |
---|
| 81 | the_period->latest_deadline, |
---|
| 82 | &queue_context |
---|
| 83 | ); |
---|
| 84 | |
---|
[625bd6a] | 85 | cpu_self = _Thread_Dispatch_disable_critical( lock_context ); |
---|
[3a46b72] | 86 | _Rate_monotonic_Release( the_period, lock_context ); |
---|
| 87 | _Thread_Priority_update( &queue_context ); |
---|
[ed24ed4e] | 88 | _Thread_Dispatch_direct( cpu_self ); |
---|
[3a46b72] | 89 | } |
---|
| 90 | |
---|
[90960bd] | 91 | static 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 | ) |
---|
[94d9bee] | 97 | { |
---|
[300f6a48] | 98 | Per_CPU_Control *cpu_self; |
---|
| 99 | Thread_queue_Context queue_context; |
---|
| 100 | uint64_t deadline; |
---|
[90960bd] | 101 | |
---|
| 102 | cpu_self = _Thread_Dispatch_disable_critical( lock_context ); |
---|
| 103 | |
---|
[91ce012c] | 104 | deadline = _Watchdog_Per_CPU_insert_ticks( |
---|
[90960bd] | 105 | &the_period->Timer, |
---|
| 106 | cpu_self, |
---|
| 107 | next_length |
---|
| 108 | ); |
---|
[300f6a48] | 109 | _Scheduler_Release_job( |
---|
| 110 | owner, |
---|
| 111 | &the_period->Priority, |
---|
| 112 | deadline, |
---|
| 113 | &queue_context |
---|
| 114 | ); |
---|
[9a78f8a5] | 115 | |
---|
[ee0e4135] | 116 | _Rate_monotonic_Release( the_period, lock_context ); |
---|
[300f6a48] | 117 | _Thread_Priority_update( &queue_context ); |
---|
[90960bd] | 118 | _Thread_Dispatch_enable( cpu_self ); |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | void _Rate_monotonic_Restart( |
---|
| 122 | Rate_monotonic_Control *the_period, |
---|
| 123 | Thread_Control *owner, |
---|
| 124 | ISR_lock_Context *lock_context |
---|
| 125 | ) |
---|
| 126 | { |
---|
[94d9bee] | 127 | /* |
---|
| 128 | * Set the starting point and the CPU time used for the statistics. |
---|
| 129 | */ |
---|
[d37adfe5] | 130 | _TOD_Get_uptime( &the_period->time_period_initiated ); |
---|
[90960bd] | 131 | _Thread_Get_CPU_time_used( owner, &the_period->cpu_usage_period_initiated ); |
---|
[03b900d] | 132 | |
---|
[90960bd] | 133 | _Rate_monotonic_Release_job( |
---|
| 134 | the_period, |
---|
| 135 | owner, |
---|
| 136 | the_period->next_length, |
---|
| 137 | lock_context |
---|
[03b900d] | 138 | ); |
---|
[94d9bee] | 139 | } |
---|
| 140 | |
---|
[e31173c] | 141 | static void _Rate_monotonic_Update_statistics( |
---|
[e1bce86] | 142 | Rate_monotonic_Control *the_period |
---|
| 143 | ) |
---|
| 144 | { |
---|
[300eaad] | 145 | Timestamp_Control executed; |
---|
| 146 | Timestamp_Control since_last_period; |
---|
| 147 | Rate_monotonic_Statistics *stats; |
---|
| 148 | bool valid_status; |
---|
[e1bce86] | 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 | /* |
---|
[94d9bee] | 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 | /* |
---|
[eb37f9d] | 166 | * Grab status for time statistics. |
---|
[e1bce86] | 167 | */ |
---|
[eb37f9d] | 168 | valid_status = |
---|
| 169 | _Rate_monotonic_Get_status( the_period, &since_last_period, &executed ); |
---|
| 170 | if (!valid_status) |
---|
| 171 | return; |
---|
[e1bce86] | 172 | |
---|
| 173 | /* |
---|
| 174 | * Update CPU time |
---|
| 175 | */ |
---|
[875c26d] | 176 | _Timestamp_Add_to( &stats->total_cpu_time, &executed ); |
---|
[c3330a8] | 177 | |
---|
[875c26d] | 178 | if ( _Timestamp_Less_than( &executed, &stats->min_cpu_time ) ) |
---|
| 179 | stats->min_cpu_time = executed; |
---|
[c3330a8] | 180 | |
---|
[875c26d] | 181 | if ( _Timestamp_Greater_than( &executed, &stats->max_cpu_time ) ) |
---|
| 182 | stats->max_cpu_time = executed; |
---|
[e1bce86] | 183 | |
---|
| 184 | /* |
---|
| 185 | * Update Wall time |
---|
| 186 | */ |
---|
[875c26d] | 187 | _Timestamp_Add_to( &stats->total_wall_time, &since_last_period ); |
---|
[c3330a8] | 188 | |
---|
[875c26d] | 189 | if ( _Timestamp_Less_than( &since_last_period, &stats->min_wall_time ) ) |
---|
| 190 | stats->min_wall_time = since_last_period; |
---|
[c3330a8] | 191 | |
---|
[875c26d] | 192 | if ( _Timestamp_Greater_than( &since_last_period, &stats->max_wall_time ) ) |
---|
| 193 | stats->max_wall_time = since_last_period; |
---|
[e1bce86] | 194 | } |
---|
| 195 | |
---|
[90960bd] | 196 | static 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 | |
---|
| 211 | static 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 | { |
---|
[3a46b72] | 218 | the_period->postponed_jobs = 0; |
---|
[90960bd] | 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 | |
---|
| 225 | static 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 ); |
---|
[ee0e4135] | 250 | _Rate_monotonic_Release( the_period, lock_context ); |
---|
[90960bd] | 251 | |
---|
| 252 | _Thread_Set_state( executing, STATES_WAITING_FOR_PERIOD ); |
---|
| 253 | |
---|
[02987728] | 254 | success = _Thread_Wait_flags_try_change_acquire( |
---|
[90960bd] | 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 | |
---|
[ed24ed4e] | 266 | _Thread_Dispatch_direct( cpu_self ); |
---|
[90960bd] | 267 | return RTEMS_SUCCESSFUL; |
---|
| 268 | } |
---|
| 269 | |
---|
[3a46b72] | 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 | */ |
---|
[90960bd] | 275 | static 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 | /* |
---|
[3a46b72] | 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 |
---|
[90960bd] | 290 | */ |
---|
| 291 | _Rate_monotonic_Update_statistics( the_period ); |
---|
| 292 | |
---|
| 293 | the_period->state = RATE_MONOTONIC_ACTIVE; |
---|
| 294 | the_period->next_length = length; |
---|
| 295 | |
---|
[cb2cbec] | 296 | _Rate_monotonic_Release_postponed_job( |
---|
| 297 | the_period, |
---|
| 298 | executing, |
---|
| 299 | length, |
---|
| 300 | lock_context |
---|
| 301 | ); |
---|
[90960bd] | 302 | return RTEMS_TIMEOUT; |
---|
| 303 | } |
---|
| 304 | |
---|
[5f9b3db] | 305 | rtems_status_code rtems_rate_monotonic_period( |
---|
[d3b72ca3] | 306 | rtems_id id, |
---|
| 307 | rtems_interval length |
---|
[5f9b3db] | 308 | ) |
---|
| 309 | { |
---|
[90960bd] | 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 | |
---|
[ee0e4135] | 327 | _Rate_monotonic_Acquire_critical( the_period, &lock_context ); |
---|
[90960bd] | 328 | |
---|
| 329 | state = the_period->state; |
---|
| 330 | |
---|
| 331 | if ( length == RTEMS_PERIOD_STATUS ) { |
---|
| 332 | status = _Rate_monotonic_Get_status_for_state( state ); |
---|
[ee0e4135] | 333 | _Rate_monotonic_Release( the_period, &lock_context ); |
---|
[90960bd] | 334 | } else { |
---|
| 335 | switch ( state ) { |
---|
| 336 | case RATE_MONOTONIC_ACTIVE: |
---|
[3a46b72] | 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 | * |
---|
[cb2cbec] | 344 | * Do nothing on the watchdog deadline assignment but release the |
---|
| 345 | * next remaining postponed job. |
---|
[3a46b72] | 346 | */ |
---|
| 347 | status = _Rate_monotonic_Block_while_expired( |
---|
| 348 | the_period, |
---|
| 349 | length, |
---|
| 350 | executing, |
---|
| 351 | &lock_context |
---|
| 352 | ); |
---|
| 353 | }else{ |
---|
| 354 | /* |
---|
[cb2cbec] | 355 | * Normal case that no postponed jobs and no expiration, so wait for |
---|
| 356 | * the period and update the deadline of watchdog accordingly. |
---|
[3a46b72] | 357 | */ |
---|
| 358 | status = _Rate_monotonic_Block_while_active( |
---|
| 359 | the_period, |
---|
| 360 | length, |
---|
| 361 | executing, |
---|
| 362 | &lock_context |
---|
| 363 | ); |
---|
| 364 | } |
---|
[90960bd] | 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: |
---|
[3a46b72] | 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 | */ |
---|
[90960bd] | 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 | } |
---|
[5f9b3db] | 392 | } |
---|
| 393 | |
---|
[90960bd] | 394 | return status; |
---|
[5f9b3db] | 395 | } |
---|