-
Notifications
You must be signed in to change notification settings - Fork 2.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
fix INTRNG interrupt release #1281
base: main
Are you sure you want to change the base?
Conversation
@markjdb prefers I'm pretty sure most, if not all callers will want the answer to the first since |
Putting this a different way, given a random
(this would be a hypothetical PIC which implemented lazy release of interrupts) If the filter hadn't yet been removed from the event, the first call to |
This was simply rebased to aid keeping track of local branches, everything is identical to the original. Notably the CI pass is still valid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm inclined to just close this as not desired. There's so much here that needs to change, I'm not sure that there's anything left
if (error != 0) | ||
return (error); | ||
|
||
return (isrc_event_destroy(isrc)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted above, this routine is never called. Why do we need to call isrc_event_destroy() here? That's new and not quite what the commit message says. How did you test this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notice how the event is created by isrc_event_create()
? Given that, it is INTRNG's job to destroy the event, yet this call was missing. I simply needed to examine intr_isrc_deregister()
and notice the missing call. The driver is sitting in my tree due it needing intr_isrc_deregister()
to actually work.
isrc->isrc_irq = INTR_IRQ_INVALID; /* just to be safe */ | ||
if (irq_sources[isrc->isrc_irq] == isrc) | ||
irq_sources[isrc->isrc_irq] = NULL; | ||
isrc->isrc_irq = INTR_IRQ_INVALID; /* mark removed */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't be papering over programing errors. It's a programming error when isrc_irq is >= intr_nirq. That normally can't happen. Likewise with setting things to NULL in this way. This is a bad change. NAK.
Hard to say for sure, though, since there's not actually any callers of this in the tree. This is only called from intr_isrc_deregister
, which is global, but has no actual callers. So how did you test this?
But this is unsafe. If they don't match, it must be an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't be papering over programing errors. It's a programming error when isrc_irq is >= intr_nirq. That normally can't happen. Likewise with setting things to NULL in this way. This is a bad change. NAK.
A hypothetical scenario, but one which I believe should be handled as it could nominally be used. Imagine a highly hot-plug bus on a system with >1000 processors. On such a hot-plug bus it could nominally be valid to have one thread which calls intr_isrc_deregister()
at 1 second intervals and a separate thread doing intr_teardown_irq()
calls. In such a case the sequence:
intr_isrc_deregister();
intr_teardown_irq();
intr_isrc_deregister();
Issue is the first intr_isrc_deregister()
call would get EBUSY
from intr_event_destroy()
since the filter wasn't yet removed. Presently the second would get EINVAL
from isrc_free_irq()
since the isrc
had already been removed from the table by the first call. When it comes down to it, either isrc_free_irq()
or isrc_event_destroy()
must tolerate being called multiple times with the same isrc
.
Hard to say for sure, though, since there's not actually any callers of this in the tree. This is only called from
intr_isrc_deregister
, which is global, but has no actual callers. So how did you test this?
By reading the source and concluding this was a nominally valid course of action. I've got a driver which is expected to call intr_isrc_deregister()
during normal operation. As such I'm examining intr_isrc_deregister()
and discovering it is rather buggy. The x86 equivalent driver is unable to release interrupts and is thus forced to keep them on a list. Whereas INTRNG has the function partially implemented, but untested.
I have no expectation to need this exact functionality, but when trying to fix the problems with intr_isrc_deregister()
this issue shows up. Presently I'm unable to submit the driver since 3 key areas it needs working aren't in usable shape.
@@ -531,7 +531,7 @@ intr_event_destroy(struct intr_event *ie) | |||
{ | |||
|
|||
if (ie == NULL) | |||
return (EINVAL); | |||
return (0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is wrong. We want this to be an error. It's a programming error to call this with NULL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
% find sys -type f -print0 | xargs -0 grep -eintr_event_destroy -l
sys/netpfil/pf/if_pfsync.c
sys/netpfil/pf/pf_ioctl.c
sys/netpfil/pf/pflow.c
sys/kern/kern_intr.c
sys/kern/subr_intr.c
sys/sys/interrupt.h
%
The ones in sys/netpfil/pf/
are unlikely to trigger this. The only present way for them to trigger this is for something to scribble zeros over memory or some other sort of memory corruption. My concern is they're all calling intr_event_destroy()
during shutdown, then either doing MPASS(rc == 0);
or returning the error to the caller. If this isn't a debug kernel then the error could simply be lost (sys/netpfil/pf/if_pfsync.c:vnet_pfsync_uninit()
). If this is a debug kernel, is a panic()
truly a desirable result? I suspect these callers would instead prefer:
if (ie == NULL) {
printf("ERROR: %s(): called with NULL event\n", __func__);
return (0);
}
As the rest of their shutdowns can still occur.
The situation for INTRNG (sys/kern/subr_intr.c
) is very different. INTRNG does lazy event allocation. After intr_isrc_register()
returns, ->isrc_event
is still NULL
. ->isrc_event
doesn't become non-NULL
until isrc_add_handler()
is called. As such:
struct intr_irqsrc *isrc;
int rc;
intr_isrc_register(isrc);
rc = intr_isrc_deregister(isrc);
printf("intr_isrc_deregister() returned: %d\n", rc);
Will reliably output intr_isrc_deregister() returned: 22
. So there is a disagreement in the implementation about what intr_event_destroy(NULL)
should do/return. Since I'm looking at a driver which needs intr_isrc_deregister()
to work, this is blocking for me.
I suppose you could modify isrc_event_destroy()
to do if (isrc->isrc_event != NULL) return(0);
. My concern with this is you're testing the same value twice and giving exactly opposite results for a problematic value. I cannot abide this, it is just too ugly (I will hold my nose and ignore it if someone else implements, but I will not implement).
Another viable approach would be to remove lazy event allocation from INTRNG. I don't think lazy event allocation significantly helps INTRNG. I also observe major downsides to lazy event allocation (both here and other places you must first test ->isrc_event
, you cannot assume non-NULL
). I think it was a mistake to allow PowerPC and INTRNG to use lazy event allocation.
Issue is this would basically invalidate the premise behind INTR_SOLO
and someone would be rather annoyed with that (D35607
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, one other approach I would be willing to implement. It would be acceptable to me to revert 39888ed and then have isrc_event_destroy()
check for isrc->isrc_event == NULL
. It is the double checking for NULL
which I have a problem with.
I though would be quite happy if lazy event allocation was deprecated, and assured removing that would be accepted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In different terms, this situation is highly similar to c1287a3, Yes, it might be an error, but handing the error off to the caller drastically escalates the damage. Whereas handling it mitigates things.
I agree with mark. All the callers want no errors at all. They all assume that it's OK to tear down this intr_event. EBUSY is an error here (not a try again considtion). It's for when the handlers haven't been unregistered. This leads to a panic in most of the callers as a programming error. I disagree with this analysis. That makes the whole basis for this change in question. And the change itself isn't just this, but has other bits in it as well. I'm inclined to just close this and something we don't want, at least not in the current form. |
Wrong. The one and only basis for this pull is that |
An alternative patch set to fix the issue: altfix As GitHub functions on branch names, I could readily push that on top of "intrfix" for this pull. It is nominally feasible to omit the first patch in that series. Problem is that leaves |
50614ed
to
21f51c4
Compare
Okay, try this. Note the later commits are cleaning out some bits which I would tend to, but they are optional to the goal of this pull request. I suspect I'm trying to check whether |
It actually only takes the two at the front to achieve my primary objective. With those The as not yet submitted driver would simply use |
Okay, figured out what was going on with I would like to purge
To fully cleanup the structure with no leaks. Notably this requires |
I finally realized As a result removing lazy event allocation works fine. This then means there is no need to check for This version has been tested. The final commit isn't absolutely needed, but I do wish to express the point that with no activity on |
I'm planning to update to FreeBSD's HEAD soon. There will be a rebase in a few hours. |
In fact, the two at the front really do fix the problem sufficiently. If the event is always allocated, then I still feel |
Given how things look now, I plan to rebase in a bit. Some of what is in here now looks worthy of being split into a separate branch. |
7e6a34c
to
fd060ad
Compare
02ba14a
to
7ca6d44
Compare
The issue 5aa7e6e is aimed at is In theory simply including the string @strejda can summon people. The main goal here is |
7f63cf4
to
02ea9ac
Compare
18963a7
to
dec13a4
Compare
e2252cc
to
9f2fcd9
Compare
dddecf4
to
0bc2651
Compare
089aca8
to
ab41b68
Compare
There is minimal benefit in delaying event allocation. Worse, this reduces opportunities for merging architectural interrupt structures together. Since the event is now created before being added to the interrupt table, there is no longer any need for locking. A few spots also no longer need to check for ->isrc_event being NULL, clean those up. Differential Revision: https://reviews.freebsd.org/D40166
SSIA. If the event is still valid when deregister is called, it needs to be destroyed. Differential Revision: https://reviews.freebsd.org/D38599
Having the table entry already wiped is acceptable. Otherwise an error may cause the function to be called again and there will be an infinite loop. Differential Revision: https://reviews.freebsd.org/D38599
A NULL event likely indicates the event was either already released or never allocated. In such case the event has already been cleaned up and that is what the caller needs to know. This gets called from cleanup paths and cleanup should not be abandoned if the event is invalid. Emit a warning message as this indicates inconsistent state. There may well simply be the precursor to a page-fault panic(). Differential Revision: https://reviews.freebsd.org/D38602
Report errors returned by intr_event_destroy(). While these should be highly unlikely, they are worth of being reported nonetheless.
SSIA.
While it is good
intr_isrc_deregister()
exists, it hasn't ever actually been used. Due to a driver which needs this, fix issues found by reviewing the source and finding solutions.