arm64: PCI: Manage controller-specific data on per-controller basis
authorTomasz Nowicki <tn@semihalf.com>
Tue, 4 Apr 2017 19:32:08 +0000 (19:32 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 12 Apr 2017 10:41:17 +0000 (12:41 +0200)
[ Upstream commit 093d24a204425f71f4f106b7e62c8df4b456e1cc ]

Currently we use one shared global acpi_pci_root_ops structure to keep
controller-specific ops. We pass its pointer to acpi_pci_root_create() and
associate it with a host bridge instance for good.  Such a design implies
serious drawback. Any potential manipulation on the single system-wide
acpi_pci_root_ops leads to kernel crash. The structure content is not
really changing even across multiple host bridges creation; thus it was not
an issue so far.

In preparation for adding ECAM quirks mechanism (where controller-specific
PCI ops may be different for each host bridge) allocate new
acpi_pci_root_ops and fill in with data for each bridge. Now it is safe to
have different controller-specific info. As a consequence free
acpi_pci_root_ops when host bridge is released.

No functional changes in this patch.

Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/arm64/kernel/pci.c

index acf3872..95e0bb4 100644 (file)
@@ -159,33 +159,36 @@ static void pci_acpi_generic_release_info(struct acpi_pci_root_info *ci)
 
        ri = container_of(ci, struct acpi_pci_generic_root_info, common);
        pci_ecam_free(ri->cfg);
+       kfree(ci->ops);
        kfree(ri);
 }
 
-static struct acpi_pci_root_ops acpi_pci_root_ops = {
-       .release_info = pci_acpi_generic_release_info,
-};
-
 /* Interface called from ACPI code to setup PCI host controller */
 struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
 {
        int node = acpi_get_node(root->device->handle);
        struct acpi_pci_generic_root_info *ri;
        struct pci_bus *bus, *child;
+       struct acpi_pci_root_ops *root_ops;
 
        ri = kzalloc_node(sizeof(*ri), GFP_KERNEL, node);
        if (!ri)
                return NULL;
 
+       root_ops = kzalloc_node(sizeof(*root_ops), GFP_KERNEL, node);
+       if (!root_ops)
+               return NULL;
+
        ri->cfg = pci_acpi_setup_ecam_mapping(root);
        if (!ri->cfg) {
                kfree(ri);
+               kfree(root_ops);
                return NULL;
        }
 
-       acpi_pci_root_ops.pci_ops = &ri->cfg->ops->pci_ops;
-       bus = acpi_pci_root_create(root, &acpi_pci_root_ops, &ri->common,
-                                  ri->cfg);
+       root_ops->release_info = pci_acpi_generic_release_info;
+       root_ops->pci_ops = &ri->cfg->ops->pci_ops;
+       bus = acpi_pci_root_create(root, root_ops, &ri->common, ri->cfg);
        if (!bus)
                return NULL;