Mercurial > ecos
diff packages/kernel/current/src/sched/mlqueue.cxx @ 177:4c750ce71ae3
Merge from eCos master repository on 2001-08-10-19:22:57-BST
| author | jlarmour |
|---|---|
| date | Fri, 10 Aug 2001 19:27:55 +0000 |
| parents | 0d0f03f76f6a |
| children | 391299f9e541 |
line wrap: on
line diff
--- a/packages/kernel/current/src/sched/mlqueue.cxx +++ b/packages/kernel/current/src/sched/mlqueue.cxx @@ -78,8 +78,7 @@ #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE -cyg_ucount32 Cyg_Scheduler_Implementation::timeslice_count = - CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; +cyg_ucount32 Cyg_Scheduler_Implementation::timeslice_count[CYGNUM_KERNEL_CPU_MAX]; #endif @@ -96,6 +95,21 @@ Cyg_Scheduler_Implementation::Cyg_Schedu queue_map = 0; +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + + pending_map = 0; + + for( int i = 0; i < CYGNUM_KERNEL_SCHED_PRIORITIES; i++ ) + pending[i] = 0; + +#endif + + for( int i = 0; i < CYGNUM_KERNEL_CPU_MAX; i++ ) + { + timeslice_count[i] = CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; + need_reschedule[i] = true; + } + CYG_REPORT_RETURN(); } @@ -113,15 +127,71 @@ Cyg_Scheduler_Implementation::schedule(v CYG_ASSERT( queue_map != 0, "Run queue empty"); CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + + Cyg_Thread *current = get_current_thread(); + register cyg_uint32 index; + + CYG_ASSERT( current->cpu != CYG_KERNEL_CPU_NONE, "Current thread does not have CPU set!"); + + // If the current thread is still runnable, return it to pending + // state so that it can be considered alongside any other threads + // for execution. + if( current->get_state() == Cyg_Thread::RUNNING ) + { + current->cpu = CYG_KERNEL_CPU_NONE; + pending[current->priority]++; + pending_map |= (1<<current->priority); + } + else + { + // Otherwise, ensure that the thread is no longer marked as + // running. + current->cpu = CYG_KERNEL_CPU_NONE; + } + + HAL_LSBIT_INDEX(index, pending_map); + + Cyg_RunQueue *queue = &run_queue[index]; + + CYG_ASSERT( !queue->empty(), "Queue for index empty"); + CYG_ASSERT( pending[index] > 0, "Pending array and map disagree"); + + Cyg_Thread *thread = queue->get_head(); + + // We know there is a runnable thread in this queue, If the thread + // we got is not it, scan until we find it. While not constant time, + // this search has an upper bound of the number of CPUs in the system. + + while( thread->cpu != CYG_KERNEL_CPU_NONE ) + thread = thread->get_next(); + + // Take newly scheduled thread out of pending map + thread->cpu = CYG_KERNEL_CPU_THIS(); + if( --pending[index] == 0 ) + pending_map &= ~(1<<index); + +#else + register cyg_uint32 index; HAL_LSBIT_INDEX(index, queue_map); - Cyg_Thread *thread = run_queue[index].highpri(); + Cyg_RunQueue *queue = &run_queue[index]; + + CYG_ASSERT( !queue->empty(), "Queue for index empty"); + + Cyg_Thread *thread = queue->get_head(); +#endif + + CYG_INSTRUMENT_MLQ( SCHEDULE, thread, index); + CYG_ASSERT( thread != NULL , "No threads in run queue"); - + CYG_ASSERT( thread->queue == NULL , "Runnable thread on a queue!"); + CYG_REPORT_RETVAL(thread); return thread; @@ -136,12 +206,16 @@ Cyg_Scheduler_Implementation::add_thread CYG_REPORT_FUNCARG1("thread=%08x", thread); cyg_priority pri = thread->priority; - Cyg_SchedulerThreadQueue_Implementation *queue = &run_queue[pri]; + Cyg_RunQueue *queue = &run_queue[pri]; + CYG_INSTRUMENT_MLQ( ADD, thread, pri); + CYG_ASSERT((CYG_THREAD_MIN_PRIORITY >= pri) && (CYG_THREAD_MAX_PRIORITY <= pri), "Priority out of range!"); + CYG_ASSERT( ((queue_map & (1<<pri))!=0) == ((!run_queue[pri].empty())!=0), "Map and queue disagree"); + // If the thread is on some other queue, remove it // here. if( thread->queue != NULL ) @@ -156,21 +230,38 @@ Cyg_Scheduler_Implementation::add_thread queue_map |= (1<<pri); - // If the new thread is higher priority than the - // current thread, request a reschedule. - - if( pri < Cyg_Scheduler::get_current_thread()->priority ) - need_reschedule = true; - } // else the queue already has an occupant, queue behind him + queue->add_tail(thread); + + // If the new thread is higher priority than any + // current thread, request a reschedule. + + set_need_reschedule(thread); + +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + + // If the thread is not currently running, increment the pending + // count for the priority, and if necessary set the bit in the + // pending map. + + if( thread->cpu == CYG_KERNEL_CPU_NONE ) + { + if( pending[pri]++ == 0 ) + pending_map |= (1<<pri); + } + // Otherwise the pending count will be dealt with in schedule(). + +#endif + + CYG_ASSERT( thread->queue == NULL , "Runnable thread on a queue!"); CYG_ASSERT( queue_map != 0, "Run queue empty"); CYG_ASSERT( queue_map & (1<<pri), "Queue map bit not set for pri"); + CYG_ASSERT( !run_queue[pri].empty(), "Queue for pri empty"); + CYG_ASSERT( ((queue_map & (1<<pri))!=0) == ((!run_queue[pri].empty())!=0), "Map and queue disagree"); CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); -// CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); - - queue->enqueue(thread); + CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); CYG_REPORT_RETURN(); } @@ -184,13 +275,39 @@ Cyg_Scheduler_Implementation::rem_thread CYG_REPORT_FUNCARG1("thread=%08x", thread); CYG_ASSERT( queue_map != 0, "Run queue empty"); - - cyg_priority pri = thread->priority; - Cyg_SchedulerThreadQueue_Implementation *queue = &run_queue[pri]; + + cyg_priority pri = thread->priority; + Cyg_RunQueue *queue = &run_queue[pri]; + + CYG_INSTRUMENT_MLQ( REM, thread, pri); + + CYG_ASSERT( pri != CYG_THREAD_MIN_PRIORITY, "Idle thread trying to sleep!"); + CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + + if( thread->cpu == CYG_KERNEL_CPU_NONE ) + { + // If the thread is not running, then we need to adjust the + // pending count array and map if necessary. - CYG_ASSERT( pri != CYG_THREAD_MIN_PRIORITY, "Idle thread trying to sleep!"); + if( --pending[pri] == 0 ) + pending_map &= ~(1<<pri); + } + else + { + // If the target thread is currently running on a different + // CPU, send a reschedule interrupt there to deschedule it. + if( thread->cpu != CYG_KERNEL_CPU_THIS() ) + CYG_KERNEL_CPU_RESCHEDULE_INTERRUPT( thread->cpu, 0 ); + } + // If the thread is current running on this CPU, then the pending + // count will be dealt with in schedule(). + +#endif + CYG_ASSERT( queue_map & (1<<pri), "Queue map bit not set for pri"); - CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + CYG_ASSERT( !run_queue[pri].empty(), "Queue for pri empty"); // remove thread from queue queue->remove(thread); @@ -206,8 +323,104 @@ Cyg_Scheduler_Implementation::rem_thread CYG_ASSERT( queue_map != 0, "Run queue empty"); CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); CYG_ASSERT( !run_queue[CYG_THREAD_MIN_PRIORITY].empty(), "Idle thread vanished!!!"); + CYG_ASSERT( ((queue_map & (1<<pri))!=0) == ((!run_queue[pri].empty())!=0), "Map and queue disagree"); + + CYG_REPORT_RETURN(); +} - CYG_REPORT_RETURN(); +// ------------------------------------------------------------------------- +// Set the need_reschedule flag +// This function overrides the definition in Cyg_Scheduler_Base and tests +// for a reschedule condition based on the priorities of the given thread +// and the current thread(s). + +void Cyg_Scheduler_Implementation::set_need_reschedule(Cyg_Thread *thread) +{ +#ifndef CYGPKG_KERNEL_SMP_SUPPORT + + if( current_thread[0]->priority > thread->priority || + current_thread[0]->get_state() != Cyg_Thread::RUNNING ) + need_reschedule[0] = true; + +#else + + HAL_SMP_CPU_TYPE cpu_this = CYG_KERNEL_CPU_THIS(); + HAL_SMP_CPU_TYPE cpu_count = CYG_KERNEL_CPU_COUNT(); + + // Start with current CPU. If we can do the job locally then + // that is most efficient. Only go on to other CPUs if that is + // not possible. + + HAL_SMP_CPU_TYPE cpu = cpu_this; + HAL_SMP_CPU_TYPE cpu_last = (cpu + cpu_count - 1) % cpu_count; + + for(;;) + { + + // If a CPU is not already marked for rescheduling, and its + // current thread is of lower priority than _thread_, then + // set its need_reschedule flag. + + Cyg_Thread *cur = current_thread[cpu]; + + if( !need_reschedule[cpu] && + (cur->priority > thread->priority) + ) + { + need_reschedule[cpu] = true; + + if( cpu != cpu_this ) + { + CYG_INSTRUMENT_SMP( RESCHED_SEND, cpu, 0 ); + CYG_KERNEL_CPU_RESCHEDULE_INTERRUPT( cpu, 0 ); + } + + // Having notionally rescheduled _thread_ onto the cpu, we + // now see if we can reschedule the former current thread of + // that CPU onto another. + + thread = cur; + cpu = (cpu + 1) % cpu_count; + cpu_last = (cpu + cpu_count - 1) % cpu_count; + continue; + } + + // If that was the last CPU in the circle, stop here. + if( cpu == cpu_last ) + break; + + // Otherwise go on to the next. + cpu = (cpu + 1) % cpu_count; + } + +#endif +} + +// ------------------------------------------------------------------------- +// Set up initial idle thread + +void Cyg_Scheduler_Implementation::set_idle_thread( Cyg_Thread *thread, HAL_SMP_CPU_TYPE cpu ) +{ + // Make the thread the current thread for this CPU. + + current_thread[cpu] = thread; + + // This will insert the thread in the run queues and make it + // available to execute. + thread->resume(); + +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + + thread->cpu = cpu; + + // In SMP, we need to take this thread out of the pending array + // and map. + + cyg_priority pri = thread->priority; + if( --pending[pri] == 0 ) + pending_map &= ~(1<<pri); +#endif + } // ------------------------------------------------------------------------- @@ -252,19 +465,61 @@ Cyg_Scheduler_Implementation::unique( cy #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE +// ------------------------------------------------------------------------- + void Cyg_Scheduler_Implementation::timeslice(void) { #ifdef CYGDBG_KERNEL_TRACE_TIMESLICE CYG_REPORT_FUNCTION(); #endif + +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + + HAL_SMP_CPU_TYPE cpu; + HAL_SMP_CPU_TYPE cpu_count = CYG_KERNEL_CPU_COUNT(); + HAL_SMP_CPU_TYPE cpu_this = CYG_KERNEL_CPU_THIS(); + + for( cpu = 0; cpu < cpu_count; cpu++ ) + { + if( --timeslice_count[cpu] == 0 ) + if( cpu == cpu_this ) + timeslice_cpu(); + else CYG_KERNEL_CPU_TIMESLICE_INTERRUPT( cpu, 0 ); + } + +#else + + if( --timeslice_count[CYG_KERNEL_CPU_THIS()] ) + timeslice_cpu(); + +#endif + +#ifdef CYGDBG_KERNEL_TRACE_TIMESLICE + CYG_REPORT_RETURN(); +#endif +} + +// ------------------------------------------------------------------------- + +void +Cyg_Scheduler_Implementation::timeslice_cpu(void) +{ +#ifdef CYGDBG_KERNEL_TRACE_TIMESLICE + CYG_REPORT_FUNCTION(); +#endif + + Cyg_Thread *thread = get_current_thread(); + HAL_SMP_CPU_TYPE cpu_this = CYG_KERNEL_CPU_THIS(); + CYG_ASSERT( queue_map != 0, "Run queue empty"); CYG_ASSERT( queue_map & (1<<CYG_THREAD_MIN_PRIORITY), "Idle thread vanished!!!"); #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE_ENABLE - if( current_thread->timeslice_enabled && --timeslice_count == 0 ) + if( thread->timeslice_enabled && + timeslice_count[cpu_this] == 0 ) #else - if( --timeslice_count == 0 ) + if( timeslice_count[cpu_this] == 0 ) #endif { CYG_INSTRUMENT_SCHED(TIMESLICE,0,0); @@ -272,9 +527,7 @@ Cyg_Scheduler_Implementation::timeslice( CYG_TRACE0( true, "quantum consumed, time to reschedule" ); #endif - CYG_ASSERT( sched_lock > 0 , "Timeslice called with zero sched_lock"); - - Cyg_Thread *thread = current_thread; + CYG_ASSERT( get_sched_lock() > 0 , "Timeslice called with zero sched_lock"); // Only try to rotate the run queue if the current thread is running. // Otherwise we are going to reschedule anyway. @@ -282,18 +535,30 @@ Cyg_Scheduler_Implementation::timeslice( { Cyg_Scheduler *sched = &Cyg_Scheduler::scheduler; + CYG_INSTRUMENT_MLQ( TIMESLICE, thread, 0); + CYG_ASSERTCLASS( thread, "Bad current thread"); CYG_ASSERTCLASS( sched, "Bad scheduler"); - cyg_priority pri = thread->priority; - Cyg_SchedulerThreadQueue_Implementation *queue = &sched->run_queue[pri]; + cyg_priority pri = thread->priority; + Cyg_RunQueue *queue = &sched->run_queue[pri]; - queue->rotate(); +#ifdef CYGPKG_KERNEL_SMP_SUPPORT - if( queue->highpri() != thread ) - sched->need_reschedule = true; + // In SMP systems we set the head of the queue to point to + // the thread immediately after the current + // thread. schedule() will then pick that thread, or one + // after it to run next. + + queue->to_head( thread->get_next() ); +#else + queue->rotate(); +#endif + + if( queue->get_head() != thread ) + sched->set_need_reschedule(); - timeslice_count = CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; +// timeslice_count[cpu_this] = CYGNUM_KERNEL_SCHED_TIMESLICE_TICKS; } } @@ -305,6 +570,13 @@ Cyg_Scheduler_Implementation::timeslice( #endif } +// ------------------------------------------------------------------------- + +__externC void cyg_scheduler_timeslice_cpu(void) +{ + Cyg_Scheduler::scheduler.timeslice_cpu(); +} + #endif //========================================================================== @@ -325,6 +597,9 @@ Cyg_SchedThread_Implementation::Cyg_Sche // If timeslice_enabled exists, set it true by default timeslice_enabled = true; #endif +#ifdef CYGPKG_KERNEL_SMP_SUPPORT + cpu = CYG_KERNEL_CPU_NONE; +#endif CYG_REPORT_RETURN(); } @@ -352,19 +627,32 @@ Cyg_SchedThread_Implementation::yield(vo // To yield we simply rotate the appropriate // run queue to the next thread and reschedule. + CYG_INSTRUMENT_MLQ( YIELD, thread, 0); + CYG_ASSERTCLASS( thread, "Bad current thread"); Cyg_Scheduler *sched = &Cyg_Scheduler::scheduler; CYG_ASSERTCLASS( sched, "Bad scheduler"); - cyg_priority pri = thread->priority; - Cyg_SchedulerThreadQueue_Implementation *queue = &sched->run_queue[pri]; + cyg_priority pri = thread->priority; + Cyg_RunQueue *queue = &sched->run_queue[pri]; + +#ifdef CYGPKG_KERNEL_SMP_SUPPORT - queue->rotate(); + // In SMP systems we set the head of the queue to point to + // the thread immediately after the current + // thread. schedule() will then pick that thread, or one + // after it to run next. + + queue->to_head( thread->get_next() ); +#else + queue->rotate(); +#endif + + if( queue->get_head() != thread ) + sched->set_need_reschedule(); - if( queue->highpri() != thread ) - sched->need_reschedule = true; #ifdef CYGSEM_KERNEL_SCHED_TIMESLICE // Reset the timeslice counter so that this thread gets a full // quantum. @@ -376,7 +664,7 @@ Cyg_SchedThread_Implementation::yield(vo #ifdef CYGDBG_USE_ASSERTS // This test keeps the assertions in unlock_inner() happy if // need_reschedule was not set above. - if( !Cyg_Scheduler::need_reschedule ) + if( !Cyg_Scheduler::get_need_reschedule() ) Cyg_Scheduler::unlock(); else #endif @@ -388,7 +676,7 @@ Cyg_SchedThread_Implementation::yield(vo // ------------------------------------------------------------------------- // Rotate the run queue at a specified priority. -// (pri is the decider, no this, so the routine is static) +// (pri is the decider, not this, so the routine is static) void Cyg_SchedThread_Implementation::rotate_queue( cyg_priority pri ) @@ -403,11 +691,11 @@ Cyg_SchedThread_Implementation::rotate_q CYG_ASSERTCLASS( sched, "Bad scheduler"); - Cyg_SchedulerThreadQueue_Implementation *queue = &sched->run_queue[pri]; + Cyg_RunQueue *queue = &sched->run_queue[pri]; if ( !queue->empty() ) { queue->rotate(); - sched->need_reschedule = true; + sched->set_need_reschedule(); } // Unlock the scheduler and switch threads @@ -454,6 +742,8 @@ Cyg_ThreadQueue_Implementation::enqueue( CYG_REPORT_FUNCTION(); CYG_REPORT_FUNCARG1("thread=%08x", thread); + CYG_INSTRUMENT_MLQ( ENQUEUE, this, thread ); + #ifdef CYGIMP_KERNEL_SCHED_SORTED_QUEUES // Insert the thread into the queue in priority order. @@ -537,6 +827,8 @@ Cyg_ThreadQueue_Implementation::dequeue( CYG_REPORT_FUNCTYPE("returning thread %08x"); Cyg_Thread *thread = rem_head(); + + CYG_INSTRUMENT_MLQ( DEQUEUE, this, thread ); if( thread != NULL ) thread->queue = NULL; @@ -553,6 +845,8 @@ Cyg_ThreadQueue_Implementation::remove( CYG_REPORT_FUNCTION(); CYG_REPORT_FUNCARG1("thread=%08x", thread); + CYG_INSTRUMENT_MLQ( REMOVE, this, thread ); + thread->queue = NULL; Cyg_CList_T<Cyg_Thread>::remove( thread ); @@ -582,22 +876,6 @@ Cyg_ThreadQueue_Implementation::set_thre // ------------------------------------------------------------------------- -void -Cyg_SchedulerThreadQueue_Implementation::enqueue(Cyg_Thread *thread) -{ - CYG_REPORT_FUNCTION(); - CYG_REPORT_FUNCARG1("thread=%08x", thread); - - add_tail( thread ); - - set_thread_queue( thread, CYG_CLASSFROMBASE(Cyg_ThreadQueue, - Cyg_SchedulerThreadQueue_Implementation, - this)); - CYG_REPORT_RETURN(); -} - -// ------------------------------------------------------------------------- - #endif // -------------------------------------------------------------------------
