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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.