diff mbox series

[1/1] PCI/PME+pciehp: Request IRQF_ONESHOT because bwctrl shares IRQ

Message ID 20241114142034.4388-1-ilpo.jarvinen@linux.intel.com (mailing list archive)
State New
Headers show
Series [1/1] PCI/PME+pciehp: Request IRQF_ONESHOT because bwctrl shares IRQ | expand

Commit Message

Ilpo Järvinen Nov. 14, 2024, 2:20 p.m. UTC
PCIe BW controller uses IRQF_ONESHOT to solve the problem fixed by the
commit 3e82a7f9031f ("PCI/LINK: Supply IRQ handler so level-triggered
IRQs are acked"). The IRQ is shared with PME and PCIe hotplug. Due to
probe order, PME and hotplug can request IRQ first without IRQF_ONESHOT
and when BW controller requests IRQ later with IRQF_ONESHOT, the IRQ
request fails. The problem is seen at least on Rasperry Pi 4:

pcieport 0000:00:00.0: PME: Signaling with IRQ 39
pcieport 0000:00:00.0: AER: enabled with IRQ 39
genirq: Flags mismatch irq 39. 00002084 (PCIe bwctrl) vs.00200084 (PCIe PME)
pcie_bwctrl 0000:00:00.0:pcie010: probe with driver pcie_bwctrl failed with error -16

BW controller is always enabled so change PME and pciehp too to use
IRQF_ONESHOT.

Fixes: 470b218c2bdf ("PCI/bwctrl: Re-add BW notification portdrv as PCIe BW controller")
Reported-by: Stefan Wahren <wahrenst@gmx.net>
Link: https://lore.kernel.org/linux-pci/dcd660fd-a265-4f47-8696-776a85e097a0@gmx.net/
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
 drivers/pci/hotplug/pciehp_hpc.c | 3 ++-
 drivers/pci/pcie/pme.c           | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

Comments

Ilpo Järvinen Nov. 15, 2024, 7:07 a.m. UTC | #1
On Thu, 14 Nov 2024, Ilpo Järvinen wrote:

> PCIe BW controller uses IRQF_ONESHOT to solve the problem fixed by the
> commit 3e82a7f9031f ("PCI/LINK: Supply IRQ handler so level-triggered
> IRQs are acked"). The IRQ is shared with PME and PCIe hotplug. Due to
> probe order, PME and hotplug can request IRQ first without IRQF_ONESHOT
> and when BW controller requests IRQ later with IRQF_ONESHOT, the IRQ
> request fails. The problem is seen at least on Rasperry Pi 4:
> 
> pcieport 0000:00:00.0: PME: Signaling with IRQ 39
> pcieport 0000:00:00.0: AER: enabled with IRQ 39
> genirq: Flags mismatch irq 39. 00002084 (PCIe bwctrl) vs.00200084 (PCIe PME)
> pcie_bwctrl 0000:00:00.0:pcie010: probe with driver pcie_bwctrl failed with error -16
> 
> BW controller is always enabled so change PME and pciehp too to use
> IRQF_ONESHOT.
> 
> Fixes: 470b218c2bdf ("PCI/bwctrl: Re-add BW notification portdrv as PCIe BW controller")

This should be:

Fixes: 058a4cb11620 ("PCI/bwctrl: Re-add BW notification portdrv as PCIe BW controller")

(I wasn't thinking and used the local commit id instead of the one from 
pci repo.)

> Reported-by: Stefan Wahren <wahrenst@gmx.net>
> Link: https://lore.kernel.org/linux-pci/dcd660fd-a265-4f47-8696-776a85e097a0@gmx.net/
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> ---
>  drivers/pci/hotplug/pciehp_hpc.c | 3 ++-
>  drivers/pci/pcie/pme.c           | 3 ++-
>  2 files changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
> index 736ad8baa2a5..0778305cff9d 100644
> --- a/drivers/pci/hotplug/pciehp_hpc.c
> +++ b/drivers/pci/hotplug/pciehp_hpc.c
> @@ -68,7 +68,8 @@ static inline int pciehp_request_irq(struct controller *ctrl)
>  
>  	/* Installs the interrupt handler */
>  	retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
> -				      IRQF_SHARED, "pciehp", ctrl);
> +				      IRQF_SHARED | IRQF_ONESHOT,
> +				      "pciehp", ctrl);
>  	if (retval)
>  		ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
>  			 irq);
> diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
> index a2daebd9806c..04f0e5a7b74c 100644
> --- a/drivers/pci/pcie/pme.c
> +++ b/drivers/pci/pcie/pme.c
> @@ -347,7 +347,8 @@ static int pcie_pme_probe(struct pcie_device *srv)
>  	pcie_pme_interrupt_enable(port, false);
>  	pcie_clear_root_pme_status(port);
>  
> -	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
> +	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED | IRQF_ONESHOT,
> +			  "PCIe PME", srv);
>  	if (ret) {
>  		kfree(data);
>  		return ret;
>
Stefan Wahren Nov. 15, 2024, 12:29 p.m. UTC | #2
Hi Ilpo,

Am 14.11.24 um 15:20 schrieb Ilpo Järvinen:
> PCIe BW controller uses IRQF_ONESHOT to solve the problem fixed by the
> commit 3e82a7f9031f ("PCI/LINK: Supply IRQ handler so level-triggered
> IRQs are acked"). The IRQ is shared with PME and PCIe hotplug. Due to
> probe order, PME and hotplug can request IRQ first without IRQF_ONESHOT
> and when BW controller requests IRQ later with IRQF_ONESHOT, the IRQ
> request fails. The problem is seen at least on Rasperry Pi 4:
>
> pcieport 0000:00:00.0: PME: Signaling with IRQ 39
> pcieport 0000:00:00.0: AER: enabled with IRQ 39
> genirq: Flags mismatch irq 39. 00002084 (PCIe bwctrl) vs.00200084 (PCIe PME)
> pcie_bwctrl 0000:00:00.0:pcie010: probe with driver pcie_bwctrl failed with error -16
>
> BW controller is always enabled so change PME and pciehp too to use
> IRQF_ONESHOT.
>
> Fixes: 470b218c2bdf ("PCI/bwctrl: Re-add BW notification portdrv as PCIe BW controller")
> Reported-by: Stefan Wahren <wahrenst@gmx.net>
> Link: https://lore.kernel.org/linux-pci/dcd660fd-a265-4f47-8696-776a85e097a0@gmx.net/
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> ---
>
I tested this patch on my Raspberry Pi 4B (linux-next, arm64/defconfig),
but unfortunately this doesn't fix the issue completely:

[    6.635741] pcieport 0000:00:00.0: PME: Signaling with IRQ 39
[    6.638845] genirq: Flags mismatch irq 39. 00000084 (aerdrv) vs.
00202084 (PCIe PME)
[    6.638954] pcieport 0000:00:00.0: AER: request AER IRQ 39 failed
[    6.638970] aer 0000:00:00.0:pcie002: probe with driver aer failed
with error -16

Regards
Lukas Wunner Nov. 15, 2024, 1 p.m. UTC | #3
On Thu, Nov 14, 2024 at 04:20:34PM +0200, Ilpo Järvinen wrote:
> --- a/drivers/pci/hotplug/pciehp_hpc.c
> +++ b/drivers/pci/hotplug/pciehp_hpc.c
> @@ -68,7 +68,8 @@ static inline int pciehp_request_irq(struct controller *ctrl)
>  
>  	/* Installs the interrupt handler */
>  	retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
> -				      IRQF_SHARED, "pciehp", ctrl);
> +				      IRQF_SHARED | IRQF_ONESHOT,
> +				      "pciehp", ctrl);
>  	if (retval)
>  		ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
>  			 irq);

