Re: SCHED_SPORADIC in Xenomai 3
Philippe Gerum <[email protected]>
| Newsgroups | dev.linux.lists.xenomai |
|---|---|
| Message-ID | <[email protected]> |
Jan Kiszka <[email protected]> writes: > 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. > The EVL core inherited the implementation of the quota scheduling class from Cobalt, therefore suffers the same problem ATM. -- Philippe.