libsel4vm: Enqueue IRQs only once - #28
Conversation
|
It appears that vgic_vcpu_inject_irq() always succeeded, either by succeeding to load the IRQ into hardware-backed LR registers (4) or if that fails, into the software-extended 64 LR registers. Because it always succeeded, vgic_handle_overflow_cpu() (part of vGIC maintenance code) would always consume the queued IRQ. But in case vgic_vcpu_inject_irq() failed to load IRQ into HW list register, the consumed IRQ was appended back to software LR registers. So that's how the code used to work, for reference. The real problem was that there was not any check whether or not the IRQ was already loaded into these list registers. I have had a fix for this already, but it was rather clumsy. Now I realized a simple refactoring is all that is needed. A little background: we have been playing with Raspberry Pi 4 for some time now and found out virtio-console makes this code bail out, saying the software-extended LRs are overflowing. The more cores you enable in the guest VM, the sooner that happens (with single core it happens never in practice, with four core it is a matter of seconds). This fix is a big improvement to that. |
acac68b to
276c55d
Compare
Not sure I follow, but the list could be full ( |
|
From my side this seems the proper fix and I'd consider this as a bug. |
276c55d to
985f110
Compare
It is entirely possible in theory, but I would refrain addressing that issue now. If we continue using the ring buffer for overflowed IRQs, it should be just a matter of enlarging the buffer. But perhaps some day we would like to emulate more recent GICs also and in the future there are more and more ARM chips that support up to 8192 interrupt IDs. Another thing is whether or not we want to support IRQ priorities some day. For that the ring buffer is not the optimal data structure IMO and for example, priority queue based on binary heap could be more proper solution. That's why I would like to leave it as it is now and guest Linux does not need priorities anyway. What I meant to say in the text you quoted was that the ill-named vgic_vcpu_inject_irq() does not fail normally, it either succeeds loading the IRQ into HW registers (or to the abstraction for them within seL4 kernel but that's another story) or if that fails, it succeeds appending the IRQ to overflow list. And this "always succeeds" resulted in unnecessary shuffling of IRQs out of that list, just to be put into that again. The first iteration that I subjected to review had to solve that problem in order to figure out whether or not the IRQ was queued. But now I realized we could use is_pending() instead and the whole bugfix would reduce to a one-liner. But since it appears we agree that the code is rather complex, I took some liberties and renamed variables, fields and functions and even added one commit which I believe adds even more readability. I'm happy to add more comments but I believe the code itself must be improved first. |
985f110 to
20d4c25
Compare
|
Any opinions whether I should split the first commit into two, one having only this "if (is_pending(...))" (the real fix) and another having the renames? The third that gets rid of per-CPU helper functions is perhaps best to left unmodified? Or one for is_pending() fix and the another for readability improvement? Personally I think the former (three commits) is the best. |
|
The number of list registers ( |
20d4c25 to
bef99fc
Compare
|
Redid the patch, personally I think it's one more step towards readability, let me know what you think. |
fdc6591 to
2d7a65e
Compare
It would make it easier to review and integrate these changes if they were in separate commits. (There's something like 4 or 5 different branches that implement gicv3 virt that may need to cherry-pick or rebase over these changes). |
That's my thinking as well. Right now I squashed all my previous changes into one, because the intermediate commits made some changes just to be changed again in later commits, so I hope this squashed commit is easier to review. After we agree on the contents, I can proceed with splitting the PR into several commits. |
2d7a65e to
6b466d2
Compare
|
Thanks for the work, the code is really getting nicer now. |
|
Thank you for your suggestions, I applied them to whole file and added two commits -- one that addresses the locations you mentioned and the other one addresses the rest. Kept them separate now for clarity. |
560c73c to
17b5a41
Compare
|
Just pondering; shouldn't we reserve the |
| vgic_handle_overflow(vgic, vcpu); | ||
| struct virq_handle *virq = vgic_irq_dequeue(vgic, vcpu); | ||
| if (virq) { | ||
| return vgic_vcpu_load_list_reg(vgic, vcpu, idx, virq); |
There was a problem hiding this comment.
I wonder, if we should print an error message here, because this would indicate a serious problem in the maintenance operation
| return vgic_vcpu_load_list_reg(vgic, vcpu, idx, virq); | |
| int err = vgic_vcpu_load_list_reg(vgic, vcpu, idx, virq); | |
| if (err) { | |
| ZF_LOGE("IRQ injection failed, code %d\n", err); | |
| } | |
| return err |
There was a problem hiding this comment.
But vgic_vcpu_load_list_reg() will print an error message regardless who calls it?
There was a problem hiding this comment.
yes, you are right. It would make a nice error stack though seeing this bubble up though the layers then.
There was a problem hiding this comment.
Now that is something I can agree.
| assert(idx >= 0); | ||
|
|
||
| err = handle_vgic_maintenance(vcpu, idx); | ||
| int err = handle_vgic_maintenance(vcpu, idx); |
There was a problem hiding this comment.
Maybe we should also log a message here if this fails, as things are badly broken then. See also comment https://github.com/seL4/seL4_projects_libs/pull/28/files#r824617661
There was a problem hiding this comment.
I think we should have a way to halt the VM execution at this point -- I think it should execute correctly or not execute at all, something in between only results in frustation and wasted time debugging something flaky.
There was a problem hiding this comment.
It's a lost interrupt in this case, which is not necessarily this fatal, but very annoying. Logging is the best one can do to allow post-mortem analysis. The error behavior should become a follow-up discussion to leave out od this PR, otherwise this never gets merged ;)
There was a problem hiding this comment.
True -- actually that reminds me I have pondered whether these readability improvements should be a separate PR (or moving the real bugfix into its own PR).
There was a problem hiding this comment.
From my side, having this in a separate commit is sufficient. It's more about how to get the others to agree to merge all the improvements, there are a few other PRs now also that seem useful (#29, #31 ...)
Opinions, @yyshen, @kent-mcleod, @lsf37
| vgic->irq[inject_vcpu->vcpu_id][i] = irq; | ||
| int err = seL4_ARM_VCPU_InjectIRQ(vcpu->vcpu.cptr, irq->virq, 0, 0, idx); | ||
| if (err) { | ||
| ZF_LOGF("Failure loading vGIC list register"); |
There was a problem hiding this comment.
| ZF_LOGF("Failure loading vGIC list register"); | |
| ZF_LOGF("Failure loading vGIC list register, code %d", err); |
|
Pushed one more commit that reverts |
|
@hlyytine is it your intention to split this into separate PRs or are you happy for this one to be updated and merged? |
wom-bat
left a comment
There was a problem hiding this comment.
This looks good; but needs rebasing to make it mergeable.
59936b4 to
e6eccc7
Compare
The real GIC has exactly zero or one pending request for any IRQ. Signed-off-by: Hannu Lyytinen <hannux@ssrc.tii.ae>
e6eccc7 to
54fefe0
Compare
| bool full; | ||
| }; | ||
|
|
||
| static inline size_t LR_OF_NEXT(size_t idx) |
There was a problem hiding this comment.
Please leave this in unless there is a strong reason to go back to the macro.
There was a problem hiding this comment.
I can do that, but I would like you to first review my patch set fully; I'm getting rid of that full field of struct lr_of and ring buffer code is much more comprehensible now IMHO. I was thinking my static_assert about the requirement of size of power of two and the and-operation for finding out the next index would be self-documenting. TBH I found your addition not exactly helping understanding the code and feel that the data structure is implemented in non-optimal way if it needs helpers/comments like that. But not really wanting to start a flame war, I just would like you to read all my code first.
There was a problem hiding this comment.
Sorry, my bad. Looked like a merge conflict to me first, but with the 2^n size this is better.
My argument for the function is, that this allows a strong binding to the actual array size in the struct and not just a constant. It's supposed to make the code more robust and the compiler is able to optimize this like the macro.
There was a problem hiding this comment.
But that stronger array binding could also be done in the macro. I may make a separate PR for this, so it's no blocker for this PR.
There was a problem hiding this comment.
Yeah, my intention was to ping you and ask how do you like my proposal, but you were faster. And I can see where you are coming from. We could have sanity checks with this 2^n also, but I guess then it boils down to more fundamental questions like a) what else should we assert and b) shouldn't we be doing the assert before using the index (right before dereferencing an array), not just when derive a new index.
Ideally the data structures should be in a separate header/source file, having them in between the code that uses them makes it quite hard to grasp sometimes. But something like that is a PITA to do in C.
There was a problem hiding this comment.
Forgot to mention that I am not that worried about performance in this context. It is just readability that matters to me.
There was a problem hiding this comment.
My argument for the function is, that this allows a strong binding to the actual array size in the struct and not just a constant. It's supposed to make the code more robust and the compiler is able to optimize this like the macro.
I think this issue could be resolved outside of this PR. The macro and array definitions are defined within 10 lines of each other and use the same size constant which is already good enough.
kent-mcleod
left a comment
There was a problem hiding this comment.
I also think this is a nice improvement. My only request is that the middle commit message has a description of the irq_queue related changes also mentions that this sets up for supporting interrupt priorities in the future as well as allowing the maintenance handling code to become simpler.
This is mostly rearranging the code so that the IRQ queueing mechanism becomes more apparent, making also the maintenance handler much simpler. The ring buffer is implemented in a bit more readable way and it is decoupled from the rest; this makes it easier to support IRQ priorities in the future. Signed-off-by: Hannu Lyytinen <hannux@ssrc.tii.ae>
Signed-off-by: Hannu Lyytinen <hannux@ssrc.tii.ae>
54fefe0 to
1e25455
Compare
|
True, added that now. |
The requested changes have been made now
The real GIC has exactly zero or one pending request for any IRQ.
Signed-off-by: Hannu Lyytinen hannux@ssrc.tii.ae