]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
irqdomain/treewide: Free firmware node after domain removal
authorJon Derrick <jonathan.derrick@intel.com>
Tue, 21 Jul 2020 20:26:09 +0000 (14:26 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 19 Aug 2020 06:16:27 +0000 (08:16 +0200)
commit ec0160891e387f4771f953b888b1fe951398e5d9 upstream.

Commit 711419e504eb ("irqdomain: Add the missing assignment of
domain->fwnode for named fwnode") unintentionally caused a dangling pointer
page fault issue on firmware nodes that were freed after IRQ domain
allocation. Commit e3beca48a45b fixed that dangling pointer issue by only
freeing the firmware node after an IRQ domain allocation failure. That fix
no longer frees the firmware node immediately, but leaves the firmware node
allocated after the domain is removed.

The firmware node must be kept around through irq_domain_remove, but should be
freed it afterwards.

Add the missing free operations after domain removal where where appropriate.

Fixes: e3beca48a45b ("irqdomain/treewide: Keep firmware node unconditionally allocated")
Signed-off-by: Jon Derrick <jonathan.derrick@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com> # drivers/pci
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/1595363169-7157-1-git-send-email-jonathan.derrick@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/mips/pci/pci-xtalk-bridge.c
arch/x86/kernel/apic/io_apic.c
drivers/iommu/intel_irq_remapping.c
drivers/pci/controller/vmd.c

index 6ce76b18186e54a773a14973cd5bb11a9c9af1e2..c4b1c6cf26606c6a2a47023a0182f44c7daf5e37 100644 (file)
@@ -539,6 +539,7 @@ err_free_resource:
        pci_free_resource_list(&host->windows);
 err_remove_domain:
        irq_domain_remove(domain);
+       irq_domain_free_fwnode(fn);
        return err;
 }
 
@@ -546,8 +547,10 @@ static int bridge_remove(struct platform_device *pdev)
 {
        struct pci_bus *bus = platform_get_drvdata(pdev);
        struct bridge_controller *bc = BRIDGE_CONTROLLER(bus);
+       struct fwnode_handle *fn = bc->domain->fwnode;
 
        irq_domain_remove(bc->domain);
+       irq_domain_free_fwnode(fn);
        pci_lock_rescan_remove();
        pci_stop_root_bus(bus);
        pci_remove_root_bus(bus);
index 16699101fd2fe35ee41d9d2f4dda76cf1ac0ae4f..ea6d9da9b09413f114f818f0f9a59dac0626c2be 100644 (file)
@@ -2348,8 +2348,13 @@ static int mp_irqdomain_create(int ioapic)
 
 static void ioapic_destroy_irqdomain(int idx)
 {
+       struct ioapic_domain_cfg *cfg = &ioapics[idx].irqdomain_cfg;
+       struct fwnode_handle *fn = ioapics[idx].irqdomain->fwnode;
+
        if (ioapics[idx].irqdomain) {
                irq_domain_remove(ioapics[idx].irqdomain);
+               if (!cfg->dev)
+                       irq_domain_free_fwnode(fn);
                ioapics[idx].irqdomain = NULL;
        }
 }
index 982d796b686b83e1dbe8748568627428aaa39499..6bfb283e6f287b651f35776be3aecb9e11f6c198 100644 (file)
@@ -628,13 +628,21 @@ out_free_table:
 
 static void intel_teardown_irq_remapping(struct intel_iommu *iommu)
 {
+       struct fwnode_handle *fn;
+
        if (iommu && iommu->ir_table) {
                if (iommu->ir_msi_domain) {
+                       fn = iommu->ir_msi_domain->fwnode;
+
                        irq_domain_remove(iommu->ir_msi_domain);
+                       irq_domain_free_fwnode(fn);
                        iommu->ir_msi_domain = NULL;
                }
                if (iommu->ir_domain) {
+                       fn = iommu->ir_domain->fwnode;
+
                        irq_domain_remove(iommu->ir_domain);
+                       irq_domain_free_fwnode(fn);
                        iommu->ir_domain = NULL;
                }
                free_pages((unsigned long)iommu->ir_table->base,
index 7c24c0aedad4ab0260267f9f48f2b47b485a4c5d..9966dcf1d112d9bb33df0b34e86ae4f7f1a7e3d6 100644 (file)
@@ -694,6 +694,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
        if (!vmd->bus) {
                pci_free_resource_list(&resources);
                irq_domain_remove(vmd->irq_domain);
+               irq_domain_free_fwnode(fn);
                return -ENODEV;
        }
 
@@ -808,6 +809,7 @@ static void vmd_cleanup_srcu(struct vmd_dev *vmd)
 static void vmd_remove(struct pci_dev *dev)
 {
        struct vmd_dev *vmd = pci_get_drvdata(dev);
+       struct fwnode_handle *fn = vmd->irq_domain->fwnode;
 
        sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
        pci_stop_root_bus(vmd->bus);
@@ -816,6 +818,7 @@ static void vmd_remove(struct pci_dev *dev)
        vmd_teardown_dma_ops(vmd);
        vmd_detach_resources(vmd);
        irq_domain_remove(vmd->irq_domain);
+       irq_domain_free_fwnode(fn);
 }
 
 #ifdef CONFIG_PM_SLEEP