diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index a0534906621..2c89ebf83fa 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -639,6 +639,10 @@ struct tcb_s int32_t timeslice; /* RR timeslice OR Sporadic budget */ /* interval remaining */ #endif +#if CONFIG_RR_INTERVAL > 0 && defined(CONFIG_SCHED_TICKLESS) + clock_t rr_starttime; /* Time when RR task was last */ + /* accounted */ +#endif #ifdef CONFIG_SCHED_SPORADIC FAR struct sporadic_s *sporadic; /* Sporadic scheduling parameters */ #endif diff --git a/sched/sched/sched.h b/sched/sched/sched.h index 653d7ebaef6..f1a8776fcf1 100644 --- a/sched/sched/sched.h +++ b/sched/sched/sched.h @@ -349,8 +349,10 @@ int nxsched_reprioritize(FAR struct tcb_s *tcb, int sched_priority); #ifdef CONFIG_SCHED_TICKLESS void nxsched_reassess_timer(void); +void nxsched_timer_start(clock_t now, clock_t delay); #else # define nxsched_reassess_timer() +# define nxsched_timer_start(now, delay) #endif /* Scheduler policy support */ @@ -358,6 +360,10 @@ void nxsched_reassess_timer(void); #if CONFIG_RR_INTERVAL > 0 clock_t nxsched_process_roundrobin(FAR struct tcb_s *tcb, clock_t ticks, bool noswitches); +# ifdef CONFIG_SCHED_TICKLESS +void nxsched_suspend_roundrobin(FAR struct tcb_s *tcb); +void nxsched_resume_roundrobin(FAR struct tcb_s *tcb); +# endif #endif #ifdef CONFIG_SCHED_SPORADIC diff --git a/sched/sched/sched_processtickless.c b/sched/sched/sched_processtickless.c index 8332d81c911..4ffe7c12e4b 100644 --- a/sched/sched/sched_processtickless.c +++ b/sched/sched/sched_processtickless.c @@ -79,7 +79,6 @@ static clock_t nxsched_cpu_scheduler(int cpu, clock_t ticks, #endif static clock_t nxsched_process_scheduler(clock_t ticks, clock_t elapsed, bool noswitches); -static void nxsched_timer_start(clock_t ticks, clock_t interval); /**************************************************************************** * Private Data @@ -203,6 +202,7 @@ static clock_t nxsched_cpu_scheduler(int cpu, clock_t ticks, * timeslice. */ + rtcb->rr_starttime = ticks; ret = nxsched_process_roundrobin(rtcb, elapsed, noswitches); } #endif @@ -314,35 +314,6 @@ clock_t nxsched_process_scheduler(clock_t ticks, clock_t elapsed, return minslice; } -/**************************************************************************** - * Name: nxsched_timer_start - * - * Description: - * Start the interval timer. - * - * Input Parameters: - * ticks - The number of ticks defining the timer interval to setup. - * interval - The number of ticks to use when setting up the next timer. - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void nxsched_timer_start(clock_t ticks, clock_t interval) -{ - if (interval != CLOCK_MAX) - { - DEBUGASSERT(interval <= UINT32_MAX); - wd_start_abstick(&g_sched_event, ticks + interval, - nxsched_wdog_expiration, 0u); - } - else - { - wd_cancel(&g_sched_event); - } -} - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -362,13 +333,6 @@ static void nxsched_timer_start(clock_t ticks, clock_t interval) * Base code implementation assumes that this function is called from * interrupt handling logic with interrupts disabled. * - * Note: - * The current SCHED_RR implementation has an issue: if a round-robin task - * is preempted, its timeslice counter does not decrement properly. - * Therefore, we must trigger the scheduler on each timer expiration to - * minimize the occurrence of this problem. - * This workaround can be removed once the SCHED_RR behavior is fixed. - * ****************************************************************************/ void nxsched_process_timer(void) @@ -380,15 +344,6 @@ void nxsched_process_timer(void) clock_update_sched_ticks(ticks); hrtimer_process(nsec); - -# if CONFIG_RR_INTERVAL > 0 - /* Workaround for SCHED_RR, see the note. */ - - irqstate_t flags = enter_critical_section(); - nxsched_process_event(ticks, true); - leave_critical_section(flags); -# endif - #else irqstate_t flags; clock_t ticks; @@ -405,12 +360,6 @@ void nxsched_process_timer(void) clock_update_sched_ticks(ticks); -#if CONFIG_RR_INTERVAL > 0 - /* Workaround for SCHED_RR, see the note. */ - - nxsched_process_event(ticks, true); -#endif - wd_timer(ticks); leave_critical_section(flags); @@ -466,6 +415,40 @@ void nxsched_reassess_timer(void) nxsched_process_event(g_wdexpired, true); } +/**************************************************************************** + * Name: nxsched_timer_start + * + * Description: + * Start the interval timer for the scheduler event. Called during + * context switches to set up the timer for the newly running RR task's + * remaining timeslice. + * + * Input Parameters: + * now - The current time in ticks. + * delay - The number of ticks to wait until the timer expires. + * + * Returned Value: + * None + * + * Assumption: + * This function is called from the critical section. + * + ****************************************************************************/ + +void nxsched_timer_start(clock_t now, clock_t delay) +{ + if (delay != CLOCK_MAX) + { + DEBUGASSERT(delay <= UINT32_MAX); + wd_start_abstick(&g_sched_event, now + delay, + nxsched_wdog_expiration, 0u); + } + else + { + wd_cancel(&g_sched_event); + } +} + /**************************************************************************** * Name: nxsched_get_next_expired * diff --git a/sched/sched/sched_roundrobin.c b/sched/sched/sched_roundrobin.c index 43dc6fcfdf1..8400b400557 100644 --- a/sched/sched/sched_roundrobin.c +++ b/sched/sched/sched_roundrobin.c @@ -229,4 +229,63 @@ clock_t nxsched_process_roundrobin(FAR struct tcb_s *tcb, clock_t ticks, return ret; } +#ifdef CONFIG_SCHED_TICKLESS + +/**************************************************************************** + * Name: nxsched_suspend_roundrobin + * + * Description: + * Called when a task using round-robin scheduling is being switched out. + * Accounts for the time consumed since the timeslice was last assessed + * so that only actual CPU execution time is charged against the task. + * + * Input Parameters: + * tcb - The TCB of the thread that is being suspended. + * + * Returned Value: + * None + * + * Assumption: + * This function is called from the critical section. + * + ****************************************************************************/ + +void nxsched_suspend_roundrobin(FAR struct tcb_s *tcb) +{ + clock_t now = clock_systime_ticks(); + clock_t elapsed = now - tcb->rr_starttime; + + nxsched_process_roundrobin(tcb, elapsed, true); +} + +/**************************************************************************** + * Name: nxsched_resume_roundrobin + * + * Description: + * Called when a task using round-robin scheduling is being switched in. + * Restarts the scheduler timer for the task's remaining timeslice so + * that the timer is always armed while an RR task is running. + * + * Input Parameters: + * tcb - The TCB of the thread that is being resumed. + * + * Returned Value: + * None + * + * Assumption: + * This function is called from the critical section. + * + ****************************************************************************/ + +void nxsched_resume_roundrobin(FAR struct tcb_s *tcb) +{ + clock_t now = clock_systime_ticks(); + + DEBUGASSERT(tcb->timeslice >= 0); + tcb->rr_starttime = now; + nxsched_timer_start(now, tcb->timeslice); +} + +#endif /* CONFIG_SCHED_TICKLESS */ + #endif /* CONFIG_RR_INTERVAL > 0 */ diff --git a/sched/sched/sched_switchcontext.c b/sched/sched/sched_switchcontext.c index f2a708f5c4f..882f4f097a1 100644 --- a/sched/sched/sched_switchcontext.c +++ b/sched/sched/sched_switchcontext.c @@ -51,6 +51,17 @@ void nxsched_switch_context(FAR struct tcb_s *from, FAR struct tcb_s *to) { nxsched_checkstackoverflow(from); +#if CONFIG_RR_INTERVAL > 0 && defined(CONFIG_SCHED_TICKLESS) + /* If the task being switched out uses round-robin scheduling, account + * for the time it has consumed from its timeslice. + */ + + if ((from->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_RR) + { + nxsched_suspend_roundrobin(from); + } +#endif + #ifdef CONFIG_SCHED_SPORADIC /* Perform sporadic schedule operations */ @@ -65,12 +76,14 @@ void nxsched_switch_context(FAR struct tcb_s *from, FAR struct tcb_s *to) } #endif -#if defined(CONFIG_SCHED_TICKLESS) && CONFIG_RR_INTERVAL > 0 - /* Before the context switch, we should set the timer for RR. */ +#if CONFIG_RR_INTERVAL > 0 && defined(CONFIG_SCHED_TICKLESS) + /* If the task being switched in uses round-robin scheduling, restart + * the timer for its remaining timeslice. + */ - if (from != to && (to->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_RR) + if ((to->flags & TCB_FLAG_POLICY_MASK) == TCB_FLAG_SCHED_RR) { - nxsched_reassess_timer(); + nxsched_resume_roundrobin(to); } #endif