Skip to content

sched: Fix the timer reprogramming and SCHED_RR issues. - #20057

Merged
acassis merged 7 commits into
apache:masterfrom
Fix-Point:fixschedtimer
Sep 7, 2026
Merged

sched: Fix the timer reprogramming and SCHED_RR issues.#20057
acassis merged 7 commits into
apache:masterfrom
Fix-Point:fixschedtimer

Conversation

@Fix-Point

Copy link
Copy Markdown
Contributor

Summary

Fix three issues in the tickless scheduler / hrtimer path:

  • sched/hrtimer: Fix reprogram with wrong expiration when reinserting hrtimer. In hrtimer_start_absolute(), when a pending hrtimer (previously the head of the queue) is removed and reinserted with a later expiration, the reprogram flag remains set although the timer is no longer the earliest one in the queue. The old code passed hrtimer->expired to hrtimer_reprogram(); use hrtimer_get_first()->expired instead so the hardware timer is always reprogrammed with the actual earliest expiration.

  • sched/sched: Fix roundrobin if SCHED_TICKLESS enabled. In tickless mode the scheduler timer is stopped whenever the 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. Reassess the scheduler timer in nxsched_switch_context() before the context switch when the incoming task uses round-robin scheduling. Every architecture invokes nxsched_switch_context() exactly once per context switch (task switch, syscall, IRQ exit and task exit paths), so this covers all switch paths.

  • sched/tickless: Fix SCHED_RR timeslice accounting on preemption. When an RR task was preempted, its timeslice counter was not decremented for the time already consumed, effectively granting the task bonus CPU time when resumed. Perform RR accounting on context switches: nxsched_suspend_roundrobin() charges the elapsed execution time against the timeslice of the RR task being switched out, and nxsched_resume_roundrobin() re-arms 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 ran the scheduler logic on every timer tick.

Impact

  • Affects only CONFIG_SCHED_TICKLESS configurations with CONFIG_RR_INTERVAL > 0 (plus hrtimer users for the first fix). Periodic-tick builds are unchanged: the new scheduler code is compiled out, and hrtimer behavior only changes in the corner case described above.
  • No user-facing API changes, no build system, documentation, security or compatibility impact.

Testing

  • Host: Linux x86_64, riscv64-unknown-elf-gcc toolchain, QEMU (qemu-system-riscv32).
  • Board: rv-virt:smp configuration with CONFIG_SCHED_TICKLESS=y.
  • Build: make -j completes without errors or new warnings.
  • Boot: qemu-system-riscv32 -semihosting -M virt,aclint=on -cpu rv32 -smp 8 -bios none -kernel nuttx -nographic — NSH starts and is fully responsive.
  • Ran the ostest application under QEMU: all subtests pass, in particular the round-robin test, verifying that RR rotation works under tickless mode and that preemption no longer grants bonus timeslice to RR tasks.

…= CLOCK_MAX

When maxticks equals CLOCK_MAX (all bits set), the loop that builds
the mask by (*mask << 1) | 1 never terminates because the shifted
value wraps around to the same mask value, making next > maxticks
always false.
Replace the loop with a single flsx-based expression that computes
the mask directly, which naturally covers the CLOCK_MAX case.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
@Fix-Point Fix-Point changed the title sched: Fix the timer reprogramming issues. sched: Fix the timer reprogramming and SCHED_RR issues. Sep 4, 2026
@github-actions github-actions Bot added Area: Drivers Drivers issues Size: M The size of the change in this PR is medium labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

qemu-armv8a

@acassis

acassis commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@Fix-Point did you use AI to help with this PR? If so, please add: Assisted-by: AI Vendor and Model

@acassis

acassis commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@Fix-Point another question, does this PR fixes this Issue: #19370 ?

xiaoxiang781216
xiaoxiang781216 previously approved these changes Sep 4, 2026
Comment thread drivers/timers/arch_alarm.c Outdated
Comment thread drivers/timers/arch_timer.c Outdated
The mask computation introduced by "fix infinite loop in
up_timer_getmask when maxticks == CLOCK_MAX" has two problems:

1. If maxticks == 0, flsx(0) expands to __builtin_clz(0), which is
   undefined behavior, and the shift count becomes 8 * sizeof(clock_t)
   = 64 for a 64-bit clock_t, which is undefined behavior as well.
   The loop-based code that was replaced kept *mask = 0 in this case.

2. CLOCK_MAX is INT64_MAX, i.e. 63 one bits, not a full-width bit
   pattern. The resulting mask is always one bit narrower than the
   one produced by the original loop; e.g. a 32-bit timer got
   0x7fffffff instead of 0xffffffff, so counter deltas >= 2^31 were
   truncated in the clock timekeeping code.

Fix this by keeping *mask = 0 when maxticks == 0 and by deriving the
mask from the full-width unsigned constant (uint64_t)-1, which
restores the all-ones semantics of the original loop and still covers
the maxticks == CLOCK_MAX case.

Also initialize maxticks in arch_timer.c: if the lower half does not
implement the maxtimeout ops, the value is left untouched and would
otherwise be read uninitialized.

Assisted-by: Zhipu GLM-5.3
Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
Use round-up logic in clkcnt_delta_time2cnt() to prevent
timer sleep duration being too short due to truncation.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
…rtimer

In hrtimer_start_absolute, when a pending hrtimer is removed (was the
head) and reinserted with a later expiration time, the reprogram flag
remains true but the hrtimer is no longer the earliest timer in the
queue. The old code passed hrtimer->expired to hrtimer_reprogram, which
was incorrect. Use hrtimer_get_first()->expired to ensure the hardware
timer is reprogrammed with the actual earliest timer's expiration time.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
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.

Reassess the scheduler timer in nxsched_switch_context() before the
context switch when the task being switched in uses round-robin
scheduling, so that the timer is always armed while an RR task is
running.  Hooking into nxsched_switch_context() covers all context
switch paths (task context switch, interrupt exit, syscall and task
exit) since every architecture calls it on every switch.

Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
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>
Fix checkpatch "Missing blank line after declarations" errors in
drivers/timers/arch_timer.c and sched/sched/sched_processtickless.c.
These are pre-existing issues, not introduced by the recent tickless
RR series.

Assisted-by: Zhipu GLM-5.3
Signed-off-by: ouyangxiangzhen <ouyangxiangzhen@xiaomi.com>
@Fix-Point

Copy link
Copy Markdown
Contributor Author

@Fix-Point did you use AI to help with this PR? If so, please add: Assisted-by: AI Vendor and Model

Done.

@Fix-Point

Copy link
Copy Markdown
Contributor Author

@Fix-Point another question, does this PR fixes this Issue: #19370 ?

The issue mentioned in #19370 is caused by unfair pthread mutex, not the scheduler. If we remove the pthread_mutex_lock/unlock in the testcase, we can get the fair result:

qemu-system-riscv32 -semihosting -M virt,aclint=on -cpu rv32 -smp 8 -bios none -nographic -kernel nuttx 
ABC
NuttShell (NSH) NuttX-12.13.0
nsh> 
nsh> 
nsh> hello
Low task 1 count: 1468315765
Low task 2 count: 1339093170
Total operations: 2807408935
Elapsed time: 5001.25 ms
Average time per operation: 0.001 us
nsh> hello
Low task 1 count: 1468315765
Low task 2 count: 1339093170
Total operations: 2807408935
Elapsed time: 5001.35 ms
Average time per operation: 0.001 us
nsh> 

@acassis

acassis commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

unfair pthread mutex

What do you mean by "unfair pthread mutex" ? Should it be "unpaired" ?

@acassis
acassis merged commit f183798 into apache:master Sep 7, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Drivers Drivers issues Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants