Mercurial > ecos
changeset 2348:73a01c1a35ba
* src/sched/mlqueue.cxx (add_thread, yield):
* src/sched/sched.cxx (unlock_inner, thread_entry):
* include/mlqueue.hxx (class Cyg_SchedThread_Implementation):
* include/kapidata.h (CYG_SCHEDTHREAD_TIMESLICE_MEMBER):
* include/bitmap.hxx (class Cyg_SchedThread_Implementation):
Reimplement timeslicing code. There is now a timeslice_count field
in each thread which is moved to and from the per-CPU counter
during thread dispatch. This approach has been taken to minimize
the changes needed to SMP code. Scheduler specific thread
functions handle counter save, restore and reset. These functions
are defined (as empty inlines) even when timeslicing is disabled,
or in non-timeslicing schedulers, to avoid adding ifdefs to the
code (this change actually removes some).
* tests/timeslice2.c:
* cdl/kernel.cdl: Added timeslice2 test to test behaviour of
timeslicing while being preempted.
| author | nickg |
|---|---|
| date | Mon, 08 Jan 2007 16:20:13 +0000 |
| parents | f5f9a2e0c73b |
| children | e82381ee1ecc |
| files | packages/kernel/current/ChangeLog packages/kernel/current/cdl/kernel.cdl packages/kernel/current/include/bitmap.hxx packages/kernel/current/include/kapidata.h packages/kernel/current/include/mlqueue.hxx packages/kernel/current/src/sched/mlqueue.cxx packages/kernel/current/src/sched/sched.cxx packages/kernel/current/tests/timeslice.c packages/kernel/current/tests/timeslice2.c packages/kernel/current/tests/tm_basic.cxx |
| diffstat | 10 files changed, 402 insertions(+), 34 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/kernel/current/ChangeLog +++ b/packages/kernel/current/ChangeLog @@ -3,6 +3,26 @@ 2007-01-07 Andrew Lunn <andrew.lunn@as * src/sync/mbox.cxx (Cyg_Mbox::get): Fix compiler warning with gcc version 4.1.2. +2006-12-08 Nick Garnett <nickg@ecoscentric.com> + + * src/sched/mlqueue.cxx (add_thread, yield): + * src/sched/sched.cxx (unlock_inner, thread_entry): + * include/mlqueue.hxx (class Cyg_SchedThread_Implementation): + * include/kapidata.h (CYG_SCHEDTHREAD_TIMESLICE_MEMBER): + * include/bitmap.hxx (class Cyg_SchedThread_Implementation): + Reimplement timeslicing code. There is now a timeslice_count field + in each thread which is moved to and from the per-CPU counter + during thread dispatch. This approach has been taken to minimize + the changes needed to SMP code. Scheduler specific thread + functions handle counter save, restore and reset. These functions + are defined (as empty inlines) even when timeslicing is disabled, + or in non-timeslicing schedulers, to avoid adding ifdefs to the + code (this change actually removes some). + + * tests/timeslice2.c: + * cdl/kernel.cdl: Added timeslice2 test to test behaviour of + timeslicing while being preempted. + 2006-10-12 Nick Garnett <nickg@ecoscentric.com> * cdl/synch.cdl: Added CYGIMP_MBOX_USE_MBOXT_PLAIN option. This is
--- a/packages/kernel/current/cdl/kernel.cdl +++ b/packages/kernel/current/cdl/kernel.cdl @@ -323,7 +323,7 @@ cdl_package CYGPKG_KERNEL { no_define calculated { "tests/bin_sem0 tests/bin_sem1 tests/bin_sem2 tests/bin_sem3 tests/clock0 tests/clock1 tests/clockcnv tests/clocktruth tests/cnt_sem0 tests/cnt_sem1 tests/except1 tests/flag0 tests/flag1 tests/intr0 tests/kill tests/mbox1 tests/mqueue1 tests/mutex0 tests/mutex1 tests/mutex2 tests/mutex3 tests/release tests/sched1 tests/sync2 tests/sync3 tests/thread0 tests/thread1 tests/thread2" - . ((CYGFUN_KERNEL_API_C) ? " tests/kclock0 tests/kclock1 tests/kexcept1 tests/kflag0 tests/kflag1 tests/kintr0 tests/klock tests/kmbox1 tests/kmutex0 tests/kmutex1 tests/kmutex3 tests/kmutex4 tests/ksched1 tests/ksem0 tests/ksem1 tests/kthread0 tests/kthread1 tests/stress_threads tests/thread_gdb tests/timeslice tests/tm_basic tests/fptest tests/kalarm0" : "") + . ((CYGFUN_KERNEL_API_C) ? " tests/kclock0 tests/kclock1 tests/kexcept1 tests/kflag0 tests/kflag1 tests/kintr0 tests/klock tests/kmbox1 tests/kmutex0 tests/kmutex1 tests/kmutex3 tests/kmutex4 tests/ksched1 tests/ksem0 tests/ksem1 tests/kthread0 tests/kthread1 tests/stress_threads tests/thread_gdb tests/timeslice tests/timeslice2 tests/tm_basic tests/fptest tests/kalarm0" : "") . ((!CYGPKG_INFRA_DEBUG && !CYGPKG_KERNEL_INSTRUMENT && CYGFUN_KERNEL_API_C) ? " tests/dhrystone" : "") . ((CYGPKG_KERNEL_SMP_SUPPORT && CYGFUN_KERNEL_API_C) ? " tests/smp" : "") . ((!CYGINT_HAL_TESTS_NO_CACHES && CYGFUN_KERNEL_API_C) ? " tests/kcache1 tests/kcache2" : "")
--- a/packages/kernel/current/include/bitmap.hxx +++ b/packages/kernel/current/include/bitmap.hxx @@ -12,6 +12,7 @@ // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2006 eCosCentric Limited // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free @@ -156,6 +157,11 @@ protected: // These are not applicable in a bitmap scheduler; placeholders: inline void rotate_queue( cyg_priority pri ) { }; inline void to_queue_head( void ) { }; + + inline void timeslice_save() {}; + inline void timeslice_restore() {}; + inline void timeslice_reset() {}; + }; // -------------------------------------------------------------------------
--- a/packages/kernel/current/include/kapidata.h +++ b/packages/kernel/current/include/kapidata.h @@ -277,6 +277,13 @@ typedef struct # define CYG_SCHEDTHREAD_CPU_MEMBER #endif +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE +# define CYG_SCHEDTHREAD_TIMESLICE_MEMBER \ + cyg_ucount32 timeslice_count; /* per-thread timeslice counter */ +#else +# define CYG_SCHEDTHREAD_TIMESLICE_MEMBER +#endif + #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE_ENABLE # define CYG_SCHEDTHREAD_TIMESLICE_ENABLED_MEMBER \ cyg_bool timeslice_enabled; /* per-thread timeslice enable */ @@ -293,6 +300,7 @@ typedef struct cyg_thread *prev; \ cyg_priority_t priority; /* current thread priority */ \ CYG_SCHEDTHREAD_CPU_MEMBER \ + CYG_SCHEDTHREAD_TIMESLICE_MEMBER \ CYG_SCHEDTHREAD_TIMESLICE_ENABLED_MEMBER #elif defined(CYGSEM_KERNEL_SCHED_LOTTERY) # define CYG_SCHEDTHREAD_SCHEDIMP_MEMBERS \
--- a/packages/kernel/current/include/mlqueue.hxx +++ b/packages/kernel/current/include/mlqueue.hxx @@ -180,8 +180,6 @@ protected: static cyg_ucount32 timeslice_count[CYGNUM_KERNEL_CPU_MAX] CYGBLD_ANNOTATE_VARIABLE_SCHED; - static void reset_timeslice_count(); - #endif Cyg_Scheduler_Implementation(); // Constructor @@ -237,14 +235,6 @@ inline void Cyg_Scheduler_Implementation need_reschedule[CYG_KERNEL_CPU_THIS()] = true; } -#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE - -inline void Cyg_Scheduler_Implementation::reset_timeslice_count() -{ - timeslice_count[CYG_KERNEL_CPU_THIS()] = CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; -} - -#endif // ------------------------------------------------------------------------- // Scheduler thread implementation. @@ -278,6 +268,16 @@ protected: // of its queue (not necessarily // a scheduler queue) +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + + cyg_ucount32 timeslice_count; + + void timeslice_save(); + + void timeslice_restore(); + + void timeslice_reset(); + #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE_ENABLE // This defines whether this thread is subject to timeslicing. @@ -291,6 +291,14 @@ public: void timeslice_disable(); +#endif + +#else + + inline void timeslice_save() {}; + inline void timeslice_restore() {}; + inline void timeslice_reset() {}; + #endif }; @@ -298,6 +306,23 @@ public: // ------------------------------------------------------------------------- // Cyg_SchedThread_Implementation inlines. +#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE + +inline void Cyg_SchedThread_Implementation::timeslice_save() +{ + timeslice_count = Cyg_Scheduler_Implementation::timeslice_count[CYG_KERNEL_CPU_THIS()]; +} + +inline void Cyg_SchedThread_Implementation::timeslice_restore() +{ + Cyg_Scheduler_Implementation::timeslice_count[CYG_KERNEL_CPU_THIS()] = timeslice_count; +} + +inline void Cyg_SchedThread_Implementation::timeslice_reset() +{ + timeslice_count = CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; +} + #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE_ENABLE inline void Cyg_SchedThread_Implementation::timeslice_enable() @@ -312,6 +337,8 @@ inline void Cyg_SchedThread_Implementati #endif +#endif + // ------------------------------------------------------------------------- #endif // ifndef CYGONCE_KERNEL_MLQUEUE_HXX
--- a/packages/kernel/current/src/sched/mlqueue.cxx +++ b/packages/kernel/current/src/sched/mlqueue.cxx @@ -236,6 +236,11 @@ Cyg_Scheduler_Implementation::add_thread // current thread, request a reschedule. set_need_reschedule(thread); + + // Also reset the timeslice_count so that this thread gets a full + // timeslice once it begins to run. + + thread->timeslice_reset(); #ifdef CYGPKG_KERNEL_SMP_SUPPORT @@ -640,12 +645,14 @@ Cyg_SchedThread_Implementation::yield(vo if( queue->get_head() != thread ) sched->set_need_reschedule(); + else + { + // Reset the timeslice counter so that this thread gets a + // full quantum as a reward for yielding when it is + // eventually rescheduled. + thread->timeslice_reset(); + } -#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE - // Reset the timeslice counter so that this thread gets a full - // quantum. - else Cyg_Scheduler::reset_timeslice_count(); -#endif } // Unlock the scheduler and switch threads
--- a/packages/kernel/current/src/sched/sched.cxx +++ b/packages/kernel/current/src/sched/sched.cxx @@ -198,7 +198,8 @@ void Cyg_Scheduler::unlock_inner( cyg_uc #ifdef CYGFUN_KERNEL_THREADS_STACK_CHECKING next->check_stack(); // before running it #endif - + current->timeslice_save(); + // Switch contexts HAL_THREAD_SWITCH_CONTEXT( ¤t->stack_ptr, &next->stack_ptr ); @@ -221,14 +222,10 @@ void Cyg_Scheduler::unlock_inner( cyg_uc CYG_ASSERTCLASS( current, "Bad current thread" ); current_thread[CYG_KERNEL_CPU_THIS()] = current; // restore current thread pointer + + current->timeslice_restore(); } -#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE - // Reset the timeslice counter so that this thread gets a full - // quantum. - reset_timeslice_count(); -#endif - clear_need_reschedule(); // finished rescheduling } @@ -318,12 +315,9 @@ void Cyg_Scheduler::thread_entry( Cyg_Th set_current_thread(thread); // restore current thread pointer CYG_INSTRUMENT_THREAD(ENTER,thread,0); - -#ifdef CYGSEM_KERNEL_SCHED_TIMESLICE - // Reset the timeslice counter so that this thread gets a full - // quantum. - reset_timeslice_count(); -#endif + + thread->timeslice_reset(); + thread->timeslice_restore(); // Finally unlock the scheduler. As well as clearing the scheduler // lock this allows any pending DSRs to execute. The new thread
--- a/packages/kernel/current/tests/timeslice.c +++ b/packages/kernel/current/tests/timeslice.c @@ -9,6 +9,7 @@ // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2006 eCosCentric Limited // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free @@ -264,16 +265,14 @@ externC void cyg_start( void ) { CYG_TEST_INIT(); - CYG_TEST_INFO("SMP test requires:\n" + CYG_TEST_INFO("Timeslice test requires:\n" "CYGSEM_KERNEL_SCHED_TIMESLICE &&\n" - "CYGPKG_KERNEL_SMP_SUPPORT &&\n" "CYGFUN_KERNEL_API_C && \n" "CYGSEM_KERNEL_SCHED_MLQUEUE &&\n" "CYGVAR_KERNEL_COUNTERS_CLOCK &&\n" - "!CYGPKG_HAL_I386_LINUX &&\n" "!CYGDBG_INFRA_DIAG_USE_DEVICE &&\n" "(CYGNUM_KERNEL_SCHED_PRIORITIES > 12)\n"); - CYG_TEST_NA("SMP test requirements"); + CYG_TEST_NA("Timeslice test requirements"); } #endif // CYGSEM_KERNEL_SCHED_TIMESLICE etc.
new file mode 100644 --- /dev/null +++ b/packages/kernel/current/tests/timeslice2.c @@ -0,0 +1,307 @@ +//========================================================================== +// +// timeslice2.c +// +// Timeslice 2 test +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2006 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2001-06-18 +// Description: An additional timeslicing test. +// +//####DESCRIPTIONEND#### +//========================================================================== + +#include <pkgconf/kernel.h> +#include <pkgconf/hal.h> + +#include <cyg/hal/hal_arch.h> + +#include <cyg/kernel/smp.hxx> + +#include <cyg/kernel/kapi.h> + +#include <cyg/infra/testcase.h> +#include <cyg/infra/diag.h> + +//========================================================================== + +#if defined(CYGSEM_KERNEL_SCHED_TIMESLICE) && \ + defined(CYGFUN_KERNEL_API_C) && \ + defined(CYGSEM_KERNEL_SCHED_MLQUEUE) && \ + defined(CYGVAR_KERNEL_COUNTERS_CLOCK) && \ + !defined(CYGDBG_INFRA_DIAG_USE_DEVICE) && \ + (CYGNUM_KERNEL_SCHED_PRIORITIES > 12) + +//========================================================================== + +#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL + +#define NTHREADS_MAX (CYGNUM_KERNEL_CPU_MAX*6) + +static int ncpus = CYGNUM_KERNEL_CPU_MAX; + +static char test_stack[STACK_SIZE]; +static cyg_thread test_thread; +static cyg_handle_t main_thread; + +static char hipri_stack[STACK_SIZE]; +static cyg_thread hipri_thread_obj; +static cyg_handle_t hipri_thread; + +static char stacks[NTHREADS_MAX][STACK_SIZE]; +static cyg_thread test_threads[NTHREADS_MAX]; +static cyg_handle_t threads[NTHREADS_MAX]; + +static volatile int failed = false; + +static volatile cyg_uint32 slicerun[NTHREADS_MAX][CYGNUM_KERNEL_CPU_MAX]; + +//========================================================================== + +void +test_thread_timeslice(CYG_ADDRESS id) +{ + for(;;) + slicerun[id][CYG_KERNEL_CPU_THIS()]++; +} + +//========================================================================== + +void run_test_timeslice(int nthread) +{ + int i,j; + cyg_uint32 cpu_total[CYGNUM_KERNEL_CPU_MAX]; + cyg_uint32 cpu_threads[CYGNUM_KERNEL_CPU_MAX]; + cyg_uint32 thread_total[NTHREADS_MAX]; + + CYG_TEST_INFO( "Timeslice2 Test: Check timeslicing works under preemption"); + + // Init flags. + for (i = 0; i < nthread; i++) + for( j = 0; j < ncpus; j++ ) + slicerun[i][j] = 0; + + // Set my priority higher than any I plan to create + cyg_thread_set_priority(cyg_thread_self(), 2); + + for (i = 0; i < nthread; i++) { + cyg_thread_create(10, // Priority - just a number + test_thread_timeslice, // entry + i, // index + "test_thread", // Name + &stacks[i][0], // Stack + STACK_SIZE, // Size + &threads[i], // Handle + &test_threads[i] // Thread data structure + ); + cyg_thread_resume( threads[i]); + } + + // Just wait a while, until the threads have all run for a bit. + cyg_thread_delay( CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS*100 ); + + // Suspend all the threads + for (i = 0; i < nthread; i++) { + cyg_thread_suspend(threads[i]); + } + + + // And check that a thread ran on each CPU, and that each thread + // ran. + + + diag_printf(" Thread "); + for( j = 0; j < ncpus; j++ ) + { + cpu_total[j] = 0; + cpu_threads[j] = 0; + // " %11d" __123456789ab" + diag_printf(" CPU %2d",j); + } + // " %11d" __123456789ab" + diag_printf(" Total\n"); + for (i = 0; i < nthread; i++) + { + thread_total[i] = 0; + diag_printf(" %2d ",i); + for( j = 0; j < ncpus; j++ ) + { + thread_total[i] += slicerun[i][j]; + cpu_total[j] += slicerun[i][j]; + if( slicerun[i][j] > 0 ) + cpu_threads[j]++; + diag_printf(" %11d",slicerun[i][j]); + } + diag_printf(" %11d\n",thread_total[i]); + if( thread_total[i] == 0 ) + failed++; + } + + diag_printf(" Total "); + for( j = 0; j < ncpus; j++ ) + diag_printf(" %11d",cpu_total[j]); + diag_printf("\n"); + diag_printf("Threads "); + for( j = 0; j < ncpus; j++ ) + { + diag_printf(" %11d",cpu_threads[j]); + if( cpu_threads[j] < 2 ) + failed++; + } + diag_printf("\n"); + + // Delete all the threads + for (i = 0; i < nthread; i++) { + cyg_thread_delete(threads[i]); + } + + CYG_TEST_INFO( "Timeslice2 Test: done"); +} + + +//========================================================================== + +void +hipri_test(CYG_ADDRESS id) +{ + while( 1 ) + { + cyg_thread_delay( CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS/2 ); + } +} + +//========================================================================== + +void +run_tests(CYG_ADDRESS id) +{ + int step; + int nthread; + + // Try to run about 10 times in total, with varying numbers of threads + // from only one extra up to the full set: + + step = (NTHREADS_MAX - (1 + CYG_KERNEL_CPU_COUNT()))/10; + if( step == 0 ) step = 1; + + for( nthread = 1 + CYG_KERNEL_CPU_COUNT() ; + nthread <= NTHREADS_MAX ; + nthread += step ) + run_test_timeslice(nthread); + + if( failed ) + CYG_TEST_FAIL_FINISH("Timeslice2 test failed\n"); + + CYG_TEST_PASS_FINISH("Timeslice2 test OK"); +} + +//========================================================================== + +void timeslice_main( void ) +{ + CYG_TEST_INIT(); + + // Work out how many CPUs we actually have. + ncpus = CYG_KERNEL_CPU_COUNT(); + + cyg_thread_create(0, // Priority - just a number + run_tests, // entry + 0, // index + "run_tests", // Name + test_stack, // Stack + STACK_SIZE, // Size + &main_thread, // Handle + &test_thread // Thread data structure + ); + cyg_thread_resume( main_thread); + + cyg_thread_create(5, // Priority - just a number + hipri_test, // entry + 0, // index + "hipri_run", // Name + hipri_stack, // Stack + STACK_SIZE, // Size + &hipri_thread, // Handle + &hipri_thread_obj // Thread data structure + ); + cyg_thread_resume( hipri_thread); + + cyg_scheduler_start(); +} + +//========================================================================== + +#ifdef CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG +externC void +cyg_hal_invoke_constructors(); +#endif + +externC void +cyg_start( void ) +{ +#ifdef CYGSEM_HAL_STOP_CONSTRUCTORS_ON_FLAG + cyg_hal_invoke_constructors(); +#endif + timeslice_main(); +} + +//========================================================================== + +#else // CYGSEM_KERNEL_SCHED_TIMESLICE etc + +externC void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_INFO("Timeslice test requires:\n" + "CYGSEM_KERNEL_SCHED_TIMESLICE &&\n" + "CYGFUN_KERNEL_API_C && \n" + "CYGSEM_KERNEL_SCHED_MLQUEUE &&\n" + "CYGVAR_KERNEL_COUNTERS_CLOCK &&\n" + "!CYGDBG_INFRA_DIAG_USE_DEVICE &&\n" + "(CYGNUM_KERNEL_SCHED_PRIORITIES > 12)\n"); + CYG_TEST_NA("Timeslice test requirements"); +} + +#endif // CYGSEM_KERNEL_SCHED_TIMESLICE etc. + +//========================================================================== +// EOF timeslice2.c
--- a/packages/kernel/current/tests/tm_basic.cxx +++ b/packages/kernel/current/tests/tm_basic.cxx @@ -86,7 +86,7 @@ typedef struct fun_times { cyg_uint32 end; } fun_times; -#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_MINIMUM +#define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL+4*1024) #ifdef CYGMEM_REGION_ram_SIZE #define CYG_THREAD_OVERHEAD (STACK_SIZE+sizeof(cyg_thread)+(sizeof(fun_times)*2))
