-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
scheduler: threads using k_sleep can be _swap()'d back too early #8128
Comments
Found the bug in #8126 and I'm running my own personal soak test with 13 IoT devices to see how well they perform. |
So.... looking at this from the context of trying to make it happen in a test, I'm flipping back. I thought I had a test case for this (about to submit) that showed the failure, but that was a bad assert. k_sleep() actually works correctly right now, though the logic is distressingly subtle:
Basically, I don't see how this can be the bug. The k_sleep() state management works now for, effecitvely, the same reason it worked before the change. I agree we want to clean it up (ideally to make PENDING and "sleeping" the same state), but I don't see a logic failure. All that said, I'm leaving the +1 in place and agree we should merge this. The change is correct logically, easily reviewed, and I'm willing to buy that it fixes some other yet-more-subtle state somewhere else, given that it seems to be resolving your problems. |
FWIW: if you want to catch the presumptive bug closer to the act, one suggestion would be to enable assertions (if possible, otherwise just a manual test + log/abort/whatever) in should_preempt() that looks like:
I'd be very curious to see the results. A pretty careful re-read and grep of every path I can see to _add_timeout() shows that it's never possible for a thread to have a timeout in a case where it gets inspected as the top of the run queue. |
@andyross I agree with your assessment that k_sleep does manage to sort of work initially. However, with patch applied, I've had no oops or hangs in the 5 hours of testing on several devices. Previously, I would have seen at least 1 device go offline by now. I'll setup an assert to see if I can catch the path that is causing errors. |
Not sure if this should be reopened then. |
@pfalcon Hard to say. The reality is that 6c95daf uses the "correct" check which makes sure (even in some back water path) that the thread we are moving to is in fact ready. It has also been running on several of my devices for the past 18-ish hours without resulting in a hang or kernel oops which was previously showing up after an hour or so. @andyross has agreed that there is more work to do in the scheduler with regards to cleanup and the first patch he is submitting in v1.13 will move k_sleep towards using the same pend/unpend methods that the other APIs use so it won't be a snowflake like it is now. Should we keep a general purpose "Cleanup the scheduler and look for concurrency bugs" issue open? Maybe? But I think this issue is a bit too specific for that. |
Sounds good, I hope news about the assert above won't be lost/forgotten then ;-) (even if the news are "didn't get to it"). |
So the k_sleep() issue is real. Using this diff on master branch: https://hastebin.com/ihoyazofad.diff I generate this log while performing an OTA update using Zephyr sample: https://hastebin.com/uxojakapul.log Here's the backtrace from gdb: https://hastebin.com/bifoneripu.pas
|
Cool, thanks. Will rework for 1.13 and bug you to retest on the rig when it's ready. |
Also here's the _kernel object: https://hastebin.com/ducusuxata.xml This shows:
Backtrace shows:
Discussion points: Q: Why is the readyq.cache showing <_idle_thread_s>? This in the backtrace looks odd:
Were we in the middle of another __pendsv? when the ISR happened? |
@andyross I don't think the scheduler is working correctly (still). The k_sleep() patch merely cleaned up the logic in should_preempt. But, TBH the above circumstances should never happen. |
Scheduling decisions should be atomic. I spent way too much time staring at the synchronization over the last too days and found nothing. But it's always possible to miss something. If you're wanting to hunt yourself, MOST of the synchronization is in the LOCKED() blocks and can be easily verified to be a sane critical section. The scarier bits are in the timeout management code which use traditional irq_lock(). (Now I know you're shouting "Ah hah!" here, since it's timeout code we're specifically looking at. And I agree. Except I couldn't see anything wrong. Nonetheless a cleanup is going to move the timeout stuff under the scheduler spinlock where it belongs, so a fix it at least plausible) And surely a PendSV exception is not itself interruptible, no? I can't imagine how that would work if it was. |
AH nm, the issue with readyq.cache pointing to idle. We're already panicked and I'm looking at current state, not what it was prior to k_panic. I'll add a logging point to print readyq.cache before panic. |
@andyross as a follow-up. The above stack is completely legitimate as the k_sleep() thread was the _current thread when the IRQ hit. I think the underlying problem is that the LwM2M engine service thread has too high of a priority for what it's doing. And it can't be preempted by most other threads. |
@andyross I think we have a problem with k_sleep() still. It has to do with this change: It assumes that all calls to __swap() go through PendSV. We know that k_sleep() doesn't pend() the thread like other paths. This means we're in an "interesting" state when we hit line 104 "msr BASEPRI, r0" in swap_helper.S doesn't it? |
Actually the bug here (if there is one) is that _current isn't update till 2 lines AFTER the |
Hrm.. that's not true either.. we shouldn't be getting an interrupt at line 104 at all. We don't turn back on interrupts till line 163: |
@cvinayak for reference |
Drop priority as a workaround was commited for 1.12. The root cause (PendSV can be interrupted before entry, making _Swap() inherently non-atomic on ARM!) needs to be documented in a separate bug and a ARM-only workaround put in place for 1.13. |
@andyross I think this can be closed. Correct? |
yes, lets close it |
Yes, this is worked around. I really should write up a bug on the root cause issue (_Swap() on ARM is nonatomic because of the way pendsv works), and the code should cleanly reflect that and maybe implement it in an ARM-specific way instead of checking scheduler pending states needlessly. Someone please nag me to do that. |
Scheduler uses a function "should_preempt()" to determine if the _current thread should be _swap()'d away from. Part of that calculation uses a function _is_thread_prevented_from_running() which evaluates if the _current thread is "runnable".
Currently, the k_sleep() API doesn't make a thread look like it isn't runnable other than the following expression being true:
thread->base.timeout.delta_ticks_from_prev != _INACTIVE.
Originally, I felt that _is_thread_ready() would be a better replacement for _is_thread_prevented_from_running() as this takes into account active timeouts and would handle the k_sleep() case. But, a patch which changed this behavior is causing problems with the scheduler tests, so more research is needed.
The text was updated successfully, but these errors were encountered: