Re: SCHED_SPORADIC in Xenomai 3
Jan Kiszka <[email protected]>
| Newsgroups | dev.linux.lists.xenomai |
|---|---|
| Message-ID | <[email protected]> |
On 10.06.26 09:21, Jan Kiszka wrote:
> On 10.06.26 08:24, Jan Kiszka wrote:
>> Hi Philippe,
>>
>> while trying to port the signal-while-suspended fix to Xenomai 3, I ran
>> into XNHELD, a state only existing there. I suppose that was once
>> forward-ported as EVL_T_HALT. The only user of XNHELD in Xenomai 3 is
>> SCHED_SPORADIC - so let's dive into that scheduling class.
>>
>> Turned out it was never documented, not even linked to the POSIX
>> standard. But it also slightly differs from it (low_prio = -1 -> suspend
>> on depletion). There is also no test case, so I asked an AI for one.
>> That worked fairly well as it seems to have revealed an issue:
>>
>> Could it be that we are not properly suspending the budget tracking when
>> a higher-prio task from a different scheduling class is preempting a
>> sporadic thread? It looks like that xnsched_sporadic_pick is not invoked
>> if a thread is selected from a higher-prio class first, namely sched-rt
>> with its weight 4 vs. 3 if sched-sporadic. Or is that an (undocumented)
>> limitation/misconfiguration? Is that issue even affecting other
>> time-slicing classes as well??
>>
>> That furthermore makes me wonder if we actually have users of
>> sched-sporadic. Likely a hard to answer question, as usual. But such a
>> limitation should have been observed earlier under real workload...
>>
>> Jan
>>
>
> Here is a trace that proves how xnsched_sporadic_pick and, thus,
> sporadic_suspend_activity are not called:
>
> disrupt-1682 [000] d..2. 94.171753: cobalt_head_sysentry: syscall=clock_nanosleep64
> disrupt-1682 [000] d..2. 94.171755: cobalt_clock_nanosleep: clock_id=1 flags=0() rqt=(0.060000000)
> disrupt-1682 [000] d..2. 94.171757: cobalt_thread_suspend: pid=1682 mask=0x4 timeout=60000001 timeout_mode=0 wchan=(nil)
> disrupt-1682 [000] d..2. 94.171759: cobalt_timer_start: timer=0xffffc900008bbb00(smokey) value=60000001 interval=0 mode=0x0
> disrupt-1682 [000] d..2. 94.171761: cobalt_tick_shot: next tick at 94.231756 (delay: 59995 us)
> disrupt-1682 [000] d..2. 94.171770: cobalt_schedule: status=0x10000000
> disrupt-1682 [000] d..2. 94.171771: cobalt_trace_pid: pid=1682, prio=30
> disrupt-1682 [000] d..2. 94.171776: bprint: xnsched_sporadic_pick: xnsched_sporadic_pick, curr=1682 next=1681
> disrupt-1682 [000] d..2. 94.171777: bprint: xnsched_sporadic_pick: sporadic_resume_activity, pss->budget 99964473
> disrupt-1682 [000] d..2. 94.171778: bprint: sporadic_schedule_drop: sporadic_schedule_drop, pss->budget 99964473
> disrupt-1682 [000] d..2. 94.171778: cobalt_timer_start: timer=0xffffc900008bc4d8(pss-drop) value=94216201725 interval=0 mode=0x1
> disrupt-1682 [000] d..2. 94.171779: cobalt_switch_context: prev_name=disrupt prev_pid=1682 prev_prio=30 prev_state=0x248044 ==> next_name=ss-d next_pid=1681 next_prio=20
> ss-d-1681 [000] d..2. 94.171784: cobalt_trace_pid: pid=1681, prio=20
> ss-d-1681 [000] d..2. 94.171788: cobalt_synch_acquire: synch=0xffffc900008bd408
> ss-d-1681 [000] d..2. 94.171789: cobalt_head_sysexit: result=0
> ss-d-1681 [000] d..2. 94.171799: cobalt_head_sysentry: syscall=mutex_unlock
> ss-d-1681 [000] d..2. 94.171801: cobalt_synch_release: synch=0xffffc900008bd408
> ss-d-1681 [000] d..2. 94.171801: cobalt_head_sysexit: result=0
> ss-d-1681 [000] d..2. 94.231786: cobalt_timer_expire: timer=0xffffc900008bbb00
> ss-d-1681 [000] d..2. 94.231789: cobalt_thread_resume: name=disrupt pid=1682 mask=0x4
> ss-d-1681 [000] d..2. 94.231790: cobalt_trace_pid: pid=1682, prio=30
> ss-d-1681 [000] d..2. 94.231791: cobalt_timer_stop: timer=0xffffc900008bbb00
> ss-d-1681 [000] d..2. 94.231794: cobalt_tick_shot: next tick at 94.271741 (delay: 39948 us)
> ss-d-1681 [000] d..2. 94.231802: cobalt_schedule: status=0x10000000
> ss-d-1681 [000] d..2. 94.231803: cobalt_trace_pid: pid=1681, prio=20
> ss-d-1681 [000] d..2. 94.231805: cobalt_switch_context: prev_name=ss-d prev_pid=1681 prev_prio=20 prev_state=0x248048 ==> next_name=disrupt next_pid=1682 next_prio=30
> disrupt-1682 [000] d..2. 94.231810: cobalt_trace_pid: pid=1682, prio=30
> disrupt-1682 [000] d..2. 94.231811: cobalt_head_sysexit: result=0
> disrupt-1682 [000] d..2. 94.271767: cobalt_timer_expire: timer=0xffffc900008bc4d8
> disrupt-1682 [000] d..2. 94.271771: cobalt_thread_suspend: pid=1681 mask=0x200 timeout=0 timeout_mode=0 wchan=(nil)
> disrupt-1682 [000] d..2. 94.271772: cobalt_tick_shot: next tick at 95.130203 (delay: 858431 us)
> disrupt-1682 [000] d..2. 94.271914: cobalt_head_sysentry: syscall=clock_nanosleep64
> disrupt-1682 [000] d..2. 94.271916: cobalt_clock_nanosleep: clock_id=1 flags=0() rqt=(0.060000000)
> disrupt-1682 [000] d..2. 94.271917: cobalt_thread_suspend: pid=1682 mask=0x4 timeout=60000001 timeout_mode=0 wchan=(nil)
>
> Jan
>
> PS: I asked AI (opus-4.7) to confirm or disprove this, and it failed in
> its code analysis. It seems like the way the code is structured and
> commented misguided it in its conclusions. Sooo statistically human...
>
Asked different ("find a path where..."):
"You're right — I need to retract part of my previous answer. Looking at this scenario through the cascade rule, sporadic_suspend_activity() is not invoked when a SCHED_FIFO thread preempts a SCHED_SPORADIC thread.
The path
1. A SCHED_SPORADIC thread runs at, say, normal_prio = 50. Its drop_timer was armed by sporadic_schedule_drop() at the last cascade, set to fire at resume_date + budget.
2. A SCHED_FIFO thread at priority 80 becomes runnable (wakes from sleep, IRQ unblocks it, etc.) and xnsched_set_resched() marks the CPU.
3. ___xnsched_run() → xnsched_pick_next() iterates for_each_xnsched_class (sched.c:322). RT has the highest weight, so xnsched_rt_pick(sched) runs first.
4. In xnsched_rt_pick (sched.c:730–762), the head of the highest-priority queue is now the FIFO thread. Its sched_class == &xnsched_class_rt.
5. The cascade check at sched.c:757 (if (unlikely(thread->sched_class != &xnsched_class_rt))) is false.
6. xnsched_rt_pick returns the FIFO thread via del_q. The for_each_xnsched_class loop sees a non-NULL result and exits.
7. xnsched_sporadic_pick is never called. sporadic_suspend_activity(curr) never runs on the outgoing sporadic thread."
This looks more useful.
Jan
--
Siemens AG, Foundational Technologies
Linux Expert Center