Skip to content

Commit bcb5d6c

Browse files
Gerd Bayerhcahca
authored andcommitted
s390/pci: introduce lock to synchronize state of zpci_dev's
There's a number of tasks that need the state of a zpci device to be stable. Other tasks need to be synchronized as they change the state. State changes could be generated by the system as availability or error events, or be requested by the user through manipulations in sysfs. Some other actions accessible through sysfs - like device resets - need the state to be stable. Unsynchronized state handling could lead to unusable devices. This has been observed in cases of concurrent state changes through systemd udev rules and DPM boot control. Some breakage can be provoked by artificial tests, e.g. through repetitively injecting "recover" on a PCI function through sysfs while running a "hotplug remove/add" in a loop through a PCI slot's "power" attribute in sysfs. After a few iterations this could result in a kernel oops. So introduce a new mutex "state_lock" to guard the state property of the struct zpci_dev. Acquire this lock in all task that modify the state: - hotplug add and remove, through the PCI hotplug slot entry, - avaiability events, as reported by the platform, - error events, as reported by the platform, - during device resets, explicit through sysfs requests or implict through the common PCI layer. Break out an inner _do_recover() routine out of recover_store() to separte the necessary synchronizations from the actual manipulations of the zpci_dev required for the reset. With the following changes I was able to run the inject loops for hours without hitting an error. Signed-off-by: Gerd Bayer <[email protected]> Reviewed-by: Niklas Schnelle <[email protected]> Signed-off-by: Heiko Carstens <[email protected]>
1 parent 0d48566 commit bcb5d6c

File tree

5 files changed

+106
-52
lines changed

5 files changed

+106
-52
lines changed

arch/s390/include/asm/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ struct zpci_dev {
122122
struct rcu_head rcu;
123123
struct hotplug_slot hotplug_slot;
124124

125+
struct mutex state_lock; /* protect state changes */
125126
enum zpci_state state;
126127
u32 fid; /* function ID, used by sclp */
127128
u32 fh; /* function handle, used by insn's */

arch/s390/pci/pci.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/jump_label.h>
2929
#include <linux/pci.h>
3030
#include <linux/printk.h>
31+
#include <linux/lockdep.h>
3132

3233
#include <asm/isc.h>
3334
#include <asm/airq.h>
@@ -730,12 +731,12 @@ EXPORT_SYMBOL_GPL(zpci_disable_device);
730731
* equivalent to its state during boot when first probing a driver.
731732
* Consequently after reset the PCI function requires re-initialization via the
732733
* common PCI code including re-enabling IRQs via pci_alloc_irq_vectors()
733-
* and enabling the function via e.g.pci_enablde_device_flags().The caller
734+
* and enabling the function via e.g. pci_enable_device_flags(). The caller
734735
* must guard against concurrent reset attempts.
735736
*
736737
* In most cases this function should not be called directly but through
737738
* pci_reset_function() or pci_reset_bus() which handle the save/restore and
738-
* locking.
739+
* locking - asserted by lockdep.
739740
*
740741
* Return: 0 on success and an error value otherwise
741742
*/
@@ -744,6 +745,7 @@ int zpci_hot_reset_device(struct zpci_dev *zdev)
744745
u8 status;
745746
int rc;
746747

748+
lockdep_assert_held(&zdev->state_lock);
747749
zpci_dbg(3, "rst fid:%x, fh:%x\n", zdev->fid, zdev->fh);
748750
if (zdev_enabled(zdev)) {
749751
/* Disables device access, DMAs and IRQs (reset state) */
@@ -806,6 +808,7 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state)
806808
zdev->state = state;
807809

808810
kref_init(&zdev->kref);
811+
mutex_init(&zdev->state_lock);
809812
mutex_init(&zdev->fmb_lock);
810813
mutex_init(&zdev->kzdev_lock);
811814

@@ -870,6 +873,10 @@ int zpci_deconfigure_device(struct zpci_dev *zdev)
870873
{
871874
int rc;
872875

876+
lockdep_assert_held(&zdev->state_lock);
877+
if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
878+
return 0;
879+
873880
if (zdev->zbus->bus)
874881
zpci_bus_remove_device(zdev, false);
875882

arch/s390/pci/pci_event.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
267267
zpci_err_hex(ccdf, sizeof(*ccdf));
268268

269269
if (zdev) {
270+
mutex_lock(&zdev->state_lock);
270271
zpci_update_fh(zdev, ccdf->fh);
271272
if (zdev->zbus->bus)
272273
pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
@@ -294,6 +295,8 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
294295
}
295296
pci_dev_put(pdev);
296297
no_pdev:
298+
if (zdev)
299+
mutex_unlock(&zdev->state_lock);
297300
zpci_zdev_put(zdev);
298301
}
299302

@@ -326,6 +329,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
326329

327330
zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n",
328331
ccdf->fid, ccdf->fh, ccdf->pec);
332+
333+
if (existing_zdev)
334+
mutex_lock(&zdev->state_lock);
335+
329336
switch (ccdf->pec) {
330337
case 0x0301: /* Reserved|Standby -> Configured */
331338
if (!zdev) {
@@ -383,8 +390,10 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
383390
default:
384391
break;
385392
}
386-
if (existing_zdev)
393+
if (existing_zdev) {
394+
mutex_unlock(&zdev->state_lock);
387395
zpci_zdev_put(zdev);
396+
}
388397
}
389398

