Skip to content

Commit

Permalink
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git…
Browse files Browse the repository at this point in the history
…/benh/powerpc

Pull powerpc fixes from Ben Herrenschmidt:
 "Here is a series of powerpc fixes.  It's a bit big, mostly because of
  the series of 11 "EEH" patches from Gavin.  The EEH (Our IBM specific
  PCI/PCIe Enhanced Error Handling) code had been rotting for a while
  and this merge window saw a significant rework & fixing of it by Gavin
  Shan.

  However, that wasn't complete and left some open issues.  There were
  still a few corner cases that didn't work properly, for example in
  relation to hotplug and devices without explicit error handlers.  We
  had some patches but they weren't quite good enough yet so I left them
  off the 3.11 merge window.

  Gavin since then fixed it all up, we ran quite a few rounds of testing
  and it seems fairly solid (at least probably more than it has ever
  been).  This should probably have made -rc1 but both Gavin and I took
  some vacation so it had to wait for -rc2.

  The rest is more bug fixes, mostly to new features recently added, for
  example, we missed the cpu table entry for one of the two models of P8
  (we didn't realize they had different PVR [Processor Version Register]
  values), some module CRC issues, etc..."

* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (23 commits)
  powerpc/perf: BHRB filter configuration should follow the task
  powerpc/perf: Ignore separate BHRB privilege state filter request
  powerpc/powernv: Mark pnv_pci_init_ioda2_phb() as __init
  powerpc/mm: Use the correct SLB(LLP) encoding in tlbie instruction
  powerpc/mm: Fix fallthrough bug in hpte_decode
  powerpc/pseries: Fix a typo in pSeries_lpar_hpte_insert()
  powerpc/eeh: Introdce flag to protect sysfs
  powerpc/eeh: Fix unbalanced enable for IRQ
  powerpc/eeh: Don't use pci_dev during BAR restore
  powerpc/eeh: Use partial hotplug for EEH unaware drivers
  powerpc/pci: Partial tree hotplug support
  powerpc/eeh: Use safe list traversal when walking EEH devices
  powerpc/eeh: Keep PE during hotplug
  powerpc/pci/hotplug: Don't need to remove from EEH cache twice
  powerpc/pci: Override pcibios_release_device()
  powerpc/eeh: Export functions for hotplug
  powerpc/eeh: Remove reference to PCI device
  powerpc: Fix the corrupt r3 error during MCE handling.
  powerpc/perf: Set PPC_FEATURE2_EBB when we register the power8 PMU
  powerpc/pseries: Drop "select HOTPLUG"
  ...
  • Loading branch information
torvalds committed Jul 24, 2013
2 parents b48a97b + ff3d79d commit 07bc9dc
Show file tree
Hide file tree
Showing 26 changed files with 390 additions and 169 deletions.
30 changes: 24 additions & 6 deletions arch/powerpc/include/asm/eeh.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ struct device_node;
#define EEH_PE_RECOVERING (1 << 1) /* Recovering PE */
#define EEH_PE_PHB_DEAD (1 << 2) /* Dead PHB */

#define EEH_PE_KEEP (1 << 8) /* Keep PE on hotplug */

struct eeh_pe {
int type; /* PE type: PHB/Bus/Device */
int state; /* PE EEH dependent mode */
Expand All @@ -72,8 +74,8 @@ struct eeh_pe {
struct list_head child; /* Child PEs */
};

#define eeh_pe_for_each_dev(pe, edev) \
list_for_each_entry(edev, &pe->edevs, list)
#define eeh_pe_for_each_dev(pe, edev, tmp) \
list_for_each_entry_safe(edev, tmp, &pe->edevs, list)

/*
* The struct is used to trace EEH state for the associated
Expand All @@ -82,19 +84,27 @@ struct eeh_pe {
* another tree except the currently existing tree of PCI
* buses and PCI devices
*/
#define EEH_DEV_IRQ_DISABLED (1<<0) /* Interrupt disabled */
#define EEH_DEV_BRIDGE (1 << 0) /* PCI bridge */
#define EEH_DEV_ROOT_PORT (1 << 1) /* PCIe root port */
#define EEH_DEV_DS_PORT (1 << 2) /* Downstream port */
#define EEH_DEV_IRQ_DISABLED (1 << 3) /* Interrupt disabled */
#define EEH_DEV_DISCONNECTED (1 << 4) /* Removing from PE */

#define EEH_DEV_SYSFS (1 << 8) /* Sysfs created */

struct eeh_dev {
int mode; /* EEH mode */
int class_code; /* Class code of the device */
int config_addr; /* Config address */
int pe_config_addr; /* PE config address */
u32 config_space[16]; /* Saved PCI config space */
u8 pcie_cap; /* Saved PCIe capability */
struct eeh_pe *pe; /* Associated PE */
struct list_head list; /* Form link list in the PE */
struct pci_controller *phb; /* Associated PHB */
struct device_node *dn; /* Associated device node */
struct pci_dev *pdev; /* Associated PCI device */
struct pci_bus *bus; /* PCI bus for partial hotplug */
};

static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev)
Expand Down Expand Up @@ -193,8 +203,10 @@ int eeh_phb_pe_create(struct pci_controller *phb);
struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb);
struct eeh_pe *eeh_pe_get(struct eeh_dev *edev);
int eeh_add_to_parent_pe(struct eeh_dev *edev);
int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe);
int eeh_rmv_from_parent_pe(struct eeh_dev *edev);
void eeh_pe_update_time_stamp(struct eeh_pe *pe);
void *eeh_pe_traverse(struct eeh_pe *root,
eeh_traverse_func fn, void *flag);
void *eeh_pe_dev_traverse(struct eeh_pe *root,
eeh_traverse_func fn, void *flag);
void eeh_pe_restore_bars(struct eeh_pe *pe);
Expand All @@ -209,10 +221,12 @@ unsigned long eeh_check_failure(const volatile void __iomem *token,
unsigned long val);
int eeh_dev_check_failure(struct eeh_dev *edev);
void eeh_addr_cache_build(void);
void eeh_add_device_early(struct device_node *);
void eeh_add_device_tree_early(struct device_node *);
void eeh_add_device_late(struct pci_dev *);
void eeh_add_device_tree_late(struct pci_bus *);
void eeh_add_sysfs_files(struct pci_bus *);
void eeh_remove_bus_device(struct pci_dev *, int);
void eeh_remove_device(struct pci_dev *);

/**
* EEH_POSSIBLE_ERROR() -- test for possible MMIO failure.
Expand Down Expand Up @@ -252,13 +266,17 @@ static inline unsigned long eeh_check_failure(const volatile void __iomem *token

static inline void eeh_addr_cache_build(void) { }

static inline void eeh_add_device_early(struct device_node *dn) { }

static inline void eeh_add_device_tree_early(struct device_node *dn) { }

static inline void eeh_add_device_late(struct pci_dev *dev) { }

static inline void eeh_add_device_tree_late(struct pci_bus *bus) { }

static inline void eeh_add_sysfs_files(struct pci_bus *bus) { }

static inline void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) { }
static inline void eeh_remove_device(struct pci_dev *dev) { }

#define EEH_POSSIBLE_ERROR(val, type) (0)
#define EEH_IO_ERROR_VALUE(size) (-1UL)
Expand Down
7 changes: 4 additions & 3 deletions arch/powerpc/include/asm/hw_irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,11 @@ static inline bool arch_irqs_disabled(void)
#endif

#define hard_irq_disable() do { \
u8 _was_enabled = get_paca()->soft_enabled; \
u8 _was_enabled; \
__hard_irq_disable(); \
get_paca()->soft_enabled = 0; \
get_paca()->irq_happened |= PACA_IRQ_HARD_DIS; \
_was_enabled = local_paca->soft_enabled; \
local_paca->soft_enabled = 0; \
local_paca->irq_happened |= PACA_IRQ_HARD_DIS; \
if (_was_enabled) \
trace_hardirqs_off(); \
} while(0)
Expand Down
5 changes: 2 additions & 3 deletions arch/powerpc/include/asm/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,9 @@ struct exception_table_entry;
void sort_ex_table(struct exception_table_entry *start,
struct exception_table_entry *finish);

#ifdef CONFIG_MODVERSIONS
#if defined(CONFIG_MODVERSIONS) && defined(CONFIG_PPC64)
#define ARCH_RELOCATES_KCRCTAB

extern const unsigned long reloc_start[];
#define reloc_start PHYSICAL_START
#endif
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_MODULE_H */
1 change: 0 additions & 1 deletion arch/powerpc/include/asm/pci-bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn)
extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn);

