Files
rtems/testsuites/tmtests/tm26/task1.c
Sebastian Huber 38b59a6d30 score: Implement forced thread migration
The current implementation of task migration in RTEMS has some
implications with respect to the interrupt latency. It is crucial to
preserve the system invariant that a task can execute on at most one
processor in the system at a time. This is accomplished with a boolean
indicator in the task context. The processor architecture specific
low-level task context switch code will mark that a task context is no
longer executing and waits that the heir context stopped execution
before it restores the heir context and resumes execution of the heir
task. So there is one point in time in which a processor is without a
task. This is essential to avoid cyclic dependencies in case multiple
tasks migrate at once. Otherwise some supervising entity is necessary to
prevent life-locks. Such a global supervisor would lead to scalability
problems so this approach is not used. Currently the thread dispatch is
performed with interrupts disabled. So in case the heir task is
currently executing on another processor then this prolongs the time of
disabled interrupts since one processor has to wait for another
processor to make progress.

It is difficult to avoid this issue with the interrupt latency since
interrupts normally store the context of the interrupted task on its
stack. In case a task is marked as not executing we must not use its
task stack to store such an interrupt context. We cannot use the heir
stack before it stopped execution on another processor. So if we enable
interrupts during this transition we have to provide an alternative task
independent stack for this time frame. This issue needs further
investigation.
2014-05-07 14:26:28 +02:00

770 lines
17 KiB
C

