diff mbox series

genirq: Respect IRQCHIP_SKIP_SET_WAKE in irq_chip_set_wake_parent()

Message ID 20190315200759.139479-1-swboyd@chromium.org
State New
Headers show
Series genirq: Respect IRQCHIP_SKIP_SET_WAKE in irq_chip_set_wake_parent() | expand

Commit Message

Stephen Boyd March 15, 2019, 8:07 p.m. UTC
This function returns an error if a child interrupt controller calls
irq_chip_set_wake_parent() but that parent interrupt controller has the
IRQCHIP_SKIP_SET_WAKE flag. Let's return 0 for success instead because
there isn't anything to do.

There's also the possibility that a parent indicates that we should skip
it, but the grandparent has an .irq_set_wake callback. Let's iterate
through the parent chain as long as the IRQCHIP_SKIP_SET_WAKE flag isn't
set so we can find the first parent that needs to handle the wake
configuration. This fixes a problem on my Qualcomm sdm845 device where
I'm trying to enable wake on an irq from the gpio controller that's a
child of the qcom pdc interrupt controller. The qcom pdc interrupt
controller has the IRQCHIP_SKIP_SET_WAKE flag set, and so does the
grandparent (ARM GIC), causing this function to return a failure because
the parent controller doesn't have the .irq_set_wake callback set.

Cc: Lina Iyer <ilina@codeaurora.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---
 kernel/irq/chip.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Comments

Thomas Gleixner March 21, 2019, 9:26 a.m. UTC | #1
On Fri, 15 Mar 2019, Stephen Boyd wrote:

> This function returns an error if a child interrupt controller calls
> irq_chip_set_wake_parent() but that parent interrupt controller has the
> IRQCHIP_SKIP_SET_WAKE flag. Let's return 0 for success instead because
> there isn't anything to do.
> 
> There's also the possibility that a parent indicates that we should skip
> it, but the grandparent has an .irq_set_wake callback. Let's iterate
> through the parent chain as long as the IRQCHIP_SKIP_SET_WAKE flag isn't
> set so we can find the first parent that needs to handle the wake
> configuration. This fixes a problem on my Qualcomm sdm845 device where
> I'm trying to enable wake on an irq from the gpio controller that's a
> child of the qcom pdc interrupt controller. The qcom pdc interrupt
> controller has the IRQCHIP_SKIP_SET_WAKE flag set, and so does the
> grandparent (ARM GIC), causing this function to return a failure because
> the parent controller doesn't have the .irq_set_wake callback set.

It took me some time to distangle that changelog.... and I don't think that
this is the right thing to do.

set_irq_wake_real() returns 0 when the topmost chip has SKIP_SET_WAKE set.

So let's assume we have the following chains:

  chip A -> chip B 

  chip A -> chip B -> chip C

chip A has SKIP_SET_WAKE not set
chip B has SKIP_SET_WAKE set
chip C has SKIP_SET_WAKE not set and invokes irq_chip_set_wake_parent()

Now assume we have interrupt X connected to chip B and interrupt Y
connected to chip C.

If irq_set_wake() is called for interrupt X, then the function returns
without trying to invoke the set_wake() callback of chip A.

If irq_set_wake() is called for interrupt Y, irq_chip_set_wake_parent() is
invoked from chip C which then skips chip B, but tries to invoke the
callback on chip A.

That's inconsistent and changes the existing behaviour. So IMO, the right
thing to do is to return 0 from irq_chip_set_wake_parent() when the parent
has SKIP_SET_WAKE set and not to try to follow the whole chain. That should
fix your problem nicely w/o changing behaviour.

Thanks,

	tglx

