SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
copyrights:
- Copyright (C) 2021, 2022 embedded brains GmbH & Co. KG
enabled-by: RTEMS_SMP
links: []
test-actions:
- action-brief: |
Construct a system state in which a sticky thread is blocked while an idle
thread executes on behalf of the thread.
action-code: |
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_STICKY_OBTAIN );
MakeBusy( ctx, WORKER_A );
WaitForBusy( ctx, WORKER_A );
checks:
- brief: |
Block the sticky worker A while it uses an idle thread in the home
scheduler.
code: |
CallWithinISR( BlockAndReuseIdle, ctx );
links:
- role: validation
uid: ../req/block-reuse-idle
- brief: |
Clean up all used resources.
code: |
StopBusy( ctx, WORKER_A );
SendAndSync( ctx, WORKER_A, EVENT_STICKY_RELEASE );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SetSelfPriority( PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
links: []
links: []
- action-brief: |
Construct a system state in which a thread is preempted while it is
blocked.
action-code: |
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
MakeBusy( ctx, WORKER_A );
WaitForBusy( ctx, WORKER_A );
checks:
- brief: |
Block worker A and preempt it before the withdraw node operations are
performed for worker A.
code: |
T_scheduler_set_event_handler( BlockAndPreempt, ctx );
SuspendTask( ctx->worker_id[ WORKER_A ] );
links:
- role: validation
uid: ../req/preempt-blocked
- brief: |
Clean up all used resources.
code: |
ResumeTask( ctx->worker_id[ WORKER_A ] );
StopBusy( ctx, WORKER_C );
StopBusy( ctx, WORKER_A );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SetSelfPriority( PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
links: []
links: []
- action-brief: |
Construct a system state in which a thread is rescheduled while it is not
scheduled on another scheduler.
action-code: |
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_STICKY_OBTAIN );
MakeBusy( ctx, WORKER_A );
WaitForBusy( ctx, WORKER_A );
checks:
- brief: |
Reschedule worker A by the home scheduler while worker A is not scheduled
on another scheduler.
code: |
CallWithinISR( ReadyToScheduled, ctx );
links:
- role: validation
uid: ../req/idle-to-scheduled
- brief: |
Clean up all used resources.
code: |
StopBusy( ctx, WORKER_C );
StopBusy( ctx, WORKER_A );
SendAndSync( ctx, WORKER_A, EVENT_STICKY_RELEASE );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SetSelfPriority( PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
links: []
links: []
- action-brief: |
Construct a system state in which an ask for help request is cancelled
while it is processed on another processor.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Unblock worker A. It cannot be scheduled on its home scheduler.
Intercept the ask for help request. Block the worker A. This will
cancel the ask for help request. Remove the request while the other
processor tries to cancel the request.
code: |
SuspendTask( ctx->worker_id[ WORKER_A ] );
T_scheduler_set_event_handler( UnblockAskForHelp, ctx );
ResumeTask( ctx->worker_id[ WORKER_A ] );
links:
- role: validation
uid: ../req/ask-for-help-request
- brief: |
Clean up all used resources.
code: |
ResumeTask( ctx->worker_id[ WORKER_A ] );
StopBusy( ctx, WORKER_C );
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a block operation.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Block the runner thread while the owner thread of the highest priority
ready node is already scheduled.
code: |
T_scheduler_set_event_handler( BlockStopBusyC, ctx );
CallWithinISR( Block, ctx );
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is blocked during a block operation.
action-code: |
PrepareOwnerBlocked( ctx );
checks:
- brief: |
Block the runner thread while the owner thread of the highest priority
ready node is blocked.
code: |
T_scheduler_set_event_handler( BlockSuspendA, ctx );
CallWithinISR( Block, ctx );
links:
- role: validation
uid: ../req/ask-for-help-helping
- brief: |
Clean up all used resources.
code: |
CleanupOwnerBlocked( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a set affinity operation.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Set the affinity of the runner thread while the owner thread of the
highest priority ready node is already scheduled.
code: |
T_scheduler_set_event_handler( SetAffinityStopBusyC, ctx );
SetSelfAffinityAll();
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a set affinity operation
while a sticky node is involved.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Set the affinity of the runner thread while the owner thread of the
highest priority ready node is already scheduled.
code: |
MakeSticky( ctx );
T_scheduler_set_event_handler( SetAffinityStopBusyC, ctx );
SetSelfAffinityAll();
CleanSticky( ctx );
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is blocked during a set affinity operation.
action-code: |
PrepareOwnerBlocked( ctx );
checks:
- brief: |
Set the affinity of the runner thread while the owner thread of the
highest priority ready node is blocked.
code: |
T_scheduler_set_event_handler( SetAffinitySuspendA, ctx );
SetSelfAffinityAll();
links:
- role: validation
uid: ../req/ask-for-help-helping
- brief: |
Clean up all used resources.
code: |
CleanupOwnerBlocked( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is blocked during a set affinity operation while a
sticky node is involved.
action-code: |
PrepareOwnerBlocked( ctx );
checks:
- brief: |
Set the affinity of the runner thread while the owner thread of the
highest priority ready node is blocked.
code: |
MakeSticky( ctx );
T_scheduler_set_event_handler( SetAffinitySuspendA, ctx );
SetSelfAffinityAll();
CleanSticky( ctx );
links:
- role: validation
uid: ../req/ask-for-help-helping
- brief: |
Clean up all used resources.
code: |
CleanupOwnerBlocked( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a set priority operation.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Set the priority of the runner thread while the owner thread of the
highest priority ready node is already scheduled.
code: |
SetSelfPriority( PRIO_HIGH );
T_scheduler_set_event_handler( UpdatePriorityStopBusyC, ctx );
SetSelfPriority( PRIO_NORMAL );
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a set priority operation
while a sticky node is involved.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Set the priority of the runner thread while the owner thread of the
highest priority ready node is already scheduled.
code: |
MakeSticky( ctx );
CallWithinISR( RaiseWorkerPriorityWithIdleRunner, ctx );
CleanSticky( ctx );
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is blocked during a set priority operation.
action-code: |
PrepareOwnerBlocked( ctx );
checks:
- brief: |
Set the priority of the runner thread while the owner thread of the
highest priority ready node is blocked.
code: |
SetSelfPriority( PRIO_HIGH );
T_scheduler_set_event_handler( UpdatePrioritySuspendA, ctx );
SetSelfPriority( PRIO_NORMAL );
links:
- role: validation
uid: ../req/ask-for-help-helping
- brief: |
Clean up all used resources.
code: |
CleanupOwnerBlocked( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a yield operation.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Yield while the owner thread of the highest priority ready node is
already scheduled.
code: |
T_scheduler_set_event_handler( YieldStopBusyC, ctx );
Yield();
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is already scheduled during a yield operation while a
sticky node is involved.
action-code: |
PrepareOwnerScheduled( ctx );
checks:
- brief: |
Yield while the owner thread of the highest priority ready node is
already scheduled.
code: |
MakeSticky( ctx );
T_scheduler_set_event_handler( YieldStopBusyC, ctx );
Yield();
CleanSticky( ctx );
links:
- role: validation
uid: ../req/ask-for-help-home
- brief: |
Clean up all used resources.
code: |
CleanupOwnerScheduled( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is blocked during a yield operation.
action-code: |
PrepareOwnerBlocked( ctx );
checks:
- brief: |
Yield while the owner thread of the highest priority ready node is
blocked.
code: |
T_scheduler_set_event_handler( YieldSuspendA, ctx );
Yield();
links:
- role: validation
uid: ../req/ask-for-help-helping
- brief: |
Clean up all used resources.
code: |
CleanupOwnerBlocked( ctx );
links: []
links: []
- action-brief: |
Construct a system state in which a scheduler tries to schedule a node
those owner thread is blocked during a yield operation while a sticky node
is involved.
action-code: |
PrepareOwnerBlocked( ctx );
checks:
- brief: |
Yield while the owner thread of the highest priority ready node is
blocked.
code: |
MakeSticky( ctx );
T_scheduler_set_event_handler( YieldSuspendA, ctx );
Yield();
CleanSticky( ctx );
links:
- role: validation
uid: ../req/ask-for-help-helping
- brief: |
Clean up all used resources.
code: |
CleanupOwnerBlocked( ctx );
links: []
links: []
- action-brief: |
Create three worker threads and a mutex. Use the mutex and the worker to
check that a not scheduled thread does not get removed from the set of
ready threads of a scheduler when a help request is reconsidered.
action-code: |
Thread_Control *worker_b;
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_LOW );
MakeBusy( ctx, WORKER_B );
WaitForBusy( ctx, WORKER_B );
MakeBusy( ctx, WORKER_C );
WaitForBusy( ctx, WORKER_C );
checks:
- brief: |
Prevent that worker B can perform a post-switch cleanup.
code: |
worker_b = GetThread( ctx->worker_id[ WORKER_B ] );
_Thread_State_acquire( worker_b, &ctx->lock_context );
_ISR_lock_ISR_enable( &ctx->lock_context );
links: []
- brief: |
Give worker C a lower priority than worker B. Worker B will try to
finish the thread dispatch by doing a post-switch cleanup. The
post-switch cleanup cannot progress since the runner owns the thread
state lock. Wait until the other processor waits on the thread state
lock of worker B.
code: |
SetPriority( ctx->worker_id[ WORKER_C ], PRIO_LOW );
TicketLockWaitForOthers( &worker_b->Join_queue.Queue.Lock, 1 );
links: []
- brief: |
Give worker C a higher priority than worker B. Let worker B do its
post-switch cleanup which will carry out the reconsider help requests for
a not scheduled thread.
code: |
ctx->counter = 0;
T_scheduler_set_event_handler( ReleaseThreadLockB, ctx );
SetPriority( ctx->worker_id[ WORKER_C ], PRIO_HIGH );
T_scheduler_set_event_handler( NULL, NULL );
T_eq_u32( ctx->counter, 4 );
links:
- role: validation
uid: ../req/reconsider-help-keep-ready
- brief: |
Clean up all used resources.
code: |
StopBusy( ctx, WORKER_B );
StopBusy( ctx, WORKER_C );
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
links: []
links: []
test-brief: |
Tests SMP-specific scheduler behaviour.
test-context:
- brief: |
This member contains the runner identifier.
description: null
member: |
rtems_id runner_id
- brief: |
This member contains the worker identifiers.
description: null
member: |
rtems_id worker_id[ WORKER_COUNT ]
- brief: |
This member contains the mutex identifier.
description: null
member: |
rtems_id mutex_id
- brief: |
This member contains the sticky mutex identifier.
description: null
member: |
rtems_id sticky_id
- brief: |
This member contains the worker busy status.
description: null
member: |
volatile bool busy[ WORKER_COUNT ]
- brief: |
This member contains an ISR lock context.
description: null
member: |
ISR_lock_Context lock_context
- brief: |
This member contains a counter.
description: null
member: |
uint32_t counter
- brief: |
If this member is true, then the worker shall be in the busy loop.
description: null
member: |
volatile bool is_busy[ WORKER_COUNT ]
- brief: |
This member contains the per-CPU jobs.
description: null
member: |
Per_CPU_Job job[ 2 ]
- brief: |
This member contains the per-CPU job contexts.
description: null
member: |
Per_CPU_Job_context job_context[ 2 ]
- brief: |
This member contains the call within ISR request.
description: null
member: |
CallWithinISRRequest request
test-context-support: |
typedef enum {
WORKER_A,
WORKER_B,
WORKER_C,
WORKER_COUNT
} WorkerIndex;
test-description: null
test-header: null
test-includes:
- rtems.h
- rtems/test-scheduler.h
- rtems/score/percpu.h
- rtems/score/schedulersmp.h
- rtems/score/threadimpl.h
test-local-includes:
- tx-support.h
test-setup:
brief: null
code: |
rtems_status_code sc;
size_t i;
ctx->runner_id = rtems_task_self();
ctx->mutex_id = CreateMutex();
for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->job ); ++i ) {
ctx->job_context[ i ].arg = ctx;
ctx->job[ i ].context = &ctx->job_context[ i ];
}
sc = rtems_semaphore_create(
rtems_build_name( 'S', 'T', 'K', 'Y' ),
1,
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY |
RTEMS_MULTIPROCESSOR_RESOURCE_SHARING,
PRIO_NORMAL,
&ctx->sticky_id
);
T_rsc_success( sc );
SetSelfPriority( PRIO_NORMAL );
ctx->worker_id[ WORKER_A ] = CreateTask( "WRKA", PRIO_HIGH );
StartTask( ctx->worker_id[ WORKER_A ], WorkerA, ctx );
ctx->worker_id[ WORKER_B ] = CreateTask( "WRKB", PRIO_HIGH );
StartTask( ctx->worker_id[ WORKER_B ], WorkerB, ctx );
ctx->worker_id[ WORKER_C ] = CreateTask( "WRKC", PRIO_HIGH );
StartTask( ctx->worker_id[ WORKER_C ], WorkerC, ctx );
description: null
test-stop: null
test-support: |
#define EVENT_OBTAIN RTEMS_EVENT_0
#define EVENT_RELEASE RTEMS_EVENT_1
#define EVENT_STICKY_OBTAIN RTEMS_EVENT_2
#define EVENT_STICKY_RELEASE RTEMS_EVENT_3
#define EVENT_SYNC_RUNNER RTEMS_EVENT_4
#define EVENT_BUSY RTEMS_EVENT_5
typedef ${.:/test-context-type} Context;
static void SendAndSync(
Context *ctx,
WorkerIndex worker,
rtems_event_set event
)
{
SendEvents( ctx->worker_id[ worker ], EVENT_SYNC_RUNNER | event );
ReceiveAllEvents( EVENT_SYNC_RUNNER );
WaitForExecutionStop( ctx->worker_id[ worker ] );
}
static void MakeBusy( Context *ctx, WorkerIndex worker )
{
ctx->is_busy[ worker ] = false;
ctx->busy[ worker ] = true;
SendEvents( ctx->worker_id[ worker ], EVENT_BUSY );
}
static void WaitForBusy( Context *ctx, WorkerIndex worker )
{
while ( !ctx->is_busy[ worker ] ) {
/* Wait */
}
}
static void StopBusy( Context *ctx, WorkerIndex worker )
{
ctx->busy[ worker ] = false;
WaitForExecutionStop( ctx->worker_id[ worker ] );
}
static void MakeSticky( const Context *ctx )
{
ObtainMutex( ctx->sticky_id );
}
static void CleanSticky( const Context *ctx )
{
ReleaseMutex( ctx->sticky_id );
}
static void Block( void *arg )
{
Context *ctx;
ctx = arg;
SuspendTask( ctx->runner_id );
ResumeTask( ctx->runner_id );
}
static void OperationStopBusyC(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when,
T_scheduler_operation op
)
{
Context *ctx;
ctx = arg;
if ( when == T_SCHEDULER_BEFORE && event->operation == op ) {
T_scheduler_set_event_handler( NULL, NULL );
StopBusy( ctx, WORKER_C );
}
}
static void BlockStopBusyC(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationStopBusyC( arg, event, when, T_SCHEDULER_BLOCK );
}
static void SetAffinityStopBusyC(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationStopBusyC( arg, event, when, T_SCHEDULER_SET_AFFINITY );
}
static void UpdatePriorityStopBusyC(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationStopBusyC( arg, event, when, T_SCHEDULER_UPDATE_PRIORITY );
}
static void YieldStopBusyC(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationStopBusyC( arg, event, when, T_SCHEDULER_YIELD );
}
static void SuspendA( void *arg )
{
Context *ctx;
ctx = arg;
SuspendTask( ctx->worker_id[ WORKER_A ] );
}
static void OperationSuspendA(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when,
T_scheduler_operation op
)
{
Context *ctx;
ctx = arg;
if ( when == T_SCHEDULER_BEFORE && event->operation == op ) {
const rtems_tcb *worker_a;
T_scheduler_set_event_handler( NULL, NULL );
ctx->job_context[ 0 ].handler = SuspendA;
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
worker_a = GetThread( ctx->worker_id[ WORKER_A ] );
while ( worker_a->Scheduler.state != THREAD_SCHEDULER_BLOCKED ) {
RTEMS_COMPILER_MEMORY_BARRIER();
}
}
}
static void BlockSuspendA(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationSuspendA( arg, event, when, T_SCHEDULER_BLOCK );
}
static void SetAffinitySuspendA(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationSuspendA( arg, event, when, T_SCHEDULER_SET_AFFINITY );
}
static void UpdatePrioritySuspendA(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationSuspendA( arg, event, when, T_SCHEDULER_UPDATE_PRIORITY );
}
static void YieldSuspendA(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
OperationSuspendA( arg, event, when, T_SCHEDULER_YIELD );
}
static void GuideAskForHelp( void *arg )
{
Context *ctx;
Per_CPU_Control *cpu;
ISR_lock_Context lock_context;
ctx = arg;
cpu = _Per_CPU_Get_by_index( 0 );
_ISR_lock_ISR_disable( &lock_context );
_Per_CPU_Acquire( cpu, &lock_context );
ISRLockWaitForOthers( &cpu->Lock, 1 );
ctx->job_context[ 0 ].handler = SuspendA;
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
ISRLockWaitForOthers( &cpu->Lock, 2 );
_Per_CPU_Release( cpu, &lock_context );
_ISR_lock_ISR_enable( &lock_context );
}
static void InterceptAskForHelp( void *arg )
{
Context *ctx;
Per_CPU_Control *cpu_self;
ctx = arg;
cpu_self = _Per_CPU_Get();
if ( rtems_scheduler_get_processor_maximum() > 2 ) {
ctx->job_context[ 1 ].handler = GuideAskForHelp;
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 2 ), &ctx->job[ 1 ] );
ISRLockWaitForOwned( &cpu_self->Lock );
} else {
ISR_lock_Context lock_context;
Chain_Node *node;
Thread_Control *thread;
_ISR_lock_ISR_disable( &lock_context );
_Per_CPU_Acquire( cpu_self, &lock_context );
ctx->job_context[ 0 ].handler = SuspendA;
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
ISRLockWaitForOthers( &cpu_self->Lock, 1 );
/* See _Thread_Preemption_intervention() */
node = _Chain_Get_first_unprotected( &cpu_self->Threads_in_need_for_help );
thread = THREAD_OF_SCHEDULER_HELP_NODE( node );
T_assert_eq_ptr( thread, GetThread( ctx->worker_id[ WORKER_A ] ) );
thread->Scheduler.ask_for_help_cpu = NULL;
_Per_CPU_Release( cpu_self, &lock_context );
_ISR_lock_ISR_enable( &lock_context );
}
}
static void UnblockAskForHelp(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
Context *ctx;
ctx = arg;
if (
when == T_SCHEDULER_BEFORE &&
event->operation == T_SCHEDULER_UNBLOCK
) {
T_scheduler_set_event_handler( NULL, NULL );
ctx->request.handler = InterceptAskForHelp;
ctx->request.arg = ctx;
CallWithinISRSubmit( &ctx->request );
}
}
static void RaiseWorkerPriorityWithIdleRunner( void *arg )
{
Context *ctx;
ctx = arg;
SuspendTask( ctx->runner_id );
T_scheduler_set_event_handler( UpdatePriorityStopBusyC, ctx );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
ResumeTask( ctx->runner_id );
}
static void MakeReady( void *arg )
{
Context *ctx;
ctx = arg;
MakeBusy( ctx, WORKER_C );
}
static void UpdatePriorityMakeReady(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
Context *ctx;
ctx = arg;
if (
when == T_SCHEDULER_BEFORE &&
event->operation == T_SCHEDULER_UPDATE_PRIORITY
) {
Thread_Control *thread;
T_scheduler_set_event_handler( NULL, NULL );
thread = GetThread( ctx->worker_id[ WORKER_A ] );
T_eq_int( thread->Scheduler.state, THREAD_SCHEDULER_SCHEDULED );
ctx->job_context[ 0 ].handler = MakeReady;
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
while ( thread->Scheduler.state != THREAD_SCHEDULER_READY ) {
RTEMS_COMPILER_MEMORY_BARRIER();
}
}
}
static void ReadyToScheduled( void *arg )
{
Context *ctx;
ctx = arg;
SuspendTask( ctx->runner_id );
T_scheduler_set_event_handler( UpdatePriorityMakeReady, ctx );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SetPriority( ctx->runner_id, PRIO_VERY_HIGH );
ResumeTask( ctx->runner_id );
}
static void BlockAndReuseIdle( void *arg )
{
Context *ctx;
ctx = arg;
SuspendTask( ctx->runner_id );
SuspendTask( ctx->worker_id[ WORKER_A ] );
ResumeTask( ctx->worker_id[ WORKER_A ] );
SetPriority( ctx->runner_id, PRIO_HIGH );
ResumeTask( ctx->runner_id );
}
static void Preempt( void *arg )
{
Context *ctx;
ctx = arg;
MakeBusy( ctx, WORKER_C );
}
static void BlockAndPreempt(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
Context *ctx;
ctx = arg;
if ( when == T_SCHEDULER_AFTER && event->operation == T_SCHEDULER_BLOCK ) {
Thread_Control *thread;
T_scheduler_set_event_handler( NULL, NULL );
thread = GetThread( ctx->worker_id[ WORKER_A ] );
T_eq_int( thread->Scheduler.state, THREAD_SCHEDULER_BLOCKED );
ctx->job_context[ 0 ].handler = Preempt;
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
_Per_CPU_Wait_for_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
}
}
static void PrepareOwnerScheduled( Context *ctx )
{
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
MakeBusy( ctx, WORKER_C );
MakeBusy( ctx, WORKER_A );
}
static void CleanupOwnerScheduled( Context *ctx )
{
StopBusy( ctx, WORKER_A );
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
}
static void PrepareOwnerBlocked( Context *ctx )
{
SetScheduler( ctx->worker_id[ WORKER_A ], SCHEDULER_B_ID, PRIO_NORMAL );
SendAndSync( ctx, WORKER_A, EVENT_OBTAIN );
SendEvents( ctx->worker_id[ WORKER_B ], EVENT_OBTAIN );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
MakeBusy( ctx, WORKER_C );
SetPriority( ctx->worker_id[ WORKER_B ], PRIO_LOW );
MakeBusy( ctx, WORKER_A );
SetPriority( ctx->worker_id[ WORKER_B ], PRIO_NORMAL );
}
static void CleanupOwnerBlocked( Context *ctx )
{
StopBusy( ctx, WORKER_C );
ResumeTask( ctx->worker_id[ WORKER_A ] );
StopBusy( ctx, WORKER_A );
SendAndSync( ctx, WORKER_A, EVENT_RELEASE );
SetPriority( ctx->worker_id[ WORKER_B ], PRIO_HIGH );
SendEvents( ctx->worker_id[ WORKER_B ], EVENT_RELEASE );
SetScheduler( ctx->worker_id[ WORKER_A ], SCHEDULER_A_ID, PRIO_HIGH );
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
}
static void ReconsiderHelpRequestB(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
Context *ctx;
(void) when;
ctx = arg;
if ( event->operation == T_SCHEDULER_RECONSIDER_HELP_REQUEST ) {
Scheduler_SMP_Node *node;
node = (Scheduler_SMP_Node *) event->node;
T_eq_int( node->state, SCHEDULER_SMP_NODE_READY );
++ctx->counter;
}
}
static void ReleaseThreadLockB(
void *arg,
const T_scheduler_event *event,
T_scheduler_when when
)
{
Context *ctx;
ctx = arg;
if (
when == T_SCHEDULER_AFTER &&
event->operation == T_SCHEDULER_UPDATE_PRIORITY
) {
Thread_Control *worker_b;
T_scheduler_set_event_handler( ReconsiderHelpRequestB, ctx );
worker_b = GetThread( ctx->worker_id[ WORKER_B ] );
T_eq_int( worker_b->Scheduler.state, THREAD_SCHEDULER_READY );
_Thread_State_release_critical( worker_b, &ctx->lock_context );
}
}
static void Worker( rtems_task_argument arg, WorkerIndex worker )
{
Context *ctx;
ctx = (Context *) arg;
while ( true ) {
rtems_event_set events;
events = ReceiveAnyEvents();
if ( ( events & EVENT_SYNC_RUNNER ) != 0 ) {
SendEvents( ctx->runner_id, EVENT_SYNC_RUNNER );
}
if ( ( events & EVENT_OBTAIN ) != 0 ) {
ObtainMutex( ctx->mutex_id );
}
if ( ( events & EVENT_RELEASE ) != 0 ) {
ReleaseMutex( ctx->mutex_id );
}
if ( ( events & EVENT_STICKY_OBTAIN ) != 0 ) {
ObtainMutex( ctx->sticky_id );
}
if ( ( events & EVENT_STICKY_RELEASE ) != 0 ) {
ReleaseMutex( ctx->sticky_id );
}
if ( ( events & EVENT_BUSY ) != 0 ) {
ctx->is_busy[ worker ] = true;
while ( ctx->busy[ worker ] ) {
/* Wait */
}
ctx->is_busy[ worker ] = false;
}
}
}
static void WorkerA( rtems_task_argument arg )
{
Worker( arg, WORKER_A );
}
static void WorkerB( rtems_task_argument arg )
{
Worker( arg, WORKER_B );
}
static void WorkerC( rtems_task_argument arg )
{
Worker( arg, WORKER_C );
}
test-target: testsuites/validation/tc-sched-smp.c
test-teardown:
brief: null
code: |
DeleteTask( ctx->worker_id[ WORKER_A ] );
DeleteTask( ctx->worker_id[ WORKER_B ] );
DeleteTask( ctx->worker_id[ WORKER_C ] );
DeleteMutex( ctx->mutex_id );
DeleteMutex( ctx->sticky_id );
RestoreRunnerPriority();
description: null
type: test-case