source: rtems/cpukit/score/include/rtems/score/mrspimpl.h @ af746b0

5
Last change on this file since af746b0 was af746b0, checked in by Sebastian Huber <sebastian.huber@…>, on 05/25/16 at 14:30:23

score: Use thread queue lock for MrsP

Replace the ISR lock in MRSP_Control with a thread queue. This
simplifies the Classic semaphore implementation. Only the lock part of
the thread queue is used.

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/*
2 * Copyright (c) 2014, 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#ifndef _RTEMS_SCORE_MRSPIMPL_H
16#define _RTEMS_SCORE_MRSPIMPL_H
17
18#include <rtems/score/mrsp.h>
19
20#if defined(RTEMS_SMP)
21
22#include <rtems/score/assert.h>
23#include <rtems/score/chainimpl.h>
24#include <rtems/score/resourceimpl.h>
25#include <rtems/score/schedulerimpl.h>
26#include <rtems/score/status.h>
27#include <rtems/score/threadqimpl.h>
28#include <rtems/score/watchdogimpl.h>
29#include <rtems/score/wkspace.h>
30
31#ifdef __cplusplus
32extern "C" {
33#endif /* __cplusplus */
34
35/**
36 * @addtogroup ScoreMRSP
37 *
38 * @{
39 */
40
41/**
42 * @brief Internal state used for MRSP_Rival::status to indicate that this
43 * rival waits for resource ownership.
44 */
45#define MRSP_WAIT_FOR_OWNERSHIP STATUS_MINUS_ONE
46
47/*
48 * FIXME: Operations with the resource dependency tree are protected by the
49 * global scheduler lock.  Since the scheduler lock should be scheduler
50 * instance specific in the future this will only work temporarily.  A more
51 * sophisticated locking strategy is necessary.
52 */
53
54RTEMS_INLINE_ROUTINE void _MRSP_Giant_acquire( ISR_lock_Context *lock_context )
55{
56  _ISR_lock_Acquire( &_Scheduler_Lock, lock_context );
57}
58
59RTEMS_INLINE_ROUTINE void _MRSP_Giant_release( ISR_lock_Context *lock_context )
60{
61  _ISR_lock_Release( &_Scheduler_Lock, lock_context );
62}
63
64RTEMS_INLINE_ROUTINE void _MRSP_Acquire_critical(
65  MRSP_Control         *mrsp,
66  Thread_queue_Context *queue_context
67)
68{
69  _Thread_queue_Acquire_critical(
70    &mrsp->Wait_queue,
71    &queue_context->Lock_context
72  );
73}
74
75RTEMS_INLINE_ROUTINE void _MRSP_Release(
76  MRSP_Control         *mrsp,
77  Thread_queue_Context *queue_context
78)
79{
80  _Thread_queue_Release( &mrsp->Wait_queue, &queue_context->Lock_context );
81}
82
83RTEMS_INLINE_ROUTINE bool _MRSP_Restore_priority_filter(
84  Thread_Control   *thread,
85  Priority_Control *new_priority,
86  void             *arg
87)
88{
89  *new_priority = _Thread_Priority_highest(
90    thread->real_priority,
91    *new_priority
92  );
93
94  return *new_priority != thread->current_priority;
95}
96
97RTEMS_INLINE_ROUTINE void _MRSP_Restore_priority(
98  Thread_Control   *thread,
99  Priority_Control  initial_priority
100)
101{
102  /*
103   * The Thread_Control::resource_count is used by the normal priority ceiling
104   * or priority inheritance semaphores.
105   */
106  if ( thread->resource_count == 0 ) {
107    _Thread_Change_priority(
108      thread,
109      initial_priority,
110      NULL,
111      _MRSP_Restore_priority_filter,
112      true
113    );
114  }
115}
116
117RTEMS_INLINE_ROUTINE void _MRSP_Claim_ownership(
118  MRSP_Control         *mrsp,
119  Thread_Control       *new_owner,
120  Priority_Control      initial_priority,
121  Priority_Control      ceiling_priority,
122  Thread_queue_Context *queue_context
123)
124{
125  Per_CPU_Control *cpu_self;
126
127  _Resource_Node_add_resource( &new_owner->Resource_node, &mrsp->Resource );
128  _Resource_Set_owner( &mrsp->Resource, &new_owner->Resource_node );
129  mrsp->initial_priority_of_owner = initial_priority;
130  _Scheduler_Thread_change_help_state( new_owner, SCHEDULER_HELP_ACTIVE_OWNER );
131
132  cpu_self = _Thread_Dispatch_disable_critical( &queue_context->Lock_context );
133  _MRSP_Release( mrsp, queue_context );
134
135  _Thread_Raise_priority( new_owner, ceiling_priority );
136
137  _Thread_Dispatch_enable( cpu_self );
138}
139
140RTEMS_INLINE_ROUTINE Status_Control _MRSP_Initialize(
141  MRSP_Control     *mrsp,
142  Priority_Control  ceiling_priority,
143  Thread_Control   *executing,
144  bool              initially_locked
145)
146{
147  uint32_t scheduler_count = _Scheduler_Count;
148  uint32_t i;
149
150  if ( initially_locked ) {
151    return STATUS_INVALID_NUMBER;
152  }
153
154  mrsp->ceiling_priorities = _Workspace_Allocate(
155    sizeof( *mrsp->ceiling_priorities ) * scheduler_count
156  );
157  if ( mrsp->ceiling_priorities == NULL ) {
158    return STATUS_NO_MEMORY;
159  }
160
161  for ( i = 0 ; i < scheduler_count ; ++i ) {
162    mrsp->ceiling_priorities[ i ] = ceiling_priority;
163  }
164
165  _Resource_Initialize( &mrsp->Resource );
166  _Chain_Initialize_empty( &mrsp->Rivals );
167  _Thread_queue_Initialize( &mrsp->Wait_queue );
168
169  return STATUS_SUCCESSFUL;
170}
171
172RTEMS_INLINE_ROUTINE Priority_Control _MRSP_Get_ceiling_priority(
173  MRSP_Control *mrsp,
174  uint32_t      scheduler_index
175)
176{
177  return mrsp->ceiling_priorities[ scheduler_index ];
178}
179
180RTEMS_INLINE_ROUTINE void _MRSP_Set_ceiling_priority(
181  MRSP_Control      *mrsp,
182  uint32_t           scheduler_index,
183  Priority_Control   ceiling_priority
184)
185{
186  mrsp->ceiling_priorities[ scheduler_index ] = ceiling_priority;
187}
188
189RTEMS_INLINE_ROUTINE void _MRSP_Timeout( Watchdog_Control *watchdog )
190{
191  MRSP_Rival *rival = RTEMS_CONTAINER_OF( watchdog, MRSP_Rival, Watchdog );
192  MRSP_Control *mrsp = rival->resource;
193  Thread_Control *thread = rival->thread;
194  Thread_queue_Context queue_context;
195
196  _Thread_queue_Context_initialize( &queue_context );
197  _ISR_lock_ISR_disable( &queue_context.Lock_context );
198  _MRSP_Acquire_critical( mrsp, &queue_context );
199
200  if ( rival->status == MRSP_WAIT_FOR_OWNERSHIP ) {
201    ISR_lock_Context giant_lock_context;
202
203    _MRSP_Giant_acquire( &giant_lock_context );
204
205    _Chain_Extract_unprotected( &rival->Node );
206    _Resource_Node_extract( &thread->Resource_node );
207    _Resource_Node_set_dependency( &thread->Resource_node, NULL );
208    _Scheduler_Thread_change_help_state( thread, rival->initial_help_state );
209    _Scheduler_Thread_change_resource_root( thread, thread );
210
211    _MRSP_Giant_release( &giant_lock_context );
212
213    rival->status = STATUS_TIMEOUT;
214
215    _MRSP_Release( mrsp, &queue_context );
216  } else {
217    _MRSP_Release( mrsp, &queue_context );
218  }
219}
220
221RTEMS_INLINE_ROUTINE Status_Control _MRSP_Wait_for_ownership(
222  MRSP_Control         *mrsp,
223  Resource_Node        *owner,
224  Thread_Control       *executing,
225  Priority_Control      initial_priority,
226  Priority_Control      ceiling_priority,
227  Watchdog_Interval     timeout,
228  Thread_queue_Context *queue_context
229)
230{
231  Status_Control status;
232  MRSP_Rival rival;
233  Thread_Life_state life_state;
234  Per_CPU_Control *cpu_self;
235  ISR_lock_Context giant_lock_context;
236  ISR_Level level;
237
238  rival.thread = executing;
239  rival.resource = mrsp;
240  rival.initial_priority = initial_priority;
241
242  _MRSP_Giant_acquire( &giant_lock_context );
243
244  rival.initial_help_state =
245    _Scheduler_Thread_change_help_state( executing, SCHEDULER_HELP_ACTIVE_RIVAL );
246  rival.status = MRSP_WAIT_FOR_OWNERSHIP;
247
248  _Chain_Append_unprotected( &mrsp->Rivals, &rival.Node );
249  _Resource_Add_rival( &mrsp->Resource, &executing->Resource_node );
250  _Resource_Node_set_dependency( &executing->Resource_node, &mrsp->Resource );
251  _Scheduler_Thread_change_resource_root(
252    executing,
253    THREAD_RESOURCE_NODE_TO_THREAD( _Resource_Node_get_root( owner ) )
254  );
255
256  _MRSP_Giant_release( &giant_lock_context );
257
258  cpu_self = _Thread_Dispatch_disable_critical( &queue_context->Lock_context );
259  _MRSP_Release( mrsp, queue_context );
260
261  _Thread_Raise_priority( executing, ceiling_priority );
262
263  if ( timeout > 0 ) {
264    _Watchdog_Preinitialize( &rival.Watchdog, cpu_self );
265    _Watchdog_Initialize( &rival.Watchdog, _MRSP_Timeout );
266    _ISR_Local_disable( level );
267    _Watchdog_Per_CPU_insert_relative( &rival.Watchdog, cpu_self, timeout );
268    _ISR_Local_enable( level );
269  }
270
271  life_state = _Thread_Set_life_protection( THREAD_LIFE_PROTECTED );
272  _Thread_Dispatch_enable( cpu_self );
273
274  _Assert( _Debug_Is_thread_dispatching_allowed() );
275
276  /* Wait for state change */
277  do {
278    status = rival.status;
279  } while ( status == MRSP_WAIT_FOR_OWNERSHIP );
280
281  _Thread_Set_life_protection( life_state );
282
283  if ( timeout > 0 ) {
284    _ISR_Local_disable( level );
285    _Watchdog_Per_CPU_remove(
286      &rival.Watchdog,
287      cpu_self,
288      &cpu_self->Watchdog.Header[ PER_CPU_WATCHDOG_RELATIVE ]
289    );
290    _ISR_Local_enable( level );
291
292    if ( status == STATUS_TIMEOUT ) {
293      _MRSP_Restore_priority( executing, initial_priority );
294    }
295  }
296
297  return status;
298}
299
300RTEMS_INLINE_ROUTINE Status_Control _MRSP_Seize(
301  MRSP_Control         *mrsp,
302  Thread_Control       *executing,
303  bool                  wait,
304  Watchdog_Interval     timeout,
305  Thread_queue_Context *queue_context
306)
307{
308  Status_Control status;
309  const Scheduler_Control *scheduler = _Scheduler_Get_own( executing );
310  uint32_t scheduler_index = _Scheduler_Get_index( scheduler );
311  Priority_Control initial_priority = executing->current_priority;
312  Priority_Control ceiling_priority =
313    _MRSP_Get_ceiling_priority( mrsp, scheduler_index );
314  bool priority_ok = !_Thread_Priority_less_than(
315    ceiling_priority,
316    initial_priority
317  );
318  Resource_Node *owner;
319
320  if ( !priority_ok) {
321    _ISR_lock_ISR_enable( &queue_context->Lock_context );
322    return STATUS_MUTEX_CEILING_VIOLATED;
323  }
324
325  _MRSP_Acquire_critical( mrsp, queue_context );
326  owner = _Resource_Get_owner( &mrsp->Resource );
327  if ( owner == NULL ) {
328    _MRSP_Claim_ownership(
329      mrsp,
330      executing,
331      initial_priority,
332      ceiling_priority,
333      queue_context
334    );
335    status = STATUS_SUCCESSFUL;
336  } else if (
337    wait
338      && _Resource_Node_get_root( owner ) != &executing->Resource_node
339  ) {
340    status = _MRSP_Wait_for_ownership(
341      mrsp,
342      owner,
343      executing,
344      initial_priority,
345      ceiling_priority,
346      timeout,
347      queue_context
348    );
349  } else {
350    _MRSP_Release( mrsp, queue_context );
351    /* Not available, nested access or deadlock */
352    status = STATUS_UNAVAILABLE;
353  }
354
355  return status;
356}
357
358RTEMS_INLINE_ROUTINE Status_Control _MRSP_Surrender(
359  MRSP_Control         *mrsp,
360  Thread_Control       *executing,
361  Thread_queue_Context *queue_context
362)
363{
364  Priority_Control initial_priority;
365  Per_CPU_Control *cpu_self;
366  ISR_lock_Context giant_lock_context;
367
368  if ( _Resource_Get_owner( &mrsp->Resource ) != &executing->Resource_node ) {
369    _ISR_lock_ISR_enable( &queue_context->Lock_context );
370    return STATUS_NOT_OWNER;
371  }
372
373  if (
374    !_Resource_Is_most_recently_obtained(
375      &mrsp->Resource,
376      &executing->Resource_node
377    )
378  ) {
379    _ISR_lock_ISR_enable( &queue_context->Lock_context );
380    return STATUS_RELEASE_ORDER_VIOLATION;
381  }
382
383  initial_priority = mrsp->initial_priority_of_owner;
384
385  _MRSP_Acquire_critical( mrsp, queue_context );
386
387  _MRSP_Giant_acquire( &giant_lock_context );
388
389  _Resource_Extract( &mrsp->Resource );
390
391  if ( _Chain_Is_empty( &mrsp->Rivals ) ) {
392    _Resource_Set_owner( &mrsp->Resource, NULL );
393  } else {
394    MRSP_Rival *rival = (MRSP_Rival *)
395      _Chain_Get_first_unprotected( &mrsp->Rivals );
396    Thread_Control *new_owner;
397
398    /*
399     * This must be inside the critical section since the status prevents a
400     * potential double extraction in _MRSP_Timeout().
401     */
402    rival->status = STATUS_SUCCESSFUL;
403
404    new_owner = rival->thread;
405    mrsp->initial_priority_of_owner = rival->initial_priority;
406    _Resource_Node_extract( &new_owner->Resource_node );
407    _Resource_Node_set_dependency( &new_owner->Resource_node, NULL );
408    _Resource_Node_add_resource( &new_owner->Resource_node, &mrsp->Resource );
409    _Resource_Set_owner( &mrsp->Resource, &new_owner->Resource_node );
410    _Scheduler_Thread_change_help_state( new_owner, SCHEDULER_HELP_ACTIVE_OWNER );
411    _Scheduler_Thread_change_resource_root( new_owner, new_owner );
412  }
413
414  if ( !_Resource_Node_owns_resources( &executing->Resource_node ) ) {
415    _Scheduler_Thread_change_help_state( executing, SCHEDULER_HELP_YOURSELF );
416  }
417
418  _MRSP_Giant_release( &giant_lock_context );
419
420  cpu_self = _Thread_Dispatch_disable_critical( &queue_context->Lock_context );
421  _MRSP_Release( mrsp, queue_context );
422
423  _MRSP_Restore_priority( executing, initial_priority );
424
425  _Thread_Dispatch_enable( cpu_self );
426
427  return STATUS_SUCCESSFUL;
428}
429
430RTEMS_INLINE_ROUTINE Status_Control _MRSP_Can_destroy( MRSP_Control *mrsp )
431{
432  if ( _Resource_Get_owner( &mrsp->Resource ) != NULL ) {
433    return STATUS_RESOURCE_IN_USE;
434  }
435
436  return STATUS_SUCCESSFUL;
437}
438
439RTEMS_INLINE_ROUTINE void _MRSP_Destroy(
440  MRSP_Control         *mrsp,
441  Thread_queue_Context *queue_context
442)
443{
444  _MRSP_Release( mrsp, queue_context );
445  _Thread_queue_Destroy( &mrsp->Wait_queue );
446  _Workspace_Free( mrsp->ceiling_priorities );
447}
448
449/** @} */
450
451#ifdef __cplusplus
452}
453#endif /* __cplusplus */
454
455#endif /* RTEMS_SMP */
456
457#endif /* _RTEMS_SCORE_MRSPIMPL_H */
Note: See TracBrowser for help on using the repository browser.