mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-05-04 17:36:12 +02:00
generic: backport MSI affinity support for DW PCIe
Currently, the DesignWare PCIe driver cannot configure interrupts on SoC that do not support MSIX. All MSI interrupts are handled by CPU0. Backport MSI affinity support for the PCI dwc driver from linux-next, so now we can adjust MSI interrupts to other CPU cores. Tested on HINLINK H28K (RK3528) and OrangePi R2S (Ky X1). Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn> Link: https://github.com/openwrt/openwrt/pull/21770 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
parent
5968b584ba
commit
ba7aa2a971
@ -0,0 +1,57 @@
|
||||
From ec57335b81d8fcf3088994c532b5ca21e839616a Mon Sep 17 00:00:00 2001
|
||||
From: Bjorn Helgaas <bhelgaas@google.com>
|
||||
Date: Fri, 17 Jan 2025 15:03:04 -0600
|
||||
Subject: [PATCH] PCI: dwc: Add dw_pcie_suspend_noirq(),
|
||||
dw_pcie_resume_noirq() stubs for !CONFIG_PCIE_DW_HOST
|
||||
|
||||
Previously pcie-designware.h declared dw_pcie_suspend_noirq() and
|
||||
dw_pcie_resume_noirq() unconditionally, even though they were only
|
||||
implemented when CONFIG_PCIE_DW_HOST was defined.
|
||||
|
||||
Add no-op stubs for them when CONFIG_PCIE_DW_HOST is not defined so
|
||||
drivers that support both Root Complex and Endpoint modes don't need
|
||||
|
||||
Link: https://lore.kernel.org/r/20250117213810.GA656803@bhelgaas
|
||||
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
|
||||
---
|
||||
drivers/pci/controller/dwc/pcie-designware.h | 15 ++++++++++++---
|
||||
1 file changed, 12 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware.h
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware.h
|
||||
@@ -498,9 +498,6 @@ void dw_pcie_iatu_detect(struct dw_pcie
|
||||
int dw_pcie_edma_detect(struct dw_pcie *pci);
|
||||
void dw_pcie_edma_remove(struct dw_pcie *pci);
|
||||
|
||||
-int dw_pcie_suspend_noirq(struct dw_pcie *pci);
|
||||
-int dw_pcie_resume_noirq(struct dw_pcie *pci);
|
||||
-
|
||||
static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
|
||||
{
|
||||
dw_pcie_write_dbi(pci, reg, 0x4, val);
|
||||
@@ -678,6 +675,8 @@ static inline enum dw_pcie_ltssm dw_pcie
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCIE_DW_HOST
|
||||
+int dw_pcie_suspend_noirq(struct dw_pcie *pci);
|
||||
+int dw_pcie_resume_noirq(struct dw_pcie *pci);
|
||||
irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_host_init(struct dw_pcie_rp *pp);
|
||||
@@ -686,6 +685,16 @@ int dw_pcie_allocate_domains(struct dw_p
|
||||
void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where);
|
||||
#else
|
||||
+static inline int dw_pcie_suspend_noirq(struct dw_pcie *pci)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int dw_pcie_resume_noirq(struct dw_pcie *pci)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static inline irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
{
|
||||
return IRQ_NONE;
|
||||
@ -0,0 +1,70 @@
|
||||
From e4d001b54f78769ba1a1404c2801ae95e19fd893 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Zyngier <maz@kernel.org>
|
||||
Date: Tue, 13 May 2025 18:28:12 +0100
|
||||
Subject: [PATCH] genirq/msi: Add helper for creating MSI-parent irq
|
||||
domains
|
||||
|
||||
Creating an irq domain that serves as an MSI parent requires
|
||||
a substantial amount of esoteric boiler-plate code, some of
|
||||
which is often provided twice (such as the bus token).
|
||||
|
||||
To make things a bit simpler for the unsuspecting MSI tinkerer,
|
||||
provide a helper that does it for them, and serves as documentation
|
||||
of what needs to be provided.
|
||||
|
||||
Signed-off-by: Marc Zyngier <maz@kernel.org>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/all/20250513172819.2216709-3-maz@kernel.org
|
||||
---
|
||||
include/linux/msi.h | 4 ++++
|
||||
kernel/irq/msi.c | 26 ++++++++++++++++++++++++++
|
||||
2 files changed, 30 insertions(+)
|
||||
|
||||
--- a/include/linux/msi.h
|
||||
+++ b/include/linux/msi.h
|
||||
@@ -586,6 +586,10 @@ struct irq_domain *msi_create_irq_domain
|
||||
struct msi_domain_info *info,
|
||||
struct irq_domain *parent);
|
||||
|
||||
+struct irq_domain_info;
|
||||
+struct irq_domain *msi_create_parent_irq_domain(struct irq_domain_info *info,
|
||||
+ const struct msi_parent_ops *msi_parent_ops);
|
||||
+
|
||||
bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
|
||||
const struct msi_domain_template *template,
|
||||
unsigned int hwsize, void *domain_data,
|
||||
--- a/kernel/irq/msi.c
|
||||
+++ b/kernel/irq/msi.c
|
||||
@@ -886,6 +886,32 @@ struct irq_domain *msi_create_irq_domain
|
||||
}
|
||||
|
||||
/**
|
||||
+ * msi_create_parent_irq_domain - Create an MSI-parent interrupt domain
|
||||
+ * @info: MSI irqdomain creation info
|
||||
+ * @msi_parent_ops: MSI parent callbacks and configuration
|
||||
+ *
|
||||
+ * Return: pointer to the created &struct irq_domain or %NULL on failure
|
||||
+ */
|
||||
+struct irq_domain *msi_create_parent_irq_domain(struct irq_domain_info *info,
|
||||
+ const struct msi_parent_ops *msi_parent_ops)
|
||||
+{
|
||||
+ struct irq_domain *d;
|
||||
+
|
||||
+ info->hwirq_max = max(info->hwirq_max, info->size);
|
||||
+ info->size = info->hwirq_max;
|
||||
+ info->domain_flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
|
||||
+ info->bus_token = msi_parent_ops->bus_select_token;
|
||||
+
|
||||
+ d = irq_domain_instantiate(info);
|
||||
+ if (IS_ERR(d))
|
||||
+ return NULL;
|
||||
+
|
||||
+ d->msi_parent_ops = msi_parent_ops;
|
||||
+ return d;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(msi_create_parent_irq_domain);
|
||||
+
|
||||
+/**
|
||||
* msi_parent_init_dev_msi_info - Delegate initialization of device MSI info down
|
||||
* in the domain hierarchy
|
||||
* @dev: The device for which the domain should be created
|
||||
@ -0,0 +1,229 @@
|
||||
From e51b27438a10391fdc94dd2046d9ffa9c2679c74 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Zyngier <maz@kernel.org>
|
||||
Date: Tue, 13 May 2025 18:28:11 +0100
|
||||
Subject: [PATCH] irqchip: Make irq-msi-lib.h globally available
|
||||
|
||||
Move irq-msi-lib.h into include/linux/irqchip, making it available
|
||||
to compilation units outside of drivers/irqchip.
|
||||
|
||||
This requires some churn in drivers to fetch it from the new location,
|
||||
generated using this script:
|
||||
|
||||
git grep -l -w \"irq-msi-lib.h\" | \
|
||||
xargs sed -i -e 's:"irq-msi-lib.h":\<linux/irqchip/irq-msi-lib.h\>:'
|
||||
|
||||
Signed-off-by: Marc Zyngier <maz@kernel.org>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/all/20250513172819.2216709-2-maz@kernel.org
|
||||
---
|
||||
drivers/irqchip/irq-bcm2712-mip.c | 2 +-
|
||||
drivers/irqchip/irq-gic-v2m.c | 2 +-
|
||||
drivers/irqchip/irq-gic-v3-its-msi-parent.c | 2 +-
|
||||
drivers/irqchip/irq-gic-v3-its.c | 2 +-
|
||||
drivers/irqchip/irq-gic-v3-mbi.c | 2 +-
|
||||
drivers/irqchip/irq-imx-mu-msi.c | 2 +-
|
||||
drivers/irqchip/irq-loongarch-avec.c | 2 +-
|
||||
drivers/irqchip/irq-loongson-pch-msi.c | 2 +-
|
||||
drivers/irqchip/irq-msi-lib.c | 2 +-
|
||||
drivers/irqchip/irq-mvebu-gicp.c | 2 +-
|
||||
drivers/irqchip/irq-mvebu-icu.c | 2 +-
|
||||
drivers/irqchip/irq-mvebu-odmi.c | 2 +-
|
||||
drivers/irqchip/irq-mvebu-sei.c | 2 +-
|
||||
drivers/irqchip/irq-riscv-imsic-platform.c | 2 +-
|
||||
drivers/irqchip/irq-sg2042-msi.c | 2 +-
|
||||
{drivers => include/linux}/irqchip/irq-msi-lib.h | 6 +++---
|
||||
16 files changed, 18 insertions(+), 18 deletions(-)
|
||||
rename {drivers => include/linux}/irqchip/irq-msi-lib.h (84%)
|
||||
|
||||
--- a/drivers/irqchip/irq-gic-v2m.c
|
||||
+++ b/drivers/irqchip/irq-gic-v2m.c
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/irqchip/arm-gic-common.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
/*
|
||||
* MSI_TYPER:
|
||||
--- a/drivers/irqchip/irq-gic-v3-its-msi-parent.c
|
||||
+++ b/drivers/irqchip/irq-gic-v3-its-msi-parent.c
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "irq-gic-common.h"
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
#define ITS_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
|
||||
MSI_FLAG_USE_DEF_CHIP_OPS | \
|
||||
--- a/drivers/irqchip/irq-gic-v3-its.c
|
||||
+++ b/drivers/irqchip/irq-gic-v3-its.c
|
||||
@@ -38,7 +38,7 @@
|
||||
#include <asm/exception.h>
|
||||
|
||||
#include "irq-gic-common.h"
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
|
||||
#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
|
||||
--- a/drivers/irqchip/irq-gic-v3-mbi.c
|
||||
+++ b/drivers/irqchip/irq-gic-v3-mbi.c
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
struct mbi_range {
|
||||
u32 spi_start;
|
||||
--- a/drivers/irqchip/irq-imx-mu-msi.c
|
||||
+++ b/drivers/irqchip/irq-imx-mu-msi.c
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
#define IMX_MU_CHANS 4
|
||||
|
||||
--- a/drivers/irqchip/irq-loongarch-avec.c
|
||||
+++ b/drivers/irqchip/irq-loongarch-avec.c
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <asm/loongarch.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
#include "irq-loongson.h"
|
||||
|
||||
#define VECTORS_PER_REG 64
|
||||
--- a/drivers/irqchip/irq-loongson-pch-msi.c
|
||||
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
#include "irq-loongson.h"
|
||||
|
||||
static int nr_pics;
|
||||
--- a/drivers/irqchip/irq-msi-lib.c
|
||||
+++ b/drivers/irqchip/irq-msi-lib.c
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <linux/export.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
/**
|
||||
* msi_lib_init_dev_msi_info - Domain info setup for MSI domains
|
||||
--- a/drivers/irqchip/irq-mvebu-gicp.c
|
||||
+++ b/drivers/irqchip/irq-mvebu-gicp.c
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
--- a/drivers/irqchip/irq-mvebu-icu.c
|
||||
+++ b/drivers/irqchip/irq-mvebu-icu.c
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/mvebu-icu.h>
|
||||
|
||||
--- a/drivers/irqchip/irq-mvebu-odmi.c
|
||||
+++ b/drivers/irqchip/irq-mvebu-odmi.c
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
--- a/drivers/irqchip/irq-mvebu-sei.c
|
||||
+++ b/drivers/irqchip/irq-mvebu-sei.c
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
-#include "irq-msi-lib.h"
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
|
||||
/* Cause register */
|
||||
#define GICP_SECR(idx) (0x0 + ((idx) * 0x4))
|
||||
--- a/drivers/irqchip/irq-msi-lib.h
|
||||
+++ /dev/null
|
||||
@@ -1,27 +0,0 @@
|
||||
-// SPDX-License-Identifier: GPL-2.0-only
|
||||
-// Copyright (C) 2022 Linutronix GmbH
|
||||
-// Copyright (C) 2022 Intel
|
||||
-
|
||||
-#ifndef _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H
|
||||
-#define _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H
|
||||
-
|
||||
-#include <linux/bits.h>
|
||||
-#include <linux/irqdomain.h>
|
||||
-#include <linux/msi.h>
|
||||
-
|
||||
-#ifdef CONFIG_PCI_MSI
|
||||
-#define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI)
|
||||
-#else
|
||||
-#define MATCH_PCI_MSI (0)
|
||||
-#endif
|
||||
-
|
||||
-#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI)
|
||||
-
|
||||
-int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
- enum irq_domain_bus_token bus_token);
|
||||
-
|
||||
-bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
|
||||
- struct irq_domain *real_parent,
|
||||
- struct msi_domain_info *info);
|
||||
-
|
||||
-#endif /* _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H */
|
||||
--- /dev/null
|
||||
+++ b/include/linux/irqchip/irq-msi-lib.h
|
||||
@@ -0,0 +1,27 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+// Copyright (C) 2022 Linutronix GmbH
|
||||
+// Copyright (C) 2022 Intel
|
||||
+
|
||||
+#ifndef _IRQCHIP_IRQ_MSI_LIB_H
|
||||
+#define _IRQCHIP_IRQ_MSI_LIB_H
|
||||
+
|
||||
+#include <linux/bits.h>
|
||||
+#include <linux/irqdomain.h>
|
||||
+#include <linux/msi.h>
|
||||
+
|
||||
+#ifdef CONFIG_PCI_MSI
|
||||
+#define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI)
|
||||
+#else
|
||||
+#define MATCH_PCI_MSI (0)
|
||||
+#endif
|
||||
+
|
||||
+#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI)
|
||||
+
|
||||
+int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
+ enum irq_domain_bus_token bus_token);
|
||||
+
|
||||
+bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
|
||||
+ struct irq_domain *real_parent,
|
||||
+ struct msi_domain_info *info);
|
||||
+
|
||||
+#endif /* _IRQCHIP_IRQ_MSI_LIB_H */
|
||||
@ -0,0 +1,34 @@
|
||||
From a8b289f0f2dcbadd8c207ad8f33cf7ba2b4eb088 Mon Sep 17 00:00:00 2001
|
||||
From: Arnd Bergmann <arnd@arndb.de>
|
||||
Date: Thu, 10 Jul 2025 10:00:12 +0200
|
||||
Subject: [PATCH] irqchip/irq-msi-lib: Fix build with PCI disabled
|
||||
|
||||
The armada-370-xp irqchip fails in some randconfig builds because
|
||||
of a missing declaration:
|
||||
|
||||
In file included from drivers/irqchip/irq-armada-370-xp.c:23:
|
||||
include/linux/irqchip/irq-msi-lib.h:25:39: error: 'struct msi_domain_info' declared inside parameter list will not be visible outside of this definition or declaration [-Werror]
|
||||
|
||||
Add a forward declaration for the msi_domain_info structure.
|
||||
|
||||
[ tglx: Fixed up the subsystem prefix. Is it really that hard to get right? ]
|
||||
|
||||
Fixes: e51b27438a10 ("irqchip: Make irq-msi-lib.h globally available")
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Acked-by: Marc Zyngier <maz@kernel.org>
|
||||
Link: https://lore.kernel.org/all/20250710080021.2303640-1-arnd@kernel.org
|
||||
---
|
||||
include/linux/irqchip/irq-msi-lib.h | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/include/linux/irqchip/irq-msi-lib.h
|
||||
+++ b/include/linux/irqchip/irq-msi-lib.h
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI)
|
||||
|
||||
+struct msi_domain_info;
|
||||
int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
enum irq_domain_bus_token bus_token);
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
From a60d92f6d941bd77bf3aaec724a7c95857c0165b Mon Sep 17 00:00:00 2001
|
||||
From: Mayank Rana <mayank.rana@oss.qualcomm.com>
|
||||
Date: Mon, 16 Jun 2025 15:42:56 -0700
|
||||
Subject: [PATCH] PCI: dwc: Export DWC MSI controller related APIs
|
||||
|
||||
Export dw_pcie_msi_host_init(), dw_pcie_msi_init(), and dw_pcie_free_msi()
|
||||
APIs to allow them to be reused by the upcoming DWC based ECAM driver
|
||||
implementation. Also, move MSI IRQ related initialization code to
|
||||
dw_pcie_msi_init(), as this code must be executed before dw_pcie_msi_init()
|
||||
API can be used with ECAM driver.
|
||||
|
||||
Signed-off-by: Mayank Rana <mayank.rana@oss.qualcomm.com>
|
||||
[mani: commit message rewording]
|
||||
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
|
||||
Link: https://patch.msgid.link/20250616224259.3549811-2-mayank.rana@oss.qualcomm.com
|
||||
---
|
||||
.../pci/controller/dwc/pcie-designware-host.c | 38 ++++++++++---------
|
||||
drivers/pci/controller/dwc/pcie-designware.h | 14 +++++++
|
||||
2 files changed, 34 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
@@ -250,7 +250,7 @@ int dw_pcie_allocate_domains(struct dw_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void dw_pcie_free_msi(struct dw_pcie_rp *pp)
|
||||
+void dw_pcie_free_msi(struct dw_pcie_rp *pp)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
@@ -263,19 +263,34 @@ static void dw_pcie_free_msi(struct dw_p
|
||||
irq_domain_remove(pp->msi_domain);
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
|
||||
|
||||
-static void dw_pcie_msi_init(struct dw_pcie_rp *pp)
|
||||
+void dw_pcie_msi_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
u64 msi_target = (u64)pp->msi_data;
|
||||
+ u32 ctrl, num_ctrls;
|
||||
|
||||
if (!pci_msi_enabled() || !pp->has_msi_ctrl)
|
||||
return;
|
||||
|
||||
+ num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
|
||||
+
|
||||
+ /* Initialize IRQ Status array */
|
||||
+ for (ctrl = 0; ctrl < num_ctrls; ctrl++) {
|
||||
+ dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK +
|
||||
+ (ctrl * MSI_REG_CTRL_BLOCK_SIZE),
|
||||
+ pp->irq_mask[ctrl]);
|
||||
+ dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE +
|
||||
+ (ctrl * MSI_REG_CTRL_BLOCK_SIZE),
|
||||
+ ~0);
|
||||
+ }
|
||||
+
|
||||
/* Program the msi_data */
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target));
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target));
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(dw_pcie_msi_init);
|
||||
|
||||
static int dw_pcie_parse_split_msi_irq(struct dw_pcie_rp *pp)
|
||||
{
|
||||
@@ -317,7 +332,7 @@ static int dw_pcie_parse_split_msi_irq(s
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
|
||||
+int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct device *dev = pci->dev;
|
||||
@@ -391,6 +406,7 @@ static int dw_pcie_msi_host_init(struct
|
||||
|
||||
return 0;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(dw_pcie_msi_host_init);
|
||||
|
||||
static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)
|
||||
{
|
||||
@@ -809,7 +825,7 @@ static int dw_pcie_iatu_setup(struct dw_
|
||||
int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
- u32 val, ctrl, num_ctrls;
|
||||
+ u32 val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@@ -820,20 +836,6 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *
|
||||
|
||||
dw_pcie_setup(pci);
|
||||
|
||||
- if (pp->has_msi_ctrl) {
|
||||
- num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
|
||||
-
|
||||
- /* Initialize IRQ Status array */
|
||||
- for (ctrl = 0; ctrl < num_ctrls; ctrl++) {
|
||||
- dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK +
|
||||
- (ctrl * MSI_REG_CTRL_BLOCK_SIZE),
|
||||
- pp->irq_mask[ctrl]);
|
||||
- dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE +
|
||||
- (ctrl * MSI_REG_CTRL_BLOCK_SIZE),
|
||||
- ~0);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
/* Setup RC BARs */
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware.h
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware.h
|
||||
@@ -678,6 +678,9 @@ static inline enum dw_pcie_ltssm dw_pcie
|
||||
int dw_pcie_suspend_noirq(struct dw_pcie *pci);
|
||||
int dw_pcie_resume_noirq(struct dw_pcie *pci);
|
||||
irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp);
|
||||
+void dw_pcie_msi_init(struct dw_pcie_rp *pp);
|
||||
+int dw_pcie_msi_host_init(struct dw_pcie_rp *pp);
|
||||
+void dw_pcie_free_msi(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_host_init(struct dw_pcie_rp *pp);
|
||||
void dw_pcie_host_deinit(struct dw_pcie_rp *pp);
|
||||
@@ -700,6 +703,17 @@ static inline irqreturn_t dw_handle_msi_
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
+static inline void dw_pcie_msi_init(struct dw_pcie_rp *pp)
|
||||
+{ }
|
||||
+
|
||||
+static inline int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
|
||||
+{
|
||||
+ return -ENODEV;
|
||||
+}
|
||||
+
|
||||
+static inline void dw_pcie_free_msi(struct dw_pcie_rp *pp)
|
||||
+{ }
|
||||
+
|
||||
static inline int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
|
||||
{
|
||||
return 0;
|
||||
@ -0,0 +1,145 @@
|
||||
From 8e717112caf35998b198d3762b381de70711bdec Mon Sep 17 00:00:00 2001
|
||||
From: Nam Cao <namcao@linutronix.de>
|
||||
Date: Thu, 26 Jun 2025 16:47:51 +0200
|
||||
Subject: [PATCH] PCI: dwc: Switch to msi_create_parent_irq_domain()
|
||||
|
||||
Switch to msi_create_parent_irq_domain() from pci_msi_create_irq_domain()
|
||||
which was using legacy MSI domain setup.
|
||||
|
||||
Signed-off-by: Nam Cao <namcao@linutronix.de>
|
||||
[mani: reworded commit message]
|
||||
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
|
||||
[bhelgaas: rebase on dev_fwnode() conversion]
|
||||
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
|
||||
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://patch.msgid.link/04d4a96046490e50139826c16423954e033cdf89.1750858083.git.namcao@linutronix.de
|
||||
[Keep of_node_to_fwnode instead of dev_fwnode]
|
||||
---
|
||||
drivers/pci/controller/dwc/Kconfig | 1 +
|
||||
.../pci/controller/dwc/pcie-designware-host.c | 69 +++++++------------
|
||||
drivers/pci/controller/dwc/pcie-designware.h | 1 -
|
||||
3 files changed, 25 insertions(+), 46 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/Kconfig
|
||||
+++ b/drivers/pci/controller/dwc/Kconfig
|
||||
@@ -9,6 +9,7 @@ config PCIE_DW
|
||||
config PCIE_DW_HOST
|
||||
bool
|
||||
select PCIE_DW
|
||||
+ select IRQ_MSI_LIB
|
||||
|
||||
config PCIE_DW_EP
|
||||
bool
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
+#include <linux/irqchip/irq-msi-lib.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
@@ -23,35 +24,21 @@
|
||||
static struct pci_ops dw_pcie_ops;
|
||||
static struct pci_ops dw_child_pcie_ops;
|
||||
|
||||
-static void dw_msi_ack_irq(struct irq_data *d)
|
||||
-{
|
||||
- irq_chip_ack_parent(d);
|
||||
-}
|
||||
-
|
||||
-static void dw_msi_mask_irq(struct irq_data *d)
|
||||
-{
|
||||
- pci_msi_mask_irq(d);
|
||||
- irq_chip_mask_parent(d);
|
||||
-}
|
||||
-
|
||||
-static void dw_msi_unmask_irq(struct irq_data *d)
|
||||
-{
|
||||
- pci_msi_unmask_irq(d);
|
||||
- irq_chip_unmask_parent(d);
|
||||
-}
|
||||
-
|
||||
-static struct irq_chip dw_pcie_msi_irq_chip = {
|
||||
- .name = "PCI-MSI",
|
||||
- .irq_ack = dw_msi_ack_irq,
|
||||
- .irq_mask = dw_msi_mask_irq,
|
||||
- .irq_unmask = dw_msi_unmask_irq,
|
||||
-};
|
||||
-
|
||||
-static struct msi_domain_info dw_pcie_msi_domain_info = {
|
||||
- .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
- MSI_FLAG_NO_AFFINITY | MSI_FLAG_PCI_MSIX |
|
||||
- MSI_FLAG_MULTI_PCI_MSI,
|
||||
- .chip = &dw_pcie_msi_irq_chip,
|
||||
+#define DW_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
|
||||
+ MSI_FLAG_USE_DEF_CHIP_OPS | \
|
||||
+ MSI_FLAG_NO_AFFINITY | \
|
||||
+ MSI_FLAG_PCI_MSI_MASK_PARENT)
|
||||
+#define DW_PCIE_MSI_FLAGS_SUPPORTED (MSI_FLAG_MULTI_PCI_MSI | \
|
||||
+ MSI_FLAG_PCI_MSIX | \
|
||||
+ MSI_GENERIC_FLAGS_MASK)
|
||||
+
|
||||
+static const struct msi_parent_ops dw_pcie_msi_parent_ops = {
|
||||
+ .required_flags = DW_PCIE_MSI_FLAGS_REQUIRED,
|
||||
+ .supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED,
|
||||
+ .bus_select_token = DOMAIN_BUS_PCI_MSI,
|
||||
+ .chip_flags = MSI_CHIP_FLAG_SET_ACK,
|
||||
+ .prefix = "DW-",
|
||||
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
|
||||
};
|
||||
|
||||
/* MSI int handler */
|
||||
@@ -227,26 +214,19 @@ static const struct irq_domain_ops dw_pc
|
||||
int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
- struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node);
|
||||
+ struct irq_domain_info info = {
|
||||
+ .fwnode = of_node_to_fwnode(pci->dev->of_node),
|
||||
+ .ops = &dw_pcie_msi_domain_ops,
|
||||
+ .size = pp->num_vectors,
|
||||
+ .host_data = pp,
|
||||
+ };
|
||||
|
||||
- pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors,
|
||||
- &dw_pcie_msi_domain_ops, pp);
|
||||
+ pp->irq_domain = msi_create_parent_irq_domain(&info, &dw_pcie_msi_parent_ops);
|
||||
if (!pp->irq_domain) {
|
||||
dev_err(pci->dev, "Failed to create IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
- irq_domain_update_bus_token(pp->irq_domain, DOMAIN_BUS_NEXUS);
|
||||
-
|
||||
- pp->msi_domain = pci_msi_create_irq_domain(fwnode,
|
||||
- &dw_pcie_msi_domain_info,
|
||||
- pp->irq_domain);
|
||||
- if (!pp->msi_domain) {
|
||||
- dev_err(pci->dev, "Failed to create MSI domain\n");
|
||||
- irq_domain_remove(pp->irq_domain);
|
||||
- return -ENOMEM;
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -260,7 +240,6 @@ void dw_pcie_free_msi(struct dw_pcie_rp
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
- irq_domain_remove(pp->msi_domain);
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware.h
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware.h
|
||||
@@ -368,7 +368,6 @@ struct dw_pcie_rp {
|
||||
const struct dw_pcie_host_ops *ops;
|
||||
int msi_irq[MAX_MSI_CTRLS];
|
||||
struct irq_domain *irq_domain;
|
||||
- struct irq_domain *msi_domain;
|
||||
dma_addr_t msi_data;
|
||||
struct irq_chip *msi_irq_chip;
|
||||
u32 num_vectors;
|
||||
@ -0,0 +1,66 @@
|
||||
From 455a65260f526cedd4680d4836ebdf2eaf1ab4c6 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Schumacher <ts@linux.ibm.com>
|
||||
Date: Thu, 4 Dec 2025 06:05:01 +0100
|
||||
Subject: [PATCH] genirq: Change hwirq parameter to irq_hw_number_t
|
||||
|
||||
The irqdomain implementation internally represents hardware IRQs as
|
||||
irq_hw_number_t, which is defined as unsigned long int. When providing
|
||||
an irq_hw_number_t to the generic_handle_domain() functions that expect
|
||||
and unsigned int hwirq, this can lead to a loss of information. Change
|
||||
the hwirq parameter to irq_hw_number_t to support the full range of
|
||||
hwirqs.
|
||||
|
||||
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
|
||||
Signed-off-by: Tobias Schumacher <ts@linux.ibm.com>
|
||||
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
|
||||
---
|
||||
include/linux/irqdesc.h | 6 +++---
|
||||
kernel/irq/irqdesc.c | 6 +++---
|
||||
2 files changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/include/linux/irqdesc.h
|
||||
+++ b/include/linux/irqdesc.h
|
||||
@@ -183,9 +183,9 @@ int generic_handle_irq_safe(unsigned int
|
||||
* and handle the result interrupt number. Return -EINVAL if
|
||||
* conversion failed.
|
||||
*/
|
||||
-int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq);
|
||||
-int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq);
|
||||
-int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq);
|
||||
+int generic_handle_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
+int generic_handle_domain_irq_safe(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
+int generic_handle_domain_nmi(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
#endif
|
||||
|
||||
/* Test to see if a driver has successfully requested an irq */
|
||||
--- a/kernel/irq/irqdesc.c
|
||||
+++ b/kernel/irq/irqdesc.c
|
||||
@@ -743,7 +743,7 @@ EXPORT_SYMBOL_GPL(generic_handle_irq_saf
|
||||
* This function must be called from an IRQ context with irq regs
|
||||
* initialized.
|
||||
*/
|
||||
-int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
|
||||
+int generic_handle_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
{
|
||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
}
|
||||
@@ -761,7 +761,7 @@ EXPORT_SYMBOL_GPL(generic_handle_domain_
|
||||
* context). If the interrupt is marked as 'enforce IRQ-context only' then
|
||||
* the function must be invoked from hard interrupt context.
|
||||
*/
|
||||
-int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq)
|
||||
+int generic_handle_domain_irq_safe(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
@@ -784,7 +784,7 @@ EXPORT_SYMBOL_GPL(generic_handle_domain_
|
||||
* This function must be called from an NMI context with irq regs
|
||||
* initialized.
|
||||
**/
|
||||
-int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq)
|
||||
+int generic_handle_domain_nmi(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
{
|
||||
WARN_ON_ONCE(!in_nmi());
|
||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
@ -0,0 +1,319 @@
|
||||
From fcc1d0dabdb65ca069f77e5b76d3b20277be4a15 Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 16:20:53 -0500
|
||||
Subject: [PATCH] genirq: Add interrupt redirection infrastructure
|
||||
|
||||
Add infrastructure to redirect interrupt handler execution to a
|
||||
different CPU when the current CPU is not part of the interrupt's CPU
|
||||
affinity mask.
|
||||
|
||||
This is primarily aimed at (de)multiplexed interrupts, where the child
|
||||
interrupt handler runs in the context of the parent interrupt handler,
|
||||
and therefore CPU affinity control for the child interrupt is typically
|
||||
not available.
|
||||
|
||||
With the new infrastructure, the child interrupt is allowed to freely
|
||||
change its affinity setting, independently of the parent. If the
|
||||
interrupt handler happens to be triggered on an "incompatible" CPU (a
|
||||
CPU that's not part of the child interrupt's affinity mask), the handler
|
||||
is redirected and runs in IRQ work context on a "compatible" CPU.
|
||||
|
||||
No functional change is being made to any existing irqchip driver, and
|
||||
irqchip drivers must be explicitly modified to use the newly added
|
||||
infrastructure to support interrupt redirection.
|
||||
|
||||
Originally-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/linux-pci/878qpg4o4t.ffs@tglx/
|
||||
Link: https://patch.msgid.link/20251128212055.1409093-2-rrendec@redhat.com
|
||||
---
|
||||
include/linux/irq.h | 10 +++++
|
||||
include/linux/irqdesc.h | 17 +++++++-
|
||||
kernel/irq/chip.c | 22 ++++++++++-
|
||||
kernel/irq/irqdesc.c | 86 ++++++++++++++++++++++++++++++++++++++++-
|
||||
kernel/irq/manage.c | 15 ++++++-
|
||||
5 files changed, 144 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/include/linux/irq.h
|
||||
+++ b/include/linux/irq.h
|
||||
@@ -469,6 +469,8 @@ static inline irq_hw_number_t irqd_to_hw
|
||||
* checks against the supplied affinity mask are not
|
||||
* required. This is used for CPU hotplug where the
|
||||
* target CPU is not yet set in the cpu_online_mask.
|
||||
+ * @irq_pre_redirect: Optional function to be invoked before redirecting
|
||||
+ * an interrupt via irq_work. Called only on CONFIG_SMP.
|
||||
* @irq_retrigger: resend an IRQ to the CPU
|
||||
* @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
|
||||
* @irq_set_wake: enable/disable power-management wake-on of an IRQ
|
||||
@@ -512,6 +514,7 @@ struct irq_chip {
|
||||
void (*irq_eoi)(struct irq_data *data);
|
||||
|
||||
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
|
||||
+ void (*irq_pre_redirect)(struct irq_data *data);
|
||||
int (*irq_retrigger)(struct irq_data *data);
|
||||
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
|
||||
int (*irq_set_wake)(struct irq_data *data, unsigned int on);
|
||||
@@ -692,6 +695,13 @@ extern int irq_chip_set_vcpu_affinity_pa
|
||||
extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type);
|
||||
extern int irq_chip_request_resources_parent(struct irq_data *data);
|
||||
extern void irq_chip_release_resources_parent(struct irq_data *data);
|
||||
+#ifdef CONFIG_SMP
|
||||
+void irq_chip_pre_redirect_parent(struct irq_data *data);
|
||||
+#endif
|
||||
+#endif
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+int irq_chip_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force);
|
||||
#endif
|
||||
|
||||
/* Disable or mask interrupts during a kernel kexec */
|
||||
--- a/include/linux/irqdesc.h
|
||||
+++ b/include/linux/irqdesc.h
|
||||
@@ -2,9 +2,10 @@
|
||||
#ifndef _LINUX_IRQDESC_H
|
||||
#define _LINUX_IRQDESC_H
|
||||
|
||||
-#include <linux/rcupdate.h>
|
||||
+#include <linux/irq_work.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/rcupdate.h>
|
||||
|
||||
/*
|
||||
* Core internal functions to deal with irq descriptors
|
||||
@@ -30,6 +31,17 @@ struct irqstat {
|
||||
};
|
||||
|
||||
/**
|
||||
+ * struct irq_redirect - interrupt redirection metadata
|
||||
+ * @work: Harg irq_work item for handler execution on a different CPU
|
||||
+ * @target_cpu: CPU to run irq handler on in case the current CPU is not part
|
||||
+ * of the irq affinity mask
|
||||
+ */
|
||||
+struct irq_redirect {
|
||||
+ struct irq_work work;
|
||||
+ unsigned int target_cpu;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
* struct irq_desc - interrupt descriptor
|
||||
* @irq_common_data: per irq and chip data passed down to chip functions
|
||||
* @kstat_irqs: irq stats per cpu
|
||||
@@ -46,6 +58,7 @@ struct irqstat {
|
||||
* @threads_handled: stats field for deferred spurious detection of threaded handlers
|
||||
* @threads_handled_last: comparator field for deferred spurious detection of threaded handlers
|
||||
* @lock: locking for SMP
|
||||
+ * @redirect: Facility for redirecting interrupts via irq_work
|
||||
* @affinity_hint: hint to user space for preferred irq affinity
|
||||
* @affinity_notify: context for notification of affinity changes
|
||||
* @pending_mask: pending rebalanced interrupts
|
||||
@@ -84,6 +97,7 @@ struct irq_desc {
|
||||
struct cpumask *percpu_enabled;
|
||||
const struct cpumask *percpu_affinity;
|
||||
#ifdef CONFIG_SMP
|
||||
+ struct irq_redirect redirect;
|
||||
const struct cpumask *affinity_hint;
|
||||
struct irq_affinity_notify *affinity_notify;
|
||||
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
||||
@@ -186,6 +200,7 @@ int generic_handle_irq_safe(unsigned int
|
||||
int generic_handle_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
int generic_handle_domain_irq_safe(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
int generic_handle_domain_nmi(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
+bool generic_handle_demux_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
#endif
|
||||
|
||||
/* Test to see if a driver has successfully requested an irq */
|
||||
--- a/kernel/irq/chip.c
|
||||
+++ b/kernel/irq/chip.c
|
||||
@@ -1196,7 +1196,7 @@ void irq_cpu_offline(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
-#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
|
||||
/**
|
||||
@@ -1301,6 +1301,15 @@ EXPORT_SYMBOL_GPL(handle_fasteoi_mask_ir
|
||||
|
||||
#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
|
||||
|
||||
+#ifdef CONFIG_SMP
|
||||
+void irq_chip_pre_redirect_parent(struct irq_data *data)
|
||||
+{
|
||||
+ data = data->parent_data;
|
||||
+ data->chip->irq_pre_redirect(data);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(irq_chip_pre_redirect_parent);
|
||||
+#endif
|
||||
+
|
||||
/**
|
||||
* irq_chip_set_parent_state - set the state of a parent interrupt.
|
||||
*
|
||||
@@ -1546,6 +1555,17 @@ void irq_chip_release_resources_parent(s
|
||||
data->chip->irq_release_resources(data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent);
|
||||
+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+int irq_chip_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
|
||||
+{
|
||||
+ struct irq_redirect *redir = &irq_data_to_desc(data)->redirect;
|
||||
+
|
||||
+ WRITE_ONCE(redir->target_cpu, cpumask_first(dest));
|
||||
+ return IRQ_SET_MASK_OK;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(irq_chip_redirect_set_affinity);
|
||||
#endif
|
||||
|
||||
/**
|
||||
--- a/kernel/irq/irqdesc.c
|
||||
+++ b/kernel/irq/irqdesc.c
|
||||
@@ -77,8 +77,12 @@ static int alloc_masks(struct irq_desc *
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void desc_smp_init(struct irq_desc *desc, int node,
|
||||
- const struct cpumask *affinity)
|
||||
+static void irq_redirect_work(struct irq_work *work)
|
||||
+{
|
||||
+ handle_irq_desc(container_of(work, struct irq_desc, redirect.work));
|
||||
+}
|
||||
+
|
||||
+static void desc_smp_init(struct irq_desc *desc, int node, const struct cpumask *affinity)
|
||||
{
|
||||
if (!affinity)
|
||||
affinity = irq_default_affinity;
|
||||
@@ -90,6 +94,7 @@ static void desc_smp_init(struct irq_des
|
||||
#ifdef CONFIG_NUMA
|
||||
desc->irq_common_data.node = node;
|
||||
#endif
|
||||
+ desc->redirect.work = IRQ_WORK_INIT_HARD(irq_redirect_work);
|
||||
}
|
||||
|
||||
static void free_masks(struct irq_desc *desc)
|
||||
@@ -789,6 +794,83 @@ int generic_handle_domain_nmi(struct irq
|
||||
WARN_ON_ONCE(!in_nmi());
|
||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
}
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+static bool demux_redirect_remote(struct irq_desc *desc)
|
||||
+{
|
||||
+ guard(raw_spinlock)(&desc->lock);
|
||||
+ const struct cpumask *m = irq_data_get_effective_affinity_mask(&desc->irq_data);
|
||||
+ unsigned int target_cpu = READ_ONCE(desc->redirect.target_cpu);
|
||||
+
|
||||
+ if (desc->irq_data.chip->irq_pre_redirect)
|
||||
+ desc->irq_data.chip->irq_pre_redirect(&desc->irq_data);
|
||||
+
|
||||
+ /*
|
||||
+ * If the interrupt handler is already running on a CPU that's included
|
||||
+ * in the interrupt's affinity mask, redirection is not necessary.
|
||||
+ */
|
||||
+ if (cpumask_test_cpu(smp_processor_id(), m))
|
||||
+ return false;
|
||||
+
|
||||
+ /*
|
||||
+ * The desc->action check protects against IRQ shutdown: __free_irq() sets
|
||||
+ * desc->action to NULL while holding desc->lock, which we also hold.
|
||||
+ *
|
||||
+ * Calling irq_work_queue_on() here is safe w.r.t. CPU unplugging:
|
||||
+ * - takedown_cpu() schedules multi_cpu_stop() on all active CPUs,
|
||||
+ * including the one that's taken down.
|
||||
+ * - multi_cpu_stop() acts like a barrier, which means all active
|
||||
+ * CPUs go through MULTI_STOP_DISABLE_IRQ and disable hard IRQs
|
||||
+ * *before* the dying CPU runs take_cpu_down() in MULTI_STOP_RUN.
|
||||
+ * - Hard IRQs are re-enabled at the end of multi_cpu_stop(), *after*
|
||||
+ * the dying CPU has run take_cpu_down() in MULTI_STOP_RUN.
|
||||
+ * - Since we run in hard IRQ context, we run either before or after
|
||||
+ * take_cpu_down() but never concurrently.
|
||||
+ * - If we run before take_cpu_down(), the dying CPU hasn't been marked
|
||||
+ * offline yet (it's marked via take_cpu_down() -> __cpu_disable()),
|
||||
+ * so the WARN in irq_work_queue_on() can't occur.
|
||||
+ * - Furthermore, the work item we queue will be flushed later via
|
||||
+ * take_cpu_down() -> cpuhp_invoke_callback_range_nofail() ->
|
||||
+ * smpcfd_dying_cpu() -> irq_work_run().
|
||||
+ * - If we run after take_cpu_down(), target_cpu has been already
|
||||
+ * updated via take_cpu_down() -> __cpu_disable(), which eventually
|
||||
+ * calls irq_do_set_affinity() during IRQ migration. So, target_cpu
|
||||
+ * no longer points to the dying CPU in this case.
|
||||
+ */
|
||||
+ if (desc->action)
|
||||
+ irq_work_queue_on(&desc->redirect.work, target_cpu);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+#else /* CONFIG_SMP */
|
||||
+static bool demux_redirect_remote(struct irq_desc *desc)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+/**
|
||||
+ * generic_handle_demux_domain_irq - Invoke the handler for a hardware interrupt
|
||||
+ * of a demultiplexing domain.
|
||||
+ * @domain: The domain where to perform the lookup
|
||||
+ * @hwirq: The hardware interrupt number to convert to a logical one
|
||||
+ *
|
||||
+ * Returns: True on success, or false if lookup has failed
|
||||
+ */
|
||||
+bool generic_handle_demux_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
+{
|
||||
+ struct irq_desc *desc = irq_resolve_mapping(domain, hwirq);
|
||||
+
|
||||
+ if (unlikely(!desc))
|
||||
+ return false;
|
||||
+
|
||||
+ if (demux_redirect_remote(desc))
|
||||
+ return true;
|
||||
+
|
||||
+ return !handle_irq_desc(desc);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(generic_handle_demux_domain_irq);
|
||||
+
|
||||
#endif
|
||||
|
||||
/* Dynamic interrupt handling */
|
||||
--- a/kernel/irq/manage.c
|
||||
+++ b/kernel/irq/manage.c
|
||||
@@ -35,6 +35,16 @@ static int __init setup_forced_irqthread
|
||||
early_param("threadirqs", setup_forced_irqthreads);
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_SMP
|
||||
+static inline void synchronize_irqwork(struct irq_desc *desc)
|
||||
+{
|
||||
+ /* Synchronize pending or on the fly redirect work */
|
||||
+ irq_work_sync(&desc->redirect.work);
|
||||
+}
|
||||
+#else
|
||||
+static inline void synchronize_irqwork(struct irq_desc *desc) { }
|
||||
+#endif
|
||||
+
|
||||
static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip)
|
||||
{
|
||||
struct irq_data *irqd = irq_desc_get_irq_data(desc);
|
||||
@@ -110,7 +120,9 @@ EXPORT_SYMBOL(synchronize_hardirq);
|
||||
|
||||
static void __synchronize_irq(struct irq_desc *desc)
|
||||
{
|
||||
+ synchronize_irqwork(desc);
|
||||
__synchronize_hardirq(desc, true);
|
||||
+
|
||||
/*
|
||||
* We made sure that no hardirq handler is running. Now verify that no
|
||||
* threaded handlers are active.
|
||||
@@ -220,8 +232,7 @@ static inline void irq_validate_effectiv
|
||||
|
||||
static DEFINE_PER_CPU(struct cpumask, __tmp_mask);
|
||||
|
||||
-int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
|
||||
- bool force)
|
||||
+int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
|
||||
{
|
||||
struct cpumask *tmp_mask = this_cpu_ptr(&__tmp_mask);
|
||||
struct irq_desc *desc = irq_data_to_desc(data);
|
||||
@ -0,0 +1,242 @@
|
||||
From f1875091a01dd634ff5f8b6fc57ab874f755c415 Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 16:20:54 -0500
|
||||
Subject: [PATCH] PCI: dwc: Code cleanup
|
||||
|
||||
Code cleanup with no functional changes. These changes were originally
|
||||
made by Thomas Gleixner (see Link tag below) in a patch that was never
|
||||
submitted as is. Other parts of that patch were eventually submitted as
|
||||
commit 8e717112caf3 ("PCI: dwc: Switch to msi_create_parent_irq_domain()")
|
||||
and the remaining parts are the code cleanup changes:
|
||||
|
||||
- Use guard()/scoped_guard() instead of open-coded lock/unlock.
|
||||
- Return void in a few functions whose return value is never used.
|
||||
- Simplify dw_handle_msi_irq() by using for_each_set_bit().
|
||||
|
||||
One notable deviation from the original patch is that it reverts back to a
|
||||
simple 1 by 1 iteration over the controllers inside dw_handle_msi_irq. The
|
||||
reason is that with the original changes, the IRQ offset was calculated
|
||||
incorrectly.
|
||||
|
||||
This prepares the ground for enabling MSI affinity support, which was
|
||||
originally part of that same series that Thomas Gleixner prepared.
|
||||
|
||||
Originally-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/linux-pci/878qpg4o4t.ffs@tglx/
|
||||
Link: https://patch.msgid.link/20251128212055.1409093-3-rrendec@redhat.com
|
||||
---
|
||||
.../pci/controller/dwc/pcie-designware-host.c | 98 ++++++-------------
|
||||
drivers/pci/controller/dwc/pcie-designware.h | 7 +-
|
||||
2 files changed, 34 insertions(+), 71 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
@@ -42,35 +42,25 @@ static const struct msi_parent_ops dw_pc
|
||||
};
|
||||
|
||||
/* MSI int handler */
|
||||
-irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
+void dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
{
|
||||
- int i, pos;
|
||||
- unsigned long val;
|
||||
- u32 status, num_ctrls;
|
||||
- irqreturn_t ret = IRQ_NONE;
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
+ unsigned int i, num_ctrls;
|
||||
|
||||
num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
for (i = 0; i < num_ctrls; i++) {
|
||||
- status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS +
|
||||
- (i * MSI_REG_CTRL_BLOCK_SIZE));
|
||||
+ unsigned int reg_off = i * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
+ unsigned int irq_off = i * MAX_MSI_IRQS_PER_CTRL;
|
||||
+ unsigned long status, pos;
|
||||
+
|
||||
+ status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + reg_off);
|
||||
if (!status)
|
||||
continue;
|
||||
|
||||
- ret = IRQ_HANDLED;
|
||||
- val = status;
|
||||
- pos = 0;
|
||||
- while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL,
|
||||
- pos)) != MAX_MSI_IRQS_PER_CTRL) {
|
||||
- generic_handle_domain_irq(pp->irq_domain,
|
||||
- (i * MAX_MSI_IRQS_PER_CTRL) +
|
||||
- pos);
|
||||
- pos++;
|
||||
- }
|
||||
+ for_each_set_bit(pos, &status, MAX_MSI_IRQS_PER_CTRL)
|
||||
+ generic_handle_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
}
|
||||
-
|
||||
- return ret;
|
||||
}
|
||||
|
||||
/* Chained MSI interrupt service routine */
|
||||
@@ -91,13 +81,10 @@ static void dw_pci_setup_msi_msg(struct
|
||||
{
|
||||
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
- u64 msi_target;
|
||||
-
|
||||
- msi_target = (u64)pp->msi_data;
|
||||
+ u64 msi_target = (u64)pp->msi_data;
|
||||
|
||||
msg->address_lo = lower_32_bits(msi_target);
|
||||
msg->address_hi = upper_32_bits(msi_target);
|
||||
-
|
||||
msg->data = d->hwirq;
|
||||
|
||||
dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
|
||||
@@ -109,18 +96,14 @@ static void dw_pci_bottom_mask(struct ir
|
||||
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
unsigned int res, bit, ctrl;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
+ guard(raw_spinlock)(&pp->lock);
|
||||
ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
pp->irq_mask[ctrl] |= BIT(bit);
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]);
|
||||
-
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
}
|
||||
|
||||
static void dw_pci_bottom_unmask(struct irq_data *d)
|
||||
@@ -128,18 +111,14 @@ static void dw_pci_bottom_unmask(struct
|
||||
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
unsigned int res, bit, ctrl;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
+ guard(raw_spinlock)(&pp->lock);
|
||||
ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
pp->irq_mask[ctrl] &= ~BIT(bit);
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]);
|
||||
-
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
}
|
||||
|
||||
static void dw_pci_bottom_ack(struct irq_data *d)
|
||||
@@ -156,54 +135,42 @@ static void dw_pci_bottom_ack(struct irq
|
||||
}
|
||||
|
||||
static struct irq_chip dw_pci_msi_bottom_irq_chip = {
|
||||
- .name = "DWPCI-MSI",
|
||||
- .irq_ack = dw_pci_bottom_ack,
|
||||
- .irq_compose_msi_msg = dw_pci_setup_msi_msg,
|
||||
- .irq_mask = dw_pci_bottom_mask,
|
||||
- .irq_unmask = dw_pci_bottom_unmask,
|
||||
+ .name = "DWPCI-MSI",
|
||||
+ .irq_ack = dw_pci_bottom_ack,
|
||||
+ .irq_compose_msi_msg = dw_pci_setup_msi_msg,
|
||||
+ .irq_mask = dw_pci_bottom_mask,
|
||||
+ .irq_unmask = dw_pci_bottom_unmask,
|
||||
};
|
||||
|
||||
-static int dw_pcie_irq_domain_alloc(struct irq_domain *domain,
|
||||
- unsigned int virq, unsigned int nr_irqs,
|
||||
- void *args)
|
||||
+static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
+ unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct dw_pcie_rp *pp = domain->host_data;
|
||||
- unsigned long flags;
|
||||
- u32 i;
|
||||
int bit;
|
||||
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
-
|
||||
- bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors,
|
||||
- order_base_2(nr_irqs));
|
||||
-
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
+ scoped_guard (raw_spinlock_irq, &pp->lock) {
|
||||
+ bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors,
|
||||
+ order_base_2(nr_irqs));
|
||||
+ }
|
||||
|
||||
if (bit < 0)
|
||||
return -ENOSPC;
|
||||
|
||||
- for (i = 0; i < nr_irqs; i++)
|
||||
- irq_domain_set_info(domain, virq + i, bit + i,
|
||||
- pp->msi_irq_chip,
|
||||
- pp, handle_edge_irq,
|
||||
- NULL, NULL);
|
||||
-
|
||||
+ for (unsigned int i = 0; i < nr_irqs; i++) {
|
||||
+ irq_domain_set_info(domain, virq + i, bit + i, pp->msi_irq_chip,
|
||||
+ pp, handle_edge_irq, NULL, NULL);
|
||||
+ }
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void dw_pcie_irq_domain_free(struct irq_domain *domain,
|
||||
- unsigned int virq, unsigned int nr_irqs)
|
||||
+static void dw_pcie_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
+ unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
struct dw_pcie_rp *pp = domain->host_data;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
-
|
||||
- bitmap_release_region(pp->msi_irq_in_use, d->hwirq,
|
||||
- order_base_2(nr_irqs));
|
||||
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
+ guard(raw_spinlock_irq)(&pp->lock);
|
||||
+ bitmap_release_region(pp->msi_irq_in_use, d->hwirq, order_base_2(nr_irqs));
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
|
||||
@@ -236,8 +203,7 @@ void dw_pcie_free_msi(struct dw_pcie_rp
|
||||
|
||||
for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) {
|
||||
if (pp->msi_irq[ctrl] > 0)
|
||||
- irq_set_chained_handler_and_data(pp->msi_irq[ctrl],
|
||||
- NULL, NULL);
|
||||
+ irq_set_chained_handler_and_data(pp->msi_irq[ctrl], NULL, NULL);
|
||||
}
|
||||
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware.h
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware.h
|
||||
@@ -676,7 +676,7 @@ static inline enum dw_pcie_ltssm dw_pcie
|
||||
#ifdef CONFIG_PCIE_DW_HOST
|
||||
int dw_pcie_suspend_noirq(struct dw_pcie *pci);
|
||||
int dw_pcie_resume_noirq(struct dw_pcie *pci);
|
||||
-irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp);
|
||||
+void dw_handle_msi_irq(struct dw_pcie_rp *pp);
|
||||
void dw_pcie_msi_init(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_msi_host_init(struct dw_pcie_rp *pp);
|
||||
void dw_pcie_free_msi(struct dw_pcie_rp *pp);
|
||||
@@ -697,10 +697,7 @@ static inline int dw_pcie_resume_noirq(s
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static inline irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
-{
|
||||
- return IRQ_NONE;
|
||||
-}
|
||||
+static inline void dw_handle_msi_irq(struct dw_pcie_rp *pp) { }
|
||||
|
||||
static inline void dw_pcie_msi_init(struct dw_pcie_rp *pp)
|
||||
{ }
|
||||
@ -0,0 +1,92 @@
|
||||
From eaf290c404f7c39f23292e9ce83b8b5b51ab598a Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 16:20:55 -0500
|
||||
Subject: [PATCH] PCI: dwc: Enable MSI affinity support
|
||||
|
||||
Leverage the interrupt redirection infrastructure to enable CPU affinity
|
||||
support for MSI interrupts. Since the parent interrupt affinity cannot
|
||||
be changed, affinity control for the child interrupt (MSI) is achieved
|
||||
by redirecting the handler to run in IRQ work context on the target CPU.
|
||||
|
||||
This patch was originally prepared by Thomas Gleixner (see Link tag below)
|
||||
in a patch series that was never submitted as is, and only parts of that
|
||||
series have made it upstream so far.
|
||||
|
||||
Originally-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/linux-pci/878qpg4o4t.ffs@tglx/
|
||||
Link: https://patch.msgid.link/20251128212055.1409093-4-rrendec@redhat.com
|
||||
---
|
||||
.../pci/controller/dwc/pcie-designware-host.c | 33 ++++++++++++++++---
|
||||
1 file changed, 28 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
@@ -24,9 +24,27 @@
|
||||
static struct pci_ops dw_pcie_ops;
|
||||
static struct pci_ops dw_child_pcie_ops;
|
||||
|
||||
+#ifdef CONFIG_SMP
|
||||
+static void dw_irq_noop(struct irq_data *d) { }
|
||||
+#endif
|
||||
+
|
||||
+static bool dw_pcie_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
|
||||
+ struct irq_domain *real_parent, struct msi_domain_info *info)
|
||||
+{
|
||||
+ if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
|
||||
+ return false;
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+ info->chip->irq_ack = dw_irq_noop;
|
||||
+ info->chip->irq_pre_redirect = irq_chip_pre_redirect_parent;
|
||||
+#else
|
||||
+ info->chip->irq_ack = irq_chip_ack_parent;
|
||||
+#endif
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
#define DW_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
|
||||
MSI_FLAG_USE_DEF_CHIP_OPS | \
|
||||
- MSI_FLAG_NO_AFFINITY | \
|
||||
MSI_FLAG_PCI_MSI_MASK_PARENT)
|
||||
#define DW_PCIE_MSI_FLAGS_SUPPORTED (MSI_FLAG_MULTI_PCI_MSI | \
|
||||
MSI_FLAG_PCI_MSIX | \
|
||||
@@ -36,9 +54,8 @@ static const struct msi_parent_ops dw_pc
|
||||
.required_flags = DW_PCIE_MSI_FLAGS_REQUIRED,
|
||||
.supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED,
|
||||
.bus_select_token = DOMAIN_BUS_PCI_MSI,
|
||||
- .chip_flags = MSI_CHIP_FLAG_SET_ACK,
|
||||
.prefix = "DW-",
|
||||
- .init_dev_msi_info = msi_lib_init_dev_msi_info,
|
||||
+ .init_dev_msi_info = dw_pcie_init_dev_msi_info,
|
||||
};
|
||||
|
||||
/* MSI int handler */
|
||||
@@ -59,7 +76,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp
|
||||
continue;
|
||||
|
||||
for_each_set_bit(pos, &status, MAX_MSI_IRQS_PER_CTRL)
|
||||
- generic_handle_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
+ generic_handle_demux_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,10 +153,16 @@ static void dw_pci_bottom_ack(struct irq
|
||||
|
||||
static struct irq_chip dw_pci_msi_bottom_irq_chip = {
|
||||
.name = "DWPCI-MSI",
|
||||
- .irq_ack = dw_pci_bottom_ack,
|
||||
.irq_compose_msi_msg = dw_pci_setup_msi_msg,
|
||||
.irq_mask = dw_pci_bottom_mask,
|
||||
.irq_unmask = dw_pci_bottom_unmask,
|
||||
+#ifdef CONFIG_SMP
|
||||
+ .irq_ack = dw_irq_noop,
|
||||
+ .irq_pre_redirect = dw_pci_bottom_ack,
|
||||
+ .irq_set_affinity = irq_chip_redirect_set_affinity,
|
||||
+#else
|
||||
+ .irq_ack = dw_pci_bottom_ack,
|
||||
+#endif
|
||||
};
|
||||
|
||||
static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
@ -0,0 +1,56 @@
|
||||
From df439718afaf23b5aa7b5711b6c14e87b5836cae Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 16:14:02 -0500
|
||||
Subject: [PATCH] genirq: Update effective affinity for redirected
|
||||
interrupts
|
||||
|
||||
For redirected interrupts, irq_chip_redirect_set_affinity() does not
|
||||
update the effective affinity mask, which then triggers the warning in
|
||||
irq_validate_effective_affinity(). Also, because the effective affinity
|
||||
mask is empty, the cpumask_test_cpu(smp_processor_id(), m) condition in
|
||||
demux_redirect_remote() is always false, and the interrupt is always
|
||||
redirected, even if it's already running on the target CPU.
|
||||
|
||||
Set the effective affinity mask to be the same as the requested affinity
|
||||
mask. It's worth noting that irq_do_set_affinity() filters out offline
|
||||
CPUs before calling chip->irq_set_affinity() (unless `force` is set), so
|
||||
the mask passed to irq_chip_redirect_set_affinity() is already filtered.
|
||||
|
||||
The solution is not ideal because it may lie about the effective
|
||||
affinity of the demultiplexed ("child") interrupt. If the requested
|
||||
affinity mask includes multiple CPUs, the effective affinity, in
|
||||
reality, is the intersection between the requested mask and the
|
||||
demultiplexing ("parent") interrupt's effective affinity mask, plus
|
||||
the first CPU in the requested mask.
|
||||
|
||||
Accurately describing the effective affinity of the demultiplexed
|
||||
interrupt is not trivial because it requires keeping track of the
|
||||
demultiplexing interrupt's effective affinity. That is tricky in the
|
||||
context of CPU hot(un)plugging, where interrupt migration ordering is
|
||||
not guaranteed. The solution in the initial version of the fixed patch,
|
||||
which stored the first CPU of the demultiplexing interrupt's effective
|
||||
affinity in the `target_cpu` field, has its own drawbacks and
|
||||
limitations.
|
||||
|
||||
Fixes: fcc1d0dabdb6 ("genirq: Add interrupt redirection infrastructure")
|
||||
Reported-by: Jon Hunter <jonathanh@nvidia.com>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
|
||||
Tested-by: Jon Hunter <jonathanh@nvidia.com>
|
||||
Link: https://patch.msgid.link/20260112211402.2927336-1-rrendec@redhat.com
|
||||
Closes: https://lore.kernel.org/all/44509520-f29b-4b8a-8986-5eae3e022eb7@nvidia.com/
|
||||
---
|
||||
kernel/irq/chip.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/kernel/irq/chip.c
|
||||
+++ b/kernel/irq/chip.c
|
||||
@@ -1563,6 +1563,8 @@ int irq_chip_redirect_set_affinity(struc
|
||||
struct irq_redirect *redir = &irq_data_to_desc(data)->redirect;
|
||||
|
||||
WRITE_ONCE(redir->target_cpu, cpumask_first(dest));
|
||||
+ irq_data_update_effective_affinity(data, dest);
|
||||
+
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_redirect_set_affinity);
|
||||
@ -0,0 +1,66 @@
|
||||
From 455a65260f526cedd4680d4836ebdf2eaf1ab4c6 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Schumacher <ts@linux.ibm.com>
|
||||
Date: Thu, 4 Dec 2025 06:05:01 +0100
|
||||
Subject: [PATCH] genirq: Change hwirq parameter to irq_hw_number_t
|
||||
|
||||
The irqdomain implementation internally represents hardware IRQs as
|
||||
irq_hw_number_t, which is defined as unsigned long int. When providing
|
||||
an irq_hw_number_t to the generic_handle_domain() functions that expect
|
||||
and unsigned int hwirq, this can lead to a loss of information. Change
|
||||
the hwirq parameter to irq_hw_number_t to support the full range of
|
||||
hwirqs.
|
||||
|
||||
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Reviewed-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
|
||||
Signed-off-by: Tobias Schumacher <ts@linux.ibm.com>
|
||||
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
|
||||
---
|
||||
include/linux/irqdesc.h | 6 +++---
|
||||
kernel/irq/irqdesc.c | 6 +++---
|
||||
2 files changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/include/linux/irqdesc.h
|
||||
+++ b/include/linux/irqdesc.h
|
||||
@@ -183,9 +183,9 @@ int generic_handle_irq_safe(unsigned int
|
||||
* and handle the result interrupt number. Return -EINVAL if
|
||||
* conversion failed.
|
||||
*/
|
||||
-int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq);
|
||||
-int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq);
|
||||
-int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq);
|
||||
+int generic_handle_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
+int generic_handle_domain_irq_safe(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
+int generic_handle_domain_nmi(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
#endif
|
||||
|
||||
/* Test to see if a driver has successfully requested an irq */
|
||||
--- a/kernel/irq/irqdesc.c
|
||||
+++ b/kernel/irq/irqdesc.c
|
||||
@@ -720,7 +720,7 @@ EXPORT_SYMBOL_GPL(generic_handle_irq_saf
|
||||
* This function must be called from an IRQ context with irq regs
|
||||
* initialized.
|
||||
*/
|
||||
-int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
|
||||
+int generic_handle_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
{
|
||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
}
|
||||
@@ -738,7 +738,7 @@ EXPORT_SYMBOL_GPL(generic_handle_domain_
|
||||
* context). If the interrupt is marked as 'enforce IRQ-context only' then
|
||||
* the function must be invoked from hard interrupt context.
|
||||
*/
|
||||
-int generic_handle_domain_irq_safe(struct irq_domain *domain, unsigned int hwirq)
|
||||
+int generic_handle_domain_irq_safe(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
@@ -761,7 +761,7 @@ EXPORT_SYMBOL_GPL(generic_handle_domain_
|
||||
* This function must be called from an NMI context with irq regs
|
||||
* initialized.
|
||||
**/
|
||||
-int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq)
|
||||
+int generic_handle_domain_nmi(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
{
|
||||
WARN_ON_ONCE(!in_nmi());
|
||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
@ -0,0 +1,319 @@
|
||||
From fcc1d0dabdb65ca069f77e5b76d3b20277be4a15 Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 16:20:53 -0500
|
||||
Subject: [PATCH] genirq: Add interrupt redirection infrastructure
|
||||
|
||||
Add infrastructure to redirect interrupt handler execution to a
|
||||
different CPU when the current CPU is not part of the interrupt's CPU
|
||||
affinity mask.
|
||||
|
||||
This is primarily aimed at (de)multiplexed interrupts, where the child
|
||||
interrupt handler runs in the context of the parent interrupt handler,
|
||||
and therefore CPU affinity control for the child interrupt is typically
|
||||
not available.
|
||||
|
||||
With the new infrastructure, the child interrupt is allowed to freely
|
||||
change its affinity setting, independently of the parent. If the
|
||||
interrupt handler happens to be triggered on an "incompatible" CPU (a
|
||||
CPU that's not part of the child interrupt's affinity mask), the handler
|
||||
is redirected and runs in IRQ work context on a "compatible" CPU.
|
||||
|
||||
No functional change is being made to any existing irqchip driver, and
|
||||
irqchip drivers must be explicitly modified to use the newly added
|
||||
infrastructure to support interrupt redirection.
|
||||
|
||||
Originally-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/linux-pci/878qpg4o4t.ffs@tglx/
|
||||
Link: https://patch.msgid.link/20251128212055.1409093-2-rrendec@redhat.com
|
||||
---
|
||||
include/linux/irq.h | 10 +++++
|
||||
include/linux/irqdesc.h | 17 +++++++-
|
||||
kernel/irq/chip.c | 22 ++++++++++-
|
||||
kernel/irq/irqdesc.c | 86 ++++++++++++++++++++++++++++++++++++++++-
|
||||
kernel/irq/manage.c | 15 ++++++-
|
||||
5 files changed, 144 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/include/linux/irq.h
|
||||
+++ b/include/linux/irq.h
|
||||
@@ -459,6 +459,8 @@ static inline irq_hw_number_t irqd_to_hw
|
||||
* checks against the supplied affinity mask are not
|
||||
* required. This is used for CPU hotplug where the
|
||||
* target CPU is not yet set in the cpu_online_mask.
|
||||
+ * @irq_pre_redirect: Optional function to be invoked before redirecting
|
||||
+ * an interrupt via irq_work. Called only on CONFIG_SMP.
|
||||
* @irq_retrigger: resend an IRQ to the CPU
|
||||
* @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
|
||||
* @irq_set_wake: enable/disable power-management wake-on of an IRQ
|
||||
@@ -503,6 +505,7 @@ struct irq_chip {
|
||||
void (*irq_eoi)(struct irq_data *data);
|
||||
|
||||
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
|
||||
+ void (*irq_pre_redirect)(struct irq_data *data);
|
||||
int (*irq_retrigger)(struct irq_data *data);
|
||||
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
|
||||
int (*irq_set_wake)(struct irq_data *data, unsigned int on);
|
||||
@@ -688,6 +691,13 @@ extern int irq_chip_set_vcpu_affinity_pa
|
||||
extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type);
|
||||
extern int irq_chip_request_resources_parent(struct irq_data *data);
|
||||
extern void irq_chip_release_resources_parent(struct irq_data *data);
|
||||
+#ifdef CONFIG_SMP
|
||||
+void irq_chip_pre_redirect_parent(struct irq_data *data);
|
||||
+#endif
|
||||
+#endif
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+int irq_chip_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force);
|
||||
#endif
|
||||
|
||||
/* Disable or mask interrupts during a kernel kexec */
|
||||
--- a/include/linux/irqdesc.h
|
||||
+++ b/include/linux/irqdesc.h
|
||||
@@ -2,9 +2,10 @@
|
||||
#ifndef _LINUX_IRQDESC_H
|
||||
#define _LINUX_IRQDESC_H
|
||||
|
||||
-#include <linux/rcupdate.h>
|
||||
+#include <linux/irq_work.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/rcupdate.h>
|
||||
|
||||
/*
|
||||
* Core internal functions to deal with irq descriptors
|
||||
@@ -30,6 +31,17 @@ struct irqstat {
|
||||
};
|
||||
|
||||
/**
|
||||
+ * struct irq_redirect - interrupt redirection metadata
|
||||
+ * @work: Harg irq_work item for handler execution on a different CPU
|
||||
+ * @target_cpu: CPU to run irq handler on in case the current CPU is not part
|
||||
+ * of the irq affinity mask
|
||||
+ */
|
||||
+struct irq_redirect {
|
||||
+ struct irq_work work;
|
||||
+ unsigned int target_cpu;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
* struct irq_desc - interrupt descriptor
|
||||
* @irq_common_data: per irq and chip data passed down to chip functions
|
||||
* @kstat_irqs: irq stats per cpu
|
||||
@@ -46,6 +58,7 @@ struct irqstat {
|
||||
* @threads_handled: stats field for deferred spurious detection of threaded handlers
|
||||
* @threads_handled_last: comparator field for deferred spurious detection of threaded handlers
|
||||
* @lock: locking for SMP
|
||||
+ * @redirect: Facility for redirecting interrupts via irq_work
|
||||
* @affinity_hint: hint to user space for preferred irq affinity
|
||||
* @affinity_notify: context for notification of affinity changes
|
||||
* @pending_mask: pending rebalanced interrupts
|
||||
@@ -84,6 +97,7 @@ struct irq_desc {
|
||||
struct cpumask *percpu_enabled;
|
||||
const struct cpumask *percpu_affinity;
|
||||
#ifdef CONFIG_SMP
|
||||
+ struct irq_redirect redirect;
|
||||
const struct cpumask *affinity_hint;
|
||||
struct irq_affinity_notify *affinity_notify;
|
||||
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
||||
@@ -186,6 +200,7 @@ int generic_handle_irq_safe(unsigned int
|
||||
int generic_handle_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
int generic_handle_domain_irq_safe(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
int generic_handle_domain_nmi(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
+bool generic_handle_demux_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq);
|
||||
#endif
|
||||
|
||||
/* Test to see if a driver has successfully requested an irq */
|
||||
--- a/kernel/irq/chip.c
|
||||
+++ b/kernel/irq/chip.c
|
||||
@@ -1143,7 +1143,7 @@ void irq_cpu_offline(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
-#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
|
||||
/**
|
||||
@@ -1215,6 +1215,15 @@ EXPORT_SYMBOL_GPL(handle_fasteoi_mask_ir
|
||||
|
||||
#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
|
||||
|
||||
+#ifdef CONFIG_SMP
|
||||
+void irq_chip_pre_redirect_parent(struct irq_data *data)
|
||||
+{
|
||||
+ data = data->parent_data;
|
||||
+ data->chip->irq_pre_redirect(data);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(irq_chip_pre_redirect_parent);
|
||||
+#endif
|
||||
+
|
||||
/**
|
||||
* irq_chip_set_parent_state - set the state of a parent interrupt.
|
||||
*
|
||||
@@ -1497,6 +1506,17 @@ void irq_chip_release_resources_parent(s
|
||||
data->chip->irq_release_resources(data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent);
|
||||
+#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+int irq_chip_redirect_set_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
|
||||
+{
|
||||
+ struct irq_redirect *redir = &irq_data_to_desc(data)->redirect;
|
||||
+
|
||||
+ WRITE_ONCE(redir->target_cpu, cpumask_first(dest));
|
||||
+ return IRQ_SET_MASK_OK;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(irq_chip_redirect_set_affinity);
|
||||
#endif
|
||||
|
||||
/**
|
||||
--- a/kernel/irq/irqdesc.c
|
||||
+++ b/kernel/irq/irqdesc.c
|
||||
@@ -78,8 +78,12 @@ static int alloc_masks(struct irq_desc *
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void desc_smp_init(struct irq_desc *desc, int node,
|
||||
- const struct cpumask *affinity)
|
||||
+static void irq_redirect_work(struct irq_work *work)
|
||||
+{
|
||||
+ handle_irq_desc(container_of(work, struct irq_desc, redirect.work));
|
||||
+}
|
||||
+
|
||||
+static void desc_smp_init(struct irq_desc *desc, int node, const struct cpumask *affinity)
|
||||
{
|
||||
if (!affinity)
|
||||
affinity = irq_default_affinity;
|
||||
@@ -91,6 +95,7 @@ static void desc_smp_init(struct irq_des
|
||||
#ifdef CONFIG_NUMA
|
||||
desc->irq_common_data.node = node;
|
||||
#endif
|
||||
+ desc->redirect.work = IRQ_WORK_INIT_HARD(irq_redirect_work);
|
||||
}
|
||||
|
||||
static void free_masks(struct irq_desc *desc)
|
||||
@@ -766,6 +771,83 @@ int generic_handle_domain_nmi(struct irq
|
||||
WARN_ON_ONCE(!in_nmi());
|
||||
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
|
||||
}
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+static bool demux_redirect_remote(struct irq_desc *desc)
|
||||
+{
|
||||
+ guard(raw_spinlock)(&desc->lock);
|
||||
+ const struct cpumask *m = irq_data_get_effective_affinity_mask(&desc->irq_data);
|
||||
+ unsigned int target_cpu = READ_ONCE(desc->redirect.target_cpu);
|
||||
+
|
||||
+ if (desc->irq_data.chip->irq_pre_redirect)
|
||||
+ desc->irq_data.chip->irq_pre_redirect(&desc->irq_data);
|
||||
+
|
||||
+ /*
|
||||
+ * If the interrupt handler is already running on a CPU that's included
|
||||
+ * in the interrupt's affinity mask, redirection is not necessary.
|
||||
+ */
|
||||
+ if (cpumask_test_cpu(smp_processor_id(), m))
|
||||
+ return false;
|
||||
+
|
||||
+ /*
|
||||
+ * The desc->action check protects against IRQ shutdown: __free_irq() sets
|
||||
+ * desc->action to NULL while holding desc->lock, which we also hold.
|
||||
+ *
|
||||
+ * Calling irq_work_queue_on() here is safe w.r.t. CPU unplugging:
|
||||
+ * - takedown_cpu() schedules multi_cpu_stop() on all active CPUs,
|
||||
+ * including the one that's taken down.
|
||||
+ * - multi_cpu_stop() acts like a barrier, which means all active
|
||||
+ * CPUs go through MULTI_STOP_DISABLE_IRQ and disable hard IRQs
|
||||
+ * *before* the dying CPU runs take_cpu_down() in MULTI_STOP_RUN.
|
||||
+ * - Hard IRQs are re-enabled at the end of multi_cpu_stop(), *after*
|
||||
+ * the dying CPU has run take_cpu_down() in MULTI_STOP_RUN.
|
||||
+ * - Since we run in hard IRQ context, we run either before or after
|
||||
+ * take_cpu_down() but never concurrently.
|
||||
+ * - If we run before take_cpu_down(), the dying CPU hasn't been marked
|
||||
+ * offline yet (it's marked via take_cpu_down() -> __cpu_disable()),
|
||||
+ * so the WARN in irq_work_queue_on() can't occur.
|
||||
+ * - Furthermore, the work item we queue will be flushed later via
|
||||
+ * take_cpu_down() -> cpuhp_invoke_callback_range_nofail() ->
|
||||
+ * smpcfd_dying_cpu() -> irq_work_run().
|
||||
+ * - If we run after take_cpu_down(), target_cpu has been already
|
||||
+ * updated via take_cpu_down() -> __cpu_disable(), which eventually
|
||||
+ * calls irq_do_set_affinity() during IRQ migration. So, target_cpu
|
||||
+ * no longer points to the dying CPU in this case.
|
||||
+ */
|
||||
+ if (desc->action)
|
||||
+ irq_work_queue_on(&desc->redirect.work, target_cpu);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+#else /* CONFIG_SMP */
|
||||
+static bool demux_redirect_remote(struct irq_desc *desc)
|
||||
+{
|
||||
+ return false;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+/**
|
||||
+ * generic_handle_demux_domain_irq - Invoke the handler for a hardware interrupt
|
||||
+ * of a demultiplexing domain.
|
||||
+ * @domain: The domain where to perform the lookup
|
||||
+ * @hwirq: The hardware interrupt number to convert to a logical one
|
||||
+ *
|
||||
+ * Returns: True on success, or false if lookup has failed
|
||||
+ */
|
||||
+bool generic_handle_demux_domain_irq(struct irq_domain *domain, irq_hw_number_t hwirq)
|
||||
+{
|
||||
+ struct irq_desc *desc = irq_resolve_mapping(domain, hwirq);
|
||||
+
|
||||
+ if (unlikely(!desc))
|
||||
+ return false;
|
||||
+
|
||||
+ if (demux_redirect_remote(desc))
|
||||
+ return true;
|
||||
+
|
||||
+ return !handle_irq_desc(desc);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(generic_handle_demux_domain_irq);
|
||||
+
|
||||
#endif
|
||||
|
||||
/* Dynamic interrupt handling */
|
||||
--- a/kernel/irq/manage.c
|
||||
+++ b/kernel/irq/manage.c
|
||||
@@ -35,6 +35,16 @@ static int __init setup_forced_irqthread
|
||||
early_param("threadirqs", setup_forced_irqthreads);
|
||||
#endif
|
||||
|
||||
+#ifdef CONFIG_SMP
|
||||
+static inline void synchronize_irqwork(struct irq_desc *desc)
|
||||
+{
|
||||
+ /* Synchronize pending or on the fly redirect work */
|
||||
+ irq_work_sync(&desc->redirect.work);
|
||||
+}
|
||||
+#else
|
||||
+static inline void synchronize_irqwork(struct irq_desc *desc) { }
|
||||
+#endif
|
||||
+
|
||||
static int __irq_get_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool *state);
|
||||
|
||||
static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip)
|
||||
@@ -107,7 +117,9 @@ EXPORT_SYMBOL(synchronize_hardirq);
|
||||
|
||||
static void __synchronize_irq(struct irq_desc *desc)
|
||||
{
|
||||
+ synchronize_irqwork(desc);
|
||||
__synchronize_hardirq(desc, true);
|
||||
+
|
||||
/*
|
||||
* We made sure that no hardirq handler is running. Now verify that no
|
||||
* threaded handlers are active.
|
||||
@@ -217,8 +229,7 @@ static inline void irq_validate_effectiv
|
||||
|
||||
static DEFINE_PER_CPU(struct cpumask, __tmp_mask);
|
||||
|
||||
-int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
|
||||
- bool force)
|
||||
+int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
|
||||
{
|
||||
struct cpumask *tmp_mask = this_cpu_ptr(&__tmp_mask);
|
||||
struct irq_desc *desc = irq_data_to_desc(data);
|
||||
@ -0,0 +1,242 @@
|
||||
From f1875091a01dd634ff5f8b6fc57ab874f755c415 Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 16:20:54 -0500
|
||||
Subject: [PATCH] PCI: dwc: Code cleanup
|
||||
|
||||
Code cleanup with no functional changes. These changes were originally
|
||||
made by Thomas Gleixner (see Link tag below) in a patch that was never
|
||||
submitted as is. Other parts of that patch were eventually submitted as
|
||||
commit 8e717112caf3 ("PCI: dwc: Switch to msi_create_parent_irq_domain()")
|
||||
and the remaining parts are the code cleanup changes:
|
||||
|
||||
- Use guard()/scoped_guard() instead of open-coded lock/unlock.
|
||||
- Return void in a few functions whose return value is never used.
|
||||
- Simplify dw_handle_msi_irq() by using for_each_set_bit().
|
||||
|
||||
One notable deviation from the original patch is that it reverts back to a
|
||||
simple 1 by 1 iteration over the controllers inside dw_handle_msi_irq. The
|
||||
reason is that with the original changes, the IRQ offset was calculated
|
||||
incorrectly.
|
||||
|
||||
This prepares the ground for enabling MSI affinity support, which was
|
||||
originally part of that same series that Thomas Gleixner prepared.
|
||||
|
||||
Originally-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/linux-pci/878qpg4o4t.ffs@tglx/
|
||||
Link: https://patch.msgid.link/20251128212055.1409093-3-rrendec@redhat.com
|
||||
---
|
||||
.../pci/controller/dwc/pcie-designware-host.c | 98 ++++++-------------
|
||||
drivers/pci/controller/dwc/pcie-designware.h | 7 +-
|
||||
2 files changed, 34 insertions(+), 71 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
@@ -46,35 +46,25 @@ static const struct msi_parent_ops dw_pc
|
||||
};
|
||||
|
||||
/* MSI int handler */
|
||||
-irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
+void dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
{
|
||||
- int i, pos;
|
||||
- unsigned long val;
|
||||
- u32 status, num_ctrls;
|
||||
- irqreturn_t ret = IRQ_NONE;
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
+ unsigned int i, num_ctrls;
|
||||
|
||||
num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
for (i = 0; i < num_ctrls; i++) {
|
||||
- status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS +
|
||||
- (i * MSI_REG_CTRL_BLOCK_SIZE));
|
||||
+ unsigned int reg_off = i * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
+ unsigned int irq_off = i * MAX_MSI_IRQS_PER_CTRL;
|
||||
+ unsigned long status, pos;
|
||||
+
|
||||
+ status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + reg_off);
|
||||
if (!status)
|
||||
continue;
|
||||
|
||||
- ret = IRQ_HANDLED;
|
||||
- val = status;
|
||||
- pos = 0;
|
||||
- while ((pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL,
|
||||
- pos)) != MAX_MSI_IRQS_PER_CTRL) {
|
||||
- generic_handle_domain_irq(pp->irq_domain,
|
||||
- (i * MAX_MSI_IRQS_PER_CTRL) +
|
||||
- pos);
|
||||
- pos++;
|
||||
- }
|
||||
+ for_each_set_bit(pos, &status, MAX_MSI_IRQS_PER_CTRL)
|
||||
+ generic_handle_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
}
|
||||
-
|
||||
- return ret;
|
||||
}
|
||||
|
||||
/* Chained MSI interrupt service routine */
|
||||
@@ -95,13 +85,10 @@ static void dw_pci_setup_msi_msg(struct
|
||||
{
|
||||
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
- u64 msi_target;
|
||||
-
|
||||
- msi_target = (u64)pp->msi_data;
|
||||
+ u64 msi_target = (u64)pp->msi_data;
|
||||
|
||||
msg->address_lo = lower_32_bits(msi_target);
|
||||
msg->address_hi = upper_32_bits(msi_target);
|
||||
-
|
||||
msg->data = d->hwirq;
|
||||
|
||||
dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
|
||||
@@ -113,18 +100,14 @@ static void dw_pci_bottom_mask(struct ir
|
||||
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
unsigned int res, bit, ctrl;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
+ guard(raw_spinlock)(&pp->lock);
|
||||
ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
pp->irq_mask[ctrl] |= BIT(bit);
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]);
|
||||
-
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
}
|
||||
|
||||
static void dw_pci_bottom_unmask(struct irq_data *d)
|
||||
@@ -132,18 +115,14 @@ static void dw_pci_bottom_unmask(struct
|
||||
struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
unsigned int res, bit, ctrl;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
|
||||
+ guard(raw_spinlock)(&pp->lock);
|
||||
ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL;
|
||||
res = ctrl * MSI_REG_CTRL_BLOCK_SIZE;
|
||||
bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL;
|
||||
|
||||
pp->irq_mask[ctrl] &= ~BIT(bit);
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]);
|
||||
-
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
}
|
||||
|
||||
static void dw_pci_bottom_ack(struct irq_data *d)
|
||||
@@ -160,54 +139,42 @@ static void dw_pci_bottom_ack(struct irq
|
||||
}
|
||||
|
||||
static struct irq_chip dw_pci_msi_bottom_irq_chip = {
|
||||
- .name = "DWPCI-MSI",
|
||||
- .irq_ack = dw_pci_bottom_ack,
|
||||
- .irq_compose_msi_msg = dw_pci_setup_msi_msg,
|
||||
- .irq_mask = dw_pci_bottom_mask,
|
||||
- .irq_unmask = dw_pci_bottom_unmask,
|
||||
+ .name = "DWPCI-MSI",
|
||||
+ .irq_ack = dw_pci_bottom_ack,
|
||||
+ .irq_compose_msi_msg = dw_pci_setup_msi_msg,
|
||||
+ .irq_mask = dw_pci_bottom_mask,
|
||||
+ .irq_unmask = dw_pci_bottom_unmask,
|
||||
};
|
||||
|
||||
-static int dw_pcie_irq_domain_alloc(struct irq_domain *domain,
|
||||
- unsigned int virq, unsigned int nr_irqs,
|
||||
- void *args)
|
||||
+static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
+ unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct dw_pcie_rp *pp = domain->host_data;
|
||||
- unsigned long flags;
|
||||
- u32 i;
|
||||
int bit;
|
||||
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
-
|
||||
- bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors,
|
||||
- order_base_2(nr_irqs));
|
||||
-
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
+ scoped_guard (raw_spinlock_irq, &pp->lock) {
|
||||
+ bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors,
|
||||
+ order_base_2(nr_irqs));
|
||||
+ }
|
||||
|
||||
if (bit < 0)
|
||||
return -ENOSPC;
|
||||
|
||||
- for (i = 0; i < nr_irqs; i++)
|
||||
- irq_domain_set_info(domain, virq + i, bit + i,
|
||||
- pp->msi_irq_chip,
|
||||
- pp, handle_edge_irq,
|
||||
- NULL, NULL);
|
||||
-
|
||||
+ for (unsigned int i = 0; i < nr_irqs; i++) {
|
||||
+ irq_domain_set_info(domain, virq + i, bit + i, pp->msi_irq_chip,
|
||||
+ pp, handle_edge_irq, NULL, NULL);
|
||||
+ }
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void dw_pcie_irq_domain_free(struct irq_domain *domain,
|
||||
- unsigned int virq, unsigned int nr_irqs)
|
||||
+static void dw_pcie_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
+ unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
||||
struct dw_pcie_rp *pp = domain->host_data;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- raw_spin_lock_irqsave(&pp->lock, flags);
|
||||
-
|
||||
- bitmap_release_region(pp->msi_irq_in_use, d->hwirq,
|
||||
- order_base_2(nr_irqs));
|
||||
|
||||
- raw_spin_unlock_irqrestore(&pp->lock, flags);
|
||||
+ guard(raw_spinlock_irq)(&pp->lock);
|
||||
+ bitmap_release_region(pp->msi_irq_in_use, d->hwirq, order_base_2(nr_irqs));
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
|
||||
@@ -240,8 +207,7 @@ void dw_pcie_free_msi(struct dw_pcie_rp
|
||||
|
||||
for (ctrl = 0; ctrl < MAX_MSI_CTRLS; ctrl++) {
|
||||
if (pp->msi_irq[ctrl] > 0)
|
||||
- irq_set_chained_handler_and_data(pp->msi_irq[ctrl],
|
||||
- NULL, NULL);
|
||||
+ irq_set_chained_handler_and_data(pp->msi_irq[ctrl], NULL, NULL);
|
||||
}
|
||||
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware.h
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware.h
|
||||
@@ -810,7 +810,7 @@ static inline enum dw_pcie_ltssm dw_pcie
|
||||
#ifdef CONFIG_PCIE_DW_HOST
|
||||
int dw_pcie_suspend_noirq(struct dw_pcie *pci);
|
||||
int dw_pcie_resume_noirq(struct dw_pcie *pci);
|
||||
-irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp);
|
||||
+void dw_handle_msi_irq(struct dw_pcie_rp *pp);
|
||||
void dw_pcie_msi_init(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_msi_host_init(struct dw_pcie_rp *pp);
|
||||
void dw_pcie_free_msi(struct dw_pcie_rp *pp);
|
||||
@@ -831,10 +831,7 @@ static inline int dw_pcie_resume_noirq(s
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static inline irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
||||
-{
|
||||
- return IRQ_NONE;
|
||||
-}
|
||||
+static inline void dw_handle_msi_irq(struct dw_pcie_rp *pp) { }
|
||||
|
||||
static inline void dw_pcie_msi_init(struct dw_pcie_rp *pp)
|
||||
{ }
|
||||
@ -0,0 +1,92 @@
|
||||
From eaf290c404f7c39f23292e9ce83b8b5b51ab598a Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 16:20:55 -0500
|
||||
Subject: [PATCH] PCI: dwc: Enable MSI affinity support
|
||||
|
||||
Leverage the interrupt redirection infrastructure to enable CPU affinity
|
||||
support for MSI interrupts. Since the parent interrupt affinity cannot
|
||||
be changed, affinity control for the child interrupt (MSI) is achieved
|
||||
by redirecting the handler to run in IRQ work context on the target CPU.
|
||||
|
||||
This patch was originally prepared by Thomas Gleixner (see Link tag below)
|
||||
in a patch series that was never submitted as is, and only parts of that
|
||||
series have made it upstream so far.
|
||||
|
||||
Originally-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Link: https://lore.kernel.org/linux-pci/878qpg4o4t.ffs@tglx/
|
||||
Link: https://patch.msgid.link/20251128212055.1409093-4-rrendec@redhat.com
|
||||
---
|
||||
.../pci/controller/dwc/pcie-designware-host.c | 33 ++++++++++++++++---
|
||||
1 file changed, 28 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
|
||||
@@ -26,9 +26,27 @@ static struct pci_ops dw_pcie_ops;
|
||||
static struct pci_ops dw_pcie_ecam_ops;
|
||||
static struct pci_ops dw_child_pcie_ops;
|
||||
|
||||
+#ifdef CONFIG_SMP
|
||||
+static void dw_irq_noop(struct irq_data *d) { }
|
||||
+#endif
|
||||
+
|
||||
+static bool dw_pcie_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
|
||||
+ struct irq_domain *real_parent, struct msi_domain_info *info)
|
||||
+{
|
||||
+ if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
|
||||
+ return false;
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
+ info->chip->irq_ack = dw_irq_noop;
|
||||
+ info->chip->irq_pre_redirect = irq_chip_pre_redirect_parent;
|
||||
+#else
|
||||
+ info->chip->irq_ack = irq_chip_ack_parent;
|
||||
+#endif
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
#define DW_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
|
||||
MSI_FLAG_USE_DEF_CHIP_OPS | \
|
||||
- MSI_FLAG_NO_AFFINITY | \
|
||||
MSI_FLAG_PCI_MSI_MASK_PARENT)
|
||||
#define DW_PCIE_MSI_FLAGS_SUPPORTED (MSI_FLAG_MULTI_PCI_MSI | \
|
||||
MSI_FLAG_PCI_MSIX | \
|
||||
@@ -40,9 +58,8 @@ static const struct msi_parent_ops dw_pc
|
||||
.required_flags = DW_PCIE_MSI_FLAGS_REQUIRED,
|
||||
.supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED,
|
||||
.bus_select_token = DOMAIN_BUS_PCI_MSI,
|
||||
- .chip_flags = MSI_CHIP_FLAG_SET_ACK,
|
||||
.prefix = "DW-",
|
||||
- .init_dev_msi_info = msi_lib_init_dev_msi_info,
|
||||
+ .init_dev_msi_info = dw_pcie_init_dev_msi_info,
|
||||
};
|
||||
|
||||
/* MSI int handler */
|
||||
@@ -63,7 +80,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp
|
||||
continue;
|
||||
|
||||
for_each_set_bit(pos, &status, MAX_MSI_IRQS_PER_CTRL)
|
||||
- generic_handle_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
+ generic_handle_demux_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,10 +157,16 @@ static void dw_pci_bottom_ack(struct irq
|
||||
|
||||
static struct irq_chip dw_pci_msi_bottom_irq_chip = {
|
||||
.name = "DWPCI-MSI",
|
||||
- .irq_ack = dw_pci_bottom_ack,
|
||||
.irq_compose_msi_msg = dw_pci_setup_msi_msg,
|
||||
.irq_mask = dw_pci_bottom_mask,
|
||||
.irq_unmask = dw_pci_bottom_unmask,
|
||||
+#ifdef CONFIG_SMP
|
||||
+ .irq_ack = dw_irq_noop,
|
||||
+ .irq_pre_redirect = dw_pci_bottom_ack,
|
||||
+ .irq_set_affinity = irq_chip_redirect_set_affinity,
|
||||
+#else
|
||||
+ .irq_ack = dw_pci_bottom_ack,
|
||||
+#endif
|
||||
};
|
||||
|
||||
static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
@ -0,0 +1,56 @@
|
||||
From df439718afaf23b5aa7b5711b6c14e87b5836cae Mon Sep 17 00:00:00 2001
|
||||
From: Radu Rendec <rrendec@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 16:14:02 -0500
|
||||
Subject: [PATCH] genirq: Update effective affinity for redirected
|
||||
interrupts
|
||||
|
||||
For redirected interrupts, irq_chip_redirect_set_affinity() does not
|
||||
update the effective affinity mask, which then triggers the warning in
|
||||
irq_validate_effective_affinity(). Also, because the effective affinity
|
||||
mask is empty, the cpumask_test_cpu(smp_processor_id(), m) condition in
|
||||
demux_redirect_remote() is always false, and the interrupt is always
|
||||
redirected, even if it's already running on the target CPU.
|
||||
|
||||
Set the effective affinity mask to be the same as the requested affinity
|
||||
mask. It's worth noting that irq_do_set_affinity() filters out offline
|
||||
CPUs before calling chip->irq_set_affinity() (unless `force` is set), so
|
||||
the mask passed to irq_chip_redirect_set_affinity() is already filtered.
|
||||
|
||||
The solution is not ideal because it may lie about the effective
|
||||
affinity of the demultiplexed ("child") interrupt. If the requested
|
||||
affinity mask includes multiple CPUs, the effective affinity, in
|
||||
reality, is the intersection between the requested mask and the
|
||||
demultiplexing ("parent") interrupt's effective affinity mask, plus
|
||||
the first CPU in the requested mask.
|
||||
|
||||
Accurately describing the effective affinity of the demultiplexed
|
||||
interrupt is not trivial because it requires keeping track of the
|
||||
demultiplexing interrupt's effective affinity. That is tricky in the
|
||||
context of CPU hot(un)plugging, where interrupt migration ordering is
|
||||
not guaranteed. The solution in the initial version of the fixed patch,
|
||||
which stored the first CPU of the demultiplexing interrupt's effective
|
||||
affinity in the `target_cpu` field, has its own drawbacks and
|
||||
limitations.
|
||||
|
||||
Fixes: fcc1d0dabdb6 ("genirq: Add interrupt redirection infrastructure")
|
||||
Reported-by: Jon Hunter <jonathanh@nvidia.com>
|
||||
Signed-off-by: Radu Rendec <rrendec@redhat.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
|
||||
Tested-by: Jon Hunter <jonathanh@nvidia.com>
|
||||
Link: https://patch.msgid.link/20260112211402.2927336-1-rrendec@redhat.com
|
||||
Closes: https://lore.kernel.org/all/44509520-f29b-4b8a-8986-5eae3e022eb7@nvidia.com/
|
||||
---
|
||||
kernel/irq/chip.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/kernel/irq/chip.c
|
||||
+++ b/kernel/irq/chip.c
|
||||
@@ -1514,6 +1514,8 @@ int irq_chip_redirect_set_affinity(struc
|
||||
struct irq_redirect *redir = &irq_data_to_desc(data)->redirect;
|
||||
|
||||
WRITE_ONCE(redir->target_cpu, cpumask_first(dest));
|
||||
+ irq_data_update_effective_affinity(data, dest);
|
||||
+
|
||||
return IRQ_SET_MASK_OK;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_redirect_set_affinity);
|
||||
@ -13,7 +13,7 @@ Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
|
||||
|
||||
--- a/drivers/pci/controller/dwc/Kconfig
|
||||
+++ b/drivers/pci/controller/dwc/Kconfig
|
||||
@@ -361,6 +361,15 @@ config PCIE_FU740
|
||||
@@ -362,6 +362,15 @@ config PCIE_FU740
|
||||
Say Y here if you want PCIe controller support for the SiFive
|
||||
FU740.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user