mirror of
https://github.com/apache/nuttx.git
synced 2026-09-08 18:06:33 +00:00
sched/tickless: Fix SCHED_RR timeslice accounting on preemption
In tickless mode, the scheduler timer is stopped whenever the currently running task requires no time slicing (CLOCK_MAX). When a SCHED_RR task was later switched in, nothing re-armed the timer, so the task could run indefinitely without round-robin rotation. Also, when a SCHED_RR task was preempted, its timeslice counter was not decremented for the time already consumed, effectively giving the task "bonus" CPU time when resumed. Solve both by performing RR accounting on context switches: - nxsched_suspend_roundrobin() charges the elapsed execution time against the timeslice of the RR task being switched out - nxsched_resume_roundrobin() restarts the scheduler timer for the remaining timeslice of the RR task being switched in, so the timer is always armed while an RR task is running This also removes the previous workaround in nxsched_process_timer that triggered the scheduler on every timer tick. Assisted-by: Zhipu GLM-5.3 Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
This commit is contained in:
parent
be9200f95a
commit
75bc159896
5 changed files with 121 additions and 56 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue