Commit 09cce79c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix multiple EEH-related bugs

From: Linas Vepstas <linas@austin.ibm.com>

This patch fixes multiple EEH-related bugs:

 - Fixes the eeh_check_failure() usage in an interrupt context.
   This routine is now safe to use in an interrupt. The fix was to
   build a cache of IO addresses and check that, instead of using
   the pci routines.
 - Merges in Olof Johansson's sizeof patch when checking for failure
 - Adds EEH tests to array/string reads
 - Fixes bugs with address resolution (some i/o addresses were handled
   incorrectly, resulting in EEH errors slipping by undetected.)
 - Adds EEH support to the PCI Hotplug system (so that devices that
   get added/removed get properly registered with the EEH subsystem.)
 - Fixes improper use of /proc filesystem.
 - Adds some misc statistics.

While merging Linas' patch I also converted the proc usage to
seq_single, used per cpu variables for the stats and removed the
eeh-force-off option.
parent 0e1311b1
This diff is collapsed.
......@@ -119,43 +119,6 @@ static void fixup_windbond_82c105(struct pci_dev* dev)
}
}
/* Given an mmio phys address, find a pci device that implements
* this address. This is of course expensive, but only used
* for device initialization or error paths.
* For io BARs it is assumed the pci_io_base has already been added
* into addr.
*
* Bridges are ignored although they could be used to optimize the search.
*/
struct pci_dev *pci_find_dev_by_addr(unsigned long addr)
{
struct pci_dev *dev = NULL;
int i;
unsigned long ioaddr;
ioaddr = (addr > isa_io_base) ? addr - isa_io_base : 0;
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
continue;
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
unsigned long start = pci_resource_start(dev,i);
unsigned long end = pci_resource_end(dev,i);
unsigned int flags = pci_resource_flags(dev,i);
if (start == 0 || ~start == 0 ||
end == 0 || ~end == 0)
continue;
if ((flags & IORESOURCE_IO) &&
(ioaddr >= start && ioaddr <= end))
return dev;
else if ((flags & IORESOURCE_MEM) &&
(addr >= start && addr <= end))
return dev;
}
}
return NULL;
}
void
pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
struct resource *res)
......@@ -359,6 +322,10 @@ static int __init pcibios_init(void)
printk("PCI: Probing PCI hardware done\n");
//ppc64_boot_msg(0x41, "PCI Done");
#ifdef CONFIG_PPC_PSERIES
pci_addr_cache_build();
#endif
return 0;
}
......
......@@ -37,11 +37,14 @@ typedef void *(*traverse_func)(struct device_node *me, void *data);
void *traverse_pci_devices(struct device_node *start, traverse_func pre, traverse_func post, void *data);
void *traverse_all_pci_devices(traverse_func pre);
struct pci_dev *pci_find_dev_by_addr(unsigned long addr);
void pci_devs_phb_init(void);
void pci_fix_bus_sysdata(void);
struct device_node *fetch_dev_dn(struct pci_dev *dev);
#define PCI_GET_PHB_PTR(dev) (((struct device_node *)(dev)->sysdata)->phb)
/* PCI address cache management routines */
void pci_addr_cache_insert_device(struct pci_dev *dev);
void pci_addr_cache_remove_device(struct pci_dev *dev);
#endif /* __PPC_KERNEL_PCI_H__ */
......@@ -148,10 +148,6 @@ EXPORT_SYMBOL(iSeries_Write_Byte);
EXPORT_SYMBOL(iSeries_Write_Word);
EXPORT_SYMBOL(iSeries_Write_Long);
#endif /* CONFIG_PPC_ISERIES */
#ifndef CONFIG_PPC_ISERIES
EXPORT_SYMBOL(eeh_check_failure);
EXPORT_SYMBOL(eeh_total_mmio_ffs);
#endif /* CONFIG_PPC_ISERIES */
#endif /* CONFIG_PCI */
EXPORT_SYMBOL(start_thread);
......
......@@ -31,6 +31,7 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <asm/eeh.h> /* for eeh_add_device() */
#include <asm/rtas.h> /* rtas_call */
#include <asm/pci-bridge.h> /* for pci_controller */
#include "../pci.h" /* for pci_add_new_bus*/
......
This diff is collapsed.
......@@ -58,6 +58,13 @@ extern unsigned long pci_io_base;
#define outb(data,addr) writeb(data,((unsigned long)(addr)))
#define outw(data,addr) writew(data,((unsigned long)(addr)))
#define outl(data,addr) writel(data,((unsigned long)(addr)))
/*
* The *_ns versions below don't do byte-swapping.
* Neither do the standard versions now, these are just here
* for older code.
*/
#define insw_ns(port, buf, ns) _insw_ns((u16 *)((port)+pci_io_base), (buf), (ns))
#define insl_ns(port, buf, nl) _insl_ns((u32 *)((port)+pci_io_base), (buf), (nl))
#else
#define __raw_readb(addr) (*(volatile unsigned char *)(addr))
#define __raw_readw(addr) (*(volatile unsigned short *)(addr))
......@@ -90,12 +97,16 @@ extern unsigned long pci_io_base;
* They are only used in practice for transferring buffers which
* are arrays of bytes, and byte-swapping is not appropriate in
* that case. - paulus */
#define insb(port, buf, ns) _insb((u8 *)((port)+pci_io_base), (buf), (ns))
#define insb(port, buf, ns) eeh_insb((port), (buf), (ns))
#define insw(port, buf, ns) eeh_insw_ns((port), (buf), (ns))
#define insl(port, buf, nl) eeh_insl_ns((port), (buf), (nl))
#define insw_ns(port, buf, ns) eeh_insw_ns((port), (buf), (ns))
#define insl_ns(port, buf, nl) eeh_insl_ns((port), (buf), (nl))
#define outsb(port, buf, ns) _outsb((u8 *)((port)+pci_io_base), (buf), (ns))
#define insw(port, buf, ns) _insw_ns((u16 *)((port)+pci_io_base), (buf), (ns))
#define outsw(port, buf, ns) _outsw_ns((u16 *)((port)+pci_io_base), (buf), (ns))
#define insl(port, buf, nl) _insl_ns((u32 *)((port)+pci_io_base), (buf), (nl))
#define outsl(port, buf, nl) _outsl_ns((u32 *)((port)+pci_io_base), (buf), (nl))
#endif
#define readb_relaxed(addr) readb(addr)
......@@ -130,9 +141,7 @@ extern void _outsl_ns(volatile u32 *port, const void *buf, int nl);
* Neither do the standard versions now, these are just here
* for older code.
*/
#define insw_ns(port, buf, ns) _insw_ns((u16 *)((port)+pci_io_base), (buf), (ns))
#define outsw_ns(port, buf, ns) _outsw_ns((u16 *)((port)+pci_io_base), (buf), (ns))
#define insl_ns(port, buf, nl) _insl_ns((u32 *)((port)+pci_io_base), (buf), (nl))
#define outsl_ns(port, buf, nl) _outsl_ns((u32 *)((port)+pci_io_base), (buf), (nl))
......@@ -204,6 +213,9 @@ static inline void iosync(void)
/*
* 8, 16 and 32 bit, big and little endian I/O operations, with barrier.
* These routines do not perform EEH-related I/O address translation,
* and should not be used directly by device drivers. Use inb/readb
* instead.
*/
static inline int in_8(volatile unsigned char *addr)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment