[PATCH 1/2] ppc64: Block config accesses during BIST #3
brking at us.ibm.com
brking at us.ibm.com
Tue Nov 9 03:19:34 EST 2004
Below is a revised patch in a attempt at sharing more code between
iSeries and pSeries and also getting full ppc64 support of the new
APIs essentially for free.
Some PCI adapters on pSeries and iSeries hardware (ipr scsi adapters)
have an exposure today in that they issue BIST to the adapter to reset
the card. If, during the time it takes to complete BIST, userspace attempts
to access PCI config space, the host bus bridge will master abort the access
since the ipr adapter does not respond on the PCI bus for a brief period of
time when running BIST. This master abort results in the host PCI bridge
isolating that PCI device from the rest of the system, making the device
unusable until Linux is rebooted. This patch is an attempt to close that
exposure by introducing some blocking code in the arch specific PCI code.
The intent is to have the ipr device driver invoke these routines to
prevent userspace PCI accesses from occurring during this window.
It has been tested by running BIST on an ipr adapter while running a
script which looped reading the config space of that adapter through sysfs.
Without the patch, an EEH error occurrs. With the patch there is no EEH
error. Tested on Power 5.
Signed-off-by: Brian King <brking at us.ibm.com>
---
linux-2.6.10-rc1-bk18-bjking1/arch/ppc64/kernel/pSeries_pci.c | 2
linux-2.6.10-rc1-bk18-bjking1/arch/ppc64/kernel/pci.c | 112 +++++++++-
linux-2.6.10-rc1-bk18-bjking1/arch/ppc64/kernel/pci.h | 1
linux-2.6.10-rc1-bk18-bjking1/include/asm-ppc64/pci-bridge.h | 4
linux-2.6.10-rc1-bk18-bjking1/include/asm-ppc64/pci.h | 13 +
5 files changed, 129 insertions(+), 3 deletions(-)
diff -puN include/asm-ppc64/pci.h~ppc64_block_cfg_io_during_bist_revised include/asm-ppc64/pci.h
--- linux-2.6.10-rc1-bk18/include/asm-ppc64/pci.h~ppc64_block_cfg_io_during_bist_revised 2004-11-08 09:32:48.000000000 -0600
+++ linux-2.6.10-rc1-bk18-bjking1/include/asm-ppc64/pci.h 2004-11-08 09:32:48.000000000 -0600
@@ -85,6 +85,7 @@ struct pci_dma_ops {
};
extern struct pci_dma_ops pci_dma_ops;
+extern struct pci_ops pci_ops;
static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle)
@@ -244,6 +245,18 @@ extern int pci_read_irq_line(struct pci_
extern void pcibios_add_platform_entries(struct pci_dev *dev);
+extern void pci_block_config_io(struct pci_dev *dev);
+
+extern void pci_unblock_config_io(struct pci_dev *dev);
+
+extern int pci_start_bist(struct pci_dev *dev);
+
+extern int pcibios_read_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val);
+
+extern int pcibios_write_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val);
+
#endif /* __KERNEL__ */
#endif /* __PPC64_PCI_H */
diff -puN arch/ppc64/kernel/pci.c~ppc64_block_cfg_io_during_bist_revised arch/ppc64/kernel/pci.c
--- linux-2.6.10-rc1-bk18/arch/ppc64/kernel/pci.c~ppc64_block_cfg_io_during_bist_revised 2004-11-08 09:32:48.000000000 -0600
+++ linux-2.6.10-rc1-bk18-bjking1/arch/ppc64/kernel/pci.c 2004-11-08 09:32:48.000000000 -0600
@@ -321,7 +321,7 @@ static int __init pcibios_init(void)
/* Scan all of the recorded PCI controllers. */
list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
hose->last_busno = 0xff;
- bus = pci_scan_bus(hose->first_busno, hose->ops,
+ bus = pci_scan_bus(hose->first_busno, &pci_ops,
hose->arch_data);
hose->bus = bus;
hose->last_busno = bus->subordinate;
@@ -547,6 +547,104 @@ int pci_mmap_page_range(struct pci_dev *
return ret;
}
+static spinlock_t config_lock = SPIN_LOCK_UNLOCKED;
+
+int pcibios_read_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&config_lock, flags);
+ if (hose && !(hose->block_cfg_io_mask & (1 << PCI_SLOT(devfn))))
+ rc = hose->ops->read(bus, devfn, where, size, val);
+ else
+ *val = -1;
+ spin_unlock_irqrestore(&config_lock, flags);
+ return rc;
+}
+EXPORT_SYMBOL(pcibios_read_config);
+
+int pcibios_write_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&config_lock, flags);
+ if (hose && !(hose->block_cfg_io_mask & (1 << PCI_SLOT(devfn))))
+ rc = hose->ops->write(bus, devfn, where, size, val);
+ spin_unlock_irqrestore(&config_lock, flags);
+ return rc;
+}
+EXPORT_SYMBOL(pcibios_write_config);
+
+struct pci_ops pci_ops = {
+ pcibios_read_config,
+ pcibios_write_config
+};
+
+/**
+ * pci_block_config_io - Block PCI config reads/writes
+ * @pdev: pci device struct
+ *
+ * This function blocks any PCI config accesses from occurring.
+ * When blocked, any writes will be ignored and treated as
+ * successful and any reads will return all 1's data.
+ *
+ * Return value:
+ * nothing
+ **/
+void pci_block_config_io(struct pci_dev *pdev)
+{
+ struct pci_controller *hose = PCI_GET_PHB_PTR(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&config_lock, flags);
+ hose->block_cfg_io_mask |= (1 << PCI_SLOT(pdev->devfn));
+ spin_unlock_irqrestore(&config_lock, flags);
+}
+EXPORT_SYMBOL(pci_block_config_io);
+
+/**
+ * pci_unblock_config_io - Unblock PCI config reads/writes
+ * @pdev: pci device struct
+ *
+ * This function allows PCI config accesses to resume.
+ *
+ * Return value:
+ * nothing
+ **/
+void pci_unblock_config_io(struct pci_dev *pdev)
+{
+ struct pci_controller *hose = PCI_GET_PHB_PTR(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&config_lock, flags);
+ hose->block_cfg_io_mask &= ~(1 << PCI_SLOT(pdev->devfn));
+ spin_unlock_irqrestore(&config_lock, flags);
+}
+EXPORT_SYMBOL(pci_unblock_config_io);
+
+/**
+ * pci_start_bist - Start BIST on a PCI device
+ * @pdev: pci device struct
+ *
+ * This function allows a device driver to start BIST
+ * when PCI config accesses are disabled.
+ *
+ * Return value:
+ * nothing
+ **/
+int pci_start_bist(struct pci_dev *pdev)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ return hose->ops->write(pdev->bus, pdev->devfn, PCI_BIST, 1, PCI_BIST_START);
+}
+EXPORT_SYMBOL(pci_start_bist);
+
#ifdef CONFIG_PPC_MULTIPLATFORM
static ssize_t pci_show_devspec(struct device *dev, char *buf)
{
@@ -852,6 +950,18 @@ struct pci_controller* pci_find_hose_for
return NULL;
}
+struct pci_controller* pci_find_hose_for_bus(struct pci_bus *bus)
+{
+ while (bus) {
+ struct pci_controller *hose, *tmp;
+ list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
+ if (hose->bus == bus)
+ return hose;
+ bus=bus->parent;
+ }
+ return NULL;
+}
+
/*
* ppc64 can have multifunction devices that do not respond to function 0.
* In this case we must scan all functions.
diff -puN include/asm-ppc64/pci-bridge.h~ppc64_block_cfg_io_during_bist_revised include/asm-ppc64/pci-bridge.h
--- linux-2.6.10-rc1-bk18/include/asm-ppc64/pci-bridge.h~ppc64_block_cfg_io_during_bist_revised 2004-11-08 09:32:48.000000000 -0600
+++ linux-2.6.10-rc1-bk18-bjking1/include/asm-ppc64/pci-bridge.h 2004-11-08 09:32:48.000000000 -0600
@@ -65,6 +65,7 @@ struct pci_controller {
unsigned long buid;
unsigned long dma_window_base_cur;
unsigned long dma_window_size;
+ unsigned int block_cfg_io_mask;
};
/*
@@ -100,6 +101,7 @@ extern int pcibios_remove_root_bus(struc
#define PCI_GET_DN(dev) ((struct device_node *)((dev)->sysdata))
extern void phbs_remap_io(void);
+extern struct pci_controller* pci_find_hose_for_bus(struct pci_bus *bus);
static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus)
{
@@ -113,7 +115,7 @@ static inline struct pci_controller *pci
busdn = b->sysdata;
}
if (busdn == NULL)
- return NULL;
+ return pci_find_hose_for_bus(bus);
return busdn->phb;
}
diff -puN arch/ppc64/kernel/pci.h~ppc64_block_cfg_io_during_bist_revised arch/ppc64/kernel/pci.h
--- linux-2.6.10-rc1-bk18/arch/ppc64/kernel/pci.h~ppc64_block_cfg_io_during_bist_revised 2004-11-08 09:32:48.000000000 -0600
+++ linux-2.6.10-rc1-bk18-bjking1/arch/ppc64/kernel/pci.h 2004-11-08 09:32:48.000000000 -0600
@@ -19,6 +19,7 @@ extern struct pci_controller* pci_alloc_
extern void pci_setup_phb_io(struct pci_controller *hose, int primary);
extern struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node);
+extern struct pci_controller* pci_find_hose_for_bus(struct pci_bus *bus);
extern void pci_setup_phb_io_dynamic(struct pci_controller *hose);
diff -puN arch/ppc64/kernel/pSeries_pci.c~ppc64_block_cfg_io_during_bist_revised arch/ppc64/kernel/pSeries_pci.c
--- linux-2.6.10-rc1-bk18/arch/ppc64/kernel/pSeries_pci.c~ppc64_block_cfg_io_during_bist_revised 2004-11-08 09:32:48.000000000 -0600
+++ linux-2.6.10-rc1-bk18-bjking1/arch/ppc64/kernel/pSeries_pci.c 2004-11-08 09:32:48.000000000 -0600
@@ -434,7 +434,7 @@ struct pci_controller * __devinit init_p
pci_devs_phb_init_dynamic(phb);
phb->last_busno = 0xff;
- bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data);
+ bus = pci_scan_bus(phb->first_busno, &pci_ops, phb->arch_data);
phb->bus = bus;
phb->last_busno = bus->subordinate;
_
More information about the Linuxppc64-dev
mailing list