testsuites/smptests: Modify smpspinwait01 test to use Classic API

As a result of review comments, changes made to test in order
to utilize RTEMS_BARRIER_CREATE and other Classic API functions
This commit is contained in:
Wayne Thornton
2026-07-23 16:09:49 -06:00
parent 8e7ba95423
commit 33205ab2ef
5 changed files with 126 additions and 64 deletions
@@ -452,9 +452,6 @@ static inline void _CPU_Spin_wait( void )
__asm__ volatile( "pause" ::: "memory" );
}
/* Explicitly define the macro so the preprocessor can see it in DHRL API */
#define _CPU_Spin_wait _CPU_Spin_wait
#ifdef __cplusplus
}
#endif
@@ -6,7 +6,10 @@ copyrights:
cppflags: []
cxxflags: []
enabled-by:
- RTEMS_SMP
and:
- RTEMS_SMP
- or:
- x86_64
features: c cprogram
includes: []
ldflags: []
@@ -17,4 +20,4 @@ stlib: []
target: testsuites/smptests/smpspinwait01.exe
type: build
use-after: []
use-before: []
use-before: []
+95 -59
View File
@@ -6,7 +6,7 @@
* @brief CPU_Spin_wait() Test 1
*
* @note This test verifies the basic functionality of CPU_Spin_wait() by performing
* a large number of spin waits across multiple CPU cores, ensuring that the
* a number of spin waits across multiple CPU cores, ensuring that the
* spin wait functionality works correctly under SMP conditions.
*
* @ingroup SMP
@@ -39,42 +39,38 @@
#include <rtems.h>
#include <rtems/score/cpu.h>
#include <rtems/score/smpbarrier.h>
#include <rtems/test.h>
#include <rtems/test-info.h>
#include <tmacros.h>
const char rtems_test_name[] = "SMPSPINWAIT 1";
#define RING_PASS_ROUNDS 5000U
#define RING_PASS_ROUNDS 10U
typedef struct {
SMP_barrier_Control barrier;
SMP_barrier_State master_state;
SMP_barrier_State worker_states[ CPU_MAXIMUM_PROCESSORS ];
rtems_id barrier_id;
/* Phase 1: Shared read contention */
volatile uint32_t broadcast_flag;
volatile uint32_t phase1_acks;
volatile uint32_t broadcast_flag;
volatile uint32_t phase1_acks;
/*
* Phase 2: Intentionally non-volatile. Proves _CPU_Spin_wait()
* enforces a compiler memory barrier across SMP core boundaries.
*/
uint32_t non_volatile_flag;
volatile uint32_t phase2_acks;
uint32_t non_volatile_flag;
volatile uint32_t phase2_acks;
/* Phase 3: Cache-line bouncing ring token */
volatile uint32_t ring_token;
volatile uint32_t ring_token;
} TestContext;
static TestContext test_instance;
static void execute_ring_stress(
TestContext *ctx,
uint32_t cpu_index,
uint32_t cpu_count
static void execute_ring_stress(
TestContext *ctx,
uint32_t cpu_index,
uint32_t cpu_count
)
{
uint32_t total_steps;
@@ -106,24 +102,24 @@ static void test_worker_task( rtems_task_argument arg )
cpu_count = rtems_scheduler_get_processor_maximum();
/* --- Phase 1: Read-Shared Bus Contention --- */
_SMP_barrier_Wait( &ctx->barrier, &ctx->worker_states[ cpu_index ], cpu_count );
rtems_barrier_wait( ctx->barrier_id, RTEMS_NO_TIMEOUT );
while ( __atomic_load_n( &ctx->broadcast_flag, __ATOMIC_ACQUIRE ) == 0 ) {
_CPU_Spin_wait();
}
__atomic_add_fetch( &ctx->phase1_acks, 1, __ATOMIC_RELEASE );
/* --- Phase 2: Compiler Memory Barrier Verification --- */
_SMP_barrier_Wait( &ctx->barrier, &ctx->worker_states[ cpu_index ], cpu_count );
rtems_barrier_wait( ctx->barrier_id, RTEMS_NO_TIMEOUT );
while ( ctx->non_volatile_flag == 0 ) {
_CPU_Spin_wait();
}
__atomic_add_fetch( &ctx->phase2_acks, 1, __ATOMIC_RELEASE );
/* --- Phase 3: MESI Cache-Line Bouncing Stress --- */
_SMP_barrier_Wait( &ctx->barrier, &ctx->worker_states[ cpu_index ], cpu_count );
rtems_barrier_wait( ctx->barrier_id, RTEMS_NO_TIMEOUT );
execute_ring_stress( ctx, cpu_index, cpu_count );
rtems_task_exit();
@@ -139,81 +135,120 @@ static void Init( rtems_task_argument arg )
/* Silence -Werror=unused-parameter for standard task signature */
(void) arg;
TEST_BEGIN();
cpu_count = rtems_scheduler_get_processor_maximum();
/* Enforce SMP execution environment */
if ( cpu_count < 2 ) {
puts( "TEST SKIPPED: SMP spin-wait validation requires at least 2 processors." );
puts(
"TEST SKIPPED: SMP spin-wait validation requires at least 2 processors."
);
TEST_END();
rtems_test_exit( 0 );
}
printf( "Running SMP spin-wait validation across %" PRIu32 " cores...\n", cpu_count );
printf(
"Running SMP spin-wait validation across %" PRIu32 " cores...\n",
cpu_count
);
sc = rtems_barrier_create(
rtems_build_name( 'B', 'A', 'R', 'R' ),
RTEMS_BARRIER_AUTOMATIC_RELEASE,
cpu_count,
&test_instance.barrier_id
);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
_SMP_barrier_Control_initialize( &test_instance.barrier );
test_instance.broadcast_flag = 0;
test_instance.phase1_acks = 0;
test_instance.non_volatile_flag = 0;
test_instance.phase2_acks = 0;
test_instance.ring_token = 0;
/* Spawn worker tasks pinned to secondary processors */
for ( cpu_index = 1; cpu_index < cpu_count; ++cpu_index ) {
cpu_set_t cpuset;
CPU_ZERO( &cpuset );
CPU_SET( cpu_index, &cpuset );
/* Spawn worker tasks pinned to secondary processors */
for ( cpu_index = 1; cpu_index < cpu_count; ++cpu_index ) {
cpu_set_t cpuset;
CPU_ZERO( &cpuset );
CPU_SET( cpu_index, &cpuset );
sc = rtems_task_create(
rtems_build_name( 'S', 'P', 'I', 'N' ),
1,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&task_id
);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
sc = rtems_task_create(
rtems_build_name( 'S', 'P', 'I', 'N' ),
1,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&task_id
);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
sc = rtems_task_set_affinity( task_id, sizeof( cpuset ), &cpuset );
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
sc = rtems_task_set_affinity( task_id, sizeof( cpuset ), &cpuset );
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
sc = rtems_task_start( task_id, test_worker_task,
(rtems_task_argument) &test_instance );
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
}
sc = rtems_task_start(
task_id,
test_worker_task,
(rtems_task_argument) &test_instance
);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
}
/* --- Phase 1: Read-Shared Bus Contention --- */
puts( "Phase 1: Testing Read-Shared bus contention and broadcast wake..." );
_SMP_barrier_Wait( &test_instance.barrier, &test_instance.master_state, cpu_count );
rtems_barrier_wait( test_instance.barrier_id, RTEMS_NO_TIMEOUT );
/* Let workers spin simultaneously on the shared cache line */
rtems_task_wake_after( rtems_clock_get_ticks_per_second() / 10 );
__atomic_store_n( &test_instance.broadcast_flag, 1, __ATOMIC_RELEASE );
while ( __atomic_load_n( &test_instance.phase1_acks, __ATOMIC_ACQUIRE ) < ( cpu_count - 1 ) ) {
while (
__atomic_load_n( &test_instance.phase1_acks, __ATOMIC_ACQUIRE ) <
( cpu_count - 1 )
) {
_CPU_Spin_wait();
}
/* --- Phase 2: Compiler Memory Barrier Verification --- */
puts( "Phase 2: Testing compiler memory barrier on non-volatile shared flag..." );
_SMP_barrier_Wait( &test_instance.barrier, &test_instance.master_state, cpu_count );
puts(
"Phase 2: Testing compiler memory barrier on non-volatile shared flag..."
);
rtems_barrier_wait( test_instance.barrier_id, RTEMS_NO_TIMEOUT );
rtems_task_wake_after( rtems_clock_get_ticks_per_second() / 10 );
test_instance.non_volatile_flag = 1;
while ( __atomic_load_n( &test_instance.phase2_acks, __ATOMIC_ACQUIRE ) < ( cpu_count - 1 ) ) {
while (
__atomic_load_n( &test_instance.phase2_acks, __ATOMIC_ACQUIRE ) <
( cpu_count - 1 )
) {
_CPU_Spin_wait();
}
/* --- Phase 3: MESI Cache-Line Bouncing Stress --- */
puts( "Phase 3: Testing cache-line invalidation stress via ring token pass..." );
_SMP_barrier_Wait( &test_instance.barrier, &test_instance.master_state, cpu_count );
puts(
"Phase 3: Testing cache-line invalidation stress via ring token pass..."
);
rtems_barrier_wait( test_instance.barrier_id, RTEMS_NO_TIMEOUT );
/* Master core participates as CPU 0 in the ring pass */
execute_ring_stress( &test_instance, 0, cpu_count );
/* Wait for the secondary cores to complete the final steps of the ring */
while (
__atomic_load_n( &test_instance.ring_token, __ATOMIC_ACQUIRE ) <
( RING_PASS_ROUNDS * cpu_count )
) {
_CPU_Spin_wait();
}
/* If ring token reached total steps, no core deadlocked during invalidation */
rtems_test_assert( __atomic_load_n( &test_instance.ring_token, __ATOMIC_ACQUIRE )
== ( RING_PASS_ROUNDS * cpu_count ) );
rtems_test_assert(
__atomic_load_n( &test_instance.ring_token, __ATOMIC_ACQUIRE ) ==
( RING_PASS_ROUNDS * cpu_count )
);
rtems_barrier_delete( test_instance.barrier_id );
TEST_END();
rtems_test_exit( 0 );
@@ -224,6 +259,7 @@ for ( cpu_index = 1; cpu_index < cpu_count; ++cpu_index ) {
#define CONFIGURE_MAXIMUM_PROCESSORS CPU_MAXIMUM_PROCESSORS
#define CONFIGURE_MAXIMUM_TASKS CPU_MAXIMUM_PROCESSORS
#define CONFIGURE_MAXIMUM_BARRIERS 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
@@ -231,4 +267,4 @@ for ( cpu_index = 1; cpu_index < cpu_count; ++cpu_index ) {
#define CONFIGURE_INIT
#include <rtems/confdefs.h>
#include <rtems/confdefs.h>
@@ -0,0 +1,20 @@
This file describes the directives and concepts tested by this test set.
test set name: smpspinwait01
directives:
- _CPU_Spin_wait()
concepts:
- Ensure that _CPU_Spin_wait() executes without faulting across multiple SMP cores.
- Verify that _CPU_Spin_wait() acts as a valid compiler memory barrier, ensuring non-volatile
shared memory flags are re-read during busy loops to prevent optimization deadlocks.
- Ensure that MESI cache-line invalidation and atomic memory visibility propagate correctly
across core boundaries under pipeline throttling via an inter-core ring token pass.
- Verify that multiple secondary cores spinning simultaneously on a read-shared cache line
reliably detect and wake up from a single broadcast release.
@@ -0,0 +1,6 @@
*** BEGIN OF TEST SMPSPINWAIT 1 ***
Running SMP spin-wait validation across 4 cores...
Phase 1: Testing Read-Shared bus contention and broadcast wake...
Phase 2: Testing compiler memory barrier on non-volatile shared flag...
Phase 3: Testing cache-line invalidation stress via ring token pass...
*** END OF TEST SMPSPINWAIT 1 ***