source: rtems-docs/c-user/symmetric_multiprocessing_services.rst @ 8ff7024

5
Last change on this file since 8ff7024 was 8ff7024, checked in by Sebastian Huber <sebastian.huber@…>, on 03/01/19 at 08:43:53

c-user: Mention atomic operations API

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