[PATCH 1/1] ppc64: Block config accesses during BIST
Brian King
brking at us.ibm.com
Tue Sep 14 07:05:39 EST 2004
I'll be sending a patch that applies cleanly fairly soon.
-Brian
Linas Vepstas wrote:
> Forwarding ...
>
> Brian sent this patch while the list was down. The problem that
> spurs this patch was discussed a number of time on this mailing list.
> I like this patch; it seems to solve the problem with a minimum of
> fuss.
>
> I suspect this patch doesn't apply cleanly after other recent
> changes.
>
> Torvalds suggests using "Pirated-by:" when forwarding a patch such as this:
> http://www.ussg.iu.edu/hypermail/linux/kernel/0405.3/0226.html
>
> Signed-off-by: Linas Vepstas <linas at linas.org>
>
> --linas
>
> ----- Forwarded message from brking at us.ibm.com -----
>
> Subject: [PATCH 1/1] ppc64: Block config accesses during BIST
>
> 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 and iSeries Power 4.
>
> Signed-off-by: Brian King <brking at us.ibm.com>
> ---
>
> linux-2.6.9-rc1-bk8-bjking1/arch/ppc64/kernel/iSeries_pci.c | 127 +++++++++-
> linux-2.6.9-rc1-bk8-bjking1/arch/ppc64/kernel/pSeries_pci.c | 103 +++++++-
> linux-2.6.9-rc1-bk8-bjking1/arch/ppc64/kernel/prom.c | 1
> linux-2.6.9-rc1-bk8-bjking1/include/asm-ppc64/iSeries/iSeries_pci.h | 2
> linux-2.6.9-rc1-bk8-bjking1/include/asm-ppc64/pci.h | 6
> linux-2.6.9-rc1-bk8-bjking1/include/asm-ppc64/prom.h | 5
> 6 files changed, 226 insertions(+), 18 deletions(-)
>
> diff -puN include/asm-ppc64/prom.h~ppc64_block_cfg_io_during_bist include/asm-ppc64/prom.h
> --- linux-2.6.9-rc1-bk8/include/asm-ppc64/prom.h~ppc64_block_cfg_io_during_bist 2004-09-01 16:20:35.000000000 -0500
> +++ linux-2.6.9-rc1-bk8-bjking1/include/asm-ppc64/prom.h 2004-09-01 16:20:35.000000000 -0500
> @@ -169,16 +169,21 @@ struct device_node {
> struct proc_dir_entry *addr_link; /* addr symlink */
> atomic_t _users; /* reference count */
> unsigned long _flags;
> + spinlock_t config_lock;
> };
>
> /* flag descriptions */
> #define OF_STALE 0 /* node is slated for deletion */
> #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
> +#define OF_NO_CFGIO 2 /* config space accesses should fail */
>
> #define OF_IS_STALE(x) test_bit(OF_STALE, &x->_flags)
> #define OF_MARK_STALE(x) set_bit(OF_STALE, &x->_flags)
> #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
> #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
> +#define OF_IS_CFGIO_BLOCKED(x) test_bit(OF_NO_CFGIO, &x->_flags)
> +#define OF_UNBLOCK_CFGIO(x) clear_bit(OF_NO_CFGIO, &x->_flags)
> +#define OF_BLOCK_CFGIO(x) set_bit(OF_NO_CFGIO, &x->_flags)
>
> /*
> * Until 32-bit ppc can add proc_dir_entries to its device_node
> diff -puN arch/ppc64/kernel/prom.c~ppc64_block_cfg_io_during_bist arch/ppc64/kernel/prom.c
> --- linux-2.6.9-rc1-bk8/arch/ppc64/kernel/prom.c~ppc64_block_cfg_io_during_bist 2004-09-01 16:20:35.000000000 -0500
> +++ linux-2.6.9-rc1-bk8-bjking1/arch/ppc64/kernel/prom.c 2004-09-01 16:20:35.000000000 -0500
> @@ -2959,6 +2959,7 @@ int of_add_node(const char *path, struct
>
> np->properties = proplist;
> OF_MARK_DYNAMIC(np);
> + spin_lock_init(&np->config_lock);
> of_node_get(np);
> np->parent = derive_parent(path);
> if (!np->parent) {
> diff -puN arch/ppc64/kernel/pSeries_pci.c~ppc64_block_cfg_io_during_bist arch/ppc64/kernel/pSeries_pci.c
> --- linux-2.6.9-rc1-bk8/arch/ppc64/kernel/pSeries_pci.c~ppc64_block_cfg_io_during_bist 2004-09-01 16:20:35.000000000 -0500
> +++ linux-2.6.9-rc1-bk8-bjking1/arch/ppc64/kernel/pSeries_pci.c 2004-09-01 16:20:35.000000000 -0500
> @@ -61,15 +61,12 @@ static int s7a_workaround;
>
> extern unsigned long pci_probe_only;
>
> -static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
> +static int __rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
> {
> int returnval = -1;
> unsigned long buid, addr;
> int ret;
>
> - if (!dn)
> - return -2;
> -
> addr = (dn->busno << 16) | (dn->devfn << 8) | where;
> buid = dn->phb->buid;
> if (buid) {
> @@ -82,6 +79,23 @@ static int rtas_read_config(struct devic
> return ret;
> }
>
> +static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
> +{
> + unsigned long flags;
> + int ret = 0;
> +
> + if (!dn)
> + return -2;
> +
> + spin_lock_irqsave(&dn->config_lock, flags);
> + if (OF_IS_CFGIO_BLOCKED(dn))
> + *val = -1;
> + else
> + ret = __rtas_read_config(dn, where, size, val);
> + spin_unlock_irqrestore(&dn->config_lock, flags);
> + return ret;
> +}
> +
> static int rtas_pci_read_config(struct pci_bus *bus,
> unsigned int devfn,
> int where, int size, u32 *val)
> @@ -100,14 +114,11 @@ static int rtas_pci_read_config(struct p
> return PCIBIOS_DEVICE_NOT_FOUND;
> }
>
> -static int rtas_write_config(struct device_node *dn, int where, int size, u32 val)
> +static int __rtas_write_config(struct device_node *dn, int where, int size, u32 val)
> {
> unsigned long buid, addr;
> int ret;
>
> - if (!dn)
> - return -2;
> -
> addr = (dn->busno << 16) | (dn->devfn << 8) | where;
> buid = dn->phb->buid;
> if (buid) {
> @@ -118,6 +129,21 @@ static int rtas_write_config(struct devi
> return ret;
> }
>
> +static int rtas_write_config(struct device_node *dn, int where, int size, u32 val)
> +{
> + unsigned long flags;
> + int ret = 0;
> +
> + if (!dn)
> + return -2;
> +
> + spin_lock_irqsave(&dn->config_lock, flags);
> + if (!OF_IS_CFGIO_BLOCKED(dn))
> + ret = __rtas_write_config(dn, where, size, val);
> + spin_unlock_irqrestore(&dn->config_lock, flags);
> + return ret;
> +}
> +
> static int rtas_pci_write_config(struct pci_bus *bus,
> unsigned int devfn,
> int where, int size, u32 val)
> @@ -141,6 +167,67 @@ struct pci_ops rtas_pci_ops = {
> rtas_pci_write_config
> };
>
> +/**
> + * pci_block_config_io - Block PCI config reads/writes
> + * @pdev: pci device struct
> + *
> + * This function blocks any PCI config accesses from occurring.
> + * Device drivers may call this prior to running BIST if the
> + * adapter cannot handle PCI config reads or writes when
> + * running BIST. 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 device_node *dn = pci_device_to_OF_node(pdev);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dn->config_lock, flags);
> + OF_BLOCK_CFGIO(dn);
> + spin_unlock_irqrestore(&dn->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 device_node *dn = pci_device_to_OF_node(pdev);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&dn->config_lock, flags);
> + OF_UNBLOCK_CFGIO(dn);
> + spin_unlock_irqrestore(&dn->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 device_node *dn = pci_device_to_OF_node(pdev);
> + return __rtas_write_config(dn, PCI_BIST, 1, PCI_BIST_START);
> +}
> +EXPORT_SYMBOL(pci_start_bist);
> +
> /******************************************************************
> * pci_read_irq_line
> *
> diff -puN include/asm-ppc64/pci.h~ppc64_block_cfg_io_during_bist include/asm-ppc64/pci.h
> --- linux-2.6.9-rc1-bk8/include/asm-ppc64/pci.h~ppc64_block_cfg_io_during_bist 2004-09-01 16:20:35.000000000 -0500
> +++ linux-2.6.9-rc1-bk8-bjking1/include/asm-ppc64/pci.h 2004-09-01 16:20:35.000000000 -0500
> @@ -233,6 +233,12 @@ 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);
> +
> #endif /* __KERNEL__ */
>
> #endif /* __PPC64_PCI_H */
> diff -puN include/asm-ppc64/iSeries/iSeries_pci.h~ppc64_block_cfg_io_during_bist include/asm-ppc64/iSeries/iSeries_pci.h
> --- linux-2.6.9-rc1-bk8/include/asm-ppc64/iSeries/iSeries_pci.h~ppc64_block_cfg_io_during_bist 2004-09-01 16:20:35.000000000 -0500
> +++ linux-2.6.9-rc1-bk8-bjking1/include/asm-ppc64/iSeries/iSeries_pci.h 2004-09-01 16:20:35.000000000 -0500
> @@ -91,6 +91,7 @@ struct iSeries_Device_Node {
> int ReturnCode; /* Return Code Holder */
> int IoRetry; /* Current Retry Count */
> int Flags; /* Possible flags(disable/bist)*/
> +#define ISERIES_CFGIO_BLOCKED 1
> u16 Vendor; /* Vendor ID */
> u8 LogicalSlot; /* Hv Slot Index for Tces */
> struct iommu_table* iommu_table;/* Device TCE Table */
> @@ -99,6 +100,7 @@ struct iSeries_Device_Node {
> u8 FrameId; /* iSeries spcn Frame Id */
> char CardLocation[4];/* Char format of planar vpd */
> char Location[20]; /* Frame 1, Card C10 */
> + spinlock_t config_lock;
> };
>
> /************************************************************************/
> diff -puN arch/ppc64/kernel/iSeries_pci.c~ppc64_block_cfg_io_during_bist arch/ppc64/kernel/iSeries_pci.c
> --- linux-2.6.9-rc1-bk8/arch/ppc64/kernel/iSeries_pci.c~ppc64_block_cfg_io_during_bist 2004-09-01 16:20:35.000000000 -0500
> +++ linux-2.6.9-rc1-bk8-bjking1/arch/ppc64/kernel/iSeries_pci.c 2004-09-01 16:20:35.000000000 -0500
> @@ -131,6 +131,7 @@ static struct iSeries_Device_Node *build
> node->AgentId = AgentId;
> node->DevFn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(AgentId), Function);
> node->IoRetry = 0;
> + spin_lock_init(&node->config_lock);
> iSeries_Get_Location_Code(node);
> PCIFR("Device 0x%02X.%2X, Node:0x%p ", ISERIES_BUS(node),
> ISERIES_DEVFUN(node), node);
> @@ -515,16 +516,12 @@ static u64 hv_cfg_write_func[4] = {
> /*
> * Read PCI config space
> */
> -static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn,
> +static int __iSeries_pci_read_config(struct iSeries_Device_Node *node,
> int offset, int size, u32 *val)
> {
> - struct iSeries_Device_Node *node = find_Device_Node(bus->number, devfn);
> u64 fn;
> struct HvCallPci_LoadReturn ret;
>
> - if (node == NULL)
> - return PCIBIOS_DEVICE_NOT_FOUND;
> -
> fn = hv_cfg_read_func[(size - 1) & 3];
> HvCall3Ret16(fn, &ret, node->DsaAddr.DsaAddr, offset, 0);
>
> @@ -537,20 +534,36 @@ static int iSeries_pci_read_config(struc
> return 0;
> }
>
> +static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn,
> + int offset, int size, u32 *val)
> +{
> + struct iSeries_Device_Node *node = find_Device_Node(bus->number, devfn);
> + int ret = PCIBIOS_DEVICE_NOT_FOUND;
> + unsigned long flags;
> +
> + if (node) {
> + ret = 0;
> + spin_lock_irqsave(&node->config_lock, flags);
> + if (node->Flags & ISERIES_CFGIO_BLOCKED)
> + *val = -1;
> + else
> + ret = __iSeries_pci_read_config(node, offset, size, val);
> + spin_unlock_irqrestore(&node->config_lock, flags);
> + }
> +
> + return ret;
> +}
> +
> /*
> * Write PCI config space
> */
>
> -static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn,
> +static int __iSeries_pci_write_config(struct iSeries_Device_Node *node,
> int offset, int size, u32 val)
> {
> - struct iSeries_Device_Node *node = find_Device_Node(bus->number, devfn);
> u64 fn;
> u64 ret;
>
> - if (node == NULL)
> - return PCIBIOS_DEVICE_NOT_FOUND;
> -
> fn = hv_cfg_write_func[(size - 1) & 3];
> ret = HvCall4(fn, node->DsaAddr.DsaAddr, offset, val, 0);
>
> @@ -560,6 +573,23 @@ static int iSeries_pci_write_config(stru
> return 0;
> }
>
> +static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn,
> + int offset, int size, u32 val)
> +{
> + struct iSeries_Device_Node *node = find_Device_Node(bus->number, devfn);
> + int ret = PCIBIOS_DEVICE_NOT_FOUND;
> + unsigned long flags;
> +
> + if (node) {
> + spin_lock_irqsave(&node->config_lock, flags);
> + if (!(node->Flags & ISERIES_CFGIO_BLOCKED))
> + ret = __iSeries_pci_write_config(node, offset, size, val);
> + spin_unlock_irqrestore(&node->config_lock, flags);
> + }
> +
> + return ret;
> +}
> +
> static struct pci_ops iSeries_pci_ops = {
> .read = iSeries_pci_read_config,
> .write = iSeries_pci_write_config
> @@ -820,3 +850,80 @@ void iSeries_Write_Long(u32 data, void *
> } while (CheckReturnCode("WWL", DevNode, rc) != 0);
> }
> EXPORT_SYMBOL(iSeries_Write_Long);
> +
> +/**
> + * pci_block_config_io - Block PCI config reads/writes
> + * @pdev: pci device struct
> + *
> + * This function blocks any PCI config accesses from occurring.
> + * Device drivers may call this prior to running BIST if the
> + * adapter cannot handle PCI config reads or writes when
> + * running BIST. 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 iSeries_Device_Node *node;
> + unsigned long flags;
> +
> + node = find_Device_Node(pdev->bus->number, pdev->devfn);
> +
> + if (node == NULL)
> + return;
> +
> + spin_lock_irqsave(&node->config_lock, flags);
> + node->Flags |= ISERIES_CFGIO_BLOCKED;
> + spin_unlock_irqrestore(&node->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 iSeries_Device_Node *node;
> + unsigned long flags;
> +
> + node = find_Device_Node(pdev->bus->number, pdev->devfn);
> +
> + if (node == NULL)
> + return;
> +
> + spin_lock_irqsave(&node->config_lock, flags);
> + node->Flags &= ~ISERIES_CFGIO_BLOCKED;
> + spin_unlock_irqrestore(&node->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 iSeries_Device_Node *node;
> +
> + node = find_Device_Node(pdev->bus->number, pdev->devfn);
> +
> + if (node == NULL)
> + return PCIBIOS_DEVICE_NOT_FOUND;
> +
> + return __iSeries_pci_write_config(node, PCI_BIST, 1, PCI_BIST_START);
> +}
> +EXPORT_SYMBOL(pci_start_bist);
> _
>
>
> ----- End forwarded message -----
>
--
Brian King
eServer Storage I/O
IBM Linux Technology Center
More information about the Linuxppc64-dev
mailing list