source: rtems/cpukit/score/include/rtems/score/schedulersmpimpl.h @ 3730a07f

4.115
Last change on this file since 3730a07f was 3730a07f, checked in by Sebastian Huber <sebastian.huber@…>, on 05/13/14 at 13:08:07

score: Use Scheduler_Context for SMP scheduler

Use the basic Scheduler_Context for the general SMP scheduler operations
to avoid usage of structure offsets to get the specialized context
variants.

  • Property mode set to 100644
File size: 16.3 KB
Line 
1/**
2 * @file
3 *
4 * @brief SMP Scheduler Implementation
5 *
6 * @ingroup ScoreSchedulerSMP
7 */
8
9/*
10 * Copyright (c) 2013-2014 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Dornierstr. 4
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.org/license/LICENSE.
21 */
22
23#ifndef _RTEMS_SCORE_SCHEDULERSMPIMPL_H
24#define _RTEMS_SCORE_SCHEDULERSMPIMPL_H
25
26#include <rtems/score/schedulersmp.h>
27#include <rtems/score/assert.h>
28#include <rtems/score/chainimpl.h>
29#include <rtems/score/schedulersimpleimpl.h>
30
31#ifdef __cplusplus
32extern "C" {
33#endif /* __cplusplus */
34
35/**
36 * @addtogroup ScoreSchedulerSMP
37 *
38 * The scheduler nodes can be in four states
39 * - @ref SCHEDULER_SMP_NODE_BLOCKED,
40 * - @ref SCHEDULER_SMP_NODE_SCHEDULED,
41 * - @ref SCHEDULER_SMP_NODE_READY, and
42 * - @ref SCHEDULER_SMP_NODE_IN_THE_AIR.
43 *
44 * State transitions are triggered via basic three operations
45 * - _Scheduler_SMP_Enqueue_ordered(),
46 * - _Scheduler_SMP_Extract(), and
47 * - _Scheduler_SMP_Schedule().
48 *
49 * @dot
50 * digraph {
51 *   node [style="filled"];
52 *
53 *   bs [label="BLOCKED"];
54 *   ss [label="SCHEDULED", fillcolor="green"];
55 *   rs [label="READY", fillcolor="red"];
56 *   as [label="IN THE AIR", fillcolor="orange"];
57 *
58 *   edge [label="enqueue"];
59 *   edge [fontcolor="darkgreen", color="darkgreen"];
60 *
61 *   bs -> ss;
62 *   as -> ss;
63 *
64 *   edge [label="enqueue"];
65 *   edge [fontcolor="red", color="red"];
66 *
67 *   bs -> rs;
68 *   as -> rs;
69 *
70 *   edge [label="enqueue other"];
71 *
72 *   ss -> rs;
73 *
74 *   edge [label="schedule"];
75 *   edge [fontcolor="black", color="black"];
76 *
77 *   as -> bs;
78 *
79 *   edge [label="extract"];
80 *   edge [fontcolor="brown", color="brown"];
81 *
82 *   ss -> as;
83 *
84 *   edge [fontcolor="black", color="black"];
85 *
86 *   rs -> bs;
87 *
88 *   edge [label="enqueue other\nschedule other"];
89 *   edge [fontcolor="darkgreen", color="darkgreen"];
90 *
91 *   rs -> ss;
92 * }
93 * @enddot
94 *
95 * During system initialization each processor of the scheduler instance starts
96 * with an idle thread assigned to it.  Lets have a look at an example with two
97 * idle threads I and J with priority 5.  We also have blocked threads A, B and
98 * C with priorities 1, 2 and 3 respectively.
99 *
100 * @dot
101 * digraph {
102 *   node [style="filled"];
103 *   edge [dir="none"];
104 *   subgraph {
105 *     rank = same;
106 *
107 *     i [label="I (5)", fillcolor="green"];
108 *     j [label="J (5)", fillcolor="green"];
109 *     a [label="A (1)"];
110 *     b [label="B (2)"];
111 *     c [label="C (3)"];
112 *     i -> j;
113 *   }
114 *
115 *   subgraph {
116 *     rank = same;
117 *
118 *     p0 [label="PROCESSOR 0", shape="box"];
119 *     p1 [label="PROCESSOR 1", shape="box"];
120 *   }
121 *
122 *   i -> p0;
123 *   j -> p1;
124 * }
125 * @enddot
126 *
127 * Lets start A.  For this an enqueue operation is performed.
128 *
129 * @dot
130 * digraph {
131 *   node [style="filled"];
132 *   edge [dir="none"];
133 *
134 *   subgraph {
135 *     rank = same;
136 *
137 *     i [label="I (5)", fillcolor="green"];
138 *     j [label="J (5)", fillcolor="red"];
139 *     a [label="A (1)", fillcolor="green"];
140 *     b [label="B (2)"];
141 *     c [label="C (3)"];
142 *     a -> i;
143 *   }
144 *
145 *   subgraph {
146 *     rank = same;
147 *
148 *     p0 [label="PROCESSOR 0", shape="box"];
149 *     p1 [label="PROCESSOR 1", shape="box"];
150 *   }
151 *
152 *   i -> p0;
153 *   a -> p1;
154 * }
155 * @enddot
156 *
157 * Lets start C.
158 *
159 * @dot
160 * digraph {
161 *   node [style="filled"];
162 *   edge [dir="none"];
163 *
164 *   subgraph {
165 *     rank = same;
166 *
167 *     a [label="A (1)", fillcolor="green"];
168 *     c [label="C (3)", fillcolor="green"];
169 *     i [label="I (5)", fillcolor="red"];
170 *     j [label="J (5)", fillcolor="red"];
171 *     b [label="B (2)"];
172 *     a -> c;
173 *     i -> j;
174 *   }
175 *
176 *   subgraph {
177 *     rank = same;
178 *
179 *     p0 [label="PROCESSOR 0", shape="box"];
180 *     p1 [label="PROCESSOR 1", shape="box"];
181 *   }
182 *
183 *   c -> p0;
184 *   a -> p1;
185 * }
186 * @enddot
187 *
188 * Lets start B.
189 *
190 * @dot
191 * digraph {
192 *   node [style="filled"];
193 *   edge [dir="none"];
194 *
195 *   subgraph {
196 *     rank = same;
197 *
198 *     a [label="A (1)", fillcolor="green"];
199 *     b [label="B (2)", fillcolor="green"];
200 *     c [label="C (3)", fillcolor="red"];
201 *     i [label="I (5)", fillcolor="red"];
202 *     j [label="J (5)", fillcolor="red"];
203 *     a -> b;
204 *     c -> i -> j;
205 *   }
206 *
207 *   subgraph {
208 *     rank = same;
209 *
210 *     p0 [label="PROCESSOR 0", shape="box"];
211 *     p1 [label="PROCESSOR 1", shape="box"];
212 *   }
213 *
214 *   b -> p0;
215 *   a -> p1;
216 * }
217 * @enddot
218 *
219 * Lets do something with A.  This can be a blocking operation or a priority
220 * change.  For this an extract operation is performed first.
221 *
222 * @dot
223 * digraph {
224 *   node [style="filled"];
225 *   edge [dir="none"];
226 *
227 *   subgraph {
228 *     rank = same;
229 *
230 *     b [label="B (2)", fillcolor="green"];
231 *     a [label="A (1)", fillcolor="orange"];
232 *     c [label="C (3)", fillcolor="red"];
233 *     i [label="I (5)", fillcolor="red"];
234 *     j [label="J (5)", fillcolor="red"];
235 *     c -> i -> j;
236 *   }
237 *
238 *   subgraph {
239 *     rank = same;
240 *
241 *     p0 [label="PROCESSOR 0", shape="box"];
242 *     p1 [label="PROCESSOR 1", shape="box"];
243 *   }
244 *
245 *   b -> p0;
246 *   a -> p1;
247 * }
248 * @enddot
249 *
250 * Lets change the priority of thread A to 4 and enqueue it.
251 *
252 * @dot
253 * digraph {
254 *   node [style="filled"];
255 *   edge [dir="none"];
256 *
257 *   subgraph {
258 *     rank = same;
259 *
260 *     b [label="B (2)", fillcolor="green"];
261 *     c [label="C (3)", fillcolor="green"];
262 *     a [label="A (4)", fillcolor="red"];
263 *     i [label="I (5)", fillcolor="red"];
264 *     j [label="J (5)", fillcolor="red"];
265 *     b -> c;
266 *     a -> i -> j;
267 *   }
268 *
269 *   subgraph {
270 *     rank = same;
271 *
272 *     p0 [label="PROCESSOR 0", shape="box"];
273 *     p1 [label="PROCESSOR 1", shape="box"];
274 *   }
275 *
276 *   b -> p0;
277 *   c -> p1;
278 * }
279 * @enddot
280 *
281 * Alternatively we can also do a blocking operation with thread A.  In this
282 * case schedule will be called.
283 *
284 * @dot
285 * digraph {
286 *   node [style="filled"];
287 *   edge [dir="none"];
288 *
289 *   subgraph {
290 *     rank = same;
291 *
292 *     b [label="B (2)", fillcolor="green"];
293 *     c [label="C (3)", fillcolor="green"];
294 *     i [label="I (5)", fillcolor="red"];
295 *     j [label="J (5)", fillcolor="red"];
296 *     a [label="A (1)"];
297 *     b -> c;
298 *     i -> j;
299 *   }
300 *
301 *   subgraph {
302 *     rank = same;
303 *
304 *     p0 [label="PROCESSOR 0", shape="box"];
305 *     p1 [label="PROCESSOR 1", shape="box"];
306 *   }
307 *
308 *   b -> p0;
309 *   c -> p1;
310 * }
311 * @enddot
312 *
313 * @{
314 */
315
316typedef Thread_Control *( *Scheduler_SMP_Get_highest_ready )(
317  Scheduler_Context *context
318);
319
320typedef void ( *Scheduler_SMP_Extract )(
321  Scheduler_Context *context,
322  Thread_Control *thread
323);
324
325typedef void ( *Scheduler_SMP_Insert )(
326  Scheduler_Context *context,
327  Thread_Control *thread_to_insert
328);
329
330typedef void ( *Scheduler_SMP_Move )(
331  Scheduler_Context *context,
332  Thread_Control *thread_to_move
333);
334
335static inline Scheduler_SMP_Context *_Scheduler_SMP_Get_self(
336  Scheduler_Context *context
337)
338{
339  return (Scheduler_SMP_Context *) context;
340}
341
342static inline void _Scheduler_SMP_Initialize(
343  Scheduler_SMP_Context *self
344)
345{
346  _Chain_Initialize_empty( &self->Scheduled );
347}
348
349static inline Scheduler_SMP_Node *_Scheduler_SMP_Node_get(
350  Thread_Control *thread
351)
352{
353  return (Scheduler_SMP_Node *) _Scheduler_Node_get( thread );
354}
355
356static inline void _Scheduler_SMP_Node_initialize(
357  Scheduler_SMP_Node *node
358)
359{
360  node->state = SCHEDULER_SMP_NODE_BLOCKED;
361}
362
363extern const bool _Scheduler_SMP_Node_valid_state_changes[ 4 ][ 4 ];
364
365static inline void _Scheduler_SMP_Node_change_state(
366  Scheduler_SMP_Node *node,
367  Scheduler_SMP_Node_state new_state
368)
369{
370  _Assert(
371    _Scheduler_SMP_Node_valid_state_changes[ node->state ][ new_state ]
372  );
373
374  node->state = new_state;
375}
376
377static inline bool _Scheduler_SMP_Is_processor_owned_by_us(
378  const Scheduler_SMP_Context *self,
379  const Per_CPU_Control *cpu
380)
381{
382  return cpu->scheduler_context == &self->Base;
383}
384
385static inline void _Scheduler_SMP_Update_heir(
386  Per_CPU_Control *cpu_self,
387  Per_CPU_Control *cpu_for_heir,
388  Thread_Control *heir
389)
390{
391  cpu_for_heir->heir = heir;
392
393  /*
394   * It is critical that we first update the heir and then the dispatch
395   * necessary so that _Thread_Get_heir_and_make_it_executing() cannot miss an
396   * update.
397   */
398  _Atomic_Fence( ATOMIC_ORDER_SEQ_CST );
399
400  /*
401   * Only update the dispatch necessary indicator if not already set to
402   * avoid superfluous inter-processor interrupts.
403   */
404  if ( !cpu_for_heir->dispatch_necessary ) {
405    cpu_for_heir->dispatch_necessary = true;
406
407    if ( cpu_for_heir != cpu_self ) {
408      _Per_CPU_Send_interrupt( cpu_for_heir );
409    }
410  }
411}
412
413static inline void _Scheduler_SMP_Allocate_processor(
414  Scheduler_SMP_Context *self,
415  Thread_Control *scheduled,
416  Thread_Control *victim
417)
418{
419  Scheduler_SMP_Node *scheduled_node = _Scheduler_SMP_Node_get( scheduled );
420  Per_CPU_Control *cpu_of_scheduled = _Thread_Get_CPU( scheduled );
421  Per_CPU_Control *cpu_of_victim = _Thread_Get_CPU( victim );
422  Per_CPU_Control *cpu_self = _Per_CPU_Get();
423  Thread_Control *heir;
424
425  _Scheduler_SMP_Node_change_state(
426    scheduled_node,
427    SCHEDULER_SMP_NODE_SCHEDULED
428  );
429
430  _Assert( _ISR_Get_level() != 0 );
431
432  if ( _Thread_Is_executing_on_a_processor( scheduled ) ) {
433    if ( _Scheduler_SMP_Is_processor_owned_by_us( self, cpu_of_scheduled ) ) {
434      heir = cpu_of_scheduled->heir;
435      _Scheduler_SMP_Update_heir( cpu_self, cpu_of_scheduled, scheduled );
436    } else {
437      /* We have to force a migration to our processor set */
438      _Assert( scheduled->debug_real_cpu->heir != scheduled );
439      heir = scheduled;
440    }
441  } else {
442    heir = scheduled;
443  }
444
445  if ( heir != victim ) {
446    _Thread_Set_CPU( heir, cpu_of_victim );
447    _Scheduler_SMP_Update_heir( cpu_self, cpu_of_victim, heir );
448  }
449}
450
451static inline Thread_Control *_Scheduler_SMP_Get_lowest_scheduled(
452  Scheduler_SMP_Context *self
453)
454{
455  Thread_Control *lowest_ready = NULL;
456  Chain_Control *scheduled = &self->Scheduled;
457
458  if ( !_Chain_Is_empty( scheduled ) ) {
459    lowest_ready = (Thread_Control *) _Chain_Last( scheduled );
460  }
461
462  return lowest_ready;
463}
464
465/**
466 * @brief Enqueues a thread according to the specified order function.
467 *
468 * @param[in] context The scheduler instance context.
469 * @param[in] thread The thread to enqueue.
470 * @param[in] order The order function.
471 * @param[in] get_highest_ready Function to get the highest ready node.
472 * @param[in] insert_ready Function to insert a node into the set of ready
473 * nodes.
474 * @param[in] insert_scheduled Function to insert a node into the set of
475 * scheduled nodes.
476 * @param[in] move_from_ready_to_scheduled Function to move a node from the set
477 * of ready nodes to the set of scheduled nodes.
478 * @param[in] move_from_scheduled_to_ready Function to move a node from the set
479 * of scheduled nodes to the set of ready nodes.
480 */
481static inline void _Scheduler_SMP_Enqueue_ordered(
482  Scheduler_Context *context,
483  Thread_Control *thread,
484  Chain_Node_order order,
485  Scheduler_SMP_Get_highest_ready get_highest_ready,
486  Scheduler_SMP_Insert insert_ready,
487  Scheduler_SMP_Insert insert_scheduled,
488  Scheduler_SMP_Move move_from_ready_to_scheduled,
489  Scheduler_SMP_Move move_from_scheduled_to_ready
490)
491{
492  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
493  Scheduler_SMP_Node *node = _Scheduler_SMP_Node_get( thread );
494
495  if ( node->state == SCHEDULER_SMP_NODE_IN_THE_AIR ) {
496    Thread_Control *highest_ready = ( *get_highest_ready )( &self->Base );
497
498    /*
499     * The thread has been extracted from the scheduled chain.  We have to
500     * place it now on the scheduled or ready chain.
501     *
502     * NOTE: Do not exchange parameters to do the negation of the order check.
503     */
504    if (
505      highest_ready != NULL
506        && !( *order )( &thread->Object.Node, &highest_ready->Object.Node )
507    ) {
508      _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
509      _Scheduler_SMP_Allocate_processor( self, highest_ready, thread );
510      ( *insert_ready )( &self->Base, thread );
511      ( *move_from_ready_to_scheduled )( &self->Base, highest_ready );
512    } else {
513      _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_SCHEDULED );
514      ( *insert_scheduled )( &self->Base, thread );
515    }
516  } else {
517    Thread_Control *lowest_scheduled =
518      _Scheduler_SMP_Get_lowest_scheduled( self );
519
520    /*
521     * The scheduled chain is empty if nested interrupts change the priority of
522     * all scheduled threads.  These threads are in the air.
523     */
524    if (
525      lowest_scheduled != NULL
526        && ( *order )( &thread->Object.Node, &lowest_scheduled->Object.Node )
527    ) {
528      Scheduler_SMP_Node *lowest_scheduled_node =
529        _Scheduler_SMP_Node_get( lowest_scheduled );
530
531      _Scheduler_SMP_Node_change_state(
532        lowest_scheduled_node,
533        SCHEDULER_SMP_NODE_READY
534      );
535      _Scheduler_SMP_Allocate_processor( self, thread, lowest_scheduled );
536      ( *insert_scheduled )( &self->Base, thread );
537      ( *move_from_scheduled_to_ready )( &self->Base, lowest_scheduled );
538    } else {
539      _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );
540      ( *insert_ready )( &self->Base, thread );
541    }
542  }
543}
544
545static inline void _Scheduler_SMP_Schedule_highest_ready(
546  Scheduler_Context *context,
547  Thread_Control *victim,
548  Scheduler_SMP_Get_highest_ready get_highest_ready,
549  Scheduler_SMP_Move move_from_ready_to_scheduled
550)
551{
552  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
553  Thread_Control *highest_ready = ( *get_highest_ready )( &self->Base );
554
555  _Scheduler_SMP_Allocate_processor( self, highest_ready, victim );
556
557  ( *move_from_ready_to_scheduled )( &self->Base, highest_ready );
558}
559
560/**
561 * @brief Finalize a scheduling operation.
562 *
563 * @param[in] context The scheduler instance context.
564 * @param[in] thread The thread of the scheduling operation.
565 * @param[in] get_highest_ready Function to get the highest ready node.
566 * @param[in] move_from_ready_to_scheduled Function to move a node from the set
567 * of ready nodes to the set of scheduled nodes.
568 */
569static inline void _Scheduler_SMP_Schedule(
570  Scheduler_Context *context,
571  Thread_Control *thread,
572  Scheduler_SMP_Get_highest_ready get_highest_ready,
573  Scheduler_SMP_Move move_from_ready_to_scheduled
574)
575{
576  Scheduler_SMP_Node *node = _Scheduler_SMP_Node_get( thread );
577
578  if ( node->state == SCHEDULER_SMP_NODE_IN_THE_AIR ) {
579    _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_BLOCKED );
580
581    _Scheduler_SMP_Schedule_highest_ready(
582      context,
583      thread,
584      get_highest_ready,
585      move_from_ready_to_scheduled
586    );
587  }
588}
589
590static inline void _Scheduler_SMP_Block(
591  Scheduler_Context *context,
592  Thread_Control *thread,
593  Scheduler_SMP_Extract extract,
594  Scheduler_SMP_Get_highest_ready get_highest_ready,
595  Scheduler_SMP_Move move_from_ready_to_scheduled
596)
597{
598  ( *extract )( context, thread );
599
600  _Scheduler_SMP_Schedule(
601    context,
602    thread,
603    get_highest_ready,
604    move_from_ready_to_scheduled
605  );
606}
607
608/**
609 * @brief Extracts a thread from the set of scheduled or ready nodes.
610 *
611 * @param[in] context The scheduler instance context.
612 * @param[in] thread The thread to extract.
613 * @param[in] extract Function to extract a node from the set of scheduled or
614 * ready nodes.
615 */
616static inline void _Scheduler_SMP_Extract(
617  Scheduler_Context *context,
618  Thread_Control *thread,
619  Scheduler_SMP_Extract extract
620)
621{
622  ( *extract )( context, thread );
623}
624
625static inline void _Scheduler_SMP_Insert_scheduled_lifo(
626  Scheduler_Context *context,
627  Thread_Control *thread
628)
629{
630  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
631
632  _Chain_Insert_ordered_unprotected(
633    &self->Scheduled,
634    &thread->Object.Node,
635    _Scheduler_simple_Insert_priority_lifo_order
636  );
637}
638
639static inline void _Scheduler_SMP_Insert_scheduled_fifo(
640  Scheduler_Context *context,
641  Thread_Control *thread
642)
643{
644  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
645
646  _Chain_Insert_ordered_unprotected(
647    &self->Scheduled,
648    &thread->Object.Node,
649    _Scheduler_simple_Insert_priority_fifo_order
650  );
651}
652
653static inline void _Scheduler_SMP_Start_idle(
654  Scheduler_Context *context,
655  Thread_Control *thread,
656  Per_CPU_Control *cpu
657)
658{
659  Scheduler_SMP_Context *self = _Scheduler_SMP_Get_self( context );
660  Scheduler_SMP_Node *node = _Scheduler_SMP_Node_get( thread );
661
662  node->state = SCHEDULER_SMP_NODE_SCHEDULED;
663
664  _Thread_Set_CPU( thread, cpu );
665  _Chain_Append_unprotected( &self->Scheduled, &thread->Object.Node );
666}
667
668/** @} */
669
670#ifdef __cplusplus
671}
672#endif /* __cplusplus */
673
674#endif /* _RTEMS_SCORE_SCHEDULERSMPIMPL_H */
Note: See TracBrowser for help on using the repository browser.