[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <933dea7a-4e5a-4d3c-8580-b94d0a9271c1@linux.ibm.com>
Date: Mon, 17 Nov 2025 14:46:20 -0800
From: Farhan Ali <alifm@...ux.ibm.com>
To: Tobias Schumacher <ts@...ux.ibm.com>, Heiko Carstens <hca@...ux.ibm.com>,
Vasily Gorbik <gor@...ux.ibm.com>,
Alexander Gordeev
<agordeev@...ux.ibm.com>,
Christian Borntraeger <borntraeger@...ux.ibm.com>,
Sven Schnelle <svens@...ux.ibm.com>,
Niklas Schnelle
<schnelle@...ux.ibm.com>,
Gerald Schaefer <gerald.schaefer@...ux.ibm.com>,
Gerd Bayer <gbayer@...ux.ibm.com>, Halil Pasic <pasic@...ux.ibm.com>,
Matthew Rosato <mjrosato@...ux.ibm.com>,
Thomas Gleixner <tglx@...utronix.de>
Cc: linux-kernel@...r.kernel.org, linux-s390@...r.kernel.org
Subject: Re: [PATCH v2 2/2] s390/pci: Migrate s390 IRQ logic to IRQ domain API
On 11/17/2025 12:59 AM, Tobias Schumacher wrote:
> s390 is one of the last architectures using the legacy API for setup and
> teardown of PCI MSI IRQs. Migrate the s390 IRQ allocation and teardown
> to the MSI parent domain API. For details, see:
>
> https://lore.kernel.org/lkml/20221111120501.026511281@linutronix.de
>
> In detail, create an MSI parent domain for zpci which is used by
> all PCI devices. When a PCI device sets up MSI or MSI-X IRQs, the
> library creates a per-device IRQ domain for this device, which is
> used by the device for allocating and freeing IRQs.
>
> The per-device domain delegates this allocation and freeing to the
> parent-domain. In the end, the corresponding callbacks of the parent
> domain are responsible for allocating and freeing the IRQs.
>
> The allocation is split into two parts:
> - zpci_msi_prepare() is called once for each device and allocates the
> required resources. On s390, each PCI function has its own airq
> vector and a summary bit, which must be configured once per function.
> This is done in prepare().
> - zpci_msi_alloc() can be called multiple times for allocating one or
> more MSI/MSI-X IRQs. This creates a mapping between the virtual IRQ
> number in the kernel and the hardware IRQ number.
>
> Freeing is split into two counterparts:
> - zpci_msi_free() reverts the effects of zpci_msi_alloc() and
> - zpci_msi_teardown() reverts the effects of zpci_msi_prepare(). This is
> callend once when all IRQs are freed before a device is removed.
>
> Since the parent domain in the end allocates the IRQs, the hwirq
> encoding must be unambiguous for all IRQs of all devices. This is
> achieved by encoding the hwirq using the PCI function id and the MSI
> index.
>
> Signed-off-by: Tobias Schumacher <ts@...ux.ibm.com>
> ---
> arch/s390/Kconfig | 1 +
> arch/s390/include/asm/pci.h | 1 +
> arch/s390/pci/pci_bus.c | 1 +
> arch/s390/pci/pci_irq.c | 335 +++++++++++++++++++++++++++-----------------
> 4 files changed, 208 insertions(+), 130 deletions(-)
>
> diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
> index df22b10d91415e1ed183cc8add9ad0ac4293c50e..48cd6a12bd04dfe4dd61ecc79d3401ba685c51bb 100644
> --- a/arch/s390/Kconfig
> +++ b/arch/s390/Kconfig
> @@ -251,6 +251,7 @@ config S390
> select HOTPLUG_SMT
> select IOMMU_HELPER if PCI
> select IOMMU_SUPPORT if PCI
> + select IRQ_MSI_LIB if PCI
> select KASAN_VMALLOC if KASAN
> select LOCK_MM_AND_FIND_VMA
> select MMU_GATHER_MERGE_VMAS
> diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
> index a32f465ecf73a5cc3408a312d94ec888d62848cc..462e87bdb7acdfa4e7df0f9ca8e82c269e1f98aa 100644
> --- a/arch/s390/include/asm/pci.h
> +++ b/arch/s390/include/asm/pci.h
> @@ -310,6 +310,7 @@ int zpci_dma_exit_device(struct zpci_dev *zdev);
> /* IRQ */
> int __init zpci_irq_init(void);
> void __init zpci_irq_exit(void);
> +void zpci_set_msi_parent_domain(struct zpci_bus *zbus);
>
> /* FMB */
> int zpci_fmb_enable_device(struct zpci_dev *);
> diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c
> index be8c697fea0cc755cfdb4fb0a9e3b95183bec0dc..2be33cfb8970409db4fcb75ea73543f49b583a5c 100644
> --- a/arch/s390/pci/pci_bus.c
> +++ b/arch/s390/pci/pci_bus.c
> @@ -210,6 +210,7 @@ static int zpci_bus_create_pci_bus(struct zpci_bus *zbus, struct zpci_dev *fr, s
> }
>
> zbus->bus = bus;
> + zpci_set_msi_parent_domain(zbus);
Why are we setting the zpci_set_msi_parent_domain per root device
instead of per zpci device?
>
> return 0;
> }
> diff --git a/arch/s390/pci/pci_irq.c b/arch/s390/pci/pci_irq.c
> index e73be96ce5fe6473fc193d65b8f0ff635d6a98ba..f2c2fc23d5693e211bcbfd94e8f2fe25dc71a4e2 100644
> --- a/arch/s390/pci/pci_irq.c
> +++ b/arch/s390/pci/pci_irq.c
> @@ -7,6 +7,7 @@
> #include <linux/kernel_stat.h>
> #include <linux/pci.h>
> #include <linux/msi.h>
> +#include <linux/irqchip/irq-msi-lib.h>
> #include <linux/smp.h>
>
> #include <asm/isc.h>
> @@ -29,6 +30,8 @@ static struct airq_iv *zpci_sbv;
> */
> static struct airq_iv **zpci_ibv;
>
> +static struct irq_domain *zpci_msi_parent_domain;
> +
> /* Modify PCI: Register floating adapter interruptions */
> static int zpci_set_airq(struct zpci_dev *zdev)
> {
> @@ -110,43 +113,42 @@ static int zpci_set_irq(struct zpci_dev *zdev)
> return rc;
> }
>
> -/* Clear adapter interruptions */
> -static int zpci_clear_irq(struct zpci_dev *zdev)
> +static int zpci_set_irq_affinity(struct irq_data *data, const struct cpumask *dest,
> + bool force)
> {
> - int rc;
> -
> - if (irq_delivery == DIRECTED)
> - rc = zpci_clear_directed_irq(zdev);
> - else
> - rc = zpci_clear_airq(zdev);
> -
> - return rc;
> + irq_data_update_affinity(data, dest);
> + return IRQ_SET_MASK_OK;
> }
>
> -static int zpci_set_irq_affinity(struct irq_data *data, const struct cpumask *dest,
> - bool force)
> +static void zpci_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
> {
> - struct msi_desc *entry = irq_data_get_msi_desc(data);
> - struct msi_msg msg = entry->msg;
> - int cpu_addr = smp_cpu_get_cpu_address(cpumask_first(dest));
> + struct msi_desc *desc = irq_data_get_msi_desc(data);
> + struct zpci_dev *zdev = to_zpci_dev(desc->dev);
>
> - msg.address_lo &= 0xff0000ff;
> - msg.address_lo |= (cpu_addr << 8);
> - pci_write_msi_msg(data->irq, &msg);
> + if (irq_delivery == DIRECTED) {
> + int cpu = cpumask_first(irq_data_get_affinity_mask(data));
>
> - return IRQ_SET_MASK_OK;
> + msg->address_lo = zdev->msi_addr & 0xff0000ff;
> + msg->address_lo |= (smp_cpu_get_cpu_address(cpu) << 8);
> + } else {
> + msg->address_lo = zdev->msi_addr & 0xffffffff;
> + }
> + msg->address_hi = zdev->msi_addr >> 32;
> + msg->data = data->hwirq & 0xffffffff;
> }
>
> static struct irq_chip zpci_irq_chip = {
> .name = "PCI-MSI",
> .irq_unmask = pci_msi_unmask_irq,
> .irq_mask = pci_msi_mask_irq,
> + .irq_compose_msi_msg = zpci_compose_msi_msg
> };
>
> static void zpci_handle_cpu_local_irq(bool rescan)
> {
> struct airq_iv *dibv = zpci_ibv[smp_processor_id()];
> union zpci_sic_iib iib = {{0}};
> + irq_hw_number_t hwirq;
> unsigned long bit;
> int irqs_on = 0;
>
> @@ -164,7 +166,8 @@ static void zpci_handle_cpu_local_irq(bool rescan)
> continue;
> }
> inc_irq_stat(IRQIO_MSI);
> - generic_handle_irq(airq_iv_get_data(dibv, bit));
> + hwirq = airq_iv_get_ptr(dibv, bit);
> + generic_handle_domain_irq(zpci_msi_parent_domain, hwirq);
> }
> }
>
> @@ -229,6 +232,7 @@ static void zpci_floating_irq_handler(struct airq_struct *airq,
> struct tpi_info *tpi_info)
> {
> union zpci_sic_iib iib = {{0}};
> + irq_hw_number_t hwirq;
> unsigned long si, ai;
> struct airq_iv *aibv;
> int irqs_on = 0;
> @@ -256,7 +260,9 @@ static void zpci_floating_irq_handler(struct airq_struct *airq,
> break;
> inc_irq_stat(IRQIO_MSI);
> airq_iv_lock(aibv, ai);
> - generic_handle_irq(airq_iv_get_data(aibv, ai));
> +
> + hwirq = airq_iv_get_ptr(aibv, ai);
> + generic_handle_domain_irq(zpci_msi_parent_domain, hwirq);
> airq_iv_unlock(aibv, ai);
> }
> }
> @@ -278,7 +284,7 @@ static int __alloc_airq(struct zpci_dev *zdev, int msi_vecs,
> zdev->aisb = *bit;
>
> /* Create adapter interrupt vector */
> - zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK, NULL);
> + zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_PTR | AIRQ_IV_BITLOCK, NULL);
> if (!zdev->aibv)
> return -ENOMEM;
>
> @@ -290,146 +296,203 @@ static int __alloc_airq(struct zpci_dev *zdev, int msi_vecs,
> return 0;
> }
>
> -int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
> +static struct airq_struct zpci_airq = {
> + .handler = zpci_floating_irq_handler,
> + .isc = PCI_ISC,
> +};
> +
> +/*
> + * Encode the hwirq number for the parent domain. The encoding must be unique
> + * for each IRQ of each device in the parent domain, so it uses the fid to
> + * identify the device and the msi_index to identify the IRQ within that device.
> + */
> +static inline irq_hw_number_t zpci_encode_hwirq(u32 fid, u16 msi_index)
> {
> - unsigned int hwirq, msi_vecs, irqs_per_msi, i, cpu;
> - struct zpci_dev *zdev = to_zpci(pdev);
> - struct msi_desc *msi;
> - struct msi_msg msg;
> - unsigned long bit;
> - int cpu_addr;
> - int rc, irq;
> + return ((irq_hw_number_t)fid << 32) | msi_index;
> +}
>
> - zdev->aisb = -1UL;
> - zdev->msi_first_bit = -1U;
> +static inline u16 zpci_decode_hwirq_msi_index(irq_hw_number_t irq)
> +{
> + return irq & 0xFFFF;
> +}
> +
> +static int zpci_msi_prepare(struct irq_domain *domain,
> + struct device *dev, int nvec,
> + msi_alloc_info_t *info)
> +{
> + struct zpci_dev *zdev = to_zpci_dev(dev);
> + struct pci_dev *pdev = to_pci_dev(dev);
> + unsigned long bit;
> + int msi_vecs, rc;
>
> msi_vecs = min_t(unsigned int, nvec, zdev->max_msi);
> - if (msi_vecs < nvec) {
> - pr_info("%s requested %d irqs, allocate system limit of %d",
> + if (msi_vecs < nvec)
> + pr_info("%s requested %d IRQs, allocate system limit of %d",
> pci_name(pdev), nvec, zdev->max_msi);
> - }
>
> rc = __alloc_airq(zdev, msi_vecs, &bit);
> - if (rc < 0)
> + if (rc) {
> + pr_err("Allocating adapter IRQs for %s failed\n", pci_name(pdev));
> return rc;
> + }
>
> - /*
> - * Request MSI interrupts:
> - * When using MSI, nvec_used interrupt sources and their irq
> - * descriptors are controlled through one msi descriptor.
> - * Thus the outer loop over msi descriptors shall run only once,
> - * while two inner loops iterate over the interrupt vectors.
> - * When using MSI-X, each interrupt vector/irq descriptor
> - * is bound to exactly one msi descriptor (nvec_used is one).
> - * So the inner loops are executed once, while the outer iterates
> - * over the MSI-X descriptors.
> - */
> - hwirq = bit;
> - msi_for_each_desc(msi, &pdev->dev, MSI_DESC_NOTASSOCIATED) {
> - if (hwirq - bit >= msi_vecs)
> - break;
> - irqs_per_msi = min_t(unsigned int, msi_vecs, msi->nvec_used);
> - irq = __irq_alloc_descs(-1, 0, irqs_per_msi, 0, THIS_MODULE,
> - (irq_delivery == DIRECTED) ?
> - msi->affinity : NULL);
> - if (irq < 0)
> - return -ENOMEM;
> -
> - for (i = 0; i < irqs_per_msi; i++) {
> - rc = irq_set_msi_desc_off(irq, i, msi);
> - if (rc)
> - return rc;
> - irq_set_chip_and_handler(irq + i, &zpci_irq_chip,
> - handle_percpu_irq);
> - }
> -
> - msg.data = hwirq - bit;
> + zdev->msi_first_bit = bit;
> + zdev->msi_nr_irqs = msi_vecs;
> + rc = zpci_set_irq(zdev);
> + if (rc) {
> + pr_err("Registering adapter IRQs for %s failed\n",
> + pci_name(pdev));
> if (irq_delivery == DIRECTED) {
> - if (msi->affinity)
> - cpu = cpumask_first(&msi->affinity->mask);
> - else
> - cpu = 0;
> - cpu_addr = smp_cpu_get_cpu_address(cpu);
> -
> - msg.address_lo = zdev->msi_addr & 0xff0000ff;
> - msg.address_lo |= (cpu_addr << 8);
> -
> - for_each_possible_cpu(cpu) {
> - for (i = 0; i < irqs_per_msi; i++)
> - airq_iv_set_data(zpci_ibv[cpu],
> - hwirq + i, irq + i);
> - }
> + airq_iv_free(zpci_ibv[0], zdev->msi_first_bit, msi_vecs);
> } else {
> - msg.address_lo = zdev->msi_addr & 0xffffffff;
> - for (i = 0; i < irqs_per_msi; i++)
> - airq_iv_set_data(zdev->aibv, hwirq + i, irq + i);
> + zpci_clear_airq(zdev);
> + airq_iv_release(zdev->aibv);
> + zdev->aibv = NULL;
> + airq_iv_free_bit(zpci_sbv, zdev->aisb);
> + zdev->aisb = -1UL;
> }
> - msg.address_hi = zdev->msi_addr >> 32;
> - pci_write_msi_msg(irq, &msg);
> - hwirq += irqs_per_msi;
> + zdev->msi_first_bit = -1U;
> + return rc;
> }
>
> - zdev->msi_first_bit = bit;
> - zdev->msi_nr_irqs = hwirq - bit;
> + return 0;
> +}
>
> - rc = zpci_set_irq(zdev);
> - if (rc)
> - return rc;
> +static void zpci_msi_teardown_directed(struct zpci_dev *zdev)
> +{
> + zpci_clear_directed_irq(zdev);
> + airq_iv_free(zpci_ibv[0], zdev->msi_first_bit, zdev->max_msi);
> + zdev->msi_first_bit = -1U;
> +}
>
> - return (zdev->msi_nr_irqs == nvec) ? 0 : zdev->msi_nr_irqs;
> +static void zpci_msi_teardown_floating(struct zpci_dev *zdev)
> +{
> + zpci_clear_airq(zdev);
> + airq_iv_release(zdev->aibv);
> + zdev->aibv = NULL;
> + airq_iv_free_bit(zpci_sbv, zdev->aisb);
> + zdev->aisb = -1UL;
> + zdev->msi_first_bit = -1U;
> }
>
> -void arch_teardown_msi_irqs(struct pci_dev *pdev)
> +static void zpci_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *arg)
> {
> - struct zpci_dev *zdev = to_zpci(pdev);
> - struct msi_desc *msi;
> - unsigned int i;
> - int rc;
> + struct zpci_dev *zdev = to_zpci_dev(domain->dev);
>
> - /* Disable interrupts */
> - rc = zpci_clear_irq(zdev);
> - if (rc)
> - return;
> + if (irq_delivery == DIRECTED)
> + zpci_msi_teardown_directed(zdev);
> + else
> + zpci_msi_teardown_floating(zdev);
> +}
>
> - /* Release MSI interrupts */
> - msi_for_each_desc(msi, &pdev->dev, MSI_DESC_ASSOCIATED) {
> - for (i = 0; i < msi->nvec_used; i++) {
> - irq_set_msi_desc(msi->irq + i, NULL);
> - irq_free_desc(msi->irq + i);
> +static int zpci_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
> + unsigned int nr_irqs, void *args)
> +{
> + struct msi_desc *desc = ((msi_alloc_info_t *)args)->desc;
> + struct zpci_dev *zdev = to_zpci_dev(desc->dev);
> + irq_hw_number_t hwirq;
> + unsigned long bit;
> + unsigned int cpu;
> + int i;
> +
> + bit = zdev->msi_first_bit + desc->msi_index;
> + hwirq = zpci_encode_hwirq(zdev->fid, desc->msi_index);
> +
> + if (desc->msi_index + nr_irqs > zdev->max_msi)
> + return -EINVAL;
> +
> + for (i = 0; i < nr_irqs; i++) {
> + irq_domain_set_info(domain, virq + i, hwirq + i,
> + &zpci_irq_chip, zdev,
> + handle_percpu_irq, NULL, NULL);
> +
> + if (irq_delivery == DIRECTED) {
> + for_each_possible_cpu(cpu) {
> + airq_iv_set_ptr(zpci_ibv[cpu],
> + bit + i, hwirq + i);
> + }
> +
> + } else {
> + airq_iv_set_ptr(zdev->aibv, bit + i, hwirq + i);
> }
> - msi->msg.address_lo = 0;
> - msi->msg.address_hi = 0;
> - msi->msg.data = 0;
> - msi->irq = 0;
> }
>
> - if (zdev->aisb != -1UL) {
> - zpci_ibv[zdev->aisb] = NULL;
> - airq_iv_free_bit(zpci_sbv, zdev->aisb);
> - zdev->aisb = -1UL;
> - }
> - if (zdev->aibv) {
> - airq_iv_release(zdev->aibv);
> - zdev->aibv = NULL;
> - }
> + return 0;
> +}
>
> - if ((irq_delivery == DIRECTED) && zdev->msi_first_bit != -1U)
> - airq_iv_free(zpci_ibv[0], zdev->msi_first_bit, zdev->msi_nr_irqs);
> +static void zpci_msi_domain_free(struct irq_domain *domain, unsigned int virq,
> + unsigned int nr_irqs)
> +{
> + irq_hw_number_t hwirq;
> + struct irq_data *d;
> + u16 msi_index;
> + int i;
> +
> + for (i = 0; i < nr_irqs; i++) {
> + d = irq_domain_get_irq_data(domain, virq + i);
> + hwirq = d->hwirq;
> + msi_index = zpci_decode_hwirq_msi_index(hwirq);
> + irq_domain_reset_irq_data(d);
> + }
> }
>
> -bool arch_restore_msi_irqs(struct pci_dev *pdev)
Why are we removing arch_restore_msi_irqs? This is called in the path
for restoring the MSI/MSI-X state of the device
(pci_restore_msi_state()). It looks like irqdomain infrastructure will
not setup airq for the device in the restore path.
> +static const struct irq_domain_ops zpci_msi_domain_ops = {
> + .alloc = zpci_msi_domain_alloc,
> + .free = zpci_msi_domain_free
> +};
> +
> +static bool zpci_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
> + struct irq_domain *real_parent,
> + struct msi_domain_info *info)
> {
> - struct zpci_dev *zdev = to_zpci(pdev);
> + if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
> + return false;
> +
> + info->ops->msi_prepare = zpci_msi_prepare;
> + info->ops->msi_teardown = zpci_msi_teardown;
>
> - zpci_set_irq(zdev);
> return true;
> }
>
> -static struct airq_struct zpci_airq = {
> - .handler = zpci_floating_irq_handler,
> - .isc = PCI_ISC,
> +static struct msi_parent_ops zpci_msi_parent_ops = {
> + .supported_flags = MSI_GENERIC_FLAGS_MASK |
> + MSI_FLAG_PCI_MSIX |
> + MSI_FLAG_MULTI_PCI_MSI,
> + .required_flags = MSI_FLAG_USE_DEF_DOM_OPS |
> + MSI_FLAG_USE_DEF_CHIP_OPS |
> + MSI_FLAG_PCI_MSI_MASK_PARENT,
> + .init_dev_msi_info = zpci_init_dev_msi_info
> };
>
> +static int __init zpci_create_parent_msi_domain(void)
> +{
> + struct irq_domain_info info = {
> + .fwnode = irq_domain_alloc_named_fwnode("zpci_msi"),
> + .ops = &zpci_msi_domain_ops
> + };
> +
> + if (!info.fwnode) {
> + pr_err("Failed to allocate fwnode for MSI IRQ domain\n");
> + return -ENOMEM;
> + }
> +
> + if (irq_delivery == FLOATING)
> + zpci_msi_parent_ops.required_flags |= MSI_FLAG_NO_AFFINITY;
> + zpci_msi_parent_domain = msi_create_parent_irq_domain(&info, &zpci_msi_parent_ops);
> + if (!zpci_msi_parent_domain) {
> + irq_domain_free_fwnode(info.fwnode);
> + pr_err("Failed to create MSI IRQ domain\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +void zpci_set_msi_parent_domain(struct zpci_bus *zbus)
> +{
> + dev_set_msi_domain(&zbus->bus->dev, zpci_msi_parent_domain);
> +}
> +
> static void __init cpu_enable_directed_irq(void *unused)
> {
> union zpci_sic_iib iib = {{0}};
> @@ -466,7 +529,7 @@ static int __init zpci_directed_irq_init(void)
> * is only done on the first vector.
> */
> zpci_ibv[cpu] = airq_iv_create(cache_line_size() * BITS_PER_BYTE,
> - AIRQ_IV_DATA |
> + AIRQ_IV_PTR |
> AIRQ_IV_CACHELINE |
> (!cpu ? AIRQ_IV_ALLOC : 0), NULL);
> if (!zpci_ibv[cpu])
> @@ -511,6 +574,11 @@ int __init zpci_irq_init(void)
> rc = register_adapter_interrupt(&zpci_airq);
> if (rc)
> goto out;
> +
> + zpci_create_parent_msi_domain();
> + if (!zpci_msi_parent_domain)
> + goto out_airq;
> +
> /* Set summary to 1 to be called every time for the ISC. */
> *zpci_airq.lsi_ptr = 1;
>
> @@ -524,7 +592,7 @@ int __init zpci_irq_init(void)
> }
>
> if (rc)
> - goto out_airq;
> + goto out_msi_domain;
>
> /*
> * Enable floating IRQs (with suppression after one IRQ). When using
> @@ -533,6 +601,8 @@ int __init zpci_irq_init(void)
> zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib);
>
> return 0;
> +out_msi_domain:
> + irq_domain_remove(zpci_msi_parent_domain);
Shouldn't this also do a irq_domain_free_fwnode?
Thanks
Farhan
> out_airq:
> unregister_adapter_interrupt(&zpci_airq);
> out:
> @@ -541,6 +611,7 @@ int __init zpci_irq_init(void)
>
> void __init zpci_irq_exit(void)
> {
> + struct fwnode_handle *fn;
> unsigned int cpu;
>
> if (irq_delivery == DIRECTED) {
> @@ -549,6 +620,10 @@ void __init zpci_irq_exit(void)
> }
> }
> kfree(zpci_ibv);
> + fn = zpci_msi_parent_domain->fwnode;
> + irq_domain_remove(zpci_msi_parent_domain);
> + irq_domain_free_fwnode(fn);
> +
> if (zpci_sbv)
> airq_iv_release(zpci_sbv);
> unregister_adapter_interrupt(&zpci_airq);
>
Powered by blists - more mailing lists