source: rtems-docs/c_user/symmetric_multiprocessing_services.rst @ 9aafb39

4.115
Last change on this file since 9aafb39 was 9aafb39, checked in by Chris Johns <chrisj@…>, on 10/28/16 at 12:56:02

c_user: Remove errors and warnings.

  • Property mode set to 100644
File size: 38.4 KB
Line 
1.. comment SPDX-License-Identifier: CC-BY-SA-4.0
2
3.. COMMENT: COPYRIGHT (c) 2011,2015
4.. COMMENT: Aeroflex Gaisler AB
5.. COMMENT: All rights reserved.
6
7Symmetric Multiprocessing Services
8##################################
9
10Introduction
11============
12
13The Symmetric Multiprocessing (SMP) support of the RTEMS 4.11.0 and later is available
14on
15
16- ARM,
17
18- PowerPC, and
19
20- SPARC.
21
22It must be explicitly enabled via the ``--enable-smp`` configure command line
23option.  To enable SMP in the application configuration see :ref:`Enable SMP
24Support for Applications`.  The default scheduler for SMP applications supports
25up to 32 processors and is a global fixed priority scheduler, see also
26:ref:`Configuring Clustered Schedulers`.  For example applications
27see:file:`testsuites/smptests`.
28
29.. warning::
30
31   The SMP support in the release of RTEMS is a work in progress. Before you
32   start using this RTEMS version for SMP ask on the RTEMS mailing list.
33
34This chapter describes the services related to Symmetric Multiprocessing
35provided by RTEMS.
36
37The application level services currently provided are:
38
39- rtems_get_processor_count_ - Get processor count
40
41- rtems_get_current_processor_ - Get current processor index
42
43- rtems_scheduler_ident_ - Get ID of a scheduler
44
45- rtems_scheduler_get_processor_set_ - Get processor set of a scheduler
46
47- rtems_task_get_scheduler_ - Get scheduler of a task
48
49- rtems_task_set_scheduler_ - Set scheduler of a task
50
51- rtems_task_get_affinity_ - Get task processor affinity
52
53- rtems_task_set_affinity_ - Set task processor affinity
54
55Background
56==========
57
58Uniprocessor versus SMP Parallelism
59-----------------------------------
60
61Uniprocessor systems have long been used in embedded systems. In this hardware
62model, there are some system execution characteristics which have long been
63taken for granted:
64
65- one task executes at a time
66
67- hardware events result in interrupts
68
69There is no true parallelism. Even when interrupts appear to occur at the same
70time, they are processed in largely a serial fashion.  This is true even when
71the interupt service routines are allowed to nest.  From a tasking viewpoint,
72it is the responsibility of the real-time operatimg system to simulate
73parallelism by switching between tasks.  These task switches occur in response
74to hardware interrupt events and explicit application events such as blocking
75for a resource or delaying.
76
77With symmetric multiprocessing, the presence of multiple processors allows for
78true concurrency and provides for cost-effective performance
79improvements. Uniprocessors tend to increase performance by increasing clock
80speed and complexity. This tends to lead to hot, power hungry microprocessors
81which are poorly suited for many embedded applications.
82
83The true concurrency is in sharp contrast to the single task and interrupt
84model of uniprocessor systems. This results in a fundamental change to
85uniprocessor system characteristics listed above. Developers are faced with a
86different set of characteristics which, in turn, break some existing
87assumptions and result in new challenges. In an SMP system with N processors,
88these are the new execution characteristics.
89
90- N tasks execute in parallel
91
92- hardware events result in interrupts
93
94There is true parallelism with a task executing on each processor and the
95possibility of interrupts occurring on each processor. Thus in contrast to
96their being one task and one interrupt to consider on a uniprocessor, there are
97N tasks and potentially N simultaneous interrupts to consider on an SMP system.
98
99This increase in hardware complexity and presence of true parallelism results
100in the application developer needing to be even more cautious about mutual
101exclusion and shared data access than in a uniprocessor embedded system. Race
102conditions that never or rarely happened when an application executed on a
103uniprocessor system, become much more likely due to multiple threads executing
104in parallel. On a uniprocessor system, these race conditions would only happen
105when a task switch occurred at just the wrong moment. Now there are N-1 tasks
106executing in parallel all the time and this results in many more opportunities
107for small windows in critical sections to be hit.
108
109Task Affinity
110-------------
111.. index:: task affinity
112.. index:: thread affinity
113
114RTEMS provides services to manipulate the affinity of a task. Affinity is used
115to specify the subset of processors in an SMP system on which a particular task
116can execute.
117
118By default, tasks have an affinity which allows them to execute on any
119available processor.
120
121Task affinity is a possible feature to be supported by SMP-aware
122schedulers. However, only a subset of the available schedulers support
123affinity. Although the behavior is scheduler specific, if the scheduler does
124not support affinity, it is likely to ignore all attempts to set affinity.
125
126The scheduler with support for arbitary processor affinities uses a proof of
127concept implementation.  See https://devel.rtems.org/ticket/2510.
128
129Task Migration
130--------------
131.. index:: task migration
132.. index:: thread migration
133
134With more than one processor in the system tasks can migrate from one processor
135to another.  There are three reasons why tasks migrate in RTEMS.
136
137- The scheduler changes explicitly via ``rtems_task_set_scheduler()`` or
138  similar directives.
139
140- The task resumes execution after a blocking operation.  On a priority based
141  scheduler it will evict the lowest priority task currently assigned to a
142  processor in the processor set managed by the scheduler instance.
143
144- The task moves temporarily to another scheduler instance due to locking
145  protocols like *Migratory Priority Inheritance* or the *Multiprocessor
146  Resource Sharing Protocol*.
147
148Task migration should be avoided so that the working set of a task can stay on
149the most local cache level.
150
151The current implementation of task migration in RTEMS has some implications
152with respect to the interrupt latency.  It is crucial to preserve the system
153invariant that a task can execute on at most one processor in the system at a
154time.  This is accomplished with a boolean indicator in the task context.  The
155processor architecture specific low-level task context switch code will mark
156that a task context is no longer executing and waits that the heir context
157stopped execution before it restores the heir context and resumes execution of
158the heir task.  So there is one point in time in which a processor is without a
159task.  This is essential to avoid cyclic dependencies in case multiple tasks
160migrate at once.  Otherwise some supervising entity is necessary to prevent
161life-locks.  Such a global supervisor would lead to scalability problems so
162this approach is not used.  Currently the thread dispatch is performed with
163interrupts disabled.  So in case the heir task is currently executing on
164another processor then this prolongs the time of disabled interrupts since one
165processor has to wait for another processor to make progress.
166
167It is difficult to avoid this issue with the interrupt latency since interrupts
168normally store the context of the interrupted task on its stack.  In case a
169task is marked as not executing we must not use its task stack to store such an
170interrupt context.  We cannot use the heir stack before it stopped execution on
171another processor.  So if we enable interrupts during this transition we have
172to provide an alternative task independent stack for this time frame.  This
173issue needs further investigation.
174
175Clustered Scheduling
176--------------------
177
178We have clustered scheduling in case the set of processors of a system is
179partitioned into non-empty pairwise-disjoint subsets. These subsets are called
180clusters.  Clusters with a cardinality of one are partitions.  Each cluster is
181owned by exactly one scheduler instance.
182
183Clustered scheduling helps to control the worst-case latencies in
184multi-processor systems, see *Brandenburg, Bjorn B.: Scheduling and Locking in
185Multiprocessor Real-Time Operating Systems. PhD thesis,
1862011.http://www.cs.unc.edu/~bbb/diss/brandenburg-diss.pdf*.  The goal is to
187reduce the amount of shared state in the system and thus prevention of lock
188contention. Modern multi-processor systems tend to have several layers of data
189and instruction caches.  With clustered scheduling it is possible to honour the
190cache topology of a system and thus avoid expensive cache synchronization
191traffic.  It is easy to implement.  The problem is to provide synchronization
192primitives for inter-cluster synchronization (more than one cluster is involved
193in the synchronization process). In RTEMS there are currently four means
194available
195
196- events,
197
198- message queues,
199
200- semaphores using the :ref:`Priority Inheritance` protocol (priority
201  boosting), and
202
203- semaphores using the :ref:`Multiprocessor Resource Sharing Protocol` (MrsP).
204
205The clustered scheduling approach enables separation of functions with
206real-time requirements and functions that profit from fairness and high
207throughput provided the scheduler instances are fully decoupled and adequate
208inter-cluster synchronization primitives are used.  This is work in progress.
209
210For the configuration of clustered schedulers see :ref:`Configuring Clustered
211Schedulers`.
212
213To set the scheduler of a task see :ref:`SCHEDULER_IDENT - Get ID of a
214scheduler` and :ref:`TASK_SET_SCHEDULER - Set scheduler of a task`.
215
216Task Priority Queues
217--------------------
218
219Due to the support for clustered scheduling the task priority queues need
220special attention.  It makes no sense to compare the priority values of two
221different scheduler instances.  Thus, it is not possible to simply use one
222plain priority queue for tasks of different scheduler instances.
223
224One solution to this problem is to use two levels of queues.  The top level
225queue provides FIFO ordering and contains priority queues.  Each priority queue
226is associated with a scheduler instance and contains only tasks of this
227scheduler instance.  Tasks are enqueued in the priority queue corresponding to
228their scheduler instance.  In case this priority queue was empty, then it is
229appended to the FIFO.  To dequeue a task the highest priority task of the first
230priority queue in the FIFO is selected.  Then the first priority queue is
231removed from the FIFO.  In case the previously first priority queue is not
232empty, then it is appended to the FIFO.  So there is FIFO fairness with respect
233to the highest priority task of each scheduler instances. See also
234*Brandenburg, Bjorn B.: A fully preemptive multiprocessor semaphore protocol
235for latency-sensitive real-time applications. In Proceedings of the 25th
236Euromicro Conference on Real-Time Systems (ECRTS 2013), pages 292-302,
2372013.http://www.mpi-sws.org/~bbb/papers/pdf/ecrts13b.pdf*.
238
239Such a two level queue may need a considerable amount of memory if fast enqueue
240and dequeue operations are desired (depends on the scheduler instance count).
241To mitigate this problem an approch of the FreeBSD kernel was implemented in
242RTEMS.  We have the invariant that a task can be enqueued on at most one task
243queue.  Thus, we need only as many queues as we have tasks.  Each task is
244equipped with spare task queue which it can give to an object on demand.  The
245task queue uses a dedicated memory space independent of the other memory used
246for the task itself. In case a task needs to block, then there are two options
247
248- the object already has task queue, then the task enqueues itself to this
249  already present queue and the spare task queue of the task is added to a list
250  of free queues for this object, or
251
252- otherwise, then the queue of the task is given to the object and the task
253  enqueues itself to this queue.
254
255In case the task is dequeued, then there are two options
256
257- the task is the last task on the queue, then it removes this queue from the
258  object and reclaims it for its own purpose, or
259
260- otherwise, then the task removes one queue from the free list of the object
261  and reclaims it for its own purpose.
262
263Since there are usually more objects than tasks, this actually reduces the
264memory demands. In addition the objects contain only a pointer to the task
265queue structure. This helps to hide implementation details and makes it
266possible to use self-contained synchronization objects in Newlib and GCC (C++
267and OpenMP run-time support).
268
269Scheduler Helping Protocol
270--------------------------
271
272The scheduler provides a helping protocol to support locking protocols like
273*Migratory Priority Inheritance* or the *Multiprocessor Resource Sharing
274Protocol*.  Each ready task can use at least one scheduler node at a time to
275gain access to a processor.  Each scheduler node has an owner, a user and an
276optional idle task.  The owner of a scheduler node is determined a task
277creation and never changes during the life time of a scheduler node.  The user
278of a scheduler node may change due to the scheduler helping protocol.  A
279scheduler node is in one of the four scheduler help states:
280
281:dfn:`help yourself`
282    This scheduler node is solely used by the owner task.  This task owns no
283    resources using a helping protocol and thus does not take part in the
284    scheduler helping protocol.  No help will be provided for other tasks.
285
286:dfn:`help active owner`
287    This scheduler node is owned by a task actively owning a resource and can
288    be used to help out tasks.  In case this scheduler node changes its state
289    from ready to scheduled and the task executes using another node, then an
290    idle task will be provided as a user of this node to temporarily execute on
291    behalf of the owner task.  Thus lower priority tasks are denied access to
292    the processors of this scheduler instance.  In case a task actively owning
293    a resource performs a blocking operation, then an idle task will be used
294    also in case this node is in the scheduled state.
295
296:dfn:`help active rival`
297    This scheduler node is owned by a task actively obtaining a resource
298    currently owned by another task and can be used to help out tasks.  The
299    task owning this node is ready and will give away its processor in case the
300    task owning the resource asks for help.
301
302:dfn:`help passive`
303    This scheduler node is owned by a task obtaining a resource currently owned
304    by another task and can be used to help out tasks.  The task owning this
305    node is blocked.
306
307The following scheduler operations return a task in need for help
308
309- unblock,
310
311- change priority,
312
313- yield, and
314
315- ask for help.
316
317A task in need for help is a task that encounters a scheduler state change from
318scheduled to ready (this is a pre-emption by a higher priority task) or a task
319that cannot be scheduled in an unblock operation.  Such a task can ask tasks
320which depend on resources owned by this task for help.
321
322In case it is not possible to schedule a task in need for help, then the
323scheduler nodes available for the task will be placed into the set of ready
324scheduler nodes of the corresponding scheduler instances.  Once a state change
325from ready to scheduled happens for one of scheduler nodes it will be used to
326schedule the task in need for help.
327
328The ask for help scheduler operation is used to help tasks in need for help
329returned by the operations mentioned above.  This operation is also used in
330case the root of a resource sub-tree owned by a task changes.
331
332The run-time of the ask for help procedures depend on the size of the resource
333tree of the task needing help and other resource trees in case tasks in need
334for help are produced during this operation.  Thus the worst-case latency in
335the system depends on the maximum resource tree size of the application.
336
337Critical Section Techniques and SMP
338-----------------------------------
339
340As discussed earlier, SMP systems have opportunities for true parallelism which
341was not possible on uniprocessor systems. Consequently, multiple techniques
342that provided adequate critical sections on uniprocessor systems are unsafe on
343SMP systems. In this section, some of these unsafe techniques will be
344discussed.
345
346In general, applications must use proper operating system provided mutual
347exclusion mechanisms to ensure correct behavior. This primarily means the use
348of binary semaphores or mutexes to implement critical sections.
349
350Disable Interrupts and Interrupt Locks
351~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
352
353A low overhead means to ensure mutual exclusion in uni-processor configurations
354is to disable interrupts around a critical section.  This is commonly used in
355device driver code and throughout the operating system core.  On SMP
356configurations, however, disabling the interrupts on one processor has no
357effect on other processors.  So, this is insufficient to ensure system wide
358mutual exclusion.  The macros
359
360- ``rtems_interrupt_disable()``,
361
362- ``rtems_interrupt_enable()``, and
363
364- ``rtems_interrupt_flush()``
365
366are disabled on SMP configurations and its use will lead to compiler warnings
367and linker errors.  In the unlikely case that interrupts must be disabled on
368the current processor, then the
369
370- ``rtems_interrupt_local_disable()``, and
371
372- ``rtems_interrupt_local_enable()``
373
374macros are now available in all configurations.
375
376Since disabling of interrupts is not enough to ensure system wide mutual
377exclusion on SMP, a new low-level synchronization primitive was added - the
378interrupt locks.  They are a simple API layer on top of the SMP locks used for
379low-level synchronization in the operating system core.  Currently they are
380implemented as a ticket lock.  On uni-processor configurations they degenerate
381to simple interrupt disable/enable sequences.  It is disallowed to acquire a
382single interrupt lock in a nested way.  This will result in an infinite loop
383with interrupts disabled.  While converting legacy code to interrupt locks care
384must be taken to avoid this situation.
385
386.. code-block:: c
387    :linenos:
388
389    void legacy_code_with_interrupt_disable_enable( void )
390    {
391        rtems_interrupt_level level;
392        rtems_interrupt_disable( level );
393        /* Some critical stuff */
394        rtems_interrupt_enable( level );
395    }
396
397    RTEMS_INTERRUPT_LOCK_DEFINE( static, lock, "Name" );
398
399    void smp_ready_code_with_interrupt_lock( void )
400    {
401        rtems_interrupt_lock_context lock_context;
402        rtems_interrupt_lock_acquire( &lock, &lock_context );
403        /* Some critical stuff */
404        rtems_interrupt_lock_release( &lock, &lock_context );
405    }
406
407The ``rtems_interrupt_lock`` structure is empty on uni-processor
408configurations.  Empty structures have a different size in C
409(implementation-defined, zero in case of GCC) and C++ (implementation-defined
410non-zero value, one in case of GCC).  Thus the
411``RTEMS_INTERRUPT_LOCK_DECLARE()``, ``RTEMS_INTERRUPT_LOCK_DEFINE()``,
412``RTEMS_INTERRUPT_LOCK_MEMBER()``, and ``RTEMS_INTERRUPT_LOCK_REFERENCE()``
413macros are provided to ensure ABI compatibility.
414
415Highest Priority Task Assumption
416~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
417
418On a uniprocessor system, it is safe to assume that when the highest priority
419task in an application executes, it will execute without being preempted until
420it voluntarily blocks. Interrupts may occur while it is executing, but there
421will be no context switch to another task unless the highest priority task
422voluntarily initiates it.
423
424Given the assumption that no other tasks will have their execution interleaved
425with the highest priority task, it is possible for this task to be constructed
426such that it does not need to acquire a binary semaphore or mutex for protected
427access to shared data.
428
429In an SMP system, it cannot be assumed there will never be a single task
430executing. It should be assumed that every processor is executing another
431application task. Further, those tasks will be ones which would not have been
432executed in a uniprocessor configuration and should be assumed to have data
433synchronization conflicts with what was formerly the highest priority task
434which executed without conflict.
435
436Disable Preemption
437~~~~~~~~~~~~~~~~~~
438
439On a uniprocessor system, disabling preemption in a task is very similar to
440making the highest priority task assumption. While preemption is disabled, no
441task context switches will occur unless the task initiates them
442voluntarily. And, just as with the highest priority task assumption, there are
443N-1 processors also running tasks. Thus the assumption that no other tasks will
444run while the task has preemption disabled is violated.
445
446Task Unique Data and SMP
447------------------------
448
449Per task variables are a service commonly provided by real-time operating
450systems for application use. They work by allowing the application to specify a
451location in memory (typically a ``void *``) which is logically added to the
452context of a task. On each task switch, the location in memory is stored and
453each task can have a unique value in the same memory location. This memory
454location is directly accessed as a variable in a program.
455
456This works well in a uniprocessor environment because there is one task
457executing and one memory location containing a task-specific value. But it is
458fundamentally broken on an SMP system because there are always N tasks
459executing. With only one location in memory, N-1 tasks will not have the
460correct value.
461
462This paradigm for providing task unique data values is fundamentally broken on
463SMP systems.
464
465Classic API Per Task Variables
466~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
467
468The Classic API provides three directives to support per task variables. These are:
469
470- ``rtems_task_variable_add`` - Associate per task variable
471
472- ``rtems_task_variable_get`` - Obtain value of a a per task variable
473
474- ``rtems_task_variable_delete`` - Remove per task variable
475
476As task variables are unsafe for use on SMP systems, the use of these services
477must be eliminated in all software that is to be used in an SMP environment.
478The task variables API is disabled on SMP. Its use will lead to compile-time
479and link-time errors. It is recommended that the application developer consider
480the use of POSIX Keys or Thread Local Storage (TLS). POSIX Keys are available
481in all RTEMS configurations.  For the availablity of TLS on a particular
482architecture please consult the *RTEMS CPU Architecture Supplement*.
483
484The only remaining user of task variables in the RTEMS code base is the Ada
485support.  So basically Ada is not available on RTEMS SMP.
486
487OpenMP
488------
489
490OpenMP support for RTEMS is available via the GCC provided libgomp.  There is
491libgomp support for RTEMS in the POSIX configuration of libgomp since GCC 4.9
492(requires a Newlib snapshot after 2015-03-12). In GCC 6.1 or later (requires a
493Newlib snapshot after 2015-07-30 for <sys/lock.h> provided self-contained
494synchronization objects) there is a specialized libgomp configuration for RTEMS
495which offers a significantly better performance compared to the POSIX
496configuration of libgomp.  In addition application configurable thread pools
497for each scheduler instance are available in GCC 6.1 or later.
498
499The run-time configuration of libgomp is done via environment variables
500documented in the `libgomp manual <https://gcc.gnu.org/onlinedocs/libgomp/>`_.
501The environment variables are evaluated in a constructor function which
502executes in the context of the first initialization task before the actual
503initialization task function is called (just like a global C++ constructor).
504To set application specific values, a higher priority constructor function must
505be used to set up the environment variables.
506
507.. code-block:: c
508
509    #include <stdlib.h>
510    void __attribute__((constructor(1000))) config_libgomp( void )
511    {
512        setenv( "OMP_DISPLAY_ENV", "VERBOSE", 1 );
513        setenv( "GOMP_SPINCOUNT", "30000", 1 );
514        setenv( "GOMP_RTEMS_THREAD_POOLS", "1$2@SCHD", 1 );
515    }
516
517The environment variable ``GOMP_RTEMS_THREAD_POOLS`` is RTEMS-specific.  It
518determines the thread pools for each scheduler instance.  The format for
519``GOMP_RTEMS_THREAD_POOLS`` is a list of optional
520``<thread-pool-count>[$<priority>]@<scheduler-name>`` configurations separated
521by ``:`` where:
522
523- ``<thread-pool-count>`` is the thread pool count for this scheduler instance.
524
525- ``$<priority>`` is an optional priority for the worker threads of a thread
526  pool according to ``pthread_setschedparam``.  In case a priority value is
527  omitted, then a worker thread will inherit the priority of the OpenMP master
528  thread that created it.  The priority of the worker thread is not changed by
529  libgomp after creation, even if a new OpenMP master thread using the worker
530  has a different priority.
531
532- ``@<scheduler-name>`` is the scheduler instance name according to the RTEMS
533  application configuration.
534
535In case no thread pool configuration is specified for a scheduler instance,
536then each OpenMP master thread of this scheduler instance will use its own
537dynamically allocated thread pool.  To limit the worker thread count of the
538thread pools, each OpenMP master thread must call ``omp_set_num_threads``.
539
540Lets suppose we have three scheduler instances ``IO``, ``WRK0``, and ``WRK1``
541with ``GOMP_RTEMS_THREAD_POOLS`` set to ``"1@WRK0:3$4@WRK1"``.  Then there are
542no thread pool restrictions for scheduler instance ``IO``.  In the scheduler
543instance ``WRK0`` there is one thread pool available.  Since no priority is
544specified for this scheduler instance, the worker thread inherits the priority
545of the OpenMP master thread that created it.  In the scheduler instance
546``WRK1`` there are three thread pools available and their worker threads run at
547priority four.
548
549Thread Dispatch Details
550-----------------------
551
552This section gives background information to developers interested in the
553interrupt latencies introduced by thread dispatching.  A thread dispatch
554consists of all work which must be done to stop the currently executing thread
555on a processor and hand over this processor to an heir thread.
556
557On SMP systems, scheduling decisions on one processor must be propagated to
558other processors through inter-processor interrupts.  So, a thread dispatch
559which must be carried out on another processor happens not instantaneous.  Thus
560several thread dispatch requests might be in the air and it is possible that
561some of them may be out of date before the corresponding processor has time to
562deal with them.  The thread dispatch mechanism uses three per-processor
563variables,
564
565- the executing thread,
566
567- the heir thread, and
568
569- an boolean flag indicating if a thread dispatch is necessary or not.
570
571Updates of the heir thread and the thread dispatch necessary indicator are
572synchronized via explicit memory barriers without the use of locks.  A thread
573can be an heir thread on at most one processor in the system.  The thread
574context is protected by a TTAS lock embedded in the context to ensure that it
575is used on at most one processor at a time.  The thread post-switch actions use
576a per-processor lock.  This implementation turned out to be quite efficient and
577no lock contention was observed in the test suite.
578
579The current implementation of thread dispatching has some implications with
580respect to the interrupt latency.  It is crucial to preserve the system
581invariant that a thread can execute on at most one processor in the system at a
582time.  This is accomplished with a boolean indicator in the thread context.
583The processor architecture specific context switch code will mark that a thread
584context is no longer executing and waits that the heir context stopped
585execution before it restores the heir context and resumes execution of the heir
586thread (the boolean indicator is basically a TTAS lock).  So, there is one
587point in time in which a processor is without a thread.  This is essential to
588avoid cyclic dependencies in case multiple threads migrate at once.  Otherwise
589some supervising entity is necessary to prevent deadlocks.  Such a global
590supervisor would lead to scalability problems so this approach is not used.
591Currently the context switch is performed with interrupts disabled.  Thus in
592case the heir thread is currently executing on another processor, the time of
593disabled interrupts is prolonged since one processor has to wait for another
594processor to make progress.
595
596It is difficult to avoid this issue with the interrupt latency since interrupts
597normally store the context of the interrupted thread on its stack.  In case a
598thread is marked as not executing, we must not use its thread stack to store
599such an interrupt context.  We cannot use the heir stack before it stopped
600execution on another processor.  If we enable interrupts during this
601transition, then we have to provide an alternative thread independent stack for
602interrupts in this time frame.  This issue needs further investigation.
603
604The problematic situation occurs in case we have a thread which executes with
605thread dispatching disabled and should execute on another processor (e.g. it is
606an heir thread on another processor).  In this case the interrupts on this
607other processor are disabled until the thread enables thread dispatching and
608starts the thread dispatch sequence.  The scheduler (an exception is the
609scheduler with thread processor affinity support) tries to avoid such a
610situation and checks if a new scheduled thread already executes on a processor.
611In case the assigned processor differs from the processor on which the thread
612already executes and this processor is a member of the processor set managed by
613this scheduler instance, it will reassign the processors to keep the already
614executing thread in place.  Therefore normal scheduler requests will not lead
615to such a situation.  Explicit thread migration requests, however, can lead to
616this situation.  Explicit thread migrations may occur due to the scheduler
617helping protocol or explicit scheduler instance changes.  The situation can
618also be provoked by interrupts which suspend and resume threads multiple times
619and produce stale asynchronous thread dispatch requests in the system.
620
621Operations
622==========
623
624Setting Affinity to a Single Processor
625--------------------------------------
626
627On some embedded applications targeting SMP systems, it may be beneficial to
628lock individual tasks to specific processors.  In this way, one can designate a
629processor for I/O tasks, another for computation, etc..  The following
630illustrates the code sequence necessary to assign a task an affinity for
631processor with index ``processor_index``.
632
633.. code-block:: c
634
635    #include <rtems.h>
636    #include <assert.h>
637
638    void pin_to_processor(rtems_id task_id, int processor_index)
639    {
640        rtems_status_code sc;
641        cpu_set_t         cpuset;
642        CPU_ZERO(&cpuset);
643        CPU_SET(processor_index, &cpuset);
644        sc = rtems_task_set_affinity(task_id, sizeof(cpuset), &cpuset);
645        assert(sc == RTEMS_SUCCESSFUL);
646    }
647
648It is important to note that the ``cpuset`` is not validated until the
649``rtems_task_set_affinity`` call is made. At that point, it is validated
650against the current system configuration.
651
652Directives
653==========
654
655This section details the symmetric multiprocessing services.  A subsection is
656dedicated to each of these services and describes the calling sequence, related
657constants, usage, and status codes.
658
659.. _rtems_get_processor_count:
660
661GET_PROCESSOR_COUNT - Get processor count
662-----------------------------------------
663
664**CALLING SEQUENCE:**
665
666.. code-block:: c
667
668    uint32_t rtems_get_processor_count(void);
669
670**DIRECTIVE STATUS CODES:**
671
672The count of processors in the system.
673
674**DESCRIPTION:**
675
676On uni-processor configurations a value of one will be returned.
677
678On SMP configurations this returns the value of a global variable set during
679system initialization to indicate the count of utilized processors.  The
680processor count depends on the physically or virtually available processors and
681application configuration.  The value will always be less than or equal to the
682maximum count of application configured processors.
683
684**NOTES:**
685
686None.
687
688.. _rtems_get_current_processor:
689
690GET_CURRENT_PROCESSOR - Get current processor index
691---------------------------------------------------
692
693**CALLING SEQUENCE:**
694
695.. code-block:: c
696
697    uint32_t rtems_get_current_processor(void);
698
699**DIRECTIVE STATUS CODES:**
700
701The index of the current processor.
702
703**DESCRIPTION:**
704
705On uni-processor configurations a value of zero will be returned.
706
707On SMP configurations an architecture specific method is used to obtain the
708index of the current processor in the system.  The set of processor indices is
709the range of integers starting with zero up to the processor count minus one.
710
711Outside of sections with disabled thread dispatching the current processor
712index may change after every instruction since the thread may migrate from one
713processor to another.  Sections with disabled interrupts are sections with
714thread dispatching disabled.
715
716**NOTES:**
717
718None.
719
720.. _rtems_scheduler_ident:
721.. _SCHEDULER_IDENT - Get ID of a scheduler:
722
723SCHEDULER_IDENT - Get ID of a scheduler
724---------------------------------------
725
726**CALLING SEQUENCE:**
727
728.. code-block:: c
729
730    rtems_status_code rtems_scheduler_ident(
731        rtems_name  name,
732        rtems_id   *id
733    );
734
735**DIRECTIVE STATUS CODES:**
736
737.. list-table::
738 :class: rtems-table
739
740 * - ``RTEMS_SUCCESSFUL``
741   - successful operation
742 * - ``RTEMS_INVALID_ADDRESS``
743   - ``id`` is NULL
744 * - ``RTEMS_INVALID_NAME``
745   - invalid scheduler name
746 * - ``RTEMS_UNSATISFIED``
747   - a scheduler with this name exists, but the processor set of this scheduler
748     is empty
749
750**DESCRIPTION:**
751
752Identifies a scheduler by its name.  The scheduler name is determined by the
753scheduler configuration.  See :ref:`Configuring a System`.
754
755**NOTES:**
756
757None.
758
759.. _rtems_scheduler_get_processor_set:
760
761SCHEDULER_GET_PROCESSOR_SET - Get processor set of a scheduler
762--------------------------------------------------------------
763
764**CALLING SEQUENCE:**
765
766.. code-block:: c
767
768    rtems_status_code rtems_scheduler_get_processor_set(
769        rtems_id   scheduler_id,
770        size_t     cpusetsize,
771        cpu_set_t *cpuset
772    );
773
774**DIRECTIVE STATUS CODES:**
775
776.. list-table::
777 :class: rtems-table
778
779 * - ``RTEMS_SUCCESSFUL``
780   - successful operation
781 * - ``RTEMS_INVALID_ADDRESS``
782   - ``cpuset`` is NULL
783 * - ``RTEMS_INVALID_ID``
784   - invalid scheduler id
785 * - ``RTEMS_INVALID_NUMBER``
786   - the affinity set buffer is too small for set of processors owned by the
787     scheduler
788
789**DESCRIPTION:**
790
791Returns the processor set owned by the scheduler in ``cpuset``.  A set bit in
792the processor set means that this processor is owned by the scheduler and a
793cleared bit means the opposite.
794
795**NOTES:**
796
797None.
798
799.. _rtems_task_get_scheduler:
800
801TASK_GET_SCHEDULER - Get scheduler of a task
802--------------------------------------------
803
804**CALLING SEQUENCE:**
805
806.. code-block:: c
807
808    rtems_status_code rtems_task_get_scheduler(
809        rtems_id  task_id,
810        rtems_id *scheduler_id
811    );
812
813**DIRECTIVE STATUS CODES:**
814
815.. list-table::
816 :class: rtems-table
817
818 * - ``RTEMS_SUCCESSFUL``
819   - successful operation
820 * - ``RTEMS_INVALID_ADDRESS``
821   - ``scheduler_id`` is NULL
822 * - ``RTEMS_INVALID_ID``
823   - invalid task id
824
825**DESCRIPTION:**
826
827Returns the scheduler identifier of a task identified by ``task_id`` in
828``scheduler_id``.
829
830**NOTES:**
831
832None.
833
834.. _rtems_task_set_scheduler:
835.. _TASK_SET_SCHEDULER - Set scheduler of a task:
836
837TASK_SET_SCHEDULER - Set scheduler of a task
838--------------------------------------------
839
840**CALLING SEQUENCE:**
841
842.. code-block:: c
843
844    rtems_status_code rtems_task_set_scheduler(
845        rtems_id task_id,
846        rtems_id scheduler_id
847    );
848
849**DIRECTIVE STATUS CODES:**
850
851.. list-table::
852 :class: rtems-table
853
854 * - ``RTEMS_SUCCESSFUL``
855   - successful operation
856 * - ``RTEMS_INVALID_ID``
857   - invalid task or scheduler id
858 * - ``RTEMS_INCORRECT_STATE``
859   - the task is in the wrong state to perform a scheduler change
860
861**DESCRIPTION:**
862
863Sets the scheduler of a task identified by ``task_id`` to the scheduler
864identified by ``scheduler_id``.  The scheduler of a task is initialized to the
865scheduler of the task that created it.
866
867**NOTES:**
868
869None.
870
871**EXAMPLE:**
872
873.. code-block:: c
874    :linenos:
875
876    #include <rtems.h>
877    #include <assert.h>
878
879    void task(rtems_task_argument arg);
880
881    void example(void)
882    {
883        rtems_status_code sc;
884        rtems_id          task_id;
885        rtems_id          scheduler_id;
886        rtems_name        scheduler_name;
887
888        scheduler_name = rtems_build_name('W', 'O', 'R', 'K');
889
890        sc = rtems_scheduler_ident(scheduler_name, &scheduler_id);
891        assert(sc == RTEMS_SUCCESSFUL);
892
893        sc = rtems_task_create(
894                rtems_build_name('T', 'A', 'S', 'K'),
895                1,
896                RTEMS_MINIMUM_STACK_SIZE,
897                RTEMS_DEFAULT_MODES,
898                RTEMS_DEFAULT_ATTRIBUTES,
899                &task_id
900             );
901        assert(sc == RTEMS_SUCCESSFUL);
902
903        sc = rtems_task_set_scheduler(task_id, scheduler_id);
904        assert(sc == RTEMS_SUCCESSFUL);
905
906        sc = rtems_task_start(task_id, task, 0);
907        assert(sc == RTEMS_SUCCESSFUL);
908    }
909
910.. _rtems_task_get_affinity:
911
912TASK_GET_AFFINITY - Get task processor affinity
913-----------------------------------------------
914
915**CALLING SEQUENCE:**
916
917.. code-block:: c
918
919    rtems_status_code rtems_task_get_affinity(
920        rtems_id   id,
921        size_t     cpusetsize,
922        cpu_set_t *cpuset
923    );
924
925**DIRECTIVE STATUS CODES:**
926
927.. list-table::
928 :class: rtems-table
929
930 * - ``RTEMS_SUCCESSFUL``
931   - successful operation
932 * - ``RTEMS_INVALID_ADDRESS``
933   - ``cpuset`` is NULL
934 * - ``RTEMS_INVALID_ID``
935   - invalid task id
936 * - ``RTEMS_INVALID_NUMBER``
937   - the affinity set buffer is too small for the current processor affinity
938     set of the task
939
940**DESCRIPTION:**
941
942Returns the current processor affinity set of the task in ``cpuset``.  A set
943bit in the affinity set means that the task can execute on this processor and a
944cleared bit means the opposite.
945
946**NOTES:**
947
948None.
949
950.. _rtems_task_set_affinity:
951
952TASK_SET_AFFINITY - Set task processor affinity
953-----------------------------------------------
954
955**CALLING SEQUENCE:**
956
957.. code-block:: c
958
959    rtems_status_code rtems_task_set_affinity(
960        rtems_id         id,
961        size_t           cpusetsize,
962        const cpu_set_t *cpuset
963    );
964
965**DIRECTIVE STATUS CODES:**
966
967.. list-table::
968 :class: rtems-table
969
970 * - ``RTEMS_SUCCESSFUL``
971   - successful operation
972 * - ``RTEMS_INVALID_ADDRESS``
973   - ``cpuset`` is NULL
974 * - ``RTEMS_INVALID_ID``
975   - invalid task id
976 * - ``RTEMS_INVALID_NUMBER``
977   - invalid processor affinity set
978
979**DESCRIPTION:**
980
981Sets the processor affinity set for the task specified by ``cpuset``.  A set
982bit in the affinity set means that the task can execute on this processor and a
983cleared bit means the opposite.
984
985**NOTES:**
986
987This function will not change the scheduler of the task.  The intersection of
988the processor affinity set and the set of processors owned by the scheduler of
989the task must be non-empty.  It is not an error if the processor affinity set
990contains processors that are not part of the set of processors owned by the
991scheduler instance of the task.  A task will simply not run under normal
992circumstances on these processors since the scheduler ignores them.  Some
993locking protocols may temporarily use processors that are not included in the
994processor affinity set of the task.  It is also not an error if the processor
995affinity set contains processors that are not part of the system.
Note: See TracBrowser for help on using the repository browser.