/** Remove all of the PCI devices under this bus */
extern void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe);
extern void pcibios_remove_pci_devices(struct pci_bus *bus);

/** Discover new pci devices under this bus, and add them */
Expand Down
3 changes: 2 additions & 1 deletion arch/powerpc/include/asm/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,8 @@
#define PVR_970MP 0x0044
#define PVR_970GX 0x0045
#define PVR_POWER7p 0x004A
#define PVR_POWER8 0x004B
#define PVR_POWER8E 0x004B
#define PVR_POWER8 0x004D
#define PVR_BE 0x0070
#define PVR_PA6T 0x0090

Expand Down
20 changes: 19 additions & 1 deletion arch/powerpc/kernel/cputable.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,27 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_restore = __restore_cpu_power7,
.platform = "power7+",
},
{ /* Power8 */
{ /* Power8E */
.pvr_mask = 0xffff0000,
.pvr_value = 0x004b0000,
.cpu_name = "POWER8E (raw)",
.cpu_features = CPU_FTRS_POWER8,
.cpu_user_features = COMMON_USER_POWER8,
.cpu_user_features2 = COMMON_USER2_POWER8,
.mmu_features = MMU_FTRS_POWER8,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
.pmc_type = PPC_PMC_IBM,
.oprofile_cpu_type = "ppc64/power8",
.oprofile_type = PPC_OPROFILE_INVALID,
.cpu_setup = __setup_cpu_power8,
.cpu_restore = __restore_cpu_power8,
.platform = "power8",
},
{ /* Power8 */
.pvr_mask = 0xffff0000,
.pvr_value = 0x004d0000,
.cpu_name = "POWER8 (raw)",
.cpu_features = CPU_FTRS_POWER8,
.cpu_user_features = COMMON_USER_POWER8,
Expand Down
70 changes: 34 additions & 36 deletions arch/powerpc/kernel/eeh.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
{
size_t loglen = 0;
struct eeh_dev *edev;
struct eeh_dev *edev, *tmp;
bool valid_cfg_log = true;

/*
Expand All @@ -251,7 +251,7 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
eeh_pe_restore_bars(pe);

pci_regs_buf[0] = 0;
eeh_pe_for_each_dev(pe, edev) {
eeh_pe_for_each_dev(pe, edev, tmp) {
loglen += eeh_gather_pci_data(edev, pci_regs_buf + loglen,
EEH_PCI_REGS_LOG_LEN - loglen);
}
Expand Down Expand Up @@ -499,8 +499,6 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
}

eeh_dev_check_failure(edev);

pci_dev_put(eeh_dev_to_pci_dev(edev));
return val;
}

Expand Down Expand Up @@ -838,7 +836,7 @@ core_initcall_sync(eeh_init);
* on the CEC architecture, type of the device, on earlier boot
* command-line arguments & etc.
*/
static void eeh_add_device_early(struct device_node *dn)
void eeh_add_device_early(struct device_node *dn)
{
struct pci_controller *phb;

Expand Down Expand Up @@ -886,7 +884,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
* This routine must be used to complete EEH initialization for PCI
* devices that were added after system boot (e.g. hotplug, dlpar).
*/
static void eeh_add_device_late(struct pci_dev *dev)
void eeh_add_device_late(struct pci_dev *dev)
{
struct device_node *dn;
struct eeh_dev *edev;
Expand All @@ -902,9 +900,23 @@ static void eeh_add_device_late(struct pci_dev *dev)
pr_debug("EEH: Already referenced !\n");
return;
}
WARN_ON(edev->pdev);

pci_dev_get(dev);
/*
* The EEH cache might not be removed correctly because of
* unbalanced kref to the device during unplug time, which
* relies on pcibios_release_device(). So we have to remove
* that here explicitly.
*/
if (edev->pdev) {
eeh_rmv_from_parent_pe(edev);
eeh_addr_cache_rmv_dev(edev->pdev);
eeh_sysfs_remove_device(edev->pdev);
edev->mode &= ~EEH_DEV_SYSFS;

edev->pdev = NULL;
dev->dev.archdata.edev = NULL;
}

edev->pdev = dev;
dev->dev.archdata.edev = edev;

Expand Down Expand Up @@ -967,15 +979,14 @@ EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
/**
* eeh_remove_device - Undo EEH setup for the indicated pci device
* @dev: pci device to be removed
* @purge_pe: remove the PE or not
*
* This routine should be called when a device is removed from
* a running system (e.g. by hotplug or dlpar). It unregisters
* the PCI device from the EEH subsystem. I/O errors affecting
* this device will no longer be detected after this call; thus,
* i/o errors affecting this slot may leave this device unusable.
*/
static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
void eeh_remove_device(struct pci_dev *dev)
{
struct eeh_dev *edev;

Expand All @@ -986,42 +997,29 @@ static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
/* Unregister the device with the EEH/PCI address search system */
pr_debug("EEH: Removing device %s\n", pci_name(dev));

if (!edev || !edev->pdev) {
if (!edev || !edev->pdev || !edev->pe) {
pr_debug("EEH: Not referenced !\n");
return;
}

/*
* During the hotplug for EEH error recovery, we need the EEH
* device attached to the parent PE in order for BAR restore
* a bit later. So we keep it for BAR restore and remove it
* from the parent PE during the BAR resotre.
*/
edev->pdev = NULL;
dev->dev.archdata.edev = NULL;
pci_dev_put(dev);
if (!(edev->pe->state & EEH_PE_KEEP))
eeh_rmv_from_parent_pe(edev);
else
edev->mode |= EEH_DEV_DISCONNECTED;

eeh_rmv_from_parent_pe(edev, purge_pe);
eeh_addr_cache_rmv_dev(dev);
eeh_sysfs_remove_device(dev);
edev->mode &= ~EEH_DEV_SYSFS;
}

/**
* eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
* @dev: PCI device
* @purge_pe: remove the corresponding PE or not
*
* This routine must be called when a device is removed from the
* running system through hotplug or dlpar. The corresponding
* PCI address cache will be removed.
*/
void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe)
{
struct pci_bus *bus = dev->subordinate;
struct pci_dev *child, *tmp;

eeh_remove_device(dev, purge_pe);

if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
eeh_remove_bus_device(child, purge_pe);
}
}
EXPORT_SYMBOL_GPL(eeh_remove_bus_device);

static int proc_eeh_show(struct seq_file *m, void *v)
{
if (0 == eeh_subsystem_enabled) {
Expand Down
18 changes: 5 additions & 13 deletions arch/powerpc/kernel/eeh_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,12 @@ static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
struct pci_io_addr_range *piar;
piar = rb_entry(n, struct pci_io_addr_range, rb_node);

if (addr < piar->addr_lo) {
if (addr < piar->addr_lo)
n = n->rb_left;
} else {
if (addr > piar->addr_hi) {
n = n->rb_right;
} else {
pci_dev_get(piar->pcidev);
return piar->edev;
}
}
else if (addr > piar->addr_hi)
n = n->rb_right;
else
return piar->edev;
}

return NULL;
Expand Down Expand Up @@ -156,7 +152,6 @@ eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
if (!piar)
return NULL;

pci_dev_get(dev);
piar->addr_lo = alo;
piar->addr_hi = ahi;
piar->edev = pci_dev_to_eeh_dev(dev);
Expand Down Expand Up @@ -250,7 +245,6 @@ static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)

if (piar->pcidev == dev) {
rb_erase(n, &pci_io_addr_cache_root.rb_root);
pci_dev_put(piar->pcidev);
kfree(piar);
goto restart;
}
Expand Down Expand Up @@ -302,12 +296,10 @@ void eeh_addr_cache_build(void)
if (!edev)
continue;

pci_dev_get(dev); /* matching put is in eeh_remove_device() */
dev->dev.archdata.edev = edev;
edev->pdev = dev;

eeh_addr_cache_insert_dev(dev);

eeh_sysfs_add_device(dev);
}

Expand Down
Loading

0 comments on commit 07bc9dc

Please sign in to comment.