[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