Re: SCHED_SPORADIC in Xenomai 3
Jan Kiszka <[email protected]>
| Newsgroups | dev.linux.lists.xenomai |
|---|---|
| Message-ID | <[email protected]> |
On 10.06.26 09:33, Jan Kiszka wrote:
> 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
>
The blast radius extends:
- I added a preempting fifo thread to the sched-quota test as well, and
it completely destroyed the thread group accounting: the group no
longer gets throttled, rather than getting time stolen by the
preemptions.
- The evl core looks identical here and should be similarly affected,
regarding quota-based scheduling.
Jan
--
Siemens AG, Foundational Technologies
Linux Expert Center