----
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 3faef4a77f71..51128bea3846 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1449,6 +1449,10 @@ int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info)
 int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on)
 {
 	data = data->parent_data;
+
+	if (data->chip->flags & IRQCHIP_SKIP_SET_WAKE)
+		return 0;
+
 	if (data->chip->irq_set_wake)
 		return data->chip->irq_set_wake(data, on);
Stephen Boyd March 22, 2019, 11:50 p.m. UTC | #2
Quoting Thomas Gleixner (2019-03-21 02:26:26)
> On Fri, 15 Mar 2019, Stephen Boyd wrote:
> 
> > This function returns an error if a child interrupt controller calls
> > irq_chip_set_wake_parent() but that parent interrupt controller has the
> > IRQCHIP_SKIP_SET_WAKE flag. Let's return 0 for success instead because
> > there isn't anything to do.
> > 
> > There's also the possibility that a parent indicates that we should skip
> > it, but the grandparent has an .irq_set_wake callback. Let's iterate
> > through the parent chain as long as the IRQCHIP_SKIP_SET_WAKE flag isn't
> > set so we can find the first parent that needs to handle the wake
> > configuration. This fixes a problem on my Qualcomm sdm845 device where
> > I'm trying to enable wake on an irq from the gpio controller that's a
> > child of the qcom pdc interrupt controller. The qcom pdc interrupt
> > controller has the IRQCHIP_SKIP_SET_WAKE flag set, and so does the
> > grandparent (ARM GIC), causing this function to return a failure because
> > the parent controller doesn't have the .irq_set_wake callback set.
> 
> It took me some time to distangle that changelog.... and I don't think that
> this is the right thing to do.

Yes, your diagram would be a useful addition to the commit text.

> 
> set_irq_wake_real() returns 0 when the topmost chip has SKIP_SET_WAKE set.

Just to confirm, the topmost chip would be chip B or chip C below?

> 
> So let's assume we have the following chains:
> 
>   chip A -> chip B 
> 
>   chip A -> chip B -> chip C
> 
> chip A has SKIP_SET_WAKE not set
> chip B has SKIP_SET_WAKE set
> chip C has SKIP_SET_WAKE not set and invokes irq_chip_set_wake_parent()
> 
> Now assume we have interrupt X connected to chip B and interrupt Y
> connected to chip C.
> 
> If irq_set_wake() is called for interrupt X, then the function returns
> without trying to invoke the set_wake() callback of chip A.

It's not clear to me that having SKIP_SET_WAKE set means "completely
ignore set wake for irqs from this domain" vs. "skip setting wake here
because the .irq_set_wake() is intentionally omitted for this chip".
Reading Santosh's reasoning in commit 60f96b41f71d ("genirq: Add
IRQCHIP_SKIP_SET_WAKE flag") just further confuses me because it sounds
like the latter.

> 
> If irq_set_wake() is called for interrupt Y, irq_chip_set_wake_parent() is
> invoked from chip C which then skips chip B, but tries to invoke the
> callback on chip A.
> 
> That's inconsistent and changes the existing behaviour. So IMO, the right
> thing to do is to return 0 from irq_chip_set_wake_parent() when the parent
> has SKIP_SET_WAKE set and not to try to follow the whole chain. That should
> fix your problem nicely w/o changing behaviour.

Ok. I understand that with hierarchical chips you want it to be explicit
in the code that a parent chip needs to be called or not. This works for
me, and it's actually how I had originally solved this problem. Will you
merge your patch or do you want me to resend it with some updated commit
text?
Thomas Gleixner March 23, 2019, 9:42 a.m. UTC | #3
Stephen,

On Fri, 22 Mar 2019, Stephen Boyd wrote:
> Quoting Thomas Gleixner (2019-03-21 02:26:26)
> > 
> > set_irq_wake_real() returns 0 when the topmost chip has SKIP_SET_WAKE set.
> 
> Just to confirm, the topmost chip would be chip B or chip C below?

Yes. A is the parent of B, resp. the grandparent of C

> > 
> > So let's assume we have the following chains:
> > 
> >   chip A -> chip B 
> > 
> >   chip A -> chip B -> chip C
> > 
> > chip A has SKIP_SET_WAKE not set
> > chip B has SKIP_SET_WAKE set
> > chip C has SKIP_SET_WAKE not set and invokes irq_chip_set_wake_parent()
> > 
> > Now assume we have interrupt X connected to chip B and interrupt Y
> > connected to chip C.
> > 
> > If irq_set_wake() is called for interrupt X, then the function returns
> > without trying to invoke the set_wake() callback of chip A.
> 
> It's not clear to me that having SKIP_SET_WAKE set means "completely
> ignore set wake for irqs from this domain" vs. "skip setting wake here
> because the .irq_set_wake() is intentionally omitted for this chip".

That's a really good question, but I'd say that if one part of the
hierarchy does not require set wake, then this means no further action
required.

> Reading Santosh's reasoning in commit 60f96b41f71d ("genirq: Add
> IRQCHIP_SKIP_SET_WAKE flag") just further confuses me because it sounds
> like the latter.

That preceeds hierarchical irq domains. So back then a hierarchy was
expressed by weird callbacks, hardcoded dependencies, etc. That means if
the top level chip had SKIP_SET_WAKE set, the whole hierarchy was excluded.

> > If irq_set_wake() is called for interrupt Y, irq_chip_set_wake_parent() is
> > invoked from chip C which then skips chip B, but tries to invoke the
> > callback on chip A.
> > 
> > That's inconsistent and changes the existing behaviour. So IMO, the right
> > thing to do is to return 0 from irq_chip_set_wake_parent() when the parent
> > has SKIP_SET_WAKE set and not to try to follow the whole chain. That should
> > fix your problem nicely w/o changing behaviour.
> 
> Ok. I understand that with hierarchical chips you want it to be explicit
> in the code that a parent chip needs to be called or not. This works for

I think so.

> me, and it's actually how I had originally solved this problem. Will you
> merge your patch or do you want me to resend it with some updated commit
> text?

Please send a new version.

Thanks,

	tglx
diff mbox series

Patch

diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 3faef4a77f71..280d612ba71b 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -1448,7 +1448,13 @@  int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info)
  */
 int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on)
 {
-	data = data->parent_data;
+	for (data = data->parent_data; data; data = data->parent_data)
+		if (!(data->chip->flags & IRQCHIP_SKIP_SET_WAKE))
+			break;
+
+	if (!data)
+		return 0;
+
 	if (data->chip->irq_set_wake)
 		return data->chip->irq_set_wake(data, on);