summaryrefslogtreecommitdiffstats
path: root/testsuites/sptests/spintrcritical_support
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2014-09-08 15:18:07 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2014-09-10 07:12:55 +0200
commit802808900053e732881795d0d748d8fdc2a54e13 (patch)
tree7e9c96202fc39ce59cbd1c7d19806348631f2d14 /testsuites/sptests/spintrcritical_support
parentscore: More strict RTEMS_DEQUALIFY implementation. (diff)
downloadrtems-802808900053e732881795d0d748d8fdc2a54e13.tar.bz2
tests: Rework interrupt critical tests
This avoids test durations of more than one hour on fast targets, since fast targets can count a lot during one clock tick period, so the minor loop iteration count was quite high. Estimate now the test body duration to iterate only through the interesting time window. Add and use interrupt_critical_section_test().
Diffstat (limited to 'testsuites/sptests/spintrcritical_support')
-rw-r--r--testsuites/sptests/spintrcritical_support/intrcritical.c230
-rw-r--r--testsuites/sptests/spintrcritical_support/intrcritical.h21
2 files changed, 211 insertions, 40 deletions
diff --git a/testsuites/sptests/spintrcritical_support/intrcritical.c b/testsuites/sptests/spintrcritical_support/intrcritical.c
index d044d141b4..b6b250726c 100644
--- a/testsuites/sptests/spintrcritical_support/intrcritical.c
+++ b/testsuites/sptests/spintrcritical_support/intrcritical.c
@@ -14,14 +14,23 @@
#include <tmacros.h>
#include <intrcritical.h>
-static uint32_t Maximum;
-static uint32_t Maximum_current;
-static rtems_id Timer;
-static rtems_timer_service_routine (*TSR)( rtems_id, void * );
+#define INTERRUPT_CRITICAL_NAME rtems_build_name( 'I', 'C', 'R', 'I' )
-static uint32_t interrupt_critical_remaining_units_of_tick( void )
+typedef struct {
+ rtems_interval minimum;
+ rtems_interval maximum;
+ rtems_interval maximum_current;
+ rtems_timer_service_routine_entry tsr;
+ rtems_id timer;
+ uint64_t t0;
+ uint64_t t1;
+} interrupt_critical_control;
+
+static interrupt_critical_control interrupt_critical;
+
+static rtems_interval estimate_busy_loop_maximum( void )
{
- uint32_t units = 0;
+ rtems_interval units = 0;
rtems_interval initial = rtems_clock_get_ticks_since_boot();
while ( initial == rtems_clock_get_ticks_since_boot() ) {
@@ -31,61 +40,204 @@ static uint32_t interrupt_critical_remaining_units_of_tick( void )
return units;
}
-static bool interrupt_critical_busy_wait( void )
+static rtems_interval wait_for_tick_change( void )
{
- uint32_t max = Maximum_current;
- uint32_t unit = 0;
rtems_interval initial = rtems_clock_get_ticks_since_boot();
+ rtems_interval now;
+
+ do {
+ now = rtems_clock_get_ticks_since_boot();
+ } while ( now == initial );
- while ( unit < max && initial == rtems_clock_get_ticks_since_boot() ) {
- ++unit;
+ return now;
+}
+
+/*
+ * It is important that we use actually use the same busy() function at the
+ * various places, since otherwise the obtained maximum value might be wrong.
+ * So the compiler must not inline this function.
+ */
+static __attribute__( ( noinline ) ) void busy( rtems_interval max )
+{
+ rtems_interval i;
+
+ for ( i = 0; i < max; ++i ) {
+ __asm__ volatile ("");
}
+}
- if ( max > 0 ) {
- Maximum_current = max - 1;
+static bool interrupt_critical_busy_wait( void )
+{
+ rtems_interval max = interrupt_critical.maximum_current;
+ bool reset = max <= interrupt_critical.minimum;
- return false;
+ if ( reset ) {
+ interrupt_critical.maximum_current = interrupt_critical.maximum;
} else {
- Maximum_current = Maximum;
-
- return true;
+ interrupt_critical.maximum_current = max - 1;
}
+
+ busy( max );
+
+ return reset;
}
void interrupt_critical_section_test_support_initialize(
- rtems_timer_service_routine (*tsr)( rtems_id, void * )
+ rtems_timer_service_routine_entry tsr
)
{
- Timer = 0;
- TSR = tsr;
- if ( tsr ) {
- rtems_status_code rc;
-
- puts( "Support - rtems_timer_create - creating timer 1" );
- rc = rtems_timer_create( rtems_build_name( 'T', 'M', '1', ' ' ), &Timer );
- directive_failed( rc, "rtems_timer_create" );
+ rtems_interval last;
+ rtems_interval now;
+ rtems_interval a;
+ rtems_interval b;
+ rtems_interval m;
+
+ interrupt_critical.tsr = tsr;
+
+ if ( tsr != NULL && interrupt_critical.timer == 0 ) {
+ rtems_status_code sc = rtems_timer_create(
+ INTERRUPT_CRITICAL_NAME,
+ &interrupt_critical.timer
+ );
+ rtems_test_assert( sc == RTEMS_SUCCESSFUL );
}
- /* Wait for tick change */
- interrupt_critical_remaining_units_of_tick();
+ /* Choose a lower bound */
+ a = 1;
+
+ /* Estimate an upper bound */
+
+ wait_for_tick_change();
+ b = 2 * estimate_busy_loop_maximum();
+
+ while ( true ) {
+ last = wait_for_tick_change();
+ busy( b );
+ now = rtems_clock_get_ticks_since_boot();
+
+ if ( now != last ) {
+ break;
+ }
- /* Get units for a hole tick */
- Maximum = interrupt_critical_remaining_units_of_tick();
- Maximum_current = Maximum;
+ b *= 2;
+ last = now;
+ }
+
+ /* Find a good value */
+ do {
+ m = ( a + b ) / 2;
+
+ last = wait_for_tick_change();
+ busy( m );
+ now = rtems_clock_get_ticks_since_boot();
+
+ if ( now != last ) {
+ b = m;
+ } else {
+ a = m;
+ }
+ } while ( b - a > 1 );
+
+ interrupt_critical.minimum = 0;
+ interrupt_critical.maximum = m;
+ interrupt_critical.maximum_current = m;
+}
- #if 0
- printf( "%d 0x%08x units\n", Maximum, Maximum );
- #endif
+static void timer_fire_after(void)
+{
+ if ( interrupt_critical.tsr != NULL ) {
+ rtems_status_code sc = rtems_timer_fire_after(
+ interrupt_critical.timer,
+ 1,
+ interrupt_critical.tsr,
+ NULL
+ );
+ rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+ }
}
bool interrupt_critical_section_test_support_delay(void)
{
- if (TSR) {
- rtems_status_code rc;
+ timer_fire_after();
- rc = rtems_timer_fire_after( Timer, 1, TSR, NULL );
- directive_failed( rc, "timer_fire_after failed" );
+ return interrupt_critical_busy_wait();
+}
+
+static bool is_idle( const Thread_Control *thread )
+{
+ return thread->Start.entry_point
+ == (Thread_Entry) rtems_configuration_get_idle_task();
+}
+
+static void thread_switch( Thread_Control *executing, Thread_Control *heir )
+{
+ (void) executing;
+ (void) heir;
+
+ if ( interrupt_critical.t1 == 0 && is_idle( heir ) ) {
+ interrupt_critical.t1 = rtems_clock_get_uptime_nanoseconds();
}
+}
- return interrupt_critical_busy_wait();
+static const rtems_extensions_table extensions = {
+ .thread_switch = thread_switch
+};
+
+bool interrupt_critical_section_test(
+ bool ( *test_body )( void * ),
+ void *test_body_arg,
+ rtems_timer_service_routine_entry tsr
+)
+{
+ bool done;
+ rtems_status_code sc;
+ rtems_id id;
+ uint64_t delta;
+ rtems_interval busy_delta;
+ int retries = 3;
+
+ interrupt_critical_section_test_support_initialize( tsr );
+
+ sc = rtems_extension_create(
+ INTERRUPT_CRITICAL_NAME,
+ &extensions,
+ &id
+ );
+ rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+ wait_for_tick_change();
+ timer_fire_after();
+
+ /* Get estimate for test body duration */
+ interrupt_critical.t0 = rtems_clock_get_uptime_nanoseconds();
+ done = ( *test_body )( test_body_arg );
+ if ( interrupt_critical.t1 == 0 ) {
+ interrupt_critical.t1 = rtems_clock_get_uptime_nanoseconds();
+ }
+
+ /* Update minimum */
+
+ delta = interrupt_critical.t1 - interrupt_critical.t0;
+ busy_delta = (rtems_interval)
+ ( ( interrupt_critical.maximum * ( 2 * delta ) )
+ / rtems_configuration_get_nanoseconds_per_tick() );
+
+ if ( busy_delta < interrupt_critical.maximum ) {
+ interrupt_critical.minimum = interrupt_critical.maximum - busy_delta;
+ }
+
+ sc = rtems_extension_delete( id );
+ rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+
+ while ( !done && retries >= 0 ) {
+ wait_for_tick_change();
+
+ if ( interrupt_critical_section_test_support_delay() ) {
+ --retries;
+ }
+
+ done = ( *test_body )( test_body_arg );
+ }
+
+ return done;
}
diff --git a/testsuites/sptests/spintrcritical_support/intrcritical.h b/testsuites/sptests/spintrcritical_support/intrcritical.h
index 7989b3f7e2..aca1ee0f2c 100644
--- a/testsuites/sptests/spintrcritical_support/intrcritical.h
+++ b/testsuites/sptests/spintrcritical_support/intrcritical.h
@@ -16,7 +16,7 @@
* @param[in] tsr is the optional timer service routine to fire
*/
void interrupt_critical_section_test_support_initialize(
- rtems_timer_service_routine (*tsr)( rtems_id, void * )
+ rtems_timer_service_routine_entry tsr
);
/**
@@ -29,5 +29,24 @@ void interrupt_critical_section_test_support_initialize(
*/
bool interrupt_critical_section_test_support_delay(void);
+/**
+ * @brief Interrupt critical section test.
+ *
+ * This function first estimates the test body duration and then repeatedly
+ * calls the test body with varying times to the next clock tick interrupt.
+ *
+ * @param[in] test_body The test body function. In case the test body returns
+ * true, then the test iteration stops.
+ * @param[in] test_body_arg The argument for the test body function.
+ * @param[in] tsr An optional timer service routine.
+ *
+ * @return The test body return status.
+ */
+bool interrupt_critical_section_test(
+ bool ( *test_body )( void * ),
+ void *test_body_arg,
+ rtems_timer_service_routine_entry tsr
+);
+
#endif