I don't think this will work.  The IRQ thread pciehp_ist() may write
to the Slot Control register and await a Command Completed event,
e.g. when turning Slot Power on/off, changing LEDs, etc.

What happens then is, the hardware sets the Command Completed bit in
the Slot Status register and signals an interrupt.  The hardirq handler
pciehp_isr() reads the Slot Status register, acknowledges the
Command Completed event, sets "ctrl->cmd_busy = 0" and wakes up the
waiting IRQ thread.

In other words, pciehp does need the interrupt to stay enabled while
the IRQ thread is running so that the hardirq handler can receive
Command Completed interrupts.

Note that DPC also does not use IRQF_ONESHOT, so you'd have to change
that as well in this patch.  The Raspberry Pi happens to not support
DPC, so Stefan didn't see an error related to it.

I'm afraid you need to amend bwctrl to work without IRQF_ONESHOT rather
than changing all the others.

Thanks,

Lukas
Ilpo Järvinen Nov. 15, 2024, 4:27 p.m. UTC | #4
On Fri, 15 Nov 2024, Lukas Wunner wrote:

> On Thu, Nov 14, 2024 at 04:20:34PM +0200, Ilpo Järvinen wrote:
> > --- a/drivers/pci/hotplug/pciehp_hpc.c
> > +++ b/drivers/pci/hotplug/pciehp_hpc.c
> > @@ -68,7 +68,8 @@ static inline int pciehp_request_irq(struct controller *ctrl)
> >  
> >  	/* Installs the interrupt handler */
> >  	retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
> > -				      IRQF_SHARED, "pciehp", ctrl);
> > +				      IRQF_SHARED | IRQF_ONESHOT,
> > +				      "pciehp", ctrl);
> >  	if (retval)
> >  		ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
> >  			 irq);
> 
> I don't think this will work.  The IRQ thread pciehp_ist() may write
> to the Slot Control register and await a Command Completed event,
> e.g. when turning Slot Power on/off, changing LEDs, etc.
> 
> What happens then is, the hardware sets the Command Completed bit in
> the Slot Status register and signals an interrupt.  The hardirq handler
> pciehp_isr() reads the Slot Status register, acknowledges the
> Command Completed event, sets "ctrl->cmd_busy = 0" and wakes up the
> waiting IRQ thread.
> 
> In other words, pciehp does need the interrupt to stay enabled while
> the IRQ thread is running so that the hardirq handler can receive
> Command Completed interrupts.
> 
> Note that DPC also does not use IRQF_ONESHOT, so you'd have to change
> that as well in this patch.  The Raspberry Pi happens to not support
> DPC, so Stefan didn't see an error related to it.
> 
> I'm afraid you need to amend bwctrl to work without IRQF_ONESHOT rather
> than changing all the others.

That isn't complicated. The current irq thread handler is simple enough 
that it will just work as hardirq handler without any changes.
diff mbox series

Patch

diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 736ad8baa2a5..0778305cff9d 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -68,7 +68,8 @@  static inline int pciehp_request_irq(struct controller *ctrl)
 
 	/* Installs the interrupt handler */
 	retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
-				      IRQF_SHARED, "pciehp", ctrl);
+				      IRQF_SHARED | IRQF_ONESHOT,
+				      "pciehp", ctrl);
 	if (retval)
 		ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
 			 irq);
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index a2daebd9806c..04f0e5a7b74c 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -347,7 +347,8 @@  static int pcie_pme_probe(struct pcie_device *srv)
 	pcie_pme_interrupt_enable(port, false);
 	pcie_clear_root_pme_status(port);
 
-	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
+	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED | IRQF_ONESHOT,
+			  "PCIe PME", srv);
 	if (ret) {
 		kfree(data);
 		return ret;