390399
void zpci_event_availability(void *data)

arch/s390/pci/pci_sysfs.c

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,46 @@ static ssize_t mio_enabled_show(struct device *dev,
4949
}
5050
static DEVICE_ATTR_RO(mio_enabled);
5151

52+
static int _do_recover(struct pci_dev *pdev, struct zpci_dev *zdev)
53+
{
54+
u8 status;
55+
int ret;
56+
57+
pci_stop_and_remove_bus_device(pdev);
58+
if (zdev_enabled(zdev)) {
59+
ret = zpci_disable_device(zdev);
60+
/*
61+
* Due to a z/VM vs LPAR inconsistency in the error
62+
* state the FH may indicate an enabled device but
63+
* disable says the device is already disabled don't
64+
* treat it as an error here.
65+
*/
66+
if (ret == -EINVAL)
67+
ret = 0;
68+
if (ret)
69+
return ret;
70+
}
71+
72+
ret = zpci_enable_device(zdev);
73+
if (ret)
74+
return ret;
75+
76+
if (zdev->dma_table) {
77+
ret = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
78+
virt_to_phys(zdev->dma_table), &status);
79+
if (ret)
80+
zpci_disable_device(zdev);
81+
}
82+
return ret;
83+
}
84+
5285
static ssize_t recover_store(struct device *dev, struct device_attribute *attr,
5386
const char *buf, size_t count)
5487
{
5588
struct kernfs_node *kn;
5689
struct pci_dev *pdev = to_pci_dev(dev);
5790
struct zpci_dev *zdev = to_zpci(pdev);
5891
int ret = 0;
59-
u8 status;
6092

6193
/* Can't use device_remove_self() here as that would lead us to lock
6294
* 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,
70102
*/
71103
kn = sysfs_break_active_protection(&dev->kobj, &attr->attr);
72104
WARN_ON_ONCE(!kn);
105+
106+
/* Device needs to be configured and state must not change */
107+
mutex_lock(&zdev->state_lock);
108+
if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
109+
goto out;
110+
73111
/* device_remove_file() serializes concurrent calls ignoring all but
74112
* the first
75113
*/
@@ -82,35 +120,13 @@ static ssize_t recover_store(struct device *dev, struct device_attribute *attr,
82120
*/
83121
pci_lock_rescan_remove();
84122
if (pci_dev_is_added(pdev)) {
85-
pci_stop_and_remove_bus_device(pdev);
86-
if (zdev_enabled(zdev)) {
87-
ret = zpci_disable_device(zdev);
88-
/*
89-
* Due to a z/VM vs LPAR inconsistency in the error
90-
* state the FH may indicate an enabled device but
91-
* disable says the device is already disabled don't
92-
* treat it as an error here.
93-
*/
94-
if (ret == -EINVAL)
95-
ret = 0;
96-
if (ret)
97-
goto out;
98-
}
99-
100-
ret = zpci_enable_device(zdev);
101-
if (ret)
102-
goto out;
103-
104-
if (zdev->dma_table) {
105-
ret = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
106-
virt_to_phys(zdev->dma_table), &status);
107-
if (ret)
108-
zpci_disable_device(zdev);
109-
}
123+
ret = _do_recover(pdev, zdev);
110124
}
111-
out:
112125
pci_rescan_bus(zdev->zbus->bus);
113126
pci_unlock_rescan_remove();
127+
128+
out:
129+
mutex_unlock(&zdev->state_lock);
114130
if (kn)
115131
sysfs_unbreak_active_protection(kn);
116132
return ret ? ret : count;

drivers/pci/hotplug/s390_pci_hpc.c

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,58 +26,79 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
2626
hotplug_slot);
2727
int rc;
2828

29-
if (zdev->state != ZPCI_FN_STATE_STANDBY)
30-
return -EIO;
29+
mutex_lock(&zdev->state_lock);
30+
if (zdev->state != ZPCI_FN_STATE_STANDBY) {
31+
rc = -EIO;
32+
goto out;
33+
}
3134

3235
rc = sclp_pci_configure(zdev->fid);
3336
zpci_dbg(3, "conf fid:%x, rc:%d\n", zdev->fid, rc);
3437
if (rc)
35-
return rc;
38+
goto out;
3639
zdev->state = ZPCI_FN_STATE_CONFIGURED;
3740

38-
return zpci_scan_configured_device(zdev, zdev->fh);
41+
rc = zpci_scan_configured_device(zdev, zdev->fh);
42+
out:
43+
mutex_unlock(&zdev->state_lock);
44+
return rc;
3945
}
4046

4147
static int disable_slot(struct hotplug_slot *hotplug_slot)
4248
{
4349
struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
4450
hotplug_slot);
45-
struct pci_dev *pdev;
51+
struct pci_dev *pdev = NULL;
52+
int rc;
4653

47-
if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
48-
return -EIO;
54+
mutex_lock(&zdev->state_lock);
55+
if (zdev->state != ZPCI_FN_STATE_CONFIGURED) {
56+
rc = -EIO;
57+
goto out;
58+
}
4959

5060
pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
5161
if (pdev && pci_num_vf(pdev)) {
5262
pci_dev_put(pdev);
53-
return -EBUSY;
63+
rc = -EBUSY;
64+
goto out;
5465
}
55-
pci_dev_put(pdev);
5666

57-
return zpci_deconfigure_device(zdev);
67+
rc = zpci_deconfigure_device(zdev);
68+
out:
69+
mutex_unlock(&zdev->state_lock);
70+
if (pdev)
71+
pci_dev_put(pdev);
72+
return rc;
5873
}
5974

6075
static int reset_slot(struct hotplug_slot *hotplug_slot, bool probe)
6176
{
6277
struct zpci_dev *zdev = container_of(hotplug_slot, struct zpci_dev,
6378
hotplug_slot);
79+
int rc = -EIO;
6480

65-
if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
66-
return -EIO;
6781
/*
68-
* We can't take the zdev->lock as reset_slot may be called during
69-
* probing and/or device removal which already happens under the
70-
* zdev->lock. Instead the user should use the higher level
71-
* pci_reset_function() or pci_bus_reset() which hold the PCI device
72-
* lock preventing concurrent removal. If not using these functions
73-
* holding the PCI device lock is required.
82+
* If we can't get the zdev->state_lock the device state is
83+
* currently undergoing a transition and we bail out - just
84+
* the same as if the device's state is not configured at all.
7485
*/
86+
if (!mutex_trylock(&zdev->state_lock))
87+
return rc;
7588

76-
/* As long as the function is configured we can reset */
77-
if (probe)
78-
return 0;
89+
/* We can reset only if the function is configured */
90+
if (zdev->state != ZPCI_FN_STATE_CONFIGURED)
91+
goto out;
92+
93+
if (probe) {
94+
rc = 0;
95+
goto out;
96+
}
7997

80-
return zpci_hot_reset_device(zdev);
98+
rc = zpci_hot_reset_device(zdev);
99+
out:
100+
mutex_unlock(&zdev->state_lock);
101+
return rc;
81102
}
82103

83104
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)

0 commit comments

Comments
 (0)