source: rtems/testsuites/sptests/spintrcritical_support/intrcritical.c @ f9eca790

4.115
Last change on this file since f9eca790 was f9eca790, checked in by Sebastian Huber <sebastian.huber@…>, on 09/12/14 at 14:04:40

sptests/spintrcritical_support: Optimize busy loop

  • Property mode set to 100644
File size: 5.3 KB
Line 
1/*
2 *  COPYRIGHT (c) 1989-2009.
3 *  On-Line Applications Research Corporation (OAR).
4 *
5 *  The license and distribution terms for this file may be
6 *  found in the file LICENSE in this distribution or at
7 *  http://www.rtems.org/license/LICENSE.
8 */
9
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
14#include <tmacros.h>
15#include <intrcritical.h>
16
17#define INTERRUPT_CRITICAL_NAME rtems_build_name( 'I', 'C', 'R', 'I' )
18
19typedef struct {
20  rtems_interval minimum;
21  rtems_interval maximum;
22  rtems_interval maximum_current;
23  rtems_timer_service_routine_entry tsr;
24  rtems_id timer;
25  uint64_t t0;
26  uint64_t t1;
27} interrupt_critical_control;
28
29static interrupt_critical_control interrupt_critical;
30
31static rtems_interval estimate_busy_loop_maximum( void )
32{
33  rtems_interval units = 0;
34  rtems_interval initial = rtems_clock_get_ticks_since_boot();
35
36  while ( initial == rtems_clock_get_ticks_since_boot() ) {
37    ++units;
38  }
39
40  return units;
41}
42
43static rtems_interval wait_for_tick_change( void )
44{
45  rtems_interval initial = rtems_clock_get_ticks_since_boot();
46  rtems_interval now;
47
48  do {
49    now = rtems_clock_get_ticks_since_boot();
50  } while ( now == initial );
51
52  return now;
53}
54
55/*
56 * It is important that we use actually use the same busy() function at the
57 * various places, since otherwise the obtained maximum value might be wrong.
58 * So the compiler must not inline this function.
59 */
60static __attribute__( ( noinline ) ) void busy( rtems_interval max )
61{
62  rtems_interval i = 0;
63
64  do {
65    __asm__ volatile ("");
66    ++i;
67  } while ( i < max );
68}
69
70static bool interrupt_critical_busy_wait( void )
71{
72  rtems_interval max = interrupt_critical.maximum_current;
73  bool reset = max <= interrupt_critical.minimum;
74
75  if ( reset ) {
76    interrupt_critical.maximum_current = interrupt_critical.maximum;
77  } else {
78    interrupt_critical.maximum_current = max - 1;
79  }
80
81  busy( max );
82
83  return reset;
84}
85
86void interrupt_critical_section_test_support_initialize(
87  rtems_timer_service_routine_entry tsr
88)
89{
90  rtems_interval last;
91  rtems_interval now;
92  rtems_interval a;
93  rtems_interval b;
94  rtems_interval m;
95
96  interrupt_critical.tsr = tsr;
97
98  if ( tsr != NULL && interrupt_critical.timer == 0 ) {
99    rtems_status_code sc = rtems_timer_create(
100      INTERRUPT_CRITICAL_NAME,
101      &interrupt_critical.timer
102    );
103    rtems_test_assert( sc == RTEMS_SUCCESSFUL );
104  }
105
106  /* Choose a lower bound */
107  a = 1;
108
109  /* Estimate an upper bound */
110
111  wait_for_tick_change();
112  b = 2 * estimate_busy_loop_maximum();
113
114  while ( true ) {
115    last = wait_for_tick_change();
116    busy( b );
117    now = rtems_clock_get_ticks_since_boot();
118
119    if ( now != last ) {
120      break;
121    }
122
123    b *= 2;
124    last = now;
125  }
126
127  /* Find a good value */
128  do {
129    m = ( a + b ) / 2;
130
131    last = wait_for_tick_change();
132    busy( m );
133    now = rtems_clock_get_ticks_since_boot();
134
135    if ( now != last ) {
136      b = m;
137    } else {
138      a = m;
139    }
140  } while ( b - a > 1 );
141
142  interrupt_critical.minimum = 0;
143  interrupt_critical.maximum = m;
144  interrupt_critical.maximum_current = m;
145}
146
147static void timer_fire_after(void)
148{
149  if ( interrupt_critical.tsr != NULL ) {
150    rtems_status_code sc = rtems_timer_fire_after(
151      interrupt_critical.timer,
152      1,
153      interrupt_critical.tsr,
154      NULL
155    );
156    rtems_test_assert( sc == RTEMS_SUCCESSFUL );
157  }
158}
159
160bool interrupt_critical_section_test_support_delay(void)
161{
162  timer_fire_after();
163
164  return interrupt_critical_busy_wait();
165}
166
167static bool is_idle( const Thread_Control *thread )
168{
169  return thread->Start.entry_point
170    == (Thread_Entry) rtems_configuration_get_idle_task();
171}
172
173static void thread_switch( Thread_Control *executing, Thread_Control *heir )
174{
175  (void) executing;
176  (void) heir;
177
178  if ( interrupt_critical.t1 == 0 && is_idle( heir ) ) {
179    interrupt_critical.t1 = rtems_clock_get_uptime_nanoseconds();
180  }
181}
182
183static const rtems_extensions_table extensions = {
184  .thread_switch = thread_switch
185};
186
187bool interrupt_critical_section_test(
188  bool                              ( *test_body )( void * ),
189  void                                *test_body_arg,
190  rtems_timer_service_routine_entry    tsr
191)
192{
193  bool done;
194  rtems_status_code sc;
195  rtems_id id;
196  uint64_t delta;
197  rtems_interval busy_delta;
198  int retries = 3;
199
200  interrupt_critical_section_test_support_initialize( tsr );
201
202  sc = rtems_extension_create(
203    INTERRUPT_CRITICAL_NAME,
204    &extensions,
205    &id
206  );
207  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
208
209  wait_for_tick_change();
210  timer_fire_after();
211
212  /* Get estimate for test body duration */
213  interrupt_critical.t0 = rtems_clock_get_uptime_nanoseconds();
214  done = ( *test_body )( test_body_arg );
215  if ( interrupt_critical.t1 == 0 ) {
216    interrupt_critical.t1 = rtems_clock_get_uptime_nanoseconds();
217  }
218
219  /* Update minimum */
220
221  delta = interrupt_critical.t1 - interrupt_critical.t0;
222  busy_delta = (rtems_interval)
223    ( ( interrupt_critical.maximum * ( 2 * delta ) )
224      / rtems_configuration_get_nanoseconds_per_tick() );
225
226  if ( busy_delta < interrupt_critical.maximum ) {
227    interrupt_critical.minimum = interrupt_critical.maximum - busy_delta;
228  }
229
230  sc = rtems_extension_delete( id );
231  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
232
233  while ( !done && retries >= 0 ) {
234    wait_for_tick_change();
235
236    if ( interrupt_critical_section_test_support_delay() ) {
237      --retries;
238    }
239
240    done = ( *test_body )( test_body_arg );
241  }
242
243  return done;
244}
Note: See TracBrowser for help on using the repository browser.