aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorGavin Shan <gwshan@linux.vnet.ibm.com>2015-02-10 18:20:49 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2015-03-16 19:31:18 -0400
commit28158cd1b75180343efa7c4d7d2f8e74ccc63b8f (patch)
tree661cd8462dc72e2a30acb6e5ec5d2dd047d4558a /arch/powerpc/kernel
parent9eccca0843205f87c00404b663188b88eb248051 (diff)
powerpc/eeh: Enhance pcibios_set_pcie_reset_state()
Function pcibios_set_pcie_reset_state() is possibly called by pci_reset_function(), on which VFIO infrastructure depends to issue reset. pcibios_set_pcie_reset_state() is issuing reset on the parent PE of the indicated PCI device. The reset causes state lost on all PCI devices except the indicated one as the argument to pcibios_set_pcie_reset_state(). Also, sideband MMIO access from guest when issuing reset would cause unexpected EEH error. For above two issues, the patch applies following enhancements to pcibios_set_pcie_reset_state(): * For all PCI devices except the indicated one, save their state prior to reset and restore state after that. * Explicitly freeze PE prior to reset and unfreeze it after that, in order to avoid unexpected EEH error. Tested-by: Priya M. A <priyama2@in.ibm.com> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/eeh.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 3b2252e7731b..19a897c810be 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -667,6 +667,55 @@ int eeh_pci_enable(struct eeh_pe *pe, int function)
667 return rc; 667 return rc;
668} 668}
669 669
670static void *eeh_disable_and_save_dev_state(void *data, void *userdata)
671{
672 struct eeh_dev *edev = data;
673 struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
674 struct pci_dev *dev = userdata;
675
676 /*
677 * The caller should have disabled and saved the
678 * state for the specified device
679 */
680 if (!pdev || pdev == dev)
681 return NULL;
682
683 /* Ensure we have D0 power state */
684 pci_set_power_state(pdev, PCI_D0);
685
686 /* Save device state */
687 pci_save_state(pdev);
688
689 /*
690 * Disable device to avoid any DMA traffic and
691 * interrupt from the device
692 */
693 pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
694
695 return NULL;
696}
697
698static void *eeh_restore_dev_state(void *data, void *userdata)
699{
700 struct eeh_dev *edev = data;
701 struct device_node *dn = eeh_dev_to_of_node(edev);
702 struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
703 struct pci_dev *dev = userdata;
704
705 if (!pdev)
706 return NULL;
707
708 /* Apply customization from firmware */
709 if (dn && eeh_ops->restore_config)
710 eeh_ops->restore_config(dn);
711
712 /* The caller should restore state for the specified device */
713 if (pdev != dev)
714 pci_save_state(pdev);
715
716 return NULL;
717}
718
670/** 719/**
671 * pcibios_set_pcie_slot_reset - Set PCI-E reset state 720 * pcibios_set_pcie_slot_reset - Set PCI-E reset state
672 * @dev: pci device struct 721 * @dev: pci device struct
@@ -689,13 +738,19 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
689 switch (state) { 738 switch (state) {
690 case pcie_deassert_reset: 739 case pcie_deassert_reset:
691 eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); 740 eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
741 eeh_unfreeze_pe(pe, false);
692 eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); 742 eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED);
743 eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev);
693 break; 744 break;
694 case pcie_hot_reset: 745 case pcie_hot_reset:
746 eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
747 eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
695 eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); 748 eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
696 eeh_ops->reset(pe, EEH_RESET_HOT); 749 eeh_ops->reset(pe, EEH_RESET_HOT);
697 break; 750 break;
698 case pcie_warm_reset: 751 case pcie_warm_reset:
752 eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
753 eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
699 eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED); 754 eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
700 eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL); 755 eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
701 break; 756 break;