SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause copyrights: - Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) enabled-by: RTEMS_SMP functional-type: action links: - role: requirement-refinement uid: group post-conditions: - name: X states: - name: Task test-code: | T_eq_ptr( scheduled, ctx->tq_ctx.worker_tcb[ TASK ] ); text: | The task ``T`` shall be scheduled on processor ``X``. - name: TaskIdle test-code: | T_true( scheduled->is_idle ); scheduler_node = _Thread_Scheduler_get_home_node( ctx->tq_ctx.worker_tcb[ TASK ] ); T_eq_ptr( scheduler_node->user, scheduled ); text: | An idle task on behalf of task ``T`` shall be scheduled on processor ``X``. - name: Alpha test-code: | T_eq_ptr( scheduled, ctx->tq_ctx.worker_tcb[ ALPHA ] ); text: | The task ``A`` shall be scheduled on processor ``X``. - name: AlphaIdle test-code: | T_true( scheduled->is_idle ); scheduler_node = _Thread_Scheduler_get_home_node( ctx->tq_ctx.worker_tcb[ ALPHA ] ); T_eq_ptr( scheduler_node->user, scheduled ); text: | An idle task on behalf of task ``A`` shall be scheduled on processor ``X``. - name: Beta test-code: | T_eq_ptr( scheduled, ctx->tq_ctx.worker_tcb[ BETA ] ); text: | The task ``B`` shall be scheduled on processor ``X``. - name: BetaIdle test-code: | T_true( scheduled->is_idle ); scheduler_node = _Thread_Scheduler_get_home_node( ctx->tq_ctx.worker_tcb[ BETA ] ); T_eq_ptr( scheduler_node->user, scheduled ); text: | An idle task on behalf of task ``B`` shall be scheduled on processor ``X``. test-epilogue: null test-prologue: | const Per_CPU_Control *cpu; const Thread_Control *scheduled; const Scheduler_Node *scheduler_node; cpu = _Per_CPU_Get_by_index( 0 ); scheduled = cpu->heir; - name: Y states: - name: Task test-code: | T_eq_ptr( scheduled, ctx->tq_ctx.worker_tcb[ TASK ] ); text: | The task ``T`` shall be scheduled on processor ``Y``. - name: TaskIdle test-code: | T_true( scheduled->is_idle ); scheduler_node = _Thread_Scheduler_get_home_node( ctx->tq_ctx.worker_tcb[ TASK ] ); T_eq_ptr( scheduler_node->user, scheduled ); text: | An idle task on behalf of task ``T`` shall be scheduled on processor ``Y``. - name: Alpha test-code: | T_eq_ptr( scheduled, ctx->tq_ctx.worker_tcb[ ALPHA ] ); text: | The task ``A`` shall be scheduled on processor ``Y``. - name: AlphaIdle test-code: | T_true( scheduled->is_idle ); scheduler_node = _Thread_Scheduler_get_home_node( ctx->tq_ctx.worker_tcb[ ALPHA ] ); T_eq_ptr( scheduler_node->user, scheduled ); text: | An idle task on behalf of task ``A`` shall be scheduled on processor ``Y``. - name: Beta test-code: | T_eq_ptr( scheduled, ctx->tq_ctx.worker_tcb[ BETA ] ); text: | The task ``B`` shall be scheduled on processor ``Y``. - name: BetaIdle test-code: | T_true( scheduled->is_idle ); scheduler_node = _Thread_Scheduler_get_home_node( ctx->tq_ctx.worker_tcb[ BETA ] ); T_eq_ptr( scheduler_node->user, scheduled ); text: | An idle task on behalf of task ``B`` shall be scheduled on processor ``Y``. test-epilogue: null test-prologue: | const Per_CPU_Control *cpu; const Thread_Control *scheduled; const Scheduler_Node *scheduler_node; cpu = _Per_CPU_Get_by_index( 1 ); scheduled = cpu->heir; pre-conditions: - name: Before states: - name: All test-code: | CPU_FILL( &ctx->task_affinity_before ); text: | While task ``T`` is affine to all processors of its ${/glossary/scheduler-home:/term} before the new thread to processor affinity is set. - name: X test-code: | CPU_ZERO( &ctx->task_affinity_before ); CPU_SET( 0, &ctx->task_affinity_before ); text: | While task ``T`` is affine to processor ``X`` before the new thread to processor affinity is set. test-epilogue: null test-prologue: null - name: After states: - name: All test-code: | CPU_FILL( &ctx->task_affinity_after ); text: | While task ``T`` is set to be affine to all processors of its ${/glossary/scheduler-home:/term}. - name: X test-code: | CPU_ZERO( &ctx->task_affinity_after ); CPU_SET( 0, &ctx->task_affinity_after ); text: | While task ``T`` is set to be affine to processor ``X``. - name: Y test-code: | CPU_ZERO( &ctx->task_affinity_after ); CPU_SET( 1, &ctx->task_affinity_after ); text: | While task ``T`` is set to be affine to processor ``Y``. test-epilogue: null test-prologue: null - name: Priority states: - name: High test-code: | ctx->task_priority = PRIO_HIGH; text: | While task ``T`` has a high priority. - name: Low test-code: | ctx->task_priority = PRIO_NORMAL; text: | While task ``T`` has a low priority. test-epilogue: null test-prologue: null - name: State states: - name: Ready test-code: | ctx->task_ready = true; text: | While task ``T`` is ready. - name: Blocked test-code: | ctx->task_ready = false; text: | While task ``T`` is blocked. test-epilogue: null test-prologue: null - name: Sticky states: - name: 'Yes' test-code: | ctx->task_sticky = true; text: | While task ``T`` is sticky. - name: 'No' test-code: | ctx->task_sticky = false; text: | While task ``T`` is not sticky. test-epilogue: null test-prologue: null - name: Pinned states: - name: 'Yes' test-code: | ctx->task_pinned = true; text: | While task ``T`` is pinned to a processor. - name: 'No' test-code: | ctx->task_pinned = false; text: | While task ``T`` is not pinned to a processor. test-epilogue: null test-prologue: null - name: AlphaPriority states: - name: High test-code: | ctx->alpha_priority = PRIO_HIGH; text: | While task ``A`` has a high priority. - name: Low test-code: | ctx->alpha_priority = PRIO_NORMAL; text: | While task ``A`` has a low priority. test-epilogue: null test-prologue: null - name: AlphaAffinity states: - name: All test-code: | CPU_FILL( &ctx->alpha_affinity ); text: | While task ``A`` is affine to all processors of its ${/glossary/scheduler-home:/term}. - name: X test-code: | CPU_ZERO( &ctx->alpha_affinity ); CPU_SET( 0, &ctx->alpha_affinity ); text: | While task ``A`` is affine to processor ``X``. test-epilogue: null test-prologue: null - name: AlphaIdle states: - name: 'Yes' test-code: | ctx->alpha_idle = true; text: | While task ``A`` is sticky, while task ``A`` is blocked. - name: 'No' test-code: | ctx->alpha_idle = false; text: | While task ``A`` is not sticky. test-epilogue: null test-prologue: null - name: BetaPriority states: - name: High test-code: | ctx->beta_priority = PRIO_HIGH; text: | While task ``B`` has a high priority. - name: Low test-code: | ctx->beta_priority = PRIO_NORMAL; text: | While task ``B`` has a low priority. test-epilogue: null test-prologue: null - name: BetaAffinity states: - name: All test-code: | CPU_FILL( &ctx->beta_affinity ); text: | While task ``B`` is affine to all processors of its ${/glossary/scheduler-home:/term}. - name: Y test-code: | CPU_ZERO( &ctx->beta_affinity ); CPU_SET( 1, &ctx->beta_affinity ); text: | While task ``B`` is affine to processor ``Y``. test-epilogue: null test-prologue: null - name: BetaIdle states: - name: 'Yes' test-code: | ctx->beta_idle = true; text: | While task ``B`` is sticky, while task ``B`` is blocked. - name: 'No' test-code: | ctx->beta_idle = false; text: | While task ``B`` is not sticky, test-epilogue: null test-prologue: null rationale: null references: [] requirement-type: functional skip-reasons: NoStickyAndPinned: | Thread pinning while the thread owns a sticky mutex is undefined behaviour. test-action: | rtems_event_set events; SetSelfPriority( PRIO_ULTRA_HIGH ); SetSelfAffinityAll(); if ( ctx->beta_idle ) { events = TQ_EVENT_MUTEX_B_OBTAIN; TQSendAndWaitForExecutionStop( &ctx->tq_ctx, BETA, events ); } else { ctx->tq_ctx.busy_wait[ BETA ] = true; events = TQ_EVENT_BUSY_WAIT; TQSendAndSynchronizeRunner( &ctx->tq_ctx, BETA, events ); } if ( ctx->alpha_idle ) { events = TQ_EVENT_MUTEX_A_OBTAIN; TQSendAndWaitForExecutionStop( &ctx->tq_ctx, ALPHA, events ); } else { ctx->tq_ctx.busy_wait[ ALPHA ] = true; events = TQ_EVENT_BUSY_WAIT; TQSendAndSynchronizeRunner( &ctx->tq_ctx, ALPHA, events ); } if ( ctx->task_pinned ) { SetSelfAffinityOne( 1 ); TQSendAndSynchronizeRunner( &ctx->tq_ctx, TASK, TQ_EVENT_PIN ); SetSelfAffinityAll(); } if ( ctx->task_ready ) { ctx->tq_ctx.busy_wait[ TASK ] = true; events = TQ_EVENT_BUSY_WAIT; } else { events = 0; } if ( ctx->task_sticky ) { events |= TQ_EVENT_MUTEX_C_OBTAIN; } TQSendAndSynchronizeRunner( &ctx->tq_ctx, TASK, events ); if ( !ctx->task_ready ) { TQWaitForExecutionStop( &ctx->tq_ctx, TASK ); } (void) _Thread_Dispatch_disable(); SetAffinity( ctx->tq_ctx.worker_id[ TASK ], &ctx->task_affinity_before ); SetAffinity( ctx->tq_ctx.worker_id[ ALPHA ], &ctx->alpha_affinity ); SetAffinity( ctx->tq_ctx.worker_id[ BETA ], &ctx->beta_affinity ); SetSelfAffinityOne( 1 ); TQSetPriority( &ctx->tq_ctx, TASK, ctx->task_priority ); SetSelfPriority( PRIO_ULTRA_LOW ); TQSetPriority( &ctx->tq_ctx, ALPHA, ctx->alpha_priority ); TQSetPriority( &ctx->tq_ctx, BETA, ctx->beta_priority ); SetAffinity( ctx->tq_ctx.worker_id[ TASK ], &ctx->task_affinity_after ); test-brief: null test-cleanup: | rtems_event_set events; SetSelfPriority( PRIO_ULTRA_HIGH ); _Thread_Dispatch_enable( _Per_CPU_Get() ); SetSelfAffinityAll(); ctx->tq_ctx.busy_wait[ TASK ] = false; ctx->tq_ctx.busy_wait[ ALPHA ] = false; ctx->tq_ctx.busy_wait[ BETA ] = false; TQSetPriority( &ctx->tq_ctx, TASK, PRIO_NORMAL ); TQSetPriority( &ctx->tq_ctx, ALPHA, PRIO_LOW ); TQSetPriority( &ctx->tq_ctx, BETA, PRIO_VERY_LOW ); if ( ctx->task_sticky ) { events = TQ_EVENT_MUTEX_C_RELEASE; } else { events = 0; } if ( ctx->task_pinned ) { events |= TQ_EVENT_UNPIN; } if ( events != 0 ) { TQSendAndWaitForExecutionStop( &ctx->tq_ctx, TASK, events ); } else { TQWaitForExecutionStop( &ctx->tq_ctx, TASK ); } SetAffinityAll( ctx->tq_ctx.worker_id[ TASK ] ); SetAffinityAll( ctx->tq_ctx.worker_id[ ALPHA ] ); if ( ctx->alpha_idle ) { events = TQ_EVENT_MUTEX_A_RELEASE; } else { events = 0; } if ( events != 0 ) { TQSendAndWaitForExecutionStop( &ctx->tq_ctx, ALPHA, events ); } else { TQWaitForExecutionStop( &ctx->tq_ctx, ALPHA ); } SetAffinityAll( ctx->tq_ctx.worker_id[ BETA ] ); if ( ctx->beta_idle ) { events = TQ_EVENT_MUTEX_B_RELEASE; } else { events = 0; } if ( events != 0 ) { TQSendAndWaitForExecutionStop( &ctx->tq_ctx, BETA, events ); } else { TQWaitForExecutionStop( &ctx->tq_ctx, BETA ); } test-context: - brief: | This member contains the thread queue test context. description: null member: | TQContext tq_ctx - brief: | This member specifies the task affinity before changing the affinity. description: null member: | cpu_set_t task_affinity_before - brief: | This member specifies the task affinity after changing the affinity. description: null member: | cpu_set_t task_affinity_after - brief: | This member specifies the priority of the task. description: null member: | rtems_task_priority task_priority - brief: | If this member is true, then the task state shall be ready. description: null member: | bool task_ready - brief: | If this member is true, then the task shall have obtained a sticky mutex. description: null member: | bool task_sticky - brief: | If this member is true, then the task shall be pinned. description: null member: | bool task_pinned - brief: | This member specifies the priority of the alpha task. description: null member: | rtems_task_priority alpha_priority - brief: | This member specifies the affinity of the alpha task. description: null member: | cpu_set_t alpha_affinity - brief: | If this member is true, then an idle task shall execute on behalf of the alpha task. description: null member: | bool alpha_idle - brief: | This member specifies the priority of the beta task. description: null member: | rtems_task_priority beta_priority - brief: | This member specifies the affinity of the beta task. description: null member: | cpu_set_t beta_affinity - brief: | If this member is true, then an idle task shall execute on behalf of the beta task. description: null member: | bool beta_idle test-context-support: null test-description: null test-header: null test-includes: - rtems.h - rtems/score/percpu.h - rtems/score/threaddispatch.h - rtems/score/threadimpl.h test-local-includes: - tx-support.h - tx-thread-queue.h test-prepare: null test-setup: brief: null code: | rtems_status_code sc; rtems_id mutex_a; rtems_id mutex_b; rtems_id mutex_c; memset( ctx, 0, sizeof( *ctx ) ); ctx->tq_ctx.deadlock = TQ_DEADLOCK_STATUS; ctx->tq_ctx.enqueue_prepare = TQEnqueuePrepareDefault; ctx->tq_ctx.enqueue_done = TQEnqueueDoneDefault; ctx->tq_ctx.enqueue = TQEnqueueClassicSem; ctx->tq_ctx.surrender = TQSurrenderClassicSem; ctx->tq_ctx.get_owner = TQGetOwnerClassicSem; ctx->tq_ctx.convert_status = TQConvertStatusClassic; TQInitialize( &ctx->tq_ctx ); DeleteMutex( ctx->tq_ctx.mutex_id[ TQ_MUTEX_A ] ); DeleteMutex( ctx->tq_ctx.mutex_id[ TQ_MUTEX_B ] ); DeleteMutex( ctx->tq_ctx.mutex_id[ TQ_MUTEX_C ] ); mutex_a = 0; sc = rtems_semaphore_create( rtems_build_name( 'M', 'T', 'X', 'A' ), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_MULTIPROCESSOR_RESOURCE_SHARING, PRIO_LOW, &mutex_a ); T_rsc_success( sc ); mutex_b = 0; sc = rtems_semaphore_create( rtems_build_name( 'M', 'T', 'X', 'B' ), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_MULTIPROCESSOR_RESOURCE_SHARING, PRIO_VERY_LOW, &mutex_b ); T_rsc_success( sc ); mutex_c = 0; sc = rtems_semaphore_create( rtems_build_name( 'M', 'T', 'X', 'C' ), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_MULTIPROCESSOR_RESOURCE_SHARING, PRIO_NORMAL, &mutex_c ); T_rsc_success( sc ); ctx->tq_ctx.mutex_id[ TQ_MUTEX_A ] = mutex_a; ctx->tq_ctx.mutex_id[ TQ_MUTEX_B ] = mutex_b; ctx->tq_ctx.mutex_id[ TQ_MUTEX_C ] = mutex_c; RemoveProcessor( SCHEDULER_B_ID, 1 ); AddProcessor( SCHEDULER_A_ID, 1 ); TQSetPriority( &ctx->tq_ctx, TASK, PRIO_NORMAL ); TQSetPriority( &ctx->tq_ctx, ALPHA, PRIO_LOW ); TQSetPriority( &ctx->tq_ctx, BETA, PRIO_VERY_LOW ); description: null test-stop: null test-support: | #define TASK TQ_BLOCKER_C #define ALPHA TQ_BLOCKER_A #define BETA TQ_BLOCKER_B test-target: testsuites/validation/tc-sched-smp-edf-set-affinity.c test-teardown: brief: null code: | SetSelfAffinityAll(); TQDestroy( &ctx->tq_ctx ); RemoveProcessor( SCHEDULER_A_ID, 1 ); AddProcessor( SCHEDULER_B_ID, 1 ); description: null text: | When the thread to processor affinity is set for task ``T``. transition-map: - enabled-by: true post-conditions: X: - if: and: - pre-conditions: State: Ready Pinned: 'Yes' - or: - pre-conditions: Priority: High - pre-conditions: Priority: Low AlphaPriority: Low - pre-conditions: Priority: Low AlphaPriority: High AlphaAffinity: All BetaPriority: Low then: Task - if: and: - pre-conditions: After: - All - X Priority: High State: Ready - or: - pre-conditions: AlphaPriority: Low AlphaAffinity: All BetaPriority: Low BetaAffinity: All - pre-conditions: AlphaPriority: High AlphaAffinity: All BetaPriority: Low BetaAffinity: All - pre-conditions: AlphaPriority: Low BetaPriority: High then: Task - if: and: - pre-conditions: After: - All - X Priority: High State: Blocked Sticky: 'Yes' - or: - pre-conditions: AlphaPriority: Low AlphaAffinity: All BetaPriority: Low BetaAffinity: All - pre-conditions: AlphaPriority: High AlphaAffinity: All BetaPriority: Low BetaAffinity: All - pre-conditions: AlphaPriority: Low BetaPriority: High then: TaskIdle - if: and: - pre-conditions: After: X Priority: High State: Ready - or: - pre-conditions: AlphaPriority: Low - pre-conditions: AlphaAffinity: All BetaPriority: Low then: Task - if: and: - pre-conditions: After: X Priority: High State: Blocked Sticky: 'Yes' - or: - pre-conditions: AlphaPriority: Low - pre-conditions: AlphaAffinity: All BetaPriority: Low then: TaskIdle - if: and: - pre-conditions: BetaAffinity: All BetaIdle: 'No' - or: - pre-conditions: State: Ready - pre-conditions: State: Blocked Sticky: 'Yes' - or: - pre-conditions: After: Y Priority: High AlphaPriority: Low BetaPriority: High - pre-conditions: Priority: High AlphaPriority: High AlphaAffinity: All BetaPriority: High - pre-conditions: Priority: Low AlphaAffinity: All BetaPriority: Low - pre-conditions: Priority: Low AlphaPriority: High AlphaAffinity: All BetaPriority: High then: Beta - if: and: - pre-conditions: BetaAffinity: All - or: - pre-conditions: State: Ready - pre-conditions: State: Blocked Sticky: 'Yes' - or: - pre-conditions: After: Y Priority: High AlphaPriority: Low BetaPriority: High - pre-conditions: Priority: High AlphaPriority: High AlphaAffinity: All BetaPriority: High - pre-conditions: Priority: Low AlphaAffinity: All BetaPriority: Low BetaAffinity: All - pre-conditions: Priority: Low AlphaPriority: High AlphaAffinity: All BetaPriority: High then: BetaIdle - if: pre-conditions: AlphaIdle: 'Yes' then: AlphaIdle - else: Alpha Y: - if: and: - post-conditions: X: - Alpha - AlphaIdle - pre-conditions: After: - All - Y Priority: High State: Ready BetaPriority: Low then: Task - if: and: - post-conditions: X: - Alpha - AlphaIdle - pre-conditions: After: - All - Y Priority: High State: Blocked Sticky: 'Yes' BetaPriority: Low then: TaskIdle - if: and: - post-conditions: X: - Beta - BetaIdle - pre-conditions: After: - All - Y Priority: High State: Ready AlphaPriority: Low then: Task - if: and: - post-conditions: X: - Beta - BetaIdle - pre-conditions: After: - All - Y Priority: High State: Blocked Sticky: 'Yes' AlphaPriority: Low then: TaskIdle - if: and: - pre-conditions: AlphaIdle: 'No' - post-conditions: X: - Beta - BetaIdle then: Alpha - if: post-conditions: X: - Beta - BetaIdle then: AlphaIdle - if: and: - pre-conditions: AlphaAffinity: All AlphaIdle: 'No' - post-conditions: X: - Task - TaskIdle - or: - pre-conditions: AlphaPriority: High - pre-conditions: BetaPriority: Low then: Alpha - if: and: - pre-conditions: AlphaAffinity: All - post-conditions: X: - Task - TaskIdle - or: - pre-conditions: AlphaPriority: High - pre-conditions: BetaPriority: Low then: AlphaIdle - if: pre-conditions: BetaIdle: 'Yes' then: BetaIdle - else: Beta pre-conditions: Before: all After: all Priority: all State: all Sticky: all Pinned: all AlphaPriority: all AlphaAffinity: all AlphaIdle: all BetaPriority: all BetaAffinity: all BetaIdle: all - enabled-by: true post-conditions: NoStickyAndPinned pre-conditions: Before: all After: all Priority: all State: all Sticky: - 'Yes' Pinned: - 'Yes' AlphaPriority: all AlphaAffinity: all AlphaIdle: all BetaPriority: all BetaAffinity: all BetaIdle: all type: requirement