diff options
Diffstat (limited to 'arch/s390/pci/pci_sysfs.c')
| -rw-r--r-- | arch/s390/pci/pci_sysfs.c | 70 | 
1 files changed, 43 insertions, 27 deletions
diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index 8a7abac51816..a0b872b74fe3 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -49,6 +49,39 @@ static ssize_t mio_enabled_show(struct device *dev,  }  static DEVICE_ATTR_RO(mio_enabled); +static int _do_recover(struct pci_dev *pdev, struct zpci_dev *zdev) +{ +	u8 status; +	int ret; + +	pci_stop_and_remove_bus_device(pdev); +	if (zdev_enabled(zdev)) { +		ret = zpci_disable_device(zdev); +		/* +		 * Due to a z/VM vs LPAR inconsistency in the error +		 * state the FH may indicate an enabled device but +		 * disable says the device is already disabled don't +		 * treat it as an error here. +		 */ +		if (ret == -EINVAL) +			ret = 0; +		if (ret) +			return ret; +	} + +	ret = zpci_enable_device(zdev); +	if (ret) +		return ret; + +	if (zdev->dma_table) { +		ret = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, +					 virt_to_phys(zdev->dma_table), &status); +		if (ret) +			zpci_disable_device(zdev); +	} +	return ret; +} +  static ssize_t recover_store(struct device *dev, struct device_attribute *attr,  			     const char *buf, size_t count)  { @@ -56,7 +89,6 @@ static ssize_t recover_store(struct device *dev, struct device_attribute *attr,  	struct pci_dev *pdev = to_pci_dev(dev);  	struct zpci_dev *zdev = to_zpci(pdev);  	int ret = 0; -	u8 status;  	/* Can't use device_remove_self() here as that would lead us to lock  	 * the pci_rescan_remove_lock while holding the device' kernfs lock. @@ -70,6 +102,12 @@ static ssize_t recover_store(struct device *dev, struct device_attribute *attr,  	 */  	kn = sysfs_break_active_protection(&dev->kobj, &attr->attr);  	WARN_ON_ONCE(!kn); + +	/* Device needs to be configured and state must not change */ +	mutex_lock(&zdev->state_lock); +	if (zdev->state != ZPCI_FN_STATE_CONFIGURED) +		goto out; +  	/* device_remove_file() serializes concurrent calls ignoring all but  	 * the first  	 */ @@ -82,35 +120,13 @@ static ssize_t recover_store(struct device *dev, struct device_attribute *attr,  	 */  	pci_lock_rescan_remove();  	if (pci_dev_is_added(pdev)) { -		pci_stop_and_remove_bus_device(pdev); -		if (zdev_enabled(zdev)) { -			ret = zpci_disable_device(zdev); -			/* -			 * Due to a z/VM vs LPAR inconsistency in the error -			 * state the FH may indicate an enabled device but -			 * disable says the device is already disabled don't -			 * treat it as an error here. -			 */ -			if (ret == -EINVAL) -				ret = 0; -			if (ret) -				goto out; -		} - -		ret = zpci_enable_device(zdev); -		if (ret) -			goto out; - -		if (zdev->dma_table) { -			ret = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, -						 virt_to_phys(zdev->dma_table), &status); -			if (ret) -				zpci_disable_device(zdev); -		} +		ret = _do_recover(pdev, zdev);  	} -out:  	pci_rescan_bus(zdev->zbus->bus);  	pci_unlock_rescan_remove(); + +out: +	mutex_unlock(&zdev->state_lock);  	if (kn)  		sysfs_unbreak_active_protection(kn);  	return ret ? ret : count;  |