/*
* COPYRIGHT (c) 1989-2013.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define CONFIGURE_INIT
#include <rtems.h>
#include "system.h"
#include "fptest.h"
#include <coverhd.h>
#include <tmacros.h>
#include <timesys.h>
#include <rtems/score/schedulerpriorityimpl.h>
#include <rtems/rtems/semimpl.h>
#if defined( RTEMS_SMP ) && defined( RTEMS_DEBUG )
#define PREVENT_SMP_ASSERT_FAILURES
#endif
const char rtems_test_name[] = "TIME TEST 26";
/* TEST DATA */
rtems_id Semaphore_id;
Objects_Locations location; /* uses internal RTEMS type */
Thread_Control *Middle_tcb; /* uses internal RTEMS type */
Thread_Control *Low_tcb; /* uses internal RTEMS type */
/*
* Variables to hold execution times until they are printed
* at the end of the test.
*/
uint32_t isr_disable_time;
uint32_t isr_flash_time;
uint32_t isr_enable_time;
uint32_t thread_disable_dispatch_time;
uint32_t thread_enable_dispatch_time;
uint32_t thread_set_state_time;
uint32_t thread_dispatch_no_fp_time;
uint32_t context_switch_no_fp_time;
uint32_t context_switch_self_time;
uint32_t context_switch_another_task_time;
uint32_t context_switch_restore_1st_fp_time;
uint32_t context_switch_save_idle_restore_initted_time;
uint32_t context_switch_save_restore_idle_time;
uint32_t context_switch_save_restore_initted_time;
uint32_t thread_resume_time;
uint32_t thread_unblock_time;
uint32_t thread_ready_time;
uint32_t thread_get_time;
uint32_t semaphore_get_time;
uint32_t thread_get_invalid_time;
rtems_task null_task(
rtems_task_argument argument
);
rtems_task High_task(
rtems_task_argument argument
);
rtems_task Middle_task(
rtems_task_argument argument
);
rtems_task Low_task(
rtems_task_argument argument
);
rtems_task Floating_point_task_1(
rtems_task_argument argument
);
rtems_task Floating_point_task_2(
rtems_task_argument argument
);
void complete_test( void );
static void set_thread_dispatch_necessary( bool dispatch_necessary )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
ISR_Level level;
_ISR_Disable_without_giant( level );
#endif
_Thread_Dispatch_necessary = dispatch_necessary;
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_ISR_Enable_without_giant( level );
#endif
}
static void set_thread_heir( Thread_Control *thread )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
ISR_Level level;
_ISR_Disable_without_giant( level );
#endif
_Thread_Heir = thread;
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_ISR_Enable_without_giant( level );
#endif
}
static void set_thread_executing( Thread_Control *thread )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
ISR_Level level;
_ISR_Disable_without_giant( level );
#endif
_Thread_Executing = thread;
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_ISR_Enable_without_giant( level );
#endif
}
static void thread_disable_dispatch( void )
{
/* Yes, RTEMS_SMP and not PREVENT_SMP_ASSERT_FAILURES */
#if defined( RTEMS_SMP )
Per_CPU_Control *self_cpu;
ISR_Level level;
_ISR_Disable_without_giant( level );
( void ) level;
self_cpu = _Per_CPU_Get();
self_cpu->thread_dispatch_disable_level = 1;
#else
_Thread_Disable_dispatch();
#endif
}
static void thread_set_state( Thread_Control *thread, States_Control state )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Disable_dispatch();
#endif
_Thread_Set_state( thread, state );
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Unnest_dispatch();
#endif
}
static void thread_resume( Thread_Control *thread )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Disable_dispatch();
#endif
_Thread_Resume( thread );
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Unnest_dispatch();
#endif
}
static void thread_unblock( Thread_Control *thread )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Disable_dispatch();
#endif
_Thread_Unblock( thread );
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Unnest_dispatch();
#endif
}
static void thread_ready( Thread_Control *thread )
{
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Disable_dispatch();
#endif
_Thread_Ready( thread );
#if defined( PREVENT_SMP_ASSERT_FAILURES )
_Thread_Unnest_dispatch();
#endif
}
rtems_task null_task(
rtems_task_argument argument
)
{
}
rtems_task Init(
rtems_task_argument argument
)
{
uint32_t index;
rtems_id task_id;
rtems_status_code status;
Print_Warning();
TEST_BEGIN();
if (
_Scheduler_Table[ 0 ].Operations.initialize
!= _Scheduler_priority_Initialize
) {
puts(" Error ==> " );
puts("Test only supported for deterministic priority scheduler\n" );
TEST_END();
rtems_test_exit( 0 );
}
#define FP1_PRIORITY (RTEMS_MAXIMUM_PRIORITY - 3u) /* 201, */
status = rtems_task_create(
rtems_build_name( 'F', 'P', '1', ' ' ),
FP1_PRIORITY,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_FLOATING_POINT,
&task_id
);
directive_failed( status, "rtems_task_create of FP1" );
status = rtems_task_start( task_id, Floating_point_task_1, 0 );
directive_failed( status, "rtems_task_start of FP1" );
#define FP2_PRIORITY (RTEMS_MAXIMUM_PRIORITY - 2u) /* 202, */
status = rtems_task_create(
rtems_build_name( 'F', 'P', '2', ' ' ),
FP2_PRIORITY,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_FLOATING_POINT,
&task_id
);
directive_failed( status, "rtems_task_create of FP2" );
status = rtems_task_start( task_id, Floating_point_task_2, 0 );
directive_failed( status, "rtems_task_start of FP2" );
#define LOW_PRIORITY (RTEMS_MAXIMUM_PRIORITY - 4u) /* 200, */
status = rtems_task_create(
rtems_build_name( 'L', 'O', 'W', ' ' ),
LOW_PRIORITY,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&task_id
);
directive_failed( status, "rtems_task_create of LOW" );
status = rtems_task_start( task_id, Low_task, 0 );
directive_failed( status, "rtems_task_start of LOW" );
#define MIDDLE_PRIORITY (RTEMS_MAXIMUM_PRIORITY - 5u) /* 128, */
status = rtems_task_create(
rtems_build_name( 'M', 'I', 'D', ' ' ),
MIDDLE_PRIORITY,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&task_id
);
directive_failed( status, "rtems_task_create of MIDDLE" );
status = rtems_task_start( task_id, Middle_task, 0 );
directive_failed( status, "rtems_task_start of MIDDLE" );
status = rtems_task_create(
rtems_build_name( 'H', 'I', 'G', 'H' ),
5,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&task_id
);
directive_failed( status, "rtems_task_create of HIGH" );
status = rtems_task_start( task_id, High_task, 0 );
directive_failed( status, "rtems_task_start of HIGH" );
status = rtems_semaphore_create(
rtems_build_name( 'S', 'E', 'M', '1' ),
OPERATION_COUNT,
RTEMS_DEFAULT_ATTRIBUTES,
RTEMS_NO_PRIORITY,
&Semaphore_id
);
directive_failed( status, "rtems_semaphore_create" );
for ( index = 1 ; index <= OPERATION_COUNT ; index++ ) {
status = rtems_task_create(
rtems_build_name( 'N', 'U', 'L', 'L' ),
RTEMS_MAXIMUM_PRIORITY - 1u, /* 254, */
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&task_id
);
directive_failed( status, "rtems_task_create LOOP" );
status = rtems_task_start( task_id, null_task, 0 );
directive_failed( status, "rtems_task_start LOOP" );
}
status = rtems_task_delete( RTEMS_SELF );
directive_failed( status, "rtems_task_delete of RTEMS_SELF" );
}
rtems_task High_task(
rtems_task_argument argument
)
{
rtems_interrupt_level level;
_Thread_Disable_dispatch();
benchmark_timer_initialize();
rtems_interrupt_disable( level );
isr_disable_time = benchmark_timer_read();
benchmark_timer_initialize();
rtems_interrupt_flash( level );
isr_flash_time = benchmark_timer_read();
benchmark_timer_initialize();
rtems_interrupt_enable( level );
isr_enable_time = benchmark_timer_read();
_Thread_Enable_dispatch();
benchmark_timer_initialize();
_Thread_Disable_dispatch();
thread_disable_dispatch_time = benchmark_timer_read();
benchmark_timer_initialize();
_Thread_Enable_dispatch();
thread_enable_dispatch_time = benchmark_timer_read();
benchmark_timer_initialize();
thread_set_state( _Thread_Get_executing(), STATES_SUSPENDED );
thread_set_state_time = benchmark_timer_read();
set_thread_dispatch_necessary( true );
benchmark_timer_initialize();
_Thread_Dispatch(); /* dispatches Middle_task */
}
rtems_task Middle_task(
rtems_task_argument argument
)
{
Scheduler_priority_Context *scheduler_context =
_Scheduler_priority_Get_context( _Scheduler_Get( _Thread_Get_executing() ) );
thread_dispatch_no_fp_time = benchmark_timer_read();
thread_set_state( _Thread_Get_executing(), STATES_SUSPENDED );
Middle_tcb = _Thread_Get_executing();
set_thread_executing(
(Thread_Control *) _Chain_First(&scheduler_context->Ready[LOW_PRIORITY])
);
/* do not force context switch */
set_thread_dispatch_necessary( false );
thread_disable_dispatch();
benchmark_timer_initialize();
_Context_Switch(
&Middle_tcb->Registers,
&_Thread_Get_executing()->Registers
);
benchmark_timer_initialize();
_Context_Switch(&Middle_tcb->Registers, &Low_tcb->Registers);
}
rtems_task Low_task(
rtems_task_argument argument
)
{
Scheduler_priority_Context *scheduler_context =
_Scheduler_priority_Get_context( _Scheduler_Get( _Thread_Get_executing() ) );
Thread_Control *executing;
context_switch_no_fp_time = benchmark_timer_read();
executing = _Thread_Get_executing();
Low_tcb = executing;
benchmark_timer_initialize();
_Context_Switch( &executing->Registers, &executing->Registers );
context_switch_self_time = benchmark_timer_read();
_Context_Switch(&executing->Registers, &Middle_tcb->Registers);
context_switch_another_task_time = benchmark_timer_read();
set_thread_executing(
(Thread_Control *) _Chain_First(&scheduler_context->Ready[FP1_PRIORITY])
);
/* do not force context switch */
set_thread_dispatch_necessary( false );
thread_disable_dispatch();
benchmark_timer_initialize();
#if (CPU_HARDWARE_FP == 1) || (CPU_SOFTWARE_FP == 1)
_Context_Restore_fp( &_Thread_Get_executing()->fp_context );
#endif
_Context_Switch(
&executing->Registers,
&_Thread_Get_executing()->Registers
);
}
rtems_task Floating_point_task_1(
rtems_task_argument argument
)
{
Scheduler_priority_Context *scheduler_context =
_Scheduler_priority_Get_context( _Scheduler_Get( _Thread_Get_executing() ) );
Thread_Control *executing;
FP_DECLARE;
context_switch_restore_1st_fp_time = benchmark_timer_read();
executing = _Thread_Get_executing();
set_thread_executing(
(Thread_Control *) _Chain_First(&scheduler_context->Ready[FP2_PRIORITY])
);
/* do not force context switch */
set_thread_dispatch_necessary( false );
thread_disable_dispatch();
benchmark_timer_initialize();
#if (CPU_HARDWARE_FP == 1) || (CPU_SOFTWARE_FP == 1)
_Context_Save_fp( &executing->fp_context );
_Context_Restore_fp( &_Thread_Get_executing()->fp_context );
#endif
_Context_Switch(
&executing->Registers,
&_Thread_Get_executing()->Registers
);
/* switch to Floating_point_task_2 */
context_switch_save_idle_restore_initted_time = benchmark_timer_read();
FP_LOAD( 1.0 );
executing = _Thread_Get_executing();
set_thread_executing(
(Thread_Control *) _Chain_First(&scheduler_context->Ready[FP2_PRIORITY])
);
benchmark_timer_initialize();
#if (CPU_HARDWARE_FP == 1) || (CPU_SOFTWARE_FP == 1)
_Context_Save_fp( &executing->fp_context );
_Context_Restore_fp( &_Thread_Get_executing()->fp_context );
#endif
_Context_Switch(
&executing->Registers,
&_Thread_Get_executing()->Registers
);
/* switch to Floating_point_task_2 */
}
rtems_task Floating_point_task_2(
rtems_task_argument argument
)
{
Scheduler_priority_Context *scheduler_context =
_Scheduler_priority_Get_context( _Scheduler_Get( _Thread_Get_executing() ) );
Thread_Control *executing;
FP_DECLARE;
context_switch_save_restore_idle_time = benchmark_timer_read();
executing = _Thread_Get_executing();
set_thread_executing(
(Thread_Control *) _Chain_First(&scheduler_context->Ready[FP1_PRIORITY])
);
FP_LOAD( 1.0 );
benchmark_timer_initialize();
#if (CPU_HARDWARE_FP == 1) || (CPU_SOFTWARE_FP == 1)
_Context_Save_fp( &executing->fp_context );
_Context_Restore_fp( &_Thread_Get_executing()->fp_context );
#endif
_Context_Switch(
&executing->Registers,
&_Thread_Get_executing()->Registers
);
/* switch to Floating_point_task_1 */
context_switch_save_restore_initted_time = benchmark_timer_read();
complete_test();
}
void complete_test( void )
{
uint32_t index;
rtems_id task_id;
benchmark_timer_initialize();
thread_resume( Middle_tcb );
thread_resume_time = benchmark_timer_read();
thread_set_state( Middle_tcb, STATES_WAITING_FOR_MESSAGE );
benchmark_timer_initialize();
thread_unblock( Middle_tcb );
thread_unblock_time = benchmark_timer_read();
thread_set_state( Middle_tcb, STATES_WAITING_FOR_MESSAGE );
benchmark_timer_initialize();
thread_ready( Middle_tcb );
thread_ready_time = benchmark_timer_read();
benchmark_timer_initialize();
for ( index=1 ; index <= OPERATION_COUNT ; index++ )
(void) benchmark_timer_empty_function();
overhead = benchmark_timer_read();
task_id = Middle_tcb->Object.id;
benchmark_timer_initialize();
for ( index=1 ; index <= OPERATION_COUNT ; index++ )
(void) _Thread_Get( task_id, &location );
thread_get_time = benchmark_timer_read();
benchmark_timer_initialize();
for ( index=1 ; index <= OPERATION_COUNT ; index++ )
(void) _Semaphore_Get( Semaphore_id, &location );
semaphore_get_time = benchmark_timer_read();
benchmark_timer_initialize();
for ( index=1 ; index <= OPERATION_COUNT ; index++ )
(void) _Thread_Get( 0x3, &location );
thread_get_invalid_time = benchmark_timer_read();
/*
* This is the running task and we have tricked RTEMS out enough where
* we need to set some internal tracking information to match this.
*/
set_thread_heir( _Thread_Get_executing() );
set_thread_dispatch_necessary( false );
for (index = 0; index < 2 * OPERATION_COUNT; ++index) {
_Thread_Unnest_dispatch();
}
/*
* Now dump all the times
*/
put_time(
"rtems interrupt: _ISR_Disable",
isr_disable_time,
1,
0,
0
);
put_time(
"rtems interrupt: _ISR_Flash",
isr_flash_time,
1,
0,
0
);
put_time(
"rtems interrupt: _ISR_Enable",
isr_enable_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Disable_dispatch",
thread_disable_dispatch_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Enable_dispatch",
thread_enable_dispatch_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Set_state",
thread_set_state_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Dispatch NO FP",
thread_dispatch_no_fp_time,
1,
0,
0
);
put_time(
"rtems internal: context switch: no floating point contexts",
context_switch_no_fp_time,
1,
0,
0
);
put_time(
"rtems internal: context switch: self",
context_switch_self_time,
1,
0,
0
);
put_time(
"rtems internal: context switch to another task",
context_switch_another_task_time,
1,
0,
0
);
#if (CPU_HARDWARE_FP == 1) || (CPU_SOFTWARE_FP == 1)
put_time(
"rtems internal: fp context switch restore 1st FP task",
context_switch_restore_1st_fp_time,
1,
0,
0
);
put_time(
"rtems internal: fp context switch save idle and restore initialized",
context_switch_save_idle_restore_initted_time,
1,
0,
0
);
put_time(
"rtems internal: fp context switch save idle, restore idle",
context_switch_save_restore_idle_time,
1,
0,
0
);
put_time(
"rtems internal: fp context switch save initialized, restore initialized",
context_switch_save_restore_initted_time,
1,
0,
0
);
#else
puts(
"rtems internal: fp context switch restore 1st FP task - NA\n"
"rtems internal: fp context switch save idle restore initialized - NA\n"
"rtems internal: fp context switch save idle restore idle - NA\n"
"rtems internal: fp context switch save initialized\n"
" restore initialized - NA"
);
#endif
put_time(
"rtems internal: _Thread_Resume",
thread_resume_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Unblock",
thread_unblock_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Ready",
thread_ready_time,
1,
0,
0
);
put_time(
"rtems internal: _Thread_Get",
thread_get_time,
OPERATION_COUNT,
0,
0
);
put_time(
"rtems internal: _Semaphore_Get",
semaphore_get_time,
OPERATION_COUNT,
0,
0
);
put_time(
"rtems internal: _Thread_Get: invalid id",
thread_get_invalid_time,
OPERATION_COUNT,
0,
0
);
TEST_END();
rtems_test_exit( 0 );
}