This patch employs the refcount in struct pci_controller to track the references from PCI devices and buses (struct pci_dev/pci_bus).
In order to do that without modifying any PCI scan/probe approach (e.g., PCI_PROBE_DEVTREE and PCI_PROBE_NORMAL), it leverages some of the PCI arch-specific callback: pci_(add|release)_device() and pci_(add|remove)_bus(). (a small change is required for PCI_PROBE_DEVTREE, which makes it consistent with PCI_PROBE_NORMAL - the pci_dev should inherit the parent pci_bus's phb pointer - see pci_setup_device() in probe.c) This also has the advantage that locking for kref_(get|put)() is satisfied by the 'pci_rescan_remove_lock' mutex, which is normal practice for usage of the PCI subsystem - thus already in place. More details added in comment on pcibios_release_device(). Signed-off-by: Mauricio Faria de Oliveira <mauri...@linux.vnet.ibm.com> --- arch/powerpc/include/asm/pci-bridge.h | 4 ++-- arch/powerpc/kernel/pci-common.c | 25 +++++++++++++++++++++++++ arch/powerpc/kernel/pci-hotplug.c | 29 +++++++++++++++++++++++++++++ arch/powerpc/kernel/pci_of_scan.c | 1 + 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 6fde0a9..d10eee3 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -131,8 +131,8 @@ struct pci_controller { /* * Reference counting for the structures: - * - TODO pci_dev - * - TODO pci_bus + * - pci_dev + * - pci_bus * - TODO pci_dn * - TODO eeh_pe * - TODO eeh_dev diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 29b37d3..c55e9c0 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1047,6 +1047,17 @@ static void pcibios_setup_device(struct pci_dev *dev) int pcibios_add_device(struct pci_dev *dev) { + struct pci_controller *phb = pci_bus_to_host(dev->bus); + + pr_debug("PCI %s, pci_dev %p, phb %p\n", dev_name(&dev->dev), dev, phb); + + if (!phb) + pr_warn("%s: PCI device %s has null PHB; refcount bug!", + __func__, dev_name(&dev->dev)); /* WARN_ON ahead */ + + /* locking: see comment on pcibios_release_device(). */ + controller_get(phb); + /* * We can only call pcibios_setup_device() after bus setup is complete, * since some of the platform specific DMA setup code depends on it. @@ -1062,6 +1073,20 @@ int pcibios_add_device(struct pci_dev *dev) return 0; } +void pcibios_add_bus(struct pci_bus *bus) +{ + struct pci_controller *phb = pci_bus_to_host(bus); + + pr_debug("PCI %s, pci_bus %p, phb %p\n", dev_name(&bus->dev), bus, phb); + + if (!phb) + pr_warn("%s: PCI bus %s has null PHB; refcount bug!", + __func__, dev_name(&bus->dev)); /* WARN_ON ahead */ + + /* locking: see comment on pcibios_release_device(). */ + controller_get(phb); +} + void pcibios_setup_bus_devices(struct pci_bus *bus) { struct pci_dev *dev; diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 2d71269..24d1a2a 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -55,15 +55,44 @@ EXPORT_SYMBOL_GPL(pci_find_bus_by_node); * @dev: PCI device * * The function is called before releasing the indicated PCI device. + * + * The locking for kref_get() and kref_put() of the PHB/pci_controller + * in pcibios_(add|release)_device() and pcibios_(add|remove)_bus() is + * satisfied by the pci_rescan_remove_lock mutex (required for rescan/ + * remove paths of PCI devices/buses; the scan path doesn't require it, + * as there is only addition of devices/buses - no removal at all.) */ void pcibios_release_device(struct pci_dev *dev) { struct pci_controller *phb = pci_bus_to_host(dev->bus); + pr_debug("PCI %s, pci_dev %p, phb %p\n", dev_name(&dev->dev), dev, phb); + eeh_remove_device(dev); if (phb->controller_ops.release_device) phb->controller_ops.release_device(dev); + + if (unlikely(!phb)) + pr_warn("%s: PCI device %s has null PHB; refcount bug!", + __func__, dev_name(&dev->dev)); /* WARN_ON ahead */ + + /* locking: see comment on pcibios_release_device(). */ + controller_put(phb); +} + +void pcibios_remove_bus(struct pci_bus *bus) +{ + struct pci_controller *phb = pci_bus_to_host(bus); + + pr_debug("PCI %s, pci_bus %p, phb %p\n", dev_name(&bus->dev), bus, phb); + + if (unlikely(!phb)) + pr_warn("%s: PCI bus %s has null PHB; refcount bug!", + __func__, dev_name(&bus->dev)); /* WARN_ON ahead */ + + /* locking: see comment on pcibios_release_device(). */ + controller_put(phb); } /** diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 526ac67..e6f0deb 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -142,6 +142,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, dev->devfn = devfn; dev->multifunction = 0; /* maybe a lie? */ dev->needs_freset = 0; /* pcie fundamental reset required */ + dev->sysdata = bus->sysdata; /* inherit bus's phb for phb->refcount */ set_pcie_port_type(dev); pci_dev_assign_slot(dev); -- 1.8.3.1