[PATCH] cxl: fix nested locking hang during EEH hotplug

Andrew Donnellan andrew.donnellan at au1.ibm.com
Tue Feb 21 18:52:46 AEDT 2017


On 06/02/17 12:07, Andrew Donnellan wrote:
> Commit 14a3ae34bfd0 ("cxl: Prevent read/write to AFU config space while AFU
> not configured") introduced a rwsem to fix an invalid memory access that
> occurred when someone attempts to access the config space of an AFU on a
> vPHB whilst the AFU is deconfigured, such as during EEH recovery.
>
> It turns out that it's possible to run into a nested locking issue when EEH
> recovery fails and a full device hotplug is required.
> cxl_pci_error_detected() deconfigures the AFU, taking a writer lock on
> configured_rwsem. When EEH recovery fails, the EEH code calls
> pci_hp_remove_devices() to remove the device, which in turn calls
> cxl_remove() -> cxl_pci_remove_afu() -> pci_deconfigure_afu(), which tries
> to grab the writer lock that's already held.
>
> Standard rwsem semantics don't express what we really want to do here and
> don't allow for nested locking. Fix this by replacing the rwsem with an
> atomic_t which we can control more finely. Allow the AFU to be locked
> multiple times so long as there are no readers.
>
> Fixes: 14a3ae34bfd0 ("cxl: Prevent read/write to AFU config space while AFU not configured")
> Cc: stable at vger.kernel.org # v4.9+
> Signed-off-by: Andrew Donnellan <andrew.donnellan at au1.ibm.com>

This patch has now been tested within IBM and should be good to go.

Michael/stable team - please make sure this gets in at the same time as 
14a3ae34bfd0.


Andrew


>
> ---
>
> I've asked Uma and Pradipta to give this a test.
>
> ---
>  drivers/misc/cxl/cxl.h  |  5 +++--
>  drivers/misc/cxl/main.c |  3 +--
>  drivers/misc/cxl/pci.c  | 11 +++++++++--
>  drivers/misc/cxl/vphb.c | 18 ++++++++++++++----
>  4 files changed, 27 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
> index b4a43fd14b99..08e7d3a54425 100644
> --- a/drivers/misc/cxl/cxl.h
> +++ b/drivers/misc/cxl/cxl.h
> @@ -418,8 +418,9 @@ struct cxl_afu {
>  	struct dentry *debugfs;
>  	struct mutex contexts_lock;
>  	spinlock_t afu_cntl_lock;
> -	/* Used to block access to AFU config space while deconfigured */
> -	struct rw_semaphore configured_rwsem;
> +
> +	/* -1: AFU deconfigured/locked, >= 0: number of readers */
> +	atomic_t configured_state;
>
>  	/* AFU error buffer fields and bin attribute for sysfs */
>  	u64 eb_len, eb_offset;
> diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c
> index 2a6bf1d0a3a4..cc1706a92ace 100644
> --- a/drivers/misc/cxl/main.c
> +++ b/drivers/misc/cxl/main.c
> @@ -268,8 +268,7 @@ struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice)
>  	idr_init(&afu->contexts_idr);
>  	mutex_init(&afu->contexts_lock);
>  	spin_lock_init(&afu->afu_cntl_lock);
> -	init_rwsem(&afu->configured_rwsem);
> -	down_write(&afu->configured_rwsem);
> +	atomic_set(&afu->configured_state, -1);
>  	afu->prefault_mode = CXL_PREFAULT_NONE;
>  	afu->irqs_max = afu->adapter->user_irqs;
>
> diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
> index f512e13ec0f2..bdfa5ff11aea 100644
> --- a/drivers/misc/cxl/pci.c
> +++ b/drivers/misc/cxl/pci.c
> @@ -1129,7 +1129,7 @@ static int pci_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pc
>  	if ((rc = cxl_native_register_psl_irq(afu)))
>  		goto err2;
>
> -	up_write(&afu->configured_rwsem);
> +	atomic_set(&afu->configured_state, 0);
>  	return 0;
>
>  err2:
> @@ -1142,7 +1142,14 @@ static int pci_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pc
>
>  static void pci_deconfigure_afu(struct cxl_afu *afu)
>  {
> -	down_write(&afu->configured_rwsem);
> +	/*
> +	 * It's okay to deconfigure when AFU is already locked, otherwise wait
> +	 * until there are no readers
> +	 */
> +	if (atomic_read(&afu->configured_state) != -1) {
> +		while (atomic_cmpxchg(&afu->configured_state, 0, -1) != -1)
> +			schedule();
> +	}
>  	cxl_native_release_psl_irq(afu);
>  	if (afu->adapter->native->sl_ops->release_serr_irq)
>  		afu->adapter->native->sl_ops->release_serr_irq(afu);
> diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c
> index 639a343b7836..512a4897dbf6 100644
> --- a/drivers/misc/cxl/vphb.c
> +++ b/drivers/misc/cxl/vphb.c
> @@ -83,6 +83,16 @@ static inline struct cxl_afu *pci_bus_to_afu(struct pci_bus *bus)
>  	return phb ? phb->private_data : NULL;
>  }
>
> +static void cxl_afu_configured_put(struct cxl_afu *afu)
> +{
> +	atomic_dec_if_positive(&afu->configured_state);
> +}
> +
> +static bool cxl_afu_configured_get(struct cxl_afu *afu)
> +{
> +	return atomic_inc_unless_negative(&afu->configured_state);
> +}
> +
>  static inline int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
>  				       struct cxl_afu *afu, int *_record)
>  {
> @@ -107,7 +117,7 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
>
>  	afu = pci_bus_to_afu(bus);
>  	/* Grab a reader lock on afu. */
> -	if (afu == NULL || !down_read_trylock(&afu->configured_rwsem))
> +	if (afu == NULL || !cxl_afu_configured_get(afu))
>  		return PCIBIOS_DEVICE_NOT_FOUND;
>
>  	rc = cxl_pcie_config_info(bus, devfn, afu, &record);
> @@ -132,7 +142,7 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
>  	}
>
>  out:
> -	up_read(&afu->configured_rwsem);
> +	cxl_afu_configured_put(afu);
>  	return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
>  }
>
> @@ -144,7 +154,7 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
>
>  	afu = pci_bus_to_afu(bus);
>  	/* Grab a reader lock on afu. */
> -	if (afu == NULL || !down_read_trylock(&afu->configured_rwsem))
> +	if (afu == NULL || !cxl_afu_configured_get(afu))
>  		return PCIBIOS_DEVICE_NOT_FOUND;
>
>  	rc = cxl_pcie_config_info(bus, devfn, afu, &record);
> @@ -166,7 +176,7 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
>  	}
>
>  out:
> -	up_read(&afu->configured_rwsem);
> +	cxl_afu_configured_put(afu);
>  	return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL;
>  }
>
>

-- 
Andrew Donnellan              OzLabs, ADL Canberra
andrew.donnellan at au1.ibm.com  IBM Australia Limited



More information about the Linuxppc-dev mailing list