source: rtems-docs/c-user/symmetric_multiprocessing_services.rst @ 969e60e

5
Last change on this file since 969e60e was 7c58036, checked in by Sebastian Huber <sebastian.huber@…>, on 09/07/18 at 13:47:43

c-user: Mention thread pinning

Close #3508.

  • Property mode set to 100644
File size: 35.6 KB
Line 
1.. comment SPDX-License-Identifier: CC-BY-SA-4.0
2
3.. COMMENT: COPYRIGHT (c) 2014.
4.. COMMENT: On-Line Applications Research Corporation (OAR).
5.. COMMENT: Copyright (c) 2017 embedded brains GmbH.
6.. COMMENT: All rights reserved.
7
8.. index:: Symmetric Multiprocessing
9.. index:: SMP
10
11Symmetric Multiprocessing (SMP)
12*******************************
13
14Introduction
15============
16
17The Symmetric Multiprocessing (SMP) support of the RTEMS is available on
18
19- ARMv7-A,
20
21- PowerPC,
22
23- RISC-V, and
24
25- SPARC.
26
27.. warning::
28
29   The SMP support must be explicitly enabled via the ``--enable-smp``
30   configure command line option for the :term:`BSP` build.
31
32RTEMS is supposed to be a real-time operating system.  What does this mean in
33the context of SMP?  The RTEMS interpretation of real-time on SMP is the
34support for :ref:`ClusteredScheduling` with priority based schedulers and
35adequate locking protocols.  One aim is to enable a schedulability analysis
36under the sporadic task model :cite:`Brandenburg:2011:SL`
37:cite:`Burns:2013:MrsP`.
38
39The directives provided by the SMP support are:
40
41- rtems_get_processor_count_ - Get processor count
42
43- rtems_get_current_processor_ - Get current processor index
44
45Background
46==========
47
48Application Configuration
49-------------------------
50
51By default, the maximum processor count is set to one in the application
52configuration.  To enable SMP, the application configuration option
53:ref:`CONFIGURE_MAXIMUM_PROCESSORS <CONFIGURE_MAXIMUM_PROCESSORS>` must be
54defined to a value greater than one.  It is recommended to use the smallest
55value suitable for the application in order to save memory.  Each processor
56needs an idle thread and interrupt stack for example.
57
58The default scheduler for SMP applications supports up to 32 processors and is
59a global fixed priority scheduler, see also :ref:`ConfigurationSchedulersClustered`.
60
61The following compile-time test can be used to check if the SMP support is
62available or not.
63
64.. code-block:: c
65
66    #include <rtems.h>
67
68    #ifdef RTEMS_SMP
69    #warning "SMP support is enabled"
70    #else
71    #warning "SMP support is disabled"
72    #endif
73
74Examples
75--------
76
77For example applications see `testsuites/smptests
78<https://git.rtems.org/rtems/tree/testsuites/smptests>`_.
79
80Uniprocessor versus SMP Parallelism
81-----------------------------------
82
83Uniprocessor systems have long been used in embedded systems. In this hardware
84model, there are some system execution characteristics which have long been
85taken for granted:
86
87- one task executes at a time
88
89- hardware events result in interrupts
90
91There is no true parallelism. Even when interrupts appear to occur at the same
92time, they are processed in largely a serial fashion.  This is true even when
93the interupt service routines are allowed to nest.  From a tasking viewpoint,
94it is the responsibility of the real-time operatimg system to simulate
95parallelism by switching between tasks.  These task switches occur in response
96to hardware interrupt events and explicit application events such as blocking
97for a resource or delaying.
98
99With symmetric multiprocessing, the presence of multiple processors allows for
100true concurrency and provides for cost-effective performance
101improvements. Uniprocessors tend to increase performance by increasing clock
102speed and complexity. This tends to lead to hot, power hungry microprocessors
103which are poorly suited for many embedded applications.
104
105The true concurrency is in sharp contrast to the single task and interrupt
106model of uniprocessor systems. This results in a fundamental change to
107uniprocessor system characteristics listed above. Developers are faced with a
108different set of characteristics which, in turn, break some existing
109assumptions and result in new challenges. In an SMP system with N processors,
110these are the new execution characteristics.
111
112- N tasks execute in parallel
113
114- hardware events result in interrupts
115
116There is true parallelism with a task executing on each processor and the
117possibility of interrupts occurring on each processor. Thus in contrast to
118their being one task and one interrupt to consider on a uniprocessor, there are
119N tasks and potentially N simultaneous interrupts to consider on an SMP system.
120
121This increase in hardware complexity and presence of true parallelism results
122in the application developer needing to be even more cautious about mutual
123exclusion and shared data access than in a uniprocessor embedded system. Race
124conditions that never or rarely happened when an application executed on a
125uniprocessor system, become much more likely due to multiple threads executing
126in parallel. On a uniprocessor system, these race conditions would only happen
127when a task switch occurred at just the wrong moment. Now there are N-1 tasks
128executing in parallel all the time and this results in many more opportunities
129for small windows in critical sections to be hit.
130
131.. index:: task affinity
132.. index:: thread affinity
133
134Task Affinity
135-------------
136
137RTEMS provides services to manipulate the affinity of a task. Affinity is used
138to specify the subset of processors in an SMP system on which a particular task
139can execute.
140
141By default, tasks have an affinity which allows them to execute on any
142available processor.
143
144Task affinity is a possible feature to be supported by SMP-aware
145schedulers. However, only a subset of the available schedulers support
146affinity. Although the behavior is scheduler specific, if the scheduler does
147not support affinity, it is likely to ignore all attempts to set affinity.
148
149The scheduler with support for arbitary processor affinities uses a proof of
150concept implementation.  See https://devel.rtems.org/ticket/2510.
151
152.. index:: task migration
153.. index:: thread migration
154
155Task Migration
156--------------
157
158With more than one processor in the system tasks can migrate from one processor
159to another.  There are four reasons why tasks migrate in RTEMS.
160
161- The scheduler changes explicitly via
162  :ref:`rtems_task_set_scheduler() <rtems_task_set_scheduler>` or similar
163  directives.
164
165- The task processor affinity changes explicitly via
166  :ref:`rtems_task_set_affinity() <rtems_task_set_affinity>` or similar
167  directives.
168
169- The task resumes execution after a blocking operation.  On a priority based
170  scheduler it will evict the lowest priority task currently assigned to a
171  processor in the processor set managed by the scheduler instance.
172
173- The task moves temporarily to another scheduler instance due to locking
174  protocols like the :ref:`MrsP` or the :ref:`OMIP`.
175
176Task migration should be avoided so that the working set of a task can stay on
177the most local cache level.
178
179.. _ClusteredScheduling:
180
181Clustered Scheduling
182--------------------
183
184The scheduler is responsible to assign processors to some of the threads which
185are ready to execute.  Trouble starts if more ready threads than processors
186exist at the same time.  There are various rules how the processor assignment
187can be performed attempting to fulfill additional constraints or yield some
188overall system properties.  As a matter of fact it is impossible to meet all
189requirements at the same time.  The way a scheduler works distinguishes
190real-time operating systems from general purpose operating systems.
191
192We have clustered scheduling in case the set of processors of a system is
193partitioned into non-empty pairwise-disjoint subsets of processors.  These
194subsets are called clusters.  Clusters with a cardinality of one are
195partitions.  Each cluster is owned by exactly one scheduler instance.  In case
196the cluster size equals the processor count, it is called global scheduling.
197
198Modern SMP systems have multi-layer caches.  An operating system which neglects
199cache constraints in the scheduler will not yield good performance.  Real-time
200operating systems usually provide priority (fixed or job-level) based
201schedulers so that each of the highest priority threads is assigned to a
202processor.  Priority based schedulers have difficulties in providing cache
203locality for threads and may suffer from excessive thread migrations
204:cite:`Brandenburg:2011:SL` :cite:`Compagnin:2014:RUN`.  Schedulers that use local run
205queues and some sort of load-balancing to improve the cache utilization may not
206fulfill global constraints :cite:`Gujarati:2013:LPP` and are more difficult to
207implement than one would normally expect :cite:`Lozi:2016:LSDWC`.
208
209Clustered scheduling was implemented for RTEMS SMP to best use the cache
210topology of a system and to keep the worst-case latencies under control.  The
211low-level SMP locks use FIFO ordering.  So, the worst-case run-time of
212operations increases with each processor involved.  The scheduler configuration
213is quite flexible and done at link-time, see
214:ref:`ConfigurationSchedulersClustered`.  It is possible to re-assign
215processors to schedulers during run-time via
216:ref:`rtems_scheduler_add_processor() <rtems_scheduler_add_processor>` and
217:ref:`rtems_scheduler_remove_processor() <rtems_scheduler_remove_processor>`.
218The schedulers are implemented in an object-oriented fashion.
219
220The problem is to provide synchronization
221primitives for inter-cluster synchronization (more than one cluster is involved
222in the synchronization process). In RTEMS there are currently some means
223available
224
225- events,
226
227- message queues,
228
229- mutexes using the :ref:`OMIP`,
230
231- mutexes using the :ref:`MrsP`, and
232
233- binary and counting semaphores.
234
235The clustered scheduling approach enables separation of functions with
236real-time requirements and functions that profit from fairness and high
237throughput provided the scheduler instances are fully decoupled and adequate
238inter-cluster synchronization primitives are used.
239
240To set the scheduler of a task see :ref:`rtems_scheduler_ident()
241<rtems_scheduler_ident>` and :ref:`rtems_task_set_scheduler()
242<rtems_task_set_scheduler>`.
243
244OpenMP
245------
246
247OpenMP support for RTEMS is available via the GCC provided libgomp.  There is
248libgomp support for RTEMS in the POSIX configuration of libgomp since GCC 4.9
249(requires a Newlib snapshot after 2015-03-12). In GCC 6.1 or later (requires a
250Newlib snapshot after 2015-07-30 for <sys/lock.h> provided self-contained
251synchronization objects) there is a specialized libgomp configuration for RTEMS
252which offers a significantly better performance compared to the POSIX
253configuration of libgomp.  In addition application configurable thread pools
254for each scheduler instance are available in GCC 6.1 or later.
255
256The run-time configuration of libgomp is done via environment variables
257documented in the `libgomp manual <https://gcc.gnu.org/onlinedocs/libgomp/>`_.
258The environment variables are evaluated in a constructor function which
259executes in the context of the first initialization task before the actual
260initialization task function is called (just like a global C++ constructor).
261To set application specific values, a higher priority constructor function must
262be used to set up the environment variables.
263
264.. code-block:: c
265
266    #include <stdlib.h>
267    void __attribute__((constructor(1000))) config_libgomp( void )
268    {
269        setenv( "OMP_DISPLAY_ENV", "VERBOSE", 1 );
270        setenv( "GOMP_SPINCOUNT", "30000", 1 );
271        setenv( "GOMP_RTEMS_THREAD_POOLS", "1$2@SCHD", 1 );
272    }
273
274The environment variable ``GOMP_RTEMS_THREAD_POOLS`` is RTEMS-specific.  It
275determines the thread pools for each scheduler instance.  The format for
276``GOMP_RTEMS_THREAD_POOLS`` is a list of optional
277``<thread-pool-count>[$<priority>]@<scheduler-name>`` configurations separated
278by ``:`` where:
279
280- ``<thread-pool-count>`` is the thread pool count for this scheduler instance.
281
282- ``$<priority>`` is an optional priority for the worker threads of a thread
283  pool according to ``pthread_setschedparam``.  In case a priority value is
284  omitted, then a worker thread will inherit the priority of the OpenMP master
285  thread that created it.  The priority of the worker thread is not changed by
286  libgomp after creation, even if a new OpenMP master thread using the worker
287  has a different priority.
288
289- ``@<scheduler-name>`` is the scheduler instance name according to the RTEMS
290  application configuration.
291
292In case no thread pool configuration is specified for a scheduler instance,
293then each OpenMP master thread of this scheduler instance will use its own
294dynamically allocated thread pool.  To limit the worker thread count of the
295thread pools, each OpenMP master thread must call ``omp_set_num_threads``.
296
297Lets suppose we have three scheduler instances ``IO``, ``WRK0``, and ``WRK1``
298with ``GOMP_RTEMS_THREAD_POOLS`` set to ``"1@WRK0:3$4@WRK1"``.  Then there are
299no thread pool restrictions for scheduler instance ``IO``.  In the scheduler
300instance ``WRK0`` there is one thread pool available.  Since no priority is
301specified for this scheduler instance, the worker thread inherits the priority
302of the OpenMP master thread that created it.  In the scheduler instance
303``WRK1`` there are three thread pools available and their worker threads run at
304priority four.
305
306Application Issues
307==================
308
309Most operating system services provided by the uniprocessor RTEMS are
310available in SMP configurations as well.  However, applications designed for an
311uniprocessor environment may need some changes to correctly run in an SMP
312configuration.
313
314As discussed earlier, SMP systems have opportunities for true parallelism which
315was not possible on uniprocessor systems. Consequently, multiple techniques
316that provided adequate critical sections on uniprocessor systems are unsafe on
317SMP systems. In this section, some of these unsafe techniques will be
318discussed.
319
320In general, applications must use proper operating system provided mutual
321exclusion mechanisms to ensure correct behavior.
322
323Task variables
324--------------
325
326Task variables are ordinary global variables with a dedicated value for each
327thread.  During a context switch from the executing thread to the heir thread,
328the value of each task variable is saved to the thread control block of the
329executing thread and restored from the thread control block of the heir thread.
330This is inherently broken if more than one executing thread exists.
331Alternatives to task variables are POSIX keys and :term:`TLS`.  All use cases
332of task variables in the RTEMS code base were replaced with alternatives.  The
333task variable API has been removed in RTEMS 5.1.
334
335Highest Priority Thread Never Walks Alone
336-----------------------------------------
337
338On a uniprocessor system, it is safe to assume that when the highest priority
339task in an application executes, it will execute without being preempted until
340it voluntarily blocks. Interrupts may occur while it is executing, but there
341will be no context switch to another task unless the highest priority task
342voluntarily initiates it.
343
344Given the assumption that no other tasks will have their execution interleaved
345with the highest priority task, it is possible for this task to be constructed
346such that it does not need to acquire a mutex for protected access to shared
347data.
348
349In an SMP system, it cannot be assumed there will never be a single task
350executing. It should be assumed that every processor is executing another
351application task. Further, those tasks will be ones which would not have been
352executed in a uniprocessor configuration and should be assumed to have data
353synchronization conflicts with what was formerly the highest priority task
354which executed without conflict.
355
356Disabling of Thread Preemption
357------------------------------
358
359A thread which disables preemption prevents that a higher priority thread gets
360hold of its processor involuntarily.  In uniprocessor configurations, this can
361be used to ensure mutual exclusion at thread level.  In SMP configurations,
362however, more than one executing thread may exist.  Thus, it is impossible to
363ensure mutual exclusion using this mechanism.  In order to prevent that
364applications using preemption for this purpose, would show inappropriate
365behaviour, this feature is disabled in SMP configurations and its use would
366case run-time errors.
367
368Disabling of Interrupts
369-----------------------
370
371A low overhead means that ensures mutual exclusion in uniprocessor
372configurations is the disabling of interrupts around a critical section.  This
373is commonly used in device driver code.  In SMP configurations, however,
374disabling the interrupts on one processor has no effect on other processors.
375So, this is insufficient to ensure system-wide mutual exclusion.  The macros
376
377* :ref:`rtems_interrupt_disable() <rtems_interrupt_disable>`,
378
379* :ref:`rtems_interrupt_enable() <rtems_interrupt_enable>`, and
380
381* :ref:`rtems_interrupt_flash() <rtems_interrupt_flash>`.
382
383are disabled in SMP configurations and its use will cause compile-time warnings
384and link-time errors.  In the unlikely case that interrupts must be disabled on
385the current processor, the
386
387* :ref:`rtems_interrupt_local_disable() <rtems_interrupt_local_disable>`, and
388
389* :ref:`rtems_interrupt_local_enable() <rtems_interrupt_local_enable>`.
390
391macros are now available in all configurations.
392
393Since disabling of interrupts is insufficient to ensure system-wide mutual
394exclusion on SMP a new low-level synchronization primitive was added --
395interrupt locks.  The interrupt locks are a simple API layer on top of the SMP
396locks used for low-level synchronization in the operating system core.
397Currently, they are implemented as a ticket lock.  In uniprocessor
398configurations, they degenerate to simple interrupt disable/enable sequences by
399means of the C pre-processor.  It is disallowed to acquire a single interrupt
400lock in a nested way.  This will result in an infinite loop with interrupts
401disabled.  While converting legacy code to interrupt locks, care must be taken
402to avoid this situation to happen.
403
404.. code-block:: c
405    :linenos:
406
407    #include <rtems.h>
408
409    void legacy_code_with_interrupt_disable_enable( void )
410    {
411      rtems_interrupt_level level;
412
413      rtems_interrupt_disable( level );
414      /* Critical section */
415      rtems_interrupt_enable( level );
416    }
417
418    RTEMS_INTERRUPT_LOCK_DEFINE( static, lock, "Name" )
419
420    void smp_ready_code_with_interrupt_lock( void )
421    {
422      rtems_interrupt_lock_context lock_context;
423
424      rtems_interrupt_lock_acquire( &lock, &lock_context );
425      /* Critical section */
426      rtems_interrupt_lock_release( &lock, &lock_context );
427    }
428
429An alternative to the RTEMS-specific interrupt locks are POSIX spinlocks.  The
430:c:type:`pthread_spinlock_t` is defined as a self-contained object, e.g. the
431user must provide the storage for this synchronization object.
432
433.. code-block:: c
434    :linenos:
435
436    #include <assert.h>
437    #include <pthread.h>
438
439    pthread_spinlock_t lock;
440
441    void smp_ready_code_with_posix_spinlock( void )
442    {
443      int error;
444
445      error = pthread_spin_lock( &lock );
446      assert( error == 0 );
447      /* Critical section */
448      error = pthread_spin_unlock( &lock );
449      assert( error == 0 );
450    }
451
452In contrast to POSIX spinlock implementation on Linux or FreeBSD, it is not
453allowed to call blocking operating system services inside the critical section.
454A recursive lock attempt is a severe usage error resulting in an infinite loop
455with interrupts disabled.  Nesting of different locks is allowed.  The user
456must ensure that no deadlock can occur.  As a non-portable feature the locks
457are zero-initialized, e.g. statically initialized global locks reside in the
458``.bss`` section and there is no need to call :c:func:`pthread_spin_init`.
459
460Interrupt Service Routines Execute in Parallel With Threads
461-----------------------------------------------------------
462
463On a machine with more than one processor, interrupt service routines (this
464includes timer service routines installed via :ref:`rtems_timer_fire_after()
465<rtems_timer_fire_after>`) and threads can execute in parallel.  Interrupt
466service routines must take this into account and use proper locking mechanisms
467to protect critical sections from interference by threads (interrupt locks or
468POSIX spinlocks).  This likely requires code modifications in legacy device
469drivers.
470
471Timers Do Not Stop Immediately
472------------------------------
473
474Timer service routines run in the context of the clock interrupt.  On
475uniprocessor configurations, it is sufficient to disable interrupts and remove
476a timer from the set of active timers to stop it.  In SMP configurations,
477however, the timer service routine may already run and wait on an SMP lock
478owned by the thread which is about to stop the timer.  This opens the door to
479subtle synchronization issues.  During destruction of objects, special care
480must be taken to ensure that timer service routines cannot access (partly or
481fully) destroyed objects.
482
483False Sharing of Cache Lines Due to Objects Table
484-------------------------------------------------
485
486The Classic API and most POSIX API objects are indirectly accessed via an
487object identifier.  The user-level functions validate the object identifier and
488map it to the actual object structure which resides in a global objects table
489for each object class.  So, unrelated objects are packed together in a table.
490This may result in false sharing of cache lines.  The effect of false sharing
491of cache lines can be observed with the `TMFINE 1
492<https://git.rtems.org/rtems/tree/testsuites/tmtests/tmfine01>`_ test program
493on a suitable platform, e.g. QorIQ T4240.  High-performance SMP applications
494need full control of the object storage :cite:`Drepper:2007:Memory`.
495Therefore, self-contained synchronization objects are now available for RTEMS.
496
497Directives
498==========
499
500This section details the symmetric multiprocessing services.  A subsection is
501dedicated to each of these services and describes the calling sequence, related
502constants, usage, and status codes.
503
504.. raw:: latex
505
506   \clearpage
507
508.. _rtems_get_processor_count:
509
510GET_PROCESSOR_COUNT - Get processor count
511-----------------------------------------
512
513CALLING SEQUENCE:
514    .. code-block:: c
515
516        uint32_t rtems_get_processor_count(void);
517
518DIRECTIVE STATUS CODES:
519
520    The count of processors in the system that can be run. The value returned
521    is the highest numbered processor index of all processors available to the
522    application (if a scheduler is assigned) plus one.
523
524DESCRIPTION:
525    In uniprocessor configurations, a value of one will be returned.
526
527    In SMP configurations, this returns the value of a global variable set
528    during system initialization to indicate the count of utilized processors.
529    The processor count depends on the physically or virtually available
530    processors and application configuration.  The value will always be less
531    than or equal to the maximum count of application configured processors.
532
533NOTES:
534    None.
535
536.. raw:: latex
537
538   \clearpage
539
540.. _rtems_get_current_processor:
541
542GET_CURRENT_PROCESSOR - Get current processor index
543---------------------------------------------------
544
545CALLING SEQUENCE:
546    .. code-block:: c
547
548        uint32_t rtems_get_current_processor(void);
549
550DIRECTIVE STATUS CODES:
551    The index of the current processor.
552
553DESCRIPTION:
554    In uniprocessor configurations, a value of zero will be returned.
555
556    In SMP configurations, an architecture specific method is used to obtain the
557    index of the current processor in the system.  The set of processor indices
558    is the range of integers starting with zero up to the processor count minus
559    one.
560
561    Outside of sections with disabled thread dispatching the current processor
562    index may change after every instruction since the thread may migrate from
563    one processor to another.  Sections with disabled interrupts are sections
564    with thread dispatching disabled.
565
566NOTES:
567    None.
568
569Implementation Details
570======================
571
572This section covers some implementation details of the RTEMS SMP support.
573
574Low-Level Synchronization
575-------------------------
576
577All low-level synchronization primitives are implemented using :term:`C11`
578atomic operations, so no target-specific hand-written assembler code is
579necessary.  Four synchronization primitives are currently available
580
581* ticket locks (mutual exclusion),
582
583* :term:`MCS` locks (mutual exclusion),
584
585* barriers, implemented as a sense barrier, and
586
587* sequence locks :cite:`Boehm:2012:Seqlock`.
588
589A vital requirement for low-level mutual exclusion is :term:`FIFO` fairness
590since we are interested in a predictable system and not maximum throughput.
591With this requirement, there are only few options to resolve this problem.  For
592reasons of simplicity, the ticket lock algorithm was chosen to implement the
593SMP locks.  However, the API is capable to support MCS locks, which may be
594interesting in the future for systems with a processor count in the range of 32
595or more, e.g.  :term:`NUMA`, many-core systems.
596
597The test program `SMPLOCK 1
598<https://git.rtems.org/rtems/tree/testsuites/smptests/smplock01>`_ can be used
599to gather performance and fairness data for several scenarios.  The SMP lock
600performance and fairness measured on the QorIQ T4240 follows as an example.
601This chip contains three L2 caches.  Each L2 cache is shared by eight
602processors.
603
604.. image:: ../images/c_user/smplock01perf-t4240.*
605   :width: 400
606   :align: center
607
608.. image:: ../images/c_user/smplock01fair-t4240.*
609   :width: 400
610   :align: center
611
612Internal Locking
613----------------
614
615In SMP configurations, the operating system uses non-recursive SMP locks for
616low-level mutual exclusion.  The locking domains are roughly
617
618* a particular data structure,
619* the thread queue operations,
620* the thread state changes, and
621* the scheduler operations.
622
623For a good average-case performance it is vital that every high-level
624synchronization object, e.g. mutex, has its own SMP lock.  In the average-case,
625only this SMP lock should be involved to carry out a specific operation, e.g.
626obtain/release a mutex.  In general, the high-level synchronization objects
627have a thread queue embedded and use its SMP lock.
628
629In case a thread must block on a thread queue, then things get complicated.
630The executing thread first acquires the SMP lock of the thread queue and then
631figures out that it needs to block.  The procedure to block the thread on this
632particular thread queue involves state changes of the thread itself and for
633this thread-specific SMP locks must be used.
634
635In order to determine if a thread is blocked on a thread queue or not
636thread-specific SMP locks must be used.  A thread priority change must
637propagate this to the thread queue (possibly recursively).  Care must be taken
638to not have a lock order reversal between thread queue and thread-specific SMP
639locks.
640
641Each scheduler instance has its own SMP lock.  For the scheduler helping
642protocol multiple scheduler instances may be in charge of a thread.  It is not
643possible to acquire two scheduler instance SMP locks at the same time,
644otherwise deadlocks would happen.  A thread-specific SMP lock is used to
645synchronize the thread data shared by different scheduler instances.
646
647The thread state SMP lock protects various things, e.g. the thread state, join
648operations, signals, post-switch actions, the home scheduler instance, etc.
649
650Profiling
651---------
652
653To identify the bottlenecks in the system, support for profiling of low-level
654synchronization is optionally available.  The profiling support is a BSP build
655time configuration option (``--enable-profiling``) and is implemented with an
656acceptable overhead, even for production systems.  A low-overhead counter for
657short time intervals must be provided by the hardware.
658
659Profiling reports are generated in XML for most test programs of the RTEMS
660testsuite (more than 500 test programs).  This gives a good sample set for
661statistics.  For example the maximum thread dispatch disable time, the maximum
662interrupt latency or lock contention can be determined.
663
664.. code-block:: xml
665
666   <ProfilingReport name="SMPMIGRATION 1">
667     <PerCPUProfilingReport processorIndex="0">
668       <MaxThreadDispatchDisabledTime unit="ns">36636</MaxThreadDispatchDisabledTime>
669       <MeanThreadDispatchDisabledTime unit="ns">5065</MeanThreadDispatchDisabledTime>
670       <TotalThreadDispatchDisabledTime unit="ns">3846635988
671         </TotalThreadDispatchDisabledTime>
672       <ThreadDispatchDisabledCount>759395</ThreadDispatchDisabledCount>
673       <MaxInterruptDelay unit="ns">8772</MaxInterruptDelay>
674       <MaxInterruptTime unit="ns">13668</MaxInterruptTime>
675       <MeanInterruptTime unit="ns">6221</MeanInterruptTime>
676       <TotalInterruptTime unit="ns">6757072</TotalInterruptTime>
677       <InterruptCount>1086</InterruptCount>
678     </PerCPUProfilingReport>
679     <PerCPUProfilingReport processorIndex="1">
680       <MaxThreadDispatchDisabledTime unit="ns">39408</MaxThreadDispatchDisabledTime>
681       <MeanThreadDispatchDisabledTime unit="ns">5060</MeanThreadDispatchDisabledTime>
682       <TotalThreadDispatchDisabledTime unit="ns">3842749508
683         </TotalThreadDispatchDisabledTime>
684       <ThreadDispatchDisabledCount>759391</ThreadDispatchDisabledCount>
685       <MaxInterruptDelay unit="ns">8412</MaxInterruptDelay>
686       <MaxInterruptTime unit="ns">15868</MaxInterruptTime>
687       <MeanInterruptTime unit="ns">3525</MeanInterruptTime>
688       <TotalInterruptTime unit="ns">3814476</TotalInterruptTime>
689       <InterruptCount>1082</InterruptCount>
690     </PerCPUProfilingReport>
691     <!-- more reports omitted --->
692     <SMPLockProfilingReport name="Scheduler">
693       <MaxAcquireTime unit="ns">7092</MaxAcquireTime>
694       <MaxSectionTime unit="ns">10984</MaxSectionTime>
695       <MeanAcquireTime unit="ns">2320</MeanAcquireTime>
696       <MeanSectionTime unit="ns">199</MeanSectionTime>
697       <TotalAcquireTime unit="ns">3523939244</TotalAcquireTime>
698       <TotalSectionTime unit="ns">302545596</TotalSectionTime>
699       <UsageCount>1518758</UsageCount>
700       <ContentionCount initialQueueLength="0">759399</ContentionCount>
701       <ContentionCount initialQueueLength="1">759359</ContentionCount>
702       <ContentionCount initialQueueLength="2">0</ContentionCount>
703       <ContentionCount initialQueueLength="3">0</ContentionCount>
704     </SMPLockProfilingReport>
705   </ProfilingReport>
706
707Scheduler Helping Protocol
708--------------------------
709
710The scheduler provides a helping protocol to support locking protocols like the
711:ref:`OMIP` or the :ref:`MrsP`.  Each thread has a scheduler node for each
712scheduler instance in the system which are located in its :term:`TCB`.  A
713thread has exactly one home scheduler instance which is set during thread
714creation.  The home scheduler instance can be changed with
715:ref:`rtems_task_set_scheduler() <rtems_task_set_scheduler>`.  Due to the
716locking protocols a thread may gain access to scheduler nodes of other
717scheduler instances.  This allows the thread to temporarily migrate to another
718scheduler instance in case of preemption.
719
720The scheduler infrastructure is based on an object-oriented design.  The
721scheduler operations for a thread are defined as virtual functions.  For the
722scheduler helping protocol the following operations must be implemented by an
723SMP-aware scheduler
724
725* ask a scheduler node for help,
726* reconsider the help request of a scheduler node,
727* withdraw a schedule node.
728
729All currently available SMP-aware schedulers use a framework which is
730customized via inline functions.  This eases the implementation of scheduler
731variants.  Up to now, only priority-based schedulers are implemented.
732
733In case a thread is allowed to use more than one scheduler node it will ask
734these nodes for help
735
736* in case of preemption, or
737* an unblock did not schedule the thread, or
738* a yield  was successful.
739
740The actual ask for help scheduler operations are carried out as a side-effect
741of the thread dispatch procedure.  Once a need for help is recognized, a help
742request is registered in one of the processors related to the thread and a
743thread dispatch is issued.  This indirection leads to a better decoupling of
744scheduler instances.  Unrelated processors are not burdened with extra work for
745threads which participate in resource sharing.  Each ask for help operation
746indicates if it could help or not.  The procedure stops after the first
747successful ask for help.  Unsuccessful ask for help operations will register
748this need in the scheduler context.
749
750After a thread dispatch the reconsider help request operation is used to clean
751up stale help registrations in the scheduler contexts.
752
753The withdraw operation takes away scheduler nodes once the thread is no longer
754allowed to use them, e.g. it released a mutex.  The availability of scheduler
755nodes for a thread is controlled by the thread queues.
756
757Thread Dispatch Details
758-----------------------
759
760This section gives background information to developers interested in the
761interrupt latencies introduced by thread dispatching.  A thread dispatch
762consists of all work which must be done to stop the currently executing thread
763on a processor and hand over this processor to an heir thread.
764
765In SMP systems, scheduling decisions on one processor must be propagated
766to other processors through inter-processor interrupts.  A thread dispatch
767which must be carried out on another processor does not happen instantaneously.
768Thus, several thread dispatch requests might be in the air and it is possible
769that some of them may be out of date before the corresponding processor has
770time to deal with them.  The thread dispatch mechanism uses three per-processor
771variables,
772
773- the executing thread,
774
775- the heir thread, and
776
777- a boolean flag indicating if a thread dispatch is necessary or not.
778
779Updates of the heir thread are done via a normal store operation.  The thread
780dispatch necessary indicator of another processor is set as a side-effect of an
781inter-processor interrupt.  So, this change notification works without the use
782of locks.  The thread context is protected by a :term:`TTAS` lock embedded in
783the context to ensure that it is used on at most one processor at a time.
784Normally, only thread-specific or per-processor locks are used during a thread
785dispatch.  This implementation turned out to be quite efficient and no lock
786contention was observed in the testsuite.  The heavy-weight thread dispatch
787sequence is only entered in case the thread dispatch indicator is set.
788
789The context-switch is performed with interrupts enabled.  During the transition
790from the executing to the heir thread neither the stack of the executing nor
791the heir thread must be used during interrupt processing.  For this purpose a
792temporary per-processor stack is set up which may be used by the interrupt
793prologue before the stack is switched to the interrupt stack.
794
795Per-Processor Data
796------------------
797
798RTEMS provides two means for per-processor data:
799
8001. Per-processor data which is used by RTEMS itself is contained in the
801   `Per_CPU_Control` structure.  The application configuration via
802   `<rtems/confdefs.h>` creates a table of these structures
803   (`_Per_CPU_Information[]`).  The table is dimensioned according to the count
804   of configured processors
805   (:ref:`CONFIGURE_MAXIMUM_PROCESSORS <CONFIGURE_MAXIMUM_PROCESSORS>`).
806
8072. For low level support libraries an API for statically allocated
808   per-processor data is available via
809   `<rtems/score/percpudata.h> <https://git.rtems.org/rtems/tree/cpukit/include/rtems/score/percpudata.h>`_.
810   This API is not intended for general application use.  Please ask on the
811   development mailing list in case you want to use it.
812
813Thread Pinning
814--------------
815
816Thread pinning ensures that a thread is only dispatched to the processor on
817which it is pinned.  It may be used to access per-processor data structures in
818critical sections with enabled thread dispatching, e.g. a pinned thread is
819allowed to block.  The `_Thread_Pin()` operation will pin the executing thread
820to its current processor.  A thread may be pinned recursively, the last unpin
821request via `_Thread_Unpin()` revokes the pinning.
822
823Thread pinning should be used only for short critical sections and not all
824the time.  Thread pinning is a very low overhead operation in case the
825thread is not preempted during the pinning.  A preemption will result in
826scheduler operations to ensure that the thread executes only on its pinned
827processor.  Thread pinning must be used with care, since it prevents help
828through the locking protocols.  This makes the :ref:`OMIP <OMIP>` and
829:ref:`MrsP <MrsP>` locking protocols ineffective if pinned threads are
830involved.
831
832The thread pinning is not intended for general application use.  Please ask on
833the development mailing list in case you want to use it.
Note: See TracBrowser for help on using the repository browser.