1 //==========================================================================
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.
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.
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
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.
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.
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.
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####
44 // Contributors: nickg
46 // Description: Some basic SMP tests.
48 //####DESCRIPTIONEND####
49 //==========================================================================
51 #include <pkgconf/kernel.h>
52 #include <pkgconf/hal.h>
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>
65 #include <cyg/kernel/kapi.h>
67 #include <cyg/infra/testcase.h>
68 #include <cyg/infra/diag.h>
70 //==========================================================================
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)
80 //==========================================================================
83 #include "testaux.hxx"
85 #define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL
87 #define NTHREADS_MAX (CYGNUM_KERNEL_CPU_MAX*3)
89 static int ncpus = CYGNUM_KERNEL_CPU_MAX;
90 static int nthread = NTHREADS_MAX;
92 static char stacks[NTHREADS_MAX][STACK_SIZE];
93 static cyg_thread test_threads[NTHREADS_MAX];
94 static cyg_handle_t threads[NTHREADS_MAX];
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];
100 static volatile cyg_uint32 slicerun[NTHREADS_MAX][CYGNUM_KERNEL_CPU_MAX];
103 static cyg_mutex_t mx;
106 //==========================================================================
107 // Compute a name for a thread
109 thread_name(char *basename, int indx) {
110 return "<<NULL>>"; // Not currently used
113 //==========================================================================
116 test_thread_cpu(CYG_ADDRESS id)
119 cpu_run[CYG_KERNEL_CPU_THIS()] = true;
122 //==========================================================================
123 // First test: just run as many threads as CPUs and check that we
124 // get to run on each CPU.
126 void run_smp_test_cpus()
130 CYG_TEST_INFO( "CPU Test: Check CPUs functional");
133 for (i = 0; i < ncpus; i++)
136 // Set my priority higher than any I plan to create
137 cyg_thread_set_priority(cyg_thread_self(), 2);
139 for (i = 0; i < ncpus; i++) {
140 cyg_thread_create(10, // Priority - just a number
141 test_thread_cpu, // entry
143 thread_name("thread", i), // Name
144 &stacks[i][0], // Stack
146 &threads[i], // Handle
147 &test_threads[i] // Thread data structure
149 cyg_thread_resume( threads[i]);
152 // Just wait a while, until the threads have all run for a bit.
153 cyg_thread_delay( 10 );
155 // Delete all the threads
156 for (i = 0; i < ncpus; i++) {
157 cyg_thread_delete(threads[i]);
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");
165 CYG_TEST_INFO( "CPU didn't run" );
170 CYG_TEST_INFO( "CPU Test: done");
174 //==========================================================================
177 test_thread_pri(CYG_ADDRESS id)
181 cpu_thread[CYG_KERNEL_CPU_THIS()] = id;
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.
190 void run_smp_test_pri()
194 CYG_TEST_INFO( "Pri Test: Check set_priority functionality");
197 for (i = 0; i < ncpus; i++)
200 // Set my priority higher than any I plan to creat
201 cyg_thread_set_priority(cyg_thread_self(), 2);
203 for (i = 0; i < ncpus; i++) {
204 cyg_thread_create(10, // Priority - just a number
205 test_thread_pri, // entry
207 thread_name("thread", i), // Name
208 &stacks[i][0], // Stack
210 &threads[i], // Handle
211 &test_threads[i] // Thread data structure
213 cyg_thread_resume( threads[i]);
216 cyg_thread_delay( 2 );
218 cyg_handle_t cthread = threads[0];
219 cyg_thread_set_priority(cthread, 25);
221 // Just wait a while, until the threads have all run for a bit.
222 cyg_thread_delay( 2 );
224 for (i = 0; i < ncpus*500; i++)
226 HAL_SMP_CPU_TYPE cpu = i % CYG_KERNEL_CPU_COUNT();
228 if( cpu != CYG_KERNEL_CPU_THIS() )
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
235 // Pick a thread on a different CPU
236 cyg_handle_t dthread;
240 dthread = threads[cpu_thread[cpu]];
241 } while( dthread == cthread );
243 // Change the priority of the victim thread to 20. It is
244 // still higher priority than cthread so it will continue
247 cyg_thread_set_priority(dthread, 20);
249 // Now change our priority to 15. We are still higher
250 // priority that cthread so we will still run.
252 cyg_thread_set_priority(cyg_thread_self(), 15);
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.
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.
267 cyg_thread_set_priority(cthread, 10);
269 // Spin here a while until the scheduler sorts itself out.
271 for( int j = 0; j < 100000; j++ );
273 // Indicate that we have run on this CPU
274 cpu_run[CYG_KERNEL_CPU_THIS()]++;
276 // Restore our priority to 2 and depress dthread to 25 and
277 // make it the new cthread.
279 cyg_thread_set_priority(cyg_thread_self(), 2);
280 cyg_thread_set_priority(dthread, 25);
286 // Delete all the threads
287 for (i = 0; i < ncpus; i++) {
288 cyg_thread_delete(threads[i]);
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");
296 CYG_TEST_INFO( "CPU didn't run" );
301 CYG_TEST_INFO( "PRI Test: done");
304 //==========================================================================
307 test_thread_timeslice(CYG_ADDRESS id)
310 slicerun[id][CYG_KERNEL_CPU_THIS()]++;
313 //==========================================================================
314 // First test: just run as many threads as CPUs and check that we
315 // get to run on each CPU.
317 void run_smp_test_timeslice()
321 CYG_TEST_INFO( "Timeslice Test: Check timeslicing works");
324 for (i = 0; i < nthread; i++)
325 for( int j = 0; j < ncpus; j++ )
328 // Set my priority higher than any I plan to create
329 cyg_thread_set_priority(cyg_thread_self(), 2);
331 for (i = 0; i < nthread; i++) {
332 cyg_thread_create(10, // Priority - just a number
333 test_thread_timeslice, // entry
335 thread_name("thread", i), // Name
336 &stacks[i][0], // Stack
338 &threads[i], // Handle
339 &test_threads[i] // Thread data structure
341 cyg_thread_resume( threads[i]);
344 // Just wait a while, until the threads have all run for a bit.
345 cyg_thread_delay( 200 );
347 // Delete all the threads
348 for (i = 0; i < nthread; i++) {
349 cyg_thread_suspend(threads[i]);
353 // And check that a thread ran on each CPU
355 cyg_uint32 cpu_total[ncpus];
356 cyg_uint32 cpu_threads[ncpus];
357 cyg_uint32 thread_total[nthread];
359 diag_printf(" Thread ");
360 for( int j = 0; j < ncpus; j++ )
364 diag_printf(" CPU %2d",j);
366 diag_printf(" Total\n");
367 for (i = 0; i < nthread; i++)
370 diag_printf(" %2d ",i);
371 for( int j = 0; j < ncpus; j++ )
373 thread_total[i] += slicerun[i][j];
374 cpu_total[j] += slicerun[i][j];
375 if( slicerun[i][j] > 0 )
377 diag_printf(" %8d",slicerun[i][j]);
379 diag_printf("%8d\n",thread_total[i]);
381 diag_printf(" Total ");
382 for( int j = 0; j < ncpus; j++ )
383 diag_printf(" %8d",cpu_total[j]);
385 diag_printf("Threads ");
386 for( int j = 0; j < ncpus; j++ )
388 diag_printf(" %8d",cpu_threads[j]);
389 if( cpu_threads[j] < 2 )
394 // Delete all the threads
395 for (i = 0; i < nthread; i++) {
396 cyg_thread_delete(threads[i]);
399 CYG_TEST_INFO( "Timeslice Test: done");
403 //==========================================================================
406 run_smp_tests(CYG_ADDRESS id)
408 cyg_mutex_init( &mx );
410 for( int i = 0; i < 100; i++ )
414 run_smp_test_timeslice();
418 CYG_TEST_FAIL_FINISH("SMP tests failed\n");
420 CYG_TEST_PASS_FINISH("SMP tests OK");
423 //==========================================================================
425 void smp_main( void )
429 // Work out how many CPUs we actually have.
430 ncpus = CYG_KERNEL_CPU_COUNT();
432 new_thread(run_smp_tests, 0);
434 cyg_scheduler_start();
437 //==========================================================================
439 #ifdef CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG
441 cyg_hal_invoke_constructors();
447 #ifdef CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG
448 cyg_hal_invoke_constructors();
453 //==========================================================================
455 #else // CYGPKG_KERNEL_SMP_SUPPORT etc.
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");
471 #endif // CYGPKG_KERNEL_SMP_SUPPORT etc.
473 //==========================================================================