The patch exports following functions, which are derived from their original implementation, so that the PCI hotplug logic can reuse the functions to add or remove pci_dn for all device nodes under specified PCI slot. traverse_pci_device_nodes() traverse_pci_devices() add_pci_device_node_info() update_dn_pci_info() remove_pci_device_node_info() newly added The patch also releases eeh_dev when its corresponding pci_dn is released, indicating they have same life cycle. Signed-off-by: Gavin Shan <gwshan@xxxxxxxxxxxxxxxxxx> --- v5: * Derived from PATCH[v4 17/21] * Fixed "assignment in if condition" from checkpatch.pl --- arch/powerpc/include/asm/pci-bridge.h | 4 +- arch/powerpc/include/asm/ppc-pci.h | 7 ++-- arch/powerpc/kernel/pci_dn.c | 71 ++++++++++++++++++++++++++++------ arch/powerpc/platforms/pseries/msi.c | 4 +- arch/powerpc/platforms/pseries/setup.c | 2 +- 5 files changed, 70 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 9a83cdb..d0b4b1a 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -222,7 +222,9 @@ extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev); extern struct pci_dn *add_dev_pci_data(struct pci_dev *pdev); extern void remove_dev_pci_data(struct pci_dev *pdev); -extern void *update_dn_pci_info(struct device_node *dn, void *data); +extern void *add_pci_device_node_info(struct device_node *dn, + struct pci_controller *phb); +extern void remove_pci_device_node_info(struct device_node *dn); static inline int pci_device_from_OF_node(struct device_node *np, u8 *bus, u8 *devfn) diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 7388316..a5b0ea0 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -33,9 +33,10 @@ extern struct pci_dev *isa_bridge_pcidev; /* may be NULL if no ISA bus */ struct device_node; struct pci_dn; -typedef void *(*traverse_func)(struct device_node *me, void *data); -void *traverse_pci_devices(struct device_node *start, traverse_func pre, - void *data); +void *traverse_pci_device_nodes(struct device_node *start, + void *(*fn)(struct device_node *, + struct pci_controller *), + void *data); void *traverse_pci_dn(struct pci_dn *root, void *(*fn)(struct pci_dn *, void *), void *data); diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index d4330d2..f821e96 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -276,13 +276,17 @@ void remove_dev_pci_data(struct pci_dev *pdev) #endif /* CONFIG_PCI_IOV */ } -/* - * Traverse_func that inits the PCI fields of the device node. - * NOTE: this *must* be done before read/write config to the device. +/** + * add_pci_device_node_info - Add pci_dn for PCI device node + * @dn: PCI device node + * @phb: PHB + * + * Add pci_dn for the indicated PCI device node. The newly created + * pci_dn will be put into the child list of the parent device node. */ -void *update_dn_pci_info(struct device_node *dn, void *data) +void *add_pci_device_node_info(struct device_node *dn, + struct pci_controller *phb) { - struct pci_controller *phb = data; const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL); const __be32 *regs; struct device_node *parent; @@ -339,8 +343,48 @@ void *update_dn_pci_info(struct device_node *dn, void *data) return NULL; } +EXPORT_SYMBOL(add_pci_device_node_info); -/* +/** + * remove_pci_device_node_info - Remove pci_dn from PCI device node + * @dn: PCI device node + * + * Remove pci_dn from PCI device node. The pci_dn is also removed + * from the child list of the parent pci_dn. + */ +void remove_pci_device_node_info(struct device_node *np) +{ + struct pci_dn *pdn = np ? PCI_DN(np) : NULL; +#ifdef CONFIG_EEH + struct eeh_dev *edev = pdn_to_eeh_dev(pdn); +#endif + + if (!pdn) + return; + +#ifdef CONFIG_EEH + if (edev) { + pdn->edev = NULL; + kfree(edev); + } +#endif + + BUG_ON(!list_empty(&pdn->child_list)); + list_del(&pdn->list); + if (pdn->parent) + of_node_put(pdn->parent->node); + + np->data = NULL; + kfree(pdn); +} +EXPORT_SYMBOL(remove_pci_device_node_info); + +/** + * traverse_pci_device_nodes - Traverse children of indicated device node + * @start: indicated device node + * @pre: callback + * @data: additional parameter to the callback + * * Traverse a device tree stopping each PCI device in the tree. * This is done depth first. As each node is processed, a "pre" * function is called and the children are processed recursively. @@ -358,8 +402,10 @@ void *update_dn_pci_info(struct device_node *dn, void *data) * one of these nodes we also assume its siblings are non-pci for * performance. */ -void *traverse_pci_devices(struct device_node *start, traverse_func pre, - void *data) +void *traverse_pci_device_nodes(struct device_node *start, + void *(*fn)(struct device_node *, + struct pci_controller *phb), + void *data) { struct device_node *dn, *nextdn; void *ret; @@ -374,7 +420,8 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre, if (classp) class = of_read_number(classp, 1); - if (pre && ((ret = pre(dn, data)) != NULL)) + ret = fn ? fn(dn, data) : NULL; + if (ret != NULL) return ret; /* If we are a PCI bridge, go down */ @@ -395,8 +442,10 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre, nextdn = dn->sibling; } } + return NULL; } +EXPORT_SYMBOL_GPL(traverse_pci_device_nodes); static struct pci_dn *pci_dn_next_one(struct pci_dn *root, struct pci_dn *pdn) @@ -452,7 +501,7 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb) struct pci_dn *pdn; /* PHB nodes themselves must not match */ - update_dn_pci_info(dn, phb); + add_pci_device_node_info(dn, phb); pdn = dn->data; if (pdn) { pdn->devfn = pdn->busno = -1; @@ -462,7 +511,7 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb) } /* Update dn->phb ptrs for new phb and children devices */ - traverse_pci_devices(dn, update_dn_pci_info, phb); + traverse_pci_device_nodes(dn, add_pci_device_node_info, phb); } /** diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index c8d24f9..9ebbd19 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -303,7 +303,7 @@ static int msi_quota_for_device(struct pci_dev *dev, int request) memset(&counts, 0, sizeof(struct msi_counts)); /* Work out how many devices we have below this PE */ - traverse_pci_devices(pe_dn, count_non_bridge_devices, &counts); + traverse_pci_device_nodes(pe_dn, count_non_bridge_devices, &counts); if (counts.num_devices == 0) { pr_err("rtas_msi: found 0 devices under PE for %s\n", @@ -318,7 +318,7 @@ static int msi_quota_for_device(struct pci_dev *dev, int request) /* else, we have some more calculating to do */ counts.requestor = pci_device_to_OF_node(dev); counts.request = request; - traverse_pci_devices(pe_dn, count_spare_msis, &counts); + traverse_pci_device_nodes(pe_dn, count_spare_msis, &counts); /* If the quota isn't an integer multiple of the total, we can * use the remainder as spare MSIs for anyone that wants them. */ diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 92974aa..ed8c894 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -262,7 +262,7 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act case OF_RECONFIG_ATTACH_NODE: pci = np->parent->data; if (pci) - update_dn_pci_info(np, pci->phb); + add_pci_device_node_info(np, pci->phb); break; default: err = NOTIFY_DONE; -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html