safe and reliable embedded linux programming: how to get there
DESCRIPTION
The talk provides an overview of techniques to design and implement reliable embedded applications. The goal is to achieve safe and analyzable behavior by construction, including handling parallel multiprocessor systems in an efficient and predictable way. The means to attain this objective is to statically configure the application to run on embedded linux platforms, and then to use run-time support to enforce constraints imposed to the system.TRANSCRIPT
Slide: 1Copyright © 2012 AdaCore
José F. Ruiz
EWiLi 2012, Lorient
Senior Software EngineerAdaCore
Safe and Reliable Embedded Linux ProgrammingHow to Get There
7 June 2012
Slide: 2Copyright © 2012 AdaCore
Outline
• Safety issues– Language matters
• Concurrent execution on monoprocessors– Ada Ravenscar Profile
• Tasking on linux
• Parallel execution on multiprocessors– Processor affinities
Slide: 3Copyright © 2012 AdaCore
Safety
Slide: 4Copyright © 2012 AdaCore
Safety-Critical Software: Background
• What is “safety critical” software?– Failure can cause loss of human life or have other catastrophic consequences
• How does safety criticality affect software development?– Regulatory agencies require compliance with certification requirements
– Safety-related standards may apply to the finished product, to the development
process, or both
• Choice of programming language has large impact on the cost of
developing / certifying safety-critical systems– Dilemma: modern features that help in developing maintainable systems interfere with
safety certification
– Examples: Object-Oriented Programming, generics, exceptions, concurrency
– Traditional approach: select subset (profile) amenable to safety certification
– Chosen features depend on the analysis methods to be used (or dictate which
methods are feasible)
Slide: 5Copyright © 2012 AdaCore
Some language requirements (I)
• Reliability– Support for development of readable, correct code
– No “traps and pitfalls”
– Intuitive lexical and syntactic features
– Support for good software engineering practice
– Encapsulation
– Avoidance of concurrency bugs
– Race conditions
– Deadlock / livelock
– Early error detection
– Compile-time checking whenever possible
– Run-time checking, at least
• Predictability– Unambiguous language semantics
– No undefined / implementation-dependent behavior
– Ability to demonstrate statically that time and space constraints will be satisfied
– Deadlines will be met
– Stack and heap space will not be exhausted
Slide: 6Copyright © 2012 AdaCore
Some language requirements (II)
• Analyzability– Control and data flow analysis
– Detects / prevents references to uninitialized data
– Information flow analysis
– Identify dependencies between a routine’s inputs and outputs
– Formal code verification
– Range checking
– Traceability between program representations and different levels
– Dynamic analysis (testing)
• Expressiveness– General-purpose features consistent with the above requirements
– Support for specialized processing typical in embedded systems (safety)
– Interrupt handling
– Low-level programming
– Fixed-point arithmetic
Slide: 7Copyright © 2012 AdaCore
Modern programming languages
• Many features help– Encapsulation / namespace control
– Limit data coupling
– Strong typing, compile-time checking
– Early error detection
• Some features present difficulties– High expressive power vs. need to constrain feature
– Dynamic flexibility vs. need for static analysis
– High-level features vs. need to certify the code that actually runs
– Generated code may contain implicit loops, conditionals
– Run-time library, API need to be certifiable
– How, if at all, use features such as exceptions, concurrency, dynamic allocation
Slide: 8Copyright © 2012 AdaCore
Modern programming languages – basic issues
• No general-purpose language is appropriate for highest safety/security levels– Conflicts with reliability, predictability, analyzability
• Key issue: can the language be subsetted so that applications restricted to the
subset can be certified– Is/are the subset(s) precisely defined?
– Can compliance with the subset(s) be enforced by a tool?
– Who defines the subset(s)?
– Standards agency? Tool/compiler vendor? Application developer?
– Are there intrinsic problems with the language that cannot be solved by subsetting?
• Current approaches– C-based
– MISRA C / C++
– Ada-based
– Ada profiles
– SPARK Ada
– Java-based
– Real-Time / Safety-Critical Java Technology
Slide: 9Copyright © 2012 AdaCore
MISRA C
• Intended for embedded automotive systems– Around 170 rules
• Prevent or restrict usage of C constructs with unpredictable behavior
• Encourage static checking tools that enforce compliance with the subset
Strengths– “Best practices” for C programming
– General good style
– Avoids C features intrinsically non-
portable / ill-defined
– Simplicity of C compared with other
languages– Smaller run-time library
– Easier certification
– Large population of candidate users
– And tools, service providers, etc.
– May be appropriate for small systems on
specialized hardware lacking compilers
for alternative languages– MISRA-C code may be produced as
output of source-to-source translator
Weaknesses– C was not designed to meet the
needs of high-integrity systems– Error-prone
– Lacks features such as
encapsulation, namespaces
– C (and thus MISRA C) does not
“scale up” to large systems
– C standard has ambiguities and
thus so does MISRA C
– Some rules are not enforceable by
static tools
– Usage of MISRA C requires license
from MISRA
Slide: 10Copyright © 2012 AdaCore
Ada – introduction
• What is Ada?– General-purpose methodology-neutral strongly-typed ISO standard language
– Intended for large, long-lived systems
– Fits reliability-critical embedded real-time applications
• Ada in a nutshell– Pascal-style foundation (syntax, basic data types)
– Exception handling
– Packages / encapsulation
– Generic templates
– Concurrency support (“tasking”)
– Object-Oriented Programming
– Support for “contracts”
– Since Ada 2012
– Specialized support for particular domains, including:
– Systems programming
– Real-time systems
– High-Integrity applications
Slide: 11Copyright © 2012 AdaCore
Ada for safety-critical systems (I)
• Full Ada not appropriate– Run-time library too large / complex
– High level features interfere with traceability and analyzability
• Why consider Ada for safety/security-critical software– General design philosophy promotes sound software engineering
– Successful history from Ada 83
• Standard support for safety/security-critical systems– High-Integrity Systems Annex
– Contracts
– Profiles
– Such as the Ravenscar profile
– User-defined tailoring, specifying features that are not used (pragma Restrictions)
– Compiler rejects programs that use these features
– Executable program excludes run-time libraries for these features
• Guidelines documents – Guide for Ada in High-Integrity Systems (an ISO Technical Report)
– Guide for Ada Ravenscar Profile in High-Integrity Systems
Slide: 12Copyright © 2012 AdaCore
Ada for safety-critical systems (II)
• Reliability– Very few features have surprising effects
– Prevention of dangling references to declared objects, subprograms
– Specification of intent on operation inheritance
– Contracts
– No garbage collection
– Programmer responsible for memory management
• Predictability– Language semantics are generally well-defined, in an ISO standard
– But there are features whose effects are implementation defined, implementation
dependent, unspecified, or “bounded errors”
– Implementation decisions can affect time or space predictability
– Functions returning unconstrained arrays
– Solutions in practice
– Analyze source program (e.g. no read of uninitialized object)
– Adhere to subset (no functions returning unconstrained arrays)
– Analyze object code so that implementation decision is known
Slide: 13Copyright © 2012 AdaCore
Ada for safety-critical systems (III)
• Analyzability– Some features help analyzability
– Child units may be used for testing packages with encapsulated state
– But full language is too complex; need to subset
– Key features are pragma Restrictions and pragma Profile
– Ada is in effect a family of profiles, where user can select features à la carte
– No such thing as the safety-critical Ada profile
– Troublesome OOP features are easily avoided
– But no standard annotation facility
• Expressibility– Support for low-level and real-time programming
– Ravenscar Profile for certifiable concurrent applications
– Good inter-language interfacing facilities, to incorporate certifiable libraries from other
languages
– Some weaknesses
– Limited support for distribution / networking
Slide: 14Copyright © 2012 AdaCore
Ada examples
package Arith is procedure Double (X : in out Integer) with Pre => X >= Integer'First / 2 and then X <= Integer'Last / 2, Post => X = 2 * X'Old;end Arith;
Spec (design by contract)
pragma Profile (Ravenscar);-- Only Ravenscar tasking allowed
pragma Restrictions (No_Allocators);-- Avoid dynamic memory
pragma Restrictions (No_Access_Subprograms);-- No pointers to subprograms
Configuration
type Parity_Kind is (Even, Odd);
type Reserved_21 is mod 2**21;for Reserved_21'Size use 21;
Type UART_Data_Register is record Data : Character; Data_Ready : Boolean; Parity : Parity_Kind; Parity_Error : Boolean; Reserved : Reserved_21; end record;
for UART_Data_Register use record Data at 0 range 0 .. 7; Data_Ready at 0 range 8 .. 8; Parity at 0 range 9 .. 9; Parity_Error at 0 range 10 .. 10; Reserved at 0 range 11 .. 31; end record;for UART_Data_Register'Size use 32;
UART_A : UART_Data_Register;pragma Atomic (UART_A);for UART_A’Address use To_Address (16#8000_0100#);
System programming
Slide: 15Copyright © 2012 AdaCore
Ada evaluation
• Strengths– Sound base language for programming high-reliability systems
– Allows construction of tailored high-integrity subsets that are:
– Powerful enough to use for real systems
– Simple enough to be amenable to certification
– Scales up to large systems
– ISO Standard
– Concurrency support / Ravenscar Profile
– Design by contract
– Free and publicly available documents
– Ada Reference Manual, Rationale
– Guidelines documents
– Good track record in avionics, train control, other safety-critical domains
• Weaknesses– Whole language is too large, so must be subsetted
– Aside from the standard Ravenscar Profile, others are vendor specific
– Language has some features with implementation-dependent or unspecified behavior
– Such as referencing a variable before its initialization
– Smaller user / tool vendor community than other languages
Slide: 16Copyright © 2012 AdaCore
SPARK introduction (I)
• SPARK is– A language, plus
– Static analysis tools based on underlying principle of correctness by construction
• Facilitate rigorous, static demonstration that program does what it is supposed to do– And only that
• Guarantee bounded time and space requirements
• Allow simple run-time library amenable to certification
• Ada-based language– Ada plus contracts minus features that interfere with above principle
– Contracts are special comments handled by the SPARK tools
• Language restrictions (Ada features intentionally omitted)– Features that complicate analysis / formal proofs, e.g.:
– Exceptions, goto statement, generic templates, function side effects
– Features that interfere with bounded time / space requirement, e.g.:
– Recursion, pointers, dynamically-sized arrays
Slide: 17Copyright © 2012 AdaCore
SPARK introduction (II)
• Language features include:– Most of Ada’s “static semantic” features, including packages, private types,
unconstrained array types, “OOP Lite”
– Concurrency (Ravenscar tasking profile)
• Contracts– Data / information flow (use of global variables, formal parameters)
– Inter-module dependencies
– Specification of dynamic behavior (pre / post-conditions, assertions)
• Analysis performed by SPARK tools– Static semantic analysis – detect aliasing, function side effects, …
– Control flow analysis – detect dead code, banned constructs (goto, …)
– Data-flow analysis – detect unused or uninitialized variables, …
– Information-flow analysis – check input / output variable coupling
– Verification condition generation – generate theorems
– Theorem proving
Slide: 18Copyright © 2012 AdaCore
SPARK example
procedure Process (Output : out T; Input1, Input2 : in T);
--# global out Global_Output;--# in Global_Input;--# derives Output from Input1, Input2 &--# Global_Output from Global_Input, Input2;--# pre Input1 /= 0;--# post Output = Input2 / Input1;
Contracts and flow information
Slide: 19Copyright © 2012 AdaCore
SPARK for safety-critical systems
• Reliability– Inherits Ada’s general advantages (few syntactic surprises)
– Avoids some errors that may occur in full Ada
– Reference to uninitialized variable
• Predictability– Unambiguous specification with no implementation dependencies
– But not a formal standard
– Contracts allow statically checkable assertions about resource requirements
• Analyzability– SPARK Ada subset eliminates features that are complicated to analyze
• Expressibility– Missing some useful functionality (e.g. generics) but does include some tasking
features
Slide: 20Copyright © 2012 AdaCore
SPARK evaluation
• Strengths– Designed from the start to satisfy high-integrity requirements
– Language rules are unambiguous, implementation independent
– Insecurities (e.g., “aliasing” of formal parameter and global variable) and many kinds of hard-to-
detect bugs (e.g. use before initialization) are prevented
– Contracts show programmer’s intent, aid readability
– Support for concurrency (Ravenscar profile)
– Tedious aspects of constructing proofs are automated
– Positive experience across a range of high-integrity projects
– Lower certification costs and post-delivery bug rates
– Excellent reference material
• Weaknesses– Language lacks some useful features
– No “pointers”, but in many kinds of safety critical systems a restricted style for pointers is
permitted• Pointers to statically declared data• Dynamic allocation at system startup
– Some restrictions (e.g. no recursion) require stylistic workarounds
– Contracts require different development style than what most programmers are used to
– Relatively small user community
– Small number of suppliers for tools, services
Slide: 21Copyright © 2012 AdaCore
Concurrency
Slide: 22Copyright © 2012 AdaCore
Concurrency in Ada (short history)
• Concurrency a first-class citizen in Ada– Easy to use and analyze
– Since the beginning
– Well-developed tasking in Ada 83
– Ada 95, Ada 2005 , Ada 2012 improved and extended tasking
Ad
a
Decades of experience in using Ada on multiprocessors
Slide: 23Copyright © 2012 AdaCore
Why Ravenscar
• Use concurrency in embedded real-time systems– Verifiable
– Simple
– Implemented reliably and efficiently
• Scheduling theory for accurate analysis of real-time behavior– Preemptive fixed priority scheduling
– Rate Monotonic Analysis (RMA)
– Response Time Analysis (RTA)
– Priority Ceiling Protocol (PCP)
– Avoids unbounded priority inversion and deadlocks
• Tool support– RMA and RTA
– Static simulation of concurrent real-time programs
Slide: 24Copyright © 2012 AdaCore
What is the Ravenscar profile
• A subset of the Ada tasking model
• Defined to meet safety-critical real-time requirements– Determinism
– Schedulability analysis
– Memory-boundedness
– Execution efficiency and small footprint
– Suitability for certification
• State-of-the-art concurrency constructs– Adequate for most types of real-time software
Slide: 25Copyright © 2012 AdaCore
Tasks
• Fixed set of tasks– Only at library level
– No dynamic allocation
– No nested declaration of tasks
– Statically created
– Task descriptors, stacks, …
• Each task is infinite loop– Single “triggering” action (delay or event)
• Task creation and activation is very simple and deterministic– All tasks are created at initialization
– Then all activated and executed according to their priority
Slide: 26Copyright © 2012 AdaCore
How tasks look like in Ravenscar
task body Cyclic is Period : constant Time_Span := Milliseconds (10); Activation : Time := Clock;begin loop delay until Activation; -- Do something
Something; -- Compute next activation time
Activation := Activation + Period; end loop;end Cyclic;
Time-triggered task
task body Sporadic isbegin loop Monitor.Wait_Event; -- Protected entry -- Do something
Something; end loop;end Sporadic;
Event-triggered task
Slide: 27Copyright © 2012 AdaCore
Periodic activity in Ada and C
task body Periodic is Activation : Time := Clock;begin loop delay until Activation; -- Do something
Something; -- Compute next activation time
Activation := Activation + Milliseconds (100); end loop;end Periodic;
static void *periodic (void *arg) { struct timespec activation;
clock_gettime (CLOCK_REALTIME, &activation);
while (1) { clock_nanosleep (CLOCK_REALTIME, TIMER_ABSTIME, &activation, NULL);
/* Do something */
something ();
/* Compute next activation time */
if (1000000000 - activation.tv_nsec < 100000000) { activation.tv_sec += 1; activation.tv_nsec += 100000000 - 1000000000; } else { activation.tv_nsec += 100000000; } }}
int main(void) { pthread_t thread; pthread_attr_t attr;
pthread_attr_init (&attr); pthread_create (&thread, &attr, &periodic, NULL); …}
C Periodic
Freq = 10 Hz
Slide: 28Copyright © 2012 AdaCore
Synchronization in Ravenscar
Pr Buffer
Put
C Producer
S Consumer
Get
task body Consumer is Element : Item;begin loop Buffer.Get (Element);
Consume (Element); end loop;end Consumer;
task body Producer is Period : constant Time_Span := Milliseconds (10); Activation : Time := Clock; Element : Item;begin loop delay until Activation;
Element := Produce_It;
Buffer.Put (Element);
Activation := Activation + Period; end loop;end Producer;
protected Buffer is procedure Put (Element : Item); entry Get (Element : out Item);private Container : Item; Received : Boolean := False;end Buffer;
protected body Buffer is procedure Put (Element : Item) is begin Container := Element; Received := True; end Put;
entry Get (Element : out Item) when Received is begin Element := Container; Received := False; end Get;end Buffer;
Slide: 29Copyright © 2012 AdaCore
Interrupt support in Ravenscar
Pr Monitor
Handler
S Interrupt_Task
Wait
External Interrupt
task body Interrupt_Task isbegin loop Monitor.Wait;
Handle_Interrupt; end loop;end Interrupt_Task;
protected Monitor is pragma Priority (Interrupt_Priority’Last);
procedure Handler; pragma Attach_Handler (Handler, Interrupt_ID);
entry Wait;private Signaled : Boolean := False;end Monitor;
protected body Monitor is procedure Handler is begin Signaled := True; end Put;
entry Wait when Signaled is begin Signaled := False; end Wait;end Monitor;
Slide: 30Copyright © 2012 AdaCore
Concurrency on linux
Slide: 31Copyright © 2012 AdaCore
The model – Ada tasks
• Ada task runs on top of operating system thread– One-to-one correspondence
• Task dispatching policy– Can be selected with pragma Dispatching_Policy
– SCHED_OTHER by default
– Preemptive priority scheduling
– pragma Dispatching_Policy (FIFO_Within_Priorities)• pthread_setschedparam (Thread, SCHED_FIFO, Param’Access)
– Means run until blocked (or preempted), no time slicing
– Reduces non-determinism
– NOTE!!: Requires special (root) privileges
• Xenomai– Real-time behavior
– The Ada run-time library can call the Xenomai API to create tasks in kernel domain
Slide: 32Copyright © 2012 AdaCore
The model – Synchronization
• A protected type is a data object with locks– Data encapsulation, accessible through interface (locked access routines)
– functions (read the data with read lock)
– procedures (read/write the data with write lock)
– entries (wait until some condition is met, then read/write the data with write lock)
• Priority inheritance– Guard against priority inversion
– low priority task grabs resource X
– high priority task needs resource X, waits
– medium priority task preempts low priority task, and runs for a long time, holding up high priority
task
– Solution, while high priority task is waiting, lend high priority to low priority task
• Implementation
– Mutual exclusion– Mutex
– Waiting / Signaling operations– Conditional variable
Slide: 33Copyright © 2012 AdaCore
The model – System programming
• Clock and delay– Clock uses gettimeofday
– Delay operations use timed conditional variables– We can wakeup the task before expiration if needed
• Interrupt handling– Underlying signal mechanism
– A server task per interrupt served– With specific mask to serve the required signal
– Call to sigwait
Slide: 34Copyright © 2012 AdaCore
Multiprocessors
Slide: 35Copyright © 2012 AdaCore
Why addressing multiprocessors?
• The answer to increasing processing demands– We cannot increase the clock frequency forever
– We cannot increase the instruction-level parallelism forever
– Provides higher performance for less consumed energy
Courtesy IEEE Computer, January 2011, page 33.
Slide: 36Copyright © 2012 AdaCore
Periodic activity in Ada and C (multiprocessor version)
task Periodic with CPU => 1;
task body Periodic is Activation : Time := Clock;begin loop delay until Activation; -- Do something
Something; -- Compute next activation time
Activation := Activation + Milliseconds (100); end loop;end Periodic;
static void *periodic (void *arg) { struct timespec activation;
clock_gettime (CLOCK_REALTIME, &activation);
while (1) { clock_nanosleep (CLOCK_REALTIME, TIMER_ABSTIME, &activation, NULL); /* Do something */ something ();
/* Compute next activation time */
if (1000000000 - activation.tv_nsec < 100000000) { activation.tv_sec += 1; activation.tv_nsec += 100000000 - 1000000000; } else { activation.tv_nsec += 100000000; } }}
int main(void) { cpu_set_t cpuset; pthread_t thread; pthread_attr_t attr;
CPU_ZERO (&cpuset); CPU_SET (0, &cpuset);
pthread_attr_init (&attr); pthread_attr_setaffinity_np (&attr, sizeof (cpu_set_t), &cpuset);
pthread_create (&thread, &attr, &periodic, NULL); …}
C Periodic
Freq = 10 HzCPU affinity = 1
Slide: 37Copyright © 2012 AdaCore
Some advantages of Ada
• Better readability– No doubts, right?
• Semantics– The Ada run time enforces required dispatching policy, signal mask, master-dependent
tasks, …
• Portability
Platform Ada C
Linux
Solaris
Windows
VxWorks
Bare board
Slide: 38Copyright © 2012 AdaCore
Ada 2012
• Ada 2012 added explicit support for controlling processor allocation– Dispatching_Domain
• Ravenscar profile for multiprocessors
• Parallel task synchronization
• Memory barriers
Slide: 39Copyright © 2012 AdaCore
Concurrent / parallel execution in Ada
• Ada has always taken into account parallel architectures– Allow concurrent/parallel execution
– Multicomputers, multiprocessors, interleaved execution
– Even allow parallel execution of a single task
– … if its effect is as executed sequentially
– A task can be on the ready queues of more than one processor
– Many partitioning schemes allowed
– Via implementation-defined pragmas or non standard library packages
Slide: 40Copyright © 2012 AdaCore
Synchronization and communication of parallel activities
• Task synchronization– Protected objects
– No language-defined ordering or queuing presumed for tasks competing to start a
protected action• Tasks are intended to spin lock on multi-processors
– Shared variables
– Cache coherence
– Rendezvous
– The call to the task entry is blocking
Slide: 41Copyright © 2012 AdaCore
Symmetric Multi-Processor (SMP)
• Several similar processors– All processors can perform the same functions
• Centralized memory with uniformed access time– Problem of cache coherence
Main memory I/O System
One ormore cache
levels
Processor
One ormore cache
levels
Processor
One ormore cache
levels
Processor
One ormore cache
levels
Processor
Slide: 42Copyright © 2012 AdaCore
• Tasks assigned to a given processor
• How to schedule a group of tasks on
a processor is known– Rate Monotonic Scheduling (static
priorities)
– Earliest Deadline First (dynamic
priorities)
• But, dividing the tasks into groups is
NP-hard
• Task migration is permitted– Overhead of task migration
increases with the number of CPUs
– Reduced cache performance
Partitioned versus global scheduling
Partitioning Global Scheduling
None is better than the other in terms of guaranteed CPU utilization
Slide: 43Copyright © 2012 AdaCore
Typical OS support for multiprocessors
• Set CPU affinity– Allocate tasks to one CPU (or to a group of CPUs)
• Get CPU affinity
• Task migration– From one CPU to another
– Either user-requested or performed by the OS
• Spin locks– Tasks wait in a loop until lock is free (busy waiting)
– Multiprocessor synchronization
Slide: 44Copyright © 2012 AdaCore
Support for multiprocessors in Ada 83, 95, 05
• Ada has always allowed a program’s implementation to be on a
multiprocessor system– Real parallelism
– Inter-processor synchronization
• No direct support for affinities– The OS can decide the best allocation
– The developer
– Implementation-defined pragmas or non standard library packages
• Allows the full range of partitioning– But no user control defined in the standard
Slide: 45Copyright © 2012 AdaCore
Explicit support for multiprocessors in Ada 2012
• Notion of dispatching domain
• Safe multiprocessor tasking
• Parallel task synchronization
• Memory barriers
Slide: 46Copyright © 2012 AdaCore
Ada 2012 dispatching domains
• Focus on SMPs
• Handle mapping of tasks to processors– Support all schemes
– Partitioned• Tasks allocated to a subset of CPUs
– Global• Implicit task migration supported• Explicit task migration allowed
• Notion of processor dispatching domain– Group of processors across which global scheduling occurs
– Non-overlapping dispatching domains
– Tasks are assigned to an unique dispatching domain
– A task may be allocated to a given processor within the dispatching domain
– Or free to be in any of the domain
Slide: 47Copyright © 2012 AdaCore
Static allocation to processors
task type Allocated_Task (Affinity : CPU) with CPU => Affinity;
T1 : Allocated_Task (1);T2 : Allocated_Task (2);T3 : Allocated_Task (3);T4 : Allocated_Task (4);
GroupA : aliased Dispatching_Domain := Create (1, 2);GroupB : aliased Dispatching_Domain := Create (3, 4);GroupC : aliased Dispatching_Domain := Create (5, 6);GroupD : aliased Dispatching_Domain := Create (7, 8);
task type Grouped_Task (Group : access Dispatching_Domain) with Dispatching_Domain => Group.all; T1, T2, T3, T4 : Grouped_Task (GroupA’Access);T5, T6 : Grouped_Task (GroupB’Access);T7, T8, T9 : Grouped_Task (GroupC’Access);T10, T11 : Grouped_Task (GroupD’Access);
Slide: 48Copyright © 2012 AdaCore
Dynamic affinity handling
GroupA : Dispatching_Domain := Create (1, 2);GroupB : Dispatching_Domain := Create (3, 4);GroupC : Dispatching_Domain := Create (5, 6);GroupD : Dispatching_Domain := Create (7, 8);
task T_In_A with Dispatching_Domain => GroupA;task T_Non_Allocated;
task body Driver isbegin -- Allocate T_Non_Allocated to GroupB
Assign_Task (GroupB, 3, T_Non_Allocated’Identity); Do_Something;
-- Move it to a different processor
if Proc_3_Overloaded then Set_CPU (4, T_Non_Allocated’Identity); end if; Do_Something;
end Driver;
task body T_In_A is Current_CPU : CPU;begin -- In processor 1 or 2
Do_Something;
-- In processor 1 only
Set_CPU (1); Do_Something; -- In processor 2 only
Set_CPU (Get_Last_CPU (GroupA)); Do_Something;
-- Now again in processor 1 or 2
Set_CPU (Not_A_Specific_CPU); Do_Something;
-- Now I am lost. Where am I?
Current_CPU := Get_CPU; pragma Assert (Current_CPU = Not_A_Specific_CPU);end T_In_A;
Slide: 49Copyright © 2012 AdaCore
• What I want– Create a group of processors
– Define an specific scheduling policy for
the group
– Execute a set of tasks within the group
• What I have to do– Create a dispatching domain
– Define a non-overlapping priority band
– Allocate tasks to the dispatching
domain
– Use priorities in the priority band
Handle affinity and dispatching policy
pragma Priority_Specific_Dispatching (FIFO_Within_Priorities, 20, 25);
Group : Dispatching_Domain := Create (1, 2);
task T1 with Dispatching_Domain => Group, Priority => 22;
task T2 with Dispatching_Domain => Group, Priority => 23, CPU => 1;
task T3 with Dispatching_Domain => Group, Priority => 24;
Slide: 50Copyright © 2012 AdaCore
Synchronization on multiprocessors
• Protected objects– There is a lock-free optimization for monoprocessors (using priorities)
– No longer viable on multiprocessors
– Currently Ada advise that tasks should busy-wait (spin) at their active priority for the
lock
• Task entries– Requires internal synchronization primitives aware of multiprocessor
– Spin locks
Slide: 51Copyright © 2012 AdaCore
Multicores for real-time safety-critical embedded systems
• We need to address:– Reliability
– Predictability
– Analyzability
• The Ravenscar profile for monoprocessors is– Deterministic
– Time analyzable
– Simple to use and implement
• Extend the Ravenscar profile model from monoprocessor to multiprocessor– Fully partitioned model
– Fixed-priority scheduling
– Static model
• Allow for– Simple implementation
– Verifiable
– Schedulability analysis
Slide: 52Copyright © 2012 AdaCore
Static model
• Concurrent entities fixed and static– Tasks and shared memory defined before execution
• Static fixed priority scheduling algorithm– Preemptive fixed priority scheduling in each CPU
– Analyzable as in Ravenscar for monoprocessors
– Dynamic-priority scheduling algorithms could increase CPU utilization but:
– Higher complexity
– Higher run-time overhead
– Lower predictability, lower robustness in case of overload
• Partitioned– Each task allocated to an user-defined processor forever
– CPU utilization of partitioned scheduling is neither better nor worse than global
– It relies on very well known monoprocessor techniques for priority allocation and timing
analysis
– It is much simpler to implement
– No task migration
Slide: 53Copyright © 2012 AdaCore
• Tasks statically allocated to
processors– No task migration
• Preemptive fixed-priority
scheduling
• Single shared run time– Per-CPU ready queues
– Spin-locks to protect shared data
– Disabling interrupts is not
enough
• Operations on a different
processors– Triggering an special interrupt in the
target processor
Task scheduling
0
3
1
2
task Cyclic
with Priority => 100,
CPU => 3;
end Cyclic;
task Cyclic with Priority => 100, CPU => 3;end Cyclic;
Ravenscar system on monoprocessor
Slide: 54Copyright © 2012 AdaCore
Task synchronization
• Library-level protected objects– Shared data with mutual exclusion
– Both for inter- and intra-processor communication
• Simple and efficient mutual exclusion changing priority for intra-processor
communication– As in Ravenscar monoprocessor
– Could be statically detected
– Efficiency
– Simple timing analysis
• Spin-locking for inter-processor synchronization
• Awaking tasks from other processors– Inter-processor interrupt facility to modify the ready queues
Slide: 55Copyright © 2012 AdaCore
Parallel task synchronization
• Goal– Effective parallel task synchronization
– Set of tasks blocked and released at once
• Typical case– A group of tasks must wait until all of them reach a synchronization point
– And then be released together to work in parallel
• Mimic the POSIX barrier mechanism
Slide: 56Copyright © 2012 AdaCore
Parallel barrier example
package Ada.Synchronous_Barriers is pragma Preelaborate (Synchronous_Barriers); subtype Barrier_Limit is Positive range 1 .. <imp-def>;
type Synchronous_Barrier (Release_Threshold : Barrier_Limit) is limited private;
procedure Wait_For_Release (The_Barrier : in out Synchronous_Barrier; Notified : out Boolean);
private -- not specified by the languageend Ada.Synchronous_Barriers;
Number_Of_Tasks : constant := 8; Barrier : Synchronous_Barrier (Number_Of_Tasks);
task type Worker (Affinity : CPU) with CPU => Affinity;
task body Worker is Notified : Boolean;begin loop Wait_For_Release (Barrier, Notified); -- Do something in parallel at the same time
Something; if Notified then -- Only one task does this
Ask_For_More_Work; end if; end loop;end Worker;
Slide: 57Copyright © 2012 AdaCore
Memory barriers
• Goal– Have control over cache memories
• Typical case– Non-blocking algorithms to effectively exploit hardware parallelism
– lock-free and wait-free
• Problem to solve– How to ensure the correct order of loads and stores with multi-level caches
– Modern multicores do not guarantee this ordering between processors
– Optimizations that can result in out-of-order execution
– Unless special instructions are used
• How to do it with Volatile– Until Ada 2005
– They can never be in cache or registers
– The Ada 2012 (more realistic) approach
– Volatiles can be handled in cache memories, but
– Guarantee serial ordering• All tasks of the program (on all processors) that read or update volatile variables see the same
order of updates to the variables• May need the use of an appropriate memory barrier to flush the cache
Slide: 58Copyright © 2012 AdaCore
• In Ada 83, 95, 2005– Shared_Data and Barrier can never be in
cache
• In Ada 2012– Shared_Data and Barrier can be in cache
Example of memory barriers
Shared_Data : Integer;pragma Volatile (Shared_Data); Barrier : Boolean := False;pragma Volatile (Barrier);
task Producer with CPU => 1;
task Consumer with CPU => 2;
task body Producer isbegin … Produce (Shared_Data); Barrier := True; …end Producer; task body Consumer isbegin … while not Barrier loop null; end loop;
-- If we see that Barrier has been updated, -- we must see the produced value of -- Shared_Data.
Use(Shared_Data); …end Consumer;
Slide: 59Copyright © 2012 AdaCore
Conclusion
• There is support in programming environments for high-integrity
• Embedded programming can use high-level abstraction constructs
• Real-time support– Computational model amenable to timing analysis
• Ada has supported execution on parallel architectures since its inception– Ada 2012 dispatching domains
– Good flexibility and analyzability
– Implementable on top of typical operating systems and kernels
– Ravenscar for multiprocessors– Simple extension to Ravenscar on monoprocessors– Partitioning into a set of monoprocessor Ravenscar systems– Keep desired properties found in monoprocessor Ravenscar