diff options
author | Gavin Shan <gwshan@linux.vnet.ibm.com> | 2015-02-10 18:20:49 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2015-03-16 19:31:18 -0400 |
commit | 28158cd1b75180343efa7c4d7d2f8e74ccc63b8f (patch) | |
tree | 661cd8462dc72e2a30acb6e5ec5d2dd047d4558a /arch/powerpc/kernel | |
parent | 9eccca0843205f87c00404b663188b88eb248051 (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.c | 55 |
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 | ||
670 | static 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 | |||
698 | static 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; |