On Wed, Nov 04, 2015 at 04:01:50PM +1100, Alexey Kardashevskiy wrote: >On 11/04/2015 02:28 PM, Wei Yang wrote: >>PFs are enumerated on PCI bus, while VFs are created by PF's driver. >> >>In EEH recovery, it has two cases: >>1. Device and driver is EEH aware, error handlers are called. >>2. Device and driver is not EEH aware, un-plug the device and plug it again >>by enumerating it. >> >>The special thing happens on the second case. For a PF, we could use the >>original pci core to enumerate the bus, while for VF we need to record the >>VFs which aer un-plugged then plug it again. >> >>Also The patch caches the VF index in pci_dn, which can be used to >>calculate VF's bus, device and function number. Those information helps to >>locate the VF's PCI device instance when doing hotplug during EEH recovery >>if necessary. >> >>Signed-off-by: Wei Yang <weiy...@linux.vnet.ibm.com> >>--- >> arch/powerpc/include/asm/eeh.h | 7 ++ >> arch/powerpc/include/asm/pci-bridge.h | 1 + >> arch/powerpc/kernel/eeh.c | 8 +++ >> arch/powerpc/kernel/eeh_dev.c | 1 + >> arch/powerpc/kernel/eeh_driver.c | 127 >> +++++++++++++++++++++++++++------- >> arch/powerpc/kernel/eeh_pe.c | 3 +- >> arch/powerpc/kernel/pci_dn.c | 4 +- >> 7 files changed, 123 insertions(+), 28 deletions(-) >> >>diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h >>index 331c856..1f68190 100644 >>--- a/arch/powerpc/include/asm/eeh.h >>+++ b/arch/powerpc/include/asm/eeh.h >>@@ -127,6 +127,11 @@ static inline bool eeh_pe_passed(struct eeh_pe *pe) >> #define EEH_DEV_SYSFS (1 << 9) /* Sysfs created >> */ >> #define EEH_DEV_REMOVED (1 << 10) /* Removed permanently >> */ >> >>+struct eeh_rmv_data { >>+ struct list_head edev_list; >>+ int removed; >>+}; > > >This struct is only used in arch/powerpc/kernel/eeh_driver.c so move it there. >
Will move this in next version. > >>+ >> struct eeh_dev { >> int mode; /* EEH mode */ >> int class_code; /* Class code of the device */ >>@@ -139,9 +144,11 @@ struct eeh_dev { >> int af_cap; /* Saved AF capability */ >> struct eeh_pe *pe; /* Associated PE */ >> struct list_head list; /* Form link list in the PE */ >>+ struct list_head rmv_list; /* Record the removed edev */ >> struct pci_controller *phb; /* Associated PHB */ >> struct pci_dn *pdn; /* Associated PCI device node */ >> struct pci_dev *pdev; /* Associated PCI device */ >>+ bool in_error; /* Error flag for eeh_dev */ >> struct pci_dev *physfn; /* Associated PF PORT */ >> struct pci_bus *bus; /* PCI bus for partial hotplug */ >> }; >>diff --git a/arch/powerpc/include/asm/pci-bridge.h >>b/arch/powerpc/include/asm/pci-bridge.h >>index 9b365d6..533e6e9 100644 >>--- a/arch/powerpc/include/asm/pci-bridge.h >>+++ b/arch/powerpc/include/asm/pci-bridge.h >>@@ -211,6 +211,7 @@ struct pci_dn { >> #define IODA_INVALID_PE (-1) >> #ifdef CONFIG_PPC_POWERNV >> int pe_number; >>+ int vf_index; /* VF index in the PF */ >> #ifdef CONFIG_PCI_IOV >> u16 vfs_expanded; /* number of VFs IOV BAR expanded */ >> u16 num_vfs; /* number of VFs enabled*/ >>diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c >>index 41a4b30..0f36750 100644 >>--- a/arch/powerpc/kernel/eeh.c >>+++ b/arch/powerpc/kernel/eeh.c >>@@ -1245,6 +1245,14 @@ void eeh_remove_device(struct pci_dev *dev) >> * from the parent PE during the BAR resotre. >> */ >> edev->pdev = NULL; >>+ >>+ /* >>+ * The flag "in_error" is used to trace EEH devices for VFs >>+ * in error state or not. It's set in eeh_report_error(). If >>+ * it's not set, eeh_report_{reset,resume}() won't be called >>+ * for the VF EEH device. >>+ */ >>+ edev->in_error = 0; > > >It is a bool, so "= false". > Correct. > >> dev->dev.archdata.edev = NULL; >> if (!(edev->pe->state & EEH_PE_KEEP)) >> eeh_rmv_from_parent_pe(edev); >>diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c >>index aabba94..7815095 100644 >>--- a/arch/powerpc/kernel/eeh_dev.c >>+++ b/arch/powerpc/kernel/eeh_dev.c >>@@ -67,6 +67,7 @@ void *eeh_dev_init(struct pci_dn *pdn, void *data) >> edev->pdn = pdn; >> edev->phb = phb; >> INIT_LIST_HEAD(&edev->list); >>+ INIT_LIST_HEAD(&edev->rmv_list); >> >> return NULL; >> } >>diff --git a/arch/powerpc/kernel/eeh_driver.c >>b/arch/powerpc/kernel/eeh_driver.c >>index 89eb4bc..06d20d6 100644 >>--- a/arch/powerpc/kernel/eeh_driver.c >>+++ b/arch/powerpc/kernel/eeh_driver.c >>@@ -211,6 +211,7 @@ static void *eeh_report_error(void *data, void *userdata) >> if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; >> if (*res == PCI_ERS_RESULT_NONE) *res = rc; >> >>+ edev->in_error = true; >> eeh_pcid_put(dev); >> return NULL; >> } >>@@ -282,7 +283,8 @@ static void *eeh_report_reset(void *data, void *userdata) >> >> if (!driver->err_handler || >> !driver->err_handler->slot_reset || >>- (edev->mode & EEH_DEV_NO_HANDLER)) { >>+ (edev->mode & EEH_DEV_NO_HANDLER) || >>+ (!edev->in_error)) { >> eeh_pcid_put(dev); >> return NULL; >> } >>@@ -326,6 +328,7 @@ static void *eeh_report_resume(void *data, void *userdata) >> { >> struct eeh_dev *edev = (struct eeh_dev *)data; >> struct pci_dev *dev = eeh_dev_to_pci_dev(edev); >>+ bool was_in_error; >> struct pci_driver *driver; >> >> if (!dev || eeh_dev_removed(edev)) >>@@ -335,11 +338,13 @@ static void *eeh_report_resume(void *data, void >>*userdata) >> driver = eeh_pcid_get(dev); >> if (!driver) return NULL; >> >>+ was_in_error = edev->in_error; >>+ edev->in_error = false; >> eeh_enable_irq(dev); >> >> if (!driver->err_handler || >> !driver->err_handler->resume || >>- (edev->mode & EEH_DEV_NO_HANDLER)) { >>+ (edev->mode & EEH_DEV_NO_HANDLER) || !was_in_error) { >> edev->mode &= ~EEH_DEV_NO_HANDLER; >> eeh_pcid_put(dev); >> return NULL; >>@@ -386,12 +391,39 @@ static void *eeh_report_failure(void *data, void >>*userdata) >> return NULL; >> } >> >>+static void *eeh_add_virt_device(void *data, void *userdata) >>+{ >>+ struct pci_driver *driver; >>+ struct eeh_dev *edev = (struct eeh_dev *)data; >>+ struct pci_dev *dev = eeh_dev_to_pci_dev(edev); >>+ struct pci_dn *pdn = eeh_dev_to_pdn(edev); >>+ >>+ if (!(edev->physfn)) { >>+ pr_warn("%s: EEH dev %04x:%02x:%02x.%01x not for VF\n", >>+ __func__, edev->phb->global_number, pdn->busno, >>+ PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); >>+ return NULL; >>+ } >>+ >>+ driver = eeh_pcid_get(dev); >>+ if (driver) { >>+ eeh_pcid_put(dev); >>+ if (driver->err_handler) >>+ return NULL; >>+ } >>+ >>+ pci_iov_virtfn_add(edev->physfn, pdn->vf_index, 0); >>+ return NULL; >>+} >>+ >> static void *eeh_rmv_device(void *data, void *userdata) >> { >> struct pci_driver *driver; >> struct eeh_dev *edev = (struct eeh_dev *)data; >> struct pci_dev *dev = eeh_dev_to_pci_dev(edev); >>- int *removed = (int *)userdata; >>+ struct eeh_rmv_data *rmv_data = (struct eeh_rmv_data *)userdata; >>+ int *removed = rmv_data ? &rmv_data->removed : NULL; >>+ struct pci_dn *pdn = eeh_dev_to_pdn(edev); >> >> /* >> * Actually, we should remove the PCI bridges as well. >>@@ -416,7 +448,7 @@ static void *eeh_rmv_device(void *data, void *userdata) >> driver = eeh_pcid_get(dev); >> if (driver) { >> eeh_pcid_put(dev); >>- if (driver->err_handler) >>+ if (removed && driver->err_handler) >> return NULL; >> } >> >>@@ -425,11 +457,26 @@ static void *eeh_rmv_device(void *data, void *userdata) >> pci_name(dev)); >> edev->bus = dev->bus; >> edev->mode |= EEH_DEV_DISCONNECTED; >>- (*removed)++; >>+ if (removed) >>+ (*removed)++; >> >>- pci_lock_rescan_remove(); >>- pci_stop_and_remove_bus_device(dev); >>- pci_unlock_rescan_remove(); >>+ if (edev->physfn) { >>+ pci_iov_virtfn_remove(edev->physfn, pdn->vf_index, 0); >>+ edev->pdev = NULL; >>+ >>+ /* >>+ * We have to set the VF PE number to invalid one, which is >>+ * required to plug the VF successfully. >>+ */ >>+ pdn->pe_number = IODA_INVALID_PE; >>+ >>+ if (rmv_data) >>+ list_add(&edev->rmv_list, &rmv_data->edev_list); >>+ } else { >>+ pci_lock_rescan_remove(); >>+ pci_stop_and_remove_bus_device(dev); >>+ pci_unlock_rescan_remove(); >>+ } >> >> return NULL; >> } >>@@ -543,11 +590,13 @@ int eeh_pe_reset_and_recover(struct eeh_pe *pe) >> * During the reset, udev might be invoked because those affected >> * PCI devices will be removed and then added. >> */ >>-static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus) >>+static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus, >>+ struct eeh_rmv_data *rmv_data) >> { >> struct pci_bus *frozen_bus = eeh_pe_bus_get(pe); >> struct timeval tstamp; >>- int cnt, rc, removed = 0; >>+ int cnt, rc; >>+ struct eeh_dev *edev; >> >> /* pcibios will clear the counter; save the value */ >> cnt = pe->freeze_count; >>@@ -561,12 +610,15 @@ static int eeh_reset_device(struct eeh_pe *pe, struct >>pci_bus *bus) >> */ >> eeh_pe_state_mark(pe, EEH_PE_KEEP); >> if (bus) { >>- pci_lock_rescan_remove(); >>- pcibios_remove_pci_devices(bus); >>- pci_unlock_rescan_remove(); >>- } else if (frozen_bus) { >>- eeh_pe_dev_traverse(pe, eeh_rmv_device, &removed); >>- } >>+ if (pe->type & EEH_PE_VF) { >>+ eeh_pe_dev_traverse(pe, eeh_rmv_device, NULL); >>+ } else { >>+ pci_lock_rescan_remove(); >>+ pcibios_remove_pci_devices(bus); >>+ pci_unlock_rescan_remove(); >>+ } >>+ } else if (frozen_bus) >>+ eeh_pe_dev_traverse(pe, eeh_rmv_device, rmv_data); >> >> /* >> * Reset the pci controller. (Asserts RST#; resets config space). >>@@ -607,14 +659,22 @@ static int eeh_reset_device(struct eeh_pe *pe, struct >>pci_bus *bus) >> * PE. We should disconnect it so the binding can be >> * rebuilt when adding PCI devices. >> */ >>+ edev = list_first_entry(&pe->edevs, struct eeh_dev, list); >> eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL); >>- pcibios_add_pci_devices(bus); >>- } else if (frozen_bus && removed) { >>+ if (pe->type & EEH_PE_VF) >>+ eeh_add_virt_device(edev, NULL); >>+ else >>+ pcibios_add_pci_devices(bus); >>+ } else if (frozen_bus && rmv_data->removed) { >> pr_info("EEH: Sleep 5s ahead of partial hotplug\n"); >> ssleep(5); >> >>+ edev = list_first_entry(&pe->edevs, struct eeh_dev, list); >> eeh_pe_traverse(pe, eeh_pe_detach_dev, NULL); >>- pcibios_add_pci_devices(frozen_bus); >>+ if (pe->type & EEH_PE_VF) >>+ eeh_add_virt_device(edev, NULL); >>+ else >>+ pcibios_add_pci_devices(frozen_bus); >> } >> eeh_pe_state_clear(pe, EEH_PE_KEEP); >> >>@@ -633,8 +693,10 @@ static int eeh_reset_device(struct eeh_pe *pe, struct >>pci_bus *bus) >> static void eeh_handle_normal_event(struct eeh_pe *pe) >> { >> struct pci_bus *frozen_bus; >>+ struct eeh_dev *edev, *tmp; >> int rc = 0; >> enum pci_ers_result result = PCI_ERS_RESULT_NONE; >>+ struct eeh_rmv_data rmv_data = {LIST_HEAD_INIT(rmv_data.edev_list), 0}; >> >> frozen_bus = eeh_pe_bus_get(pe); >> if (!frozen_bus) { >>@@ -681,7 +743,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) >> */ >> if (result == PCI_ERS_RESULT_NONE) { >> pr_info("EEH: Reset with hotplug activity\n"); >>- rc = eeh_reset_device(pe, frozen_bus); >>+ rc = eeh_reset_device(pe, frozen_bus, NULL); >> if (rc) { >> pr_warn("%s: Unable to reset, err=%d\n", >> __func__, rc); >>@@ -733,7 +795,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) >> /* If any device called out for a reset, then reset the slot */ >> if (result == PCI_ERS_RESULT_NEED_RESET) { >> pr_info("EEH: Reset without hotplug activity\n"); >>- rc = eeh_reset_device(pe, NULL); >>+ rc = eeh_reset_device(pe, NULL, &rmv_data); >> if (rc) { >> pr_warn("%s: Cannot reset, err=%d\n", >> __func__, rc); >>@@ -753,6 +815,15 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) >> goto hard_fail; >> } >> >>+ /* >>+ * For those hot removed VFs, we should add back them after PF get >>+ * recovered properly. >>+ */ >>+ list_for_each_entry_safe(edev, tmp, &rmv_data.edev_list, rmv_list) { >>+ eeh_add_virt_device(edev, NULL); >>+ list_del(&edev->rmv_list); >>+ } >>+ >> /* Tell all device drivers that they can resume operations */ >> pr_info("EEH: Notify device driver to resume\n"); >> eeh_pe_dev_traverse(pe, eeh_report_resume, NULL); >>@@ -792,11 +863,15 @@ perm_error: >> * the their PCI config any more. >> */ >> if (frozen_bus) { >>- eeh_pe_dev_mode_mark(pe, EEH_DEV_REMOVED); >>- >>- pci_lock_rescan_remove(); >>- pcibios_remove_pci_devices(frozen_bus); >>- pci_unlock_rescan_remove(); >>+ if (pe->type & EEH_PE_VF) { >>+ eeh_pe_dev_traverse(pe, eeh_rmv_device, NULL); >>+ eeh_pe_dev_mode_mark(pe, EEH_DEV_REMOVED); >>+ } else { >>+ eeh_pe_dev_mode_mark(pe, EEH_DEV_REMOVED); >>+ pci_lock_rescan_remove(); >>+ pcibios_remove_pci_devices(frozen_bus); >>+ pci_unlock_rescan_remove(); >>+ } >> } >> } >> >>diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c >>index 29240ad..b7facb9 100644 >>--- a/arch/powerpc/kernel/eeh_pe.c >>+++ b/arch/powerpc/kernel/eeh_pe.c >>@@ -936,7 +936,8 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe) >> if (pe->type & EEH_PE_PHB) { >> bus = pe->phb->bus; >> } else if (pe->type & EEH_PE_BUS || >>- pe->type & EEH_PE_DEVICE) { >>+ pe->type & EEH_PE_DEVICE || >>+ pe->type & EEH_PE_VF) { >> if (pe->bus) { >> bus = pe->bus; >> goto out; >>diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c >>index 5091b05..9e9cb16 100644 >>--- a/arch/powerpc/kernel/pci_dn.c >>+++ b/arch/powerpc/kernel/pci_dn.c >>@@ -139,6 +139,7 @@ struct pci_dn *pci_get_pdn(struct pci_dev *pdev) >> #ifdef CONFIG_PCI_IOV >> static struct pci_dn *add_one_dev_pci_data(struct pci_dn *parent, >> struct pci_dev *pdev, >>+ int vf_index, >> int busno, int devfn) >> { >> struct pci_dn *pdn; >>@@ -158,6 +159,7 @@ static struct pci_dn *add_one_dev_pci_data(struct pci_dn >>*parent, >> pdn->busno = busno; >> pdn->devfn = devfn; >> #ifdef CONFIG_PPC_POWERNV >>+ pdn->vf_index = vf_index; >> pdn->pe_number = IODA_INVALID_PE; >> #endif >> INIT_LIST_HEAD(&pdn->child_list); >>@@ -198,7 +200,7 @@ struct pci_dn *add_dev_pci_data(struct pci_dev *pdev) >> return NULL; >> >> for (i = 0; i < pci_sriov_get_totalvfs(pdev); i++) { >>- pdn = add_one_dev_pci_data(parent, NULL, >>+ pdn = add_one_dev_pci_data(parent, NULL, i, >> pci_iov_virtfn_bus(pdev, i), >> pci_iov_virtfn_devfn(pdev, i)); >> if (!pdn) { >> > > >-- >Alexey -- Richard Yang Help you, Help me _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev