]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/kernel/v2_0/tests/smp.cxx
Initial revision
[karo-tx-redboot.git] / packages / kernel / v2_0 / tests / smp.cxx
1 //==========================================================================
2 //
3 //        smp.cxx
4 //
5 //        SMP tests
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):     nickg
44 // Contributors:  nickg
45 // Date:          2001-06-18
46 // Description:   Some basic SMP tests.
47 //
48 //####DESCRIPTIONEND####
49 //==========================================================================
50
51 #include <pkgconf/kernel.h>
52 #include <pkgconf/hal.h>
53
54 #if 1
55 #include <cyg/kernel/sched.hxx>
56 #include <cyg/kernel/thread.hxx>
57 #include <cyg/kernel/thread.inl>
58 #include <cyg/kernel/mutex.hxx>
59 #include <cyg/kernel/sema.hxx>
60 #include <cyg/kernel/sched.inl>
61 #include <cyg/kernel/clock.hxx>
62 #include <cyg/kernel/clock.inl>
63 #endif
64
65 #include <cyg/kernel/kapi.h>
66
67 #include <cyg/infra/testcase.h>
68 #include <cyg/infra/diag.h>
69
70 //==========================================================================
71
72 #if defined(CYGPKG_KERNEL_SMP_SUPPORT) &&       \
73     defined(CYGFUN_KERNEL_API_C) &&             \
74     defined(CYGSEM_KERNEL_SCHED_MLQUEUE) &&     \
75     defined(CYGVAR_KERNEL_COUNTERS_CLOCK) &&    \
76     !defined(CYGPKG_HAL_I386_LINUX) &&          \
77     !defined(CYGDBG_INFRA_DIAG_USE_DEVICE) &&   \
78     (CYGNUM_KERNEL_SCHED_PRIORITIES > 12)
79
80 //==========================================================================
81
82 #define NTHREADS 1
83 #include "testaux.hxx"
84
85 #define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL
86
87 #define NTHREADS_MAX (CYGNUM_KERNEL_CPU_MAX*3)
88
89 static int ncpus = CYGNUM_KERNEL_CPU_MAX;
90 static int nthread = NTHREADS_MAX;
91
92 static char stacks[NTHREADS_MAX][STACK_SIZE];
93 static cyg_thread test_threads[NTHREADS_MAX];
94 static cyg_handle_t threads[NTHREADS_MAX];
95
96 static volatile cyg_uint32 cpu_run[CYGNUM_KERNEL_CPU_MAX];
97 static volatile int failed = false;
98 static volatile cyg_uint32 cpu_thread[CYGNUM_KERNEL_CPU_MAX];
99
100 static volatile cyg_uint32 slicerun[NTHREADS_MAX][CYGNUM_KERNEL_CPU_MAX];
101
102
103 static cyg_mutex_t mx;
104
105
106 //==========================================================================
107 // Compute a name for a thread
108 char *
109 thread_name(char *basename, int indx) {
110     return "<<NULL>>";  // Not currently used
111 }
112
113 //==========================================================================
114
115 void 
116 test_thread_cpu(CYG_ADDRESS id)
117 {
118     for(;;)
119         cpu_run[CYG_KERNEL_CPU_THIS()] = true;
120 }
121
122 //==========================================================================
123 // First test: just run as many threads as CPUs and check that we
124 // get to run on each CPU.
125
126 void run_smp_test_cpus()
127 {
128     int i;
129
130     CYG_TEST_INFO( "CPU Test: Check CPUs functional");
131     
132     // Init flags.
133     for (i = 0;  i < ncpus;  i++)
134         cpu_run[i] = false;
135     
136     // Set my priority higher than any I plan to create
137     cyg_thread_set_priority(cyg_thread_self(), 2);
138
139     for (i = 0;  i < ncpus;  i++) {
140         cyg_thread_create(10,              // Priority - just a number
141                           test_thread_cpu, // entry
142                           i,               // index
143                           thread_name("thread", i),     // Name
144                           &stacks[i][0],   // Stack
145                           STACK_SIZE,      // Size
146                           &threads[i],     // Handle
147                           &test_threads[i] // Thread data structure
148             );
149         cyg_thread_resume( threads[i]);
150     }
151
152     // Just wait a while, until the threads have all run for a bit.
153     cyg_thread_delay( 10 );
154
155     // Delete all the threads
156     for (i = 0;  i < ncpus;  i++) {
157         cyg_thread_delete(threads[i]);
158     }
159
160     // And check that a thread ran on each CPU
161     for (i = 0;  i < ncpus;  i++) {
162 //        CYG_TEST_CHECK( cpu_run[i], "CPU didn't run");
163         if( !cpu_run[i] )
164         {
165             CYG_TEST_INFO( "CPU didn't run" );
166             failed++;
167         }
168     }
169
170     CYG_TEST_INFO( "CPU Test: done");
171 }
172
173
174 //==========================================================================
175
176 void 
177 test_thread_pri(CYG_ADDRESS id)
178 {
179     for(;;)
180     {
181         cpu_thread[CYG_KERNEL_CPU_THIS()] = id;
182     }
183 }
184
185 //==========================================================================
186 // Second test: Run a thread on each CPU and then by manipulating the
187 // priorities, get the current thread to migrate to each CPU in turn.
188
189
190 void run_smp_test_pri()
191 {
192     int i;
193
194     CYG_TEST_INFO( "Pri Test: Check set_priority functionality");
195     
196     // Init flags.
197     for (i = 0;  i < ncpus;  i++)
198         cpu_run[i] = false;
199
200     // Set my priority higher than any I plan to creat
201     cyg_thread_set_priority(cyg_thread_self(), 2);
202
203     for (i = 0;  i < ncpus;  i++) {
204         cyg_thread_create(10,              // Priority - just a number
205                           test_thread_pri, // entry
206                           i,               // index
207                           thread_name("thread", i),     // Name
208                           &stacks[i][0],   // Stack
209                           STACK_SIZE,      // Size
210                           &threads[i],     // Handle
211                           &test_threads[i] // Thread data structure
212             );
213         cyg_thread_resume( threads[i]);
214     }
215
216     cyg_thread_delay( 2 );
217     
218     cyg_handle_t cthread = threads[0];
219     cyg_thread_set_priority(cthread, 25);
220     
221     // Just wait a while, until the threads have all run for a bit.
222     cyg_thread_delay( 2 );
223
224     for (i = 0;  i < ncpus*500;  i++)
225     {
226         HAL_SMP_CPU_TYPE cpu = i % CYG_KERNEL_CPU_COUNT();
227         
228         if( cpu != CYG_KERNEL_CPU_THIS() )
229         {
230             // At this point we have the current thread running on a
231             // CPU at priority 2, ncpus-1 threads running at priority
232             // 10 and the last thread (cthread) in the run queue at
233             // priority 25.
234             
235             // Pick a thread on a different CPU
236             cyg_handle_t dthread;
237
238             do
239             {
240                 dthread = threads[cpu_thread[cpu]];
241             } while( dthread == cthread );
242
243             // Change the priority of the victim thread to 20. It is
244             // still higher priority than cthread so it will continue
245             // running.
246
247             cyg_thread_set_priority(dthread, 20);
248
249             // Now change our priority to 15. We are still higher
250             // priority that cthread so we will still run.
251             
252             cyg_thread_set_priority(cyg_thread_self(), 15);
253
254             // Finally change the priority of cthread to 10. This will
255             // cause it to preempt us on the current CPU. In turn we
256             // will preempt dthread on its CPU.
257
258             // NOTE: This relies somewhat on the SMP scheduler doing
259             // what we expect here. Specifically, that it will preempt
260             // the current thread with cthread locally. A more
261             // sophisticated scheduler might decide that the most
262             // efficient thing to do is to preempt dthread with
263             // cthread remotely, leaving the current thread where it
264             // is. If we ever bother to implement this, then this test
265             // will need to change.
266             
267             cyg_thread_set_priority(cthread, 10);
268
269             // Spin here a while until the scheduler sorts itself out.
270             
271             for( int j = 0; j < 100000; j++ );
272
273             // Indicate that we have run on this CPU
274             cpu_run[CYG_KERNEL_CPU_THIS()]++;
275
276             // Restore our priority to 2 and depress dthread to 25 and
277             // make it the new cthread.
278             
279             cyg_thread_set_priority(cyg_thread_self(), 2);
280             cyg_thread_set_priority(dthread, 25);
281             cthread = dthread;
282         }
283     }
284
285     
286     // Delete all the threads
287     for (i = 0;  i < ncpus;  i++) {
288         cyg_thread_delete(threads[i]);
289     }
290
291     // And check that a thread ran on each CPU
292     for (i = 0;  i < ncpus;  i++) {
293 //        CYG_TEST_CHECK( cpu_run[i], "CPU didn't run");
294         if( !cpu_run[i] )
295         {
296             CYG_TEST_INFO( "CPU didn't run" );            
297             failed++;
298         }
299     }
300
301     CYG_TEST_INFO( "PRI Test: done");
302 }
303
304 //==========================================================================
305
306 void 
307 test_thread_timeslice(CYG_ADDRESS id)
308 {
309     for(;;)
310         slicerun[id][CYG_KERNEL_CPU_THIS()]++;
311 }
312
313 //==========================================================================
314 // First test: just run as many threads as CPUs and check that we
315 // get to run on each CPU.
316
317 void run_smp_test_timeslice()
318 {
319     int i;
320
321     CYG_TEST_INFO( "Timeslice Test: Check timeslicing works");
322     
323     // Init flags.
324     for (i = 0;  i < nthread;  i++)
325         for( int j = 0; j < ncpus; j++ )
326             slicerun[i][j] = 0;
327     
328     // Set my priority higher than any I plan to create
329     cyg_thread_set_priority(cyg_thread_self(), 2);
330
331     for (i = 0;  i < nthread;  i++) {
332         cyg_thread_create(10,              // Priority - just a number
333                           test_thread_timeslice, // entry
334                           i,               // index
335                           thread_name("thread", i),     // Name
336                           &stacks[i][0],   // Stack
337                           STACK_SIZE,      // Size
338                           &threads[i],     // Handle
339                           &test_threads[i] // Thread data structure
340             );
341         cyg_thread_resume( threads[i]);
342     }
343
344     // Just wait a while, until the threads have all run for a bit.
345     cyg_thread_delay( 200 );
346
347     // Delete all the threads
348     for (i = 0;  i < nthread;  i++) {
349         cyg_thread_suspend(threads[i]);
350     }
351
352     
353     // And check that a thread ran on each CPU
354
355     cyg_uint32 cpu_total[ncpus];
356     cyg_uint32 cpu_threads[ncpus];
357     cyg_uint32 thread_total[nthread];
358     
359     diag_printf(" Thread ");
360     for( int j = 0; j < ncpus; j++ )
361     {
362         cpu_total[j] = 0;
363         cpu_threads[j] = 0;
364         diag_printf("   CPU %2d",j);
365     }
366     diag_printf("   Total\n");
367     for (i = 0;  i < nthread;  i++)
368     {
369         thread_total[i] = 0;
370         diag_printf("     %2d ",i);
371         for( int j = 0; j < ncpus; j++ )
372         {
373             thread_total[i] += slicerun[i][j];
374             cpu_total[j] += slicerun[i][j];
375             if( slicerun[i][j] > 0 )
376                 cpu_threads[j]++;
377             diag_printf(" %8d",slicerun[i][j]);
378         }
379         diag_printf("%8d\n",thread_total[i]);
380     }
381     diag_printf(" Total  ");
382     for( int j = 0; j < ncpus; j++ )
383         diag_printf(" %8d",cpu_total[j]);
384     diag_printf("\n");
385     diag_printf("Threads ");
386     for( int j = 0; j < ncpus; j++ )
387     {
388         diag_printf(" %8d",cpu_threads[j]);
389         if( cpu_threads[j] < 2 )
390             failed++;
391     }
392     diag_printf("\n");
393
394     // Delete all the threads
395     for (i = 0;  i < nthread;  i++) {
396         cyg_thread_delete(threads[i]);
397     }
398
399     CYG_TEST_INFO( "Timeslice Test: done");
400 }
401
402
403 //==========================================================================
404
405 void 
406 run_smp_tests(CYG_ADDRESS id)
407 {
408     cyg_mutex_init( &mx );
409
410     for( int i = 0; i < 100; i++ )
411     {
412         run_smp_test_cpus();
413         run_smp_test_pri();
414         run_smp_test_timeslice();
415     }
416
417     if( failed )
418         CYG_TEST_FAIL_FINISH("SMP tests failed\n");
419     
420     CYG_TEST_PASS_FINISH("SMP tests OK");    
421 }
422
423 //==========================================================================
424
425 void smp_main( void )
426 {
427     CYG_TEST_INIT();
428
429     // Work out how many CPUs we actually have.
430     ncpus = CYG_KERNEL_CPU_COUNT();
431
432     new_thread(run_smp_tests, 0);
433
434     cyg_scheduler_start();
435 }
436
437 //==========================================================================
438
439 #ifdef CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG
440 externC void
441 cyg_hal_invoke_constructors();
442 #endif
443
444 externC void
445 cyg_start( void )
446 {
447 #ifdef CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG
448     cyg_hal_invoke_constructors();
449 #endif
450     smp_main();
451 }   
452
453 //==========================================================================
454
455 #else // CYGPKG_KERNEL_SMP_SUPPORT etc.
456
457 externC void
458 cyg_start( void )
459 {
460     CYG_TEST_INIT();
461     CYG_TEST_INFO("SMP test requires:\n"
462                 "CYGPKG_KERNEL_SMP_SUPPORT &&\n"
463                 "CYGFUN_KERNEL_API_C && \n"
464                 "CYGSEM_KERNEL_SCHED_MLQUEUE &&\n"
465                 "CYGVAR_KERNEL_COUNTERS_CLOCK &&\n"
466                 "!CYGPKG_HAL_I386_LINUX &&\n"
467                 "!CYGDBG_INFRA_DIAG_USE_DEVICE &&\n"
468                 "(CYGNUM_KERNEL_SCHED_PRIORITIES > 12)\n");
469     CYG_TEST_NA("SMP test requirements");
470 }
471 #endif // CYGPKG_KERNEL_SMP_SUPPORT etc.
472
473 //==========================================================================
474 // EOF tm_basic.cxx