Skip to content

Commit 79629b2

Browse files
Heinz Graalfscohuck
authored andcommitted
virtio_ccw: fix hang in set offline processing
During set offline processing virtio_grab_drvdata() incorrectly calls dev_set_drvdata() to remove the virtio_ccw_device from the parent ccw_device's driver data. This is wrong and ends up in a hang during virtio_ccw_reset(), as the interrupt handler still has need of the virtio_ccw_device. A new field 'going_away' is introduced in struct virtio_ccw_device to control the usage of the ccw_device's driver data pointer in virtio_grab_drvdata(). Signed-off-by: Heinz Graalfs <[email protected]> Reviewed-by: Cornelia Huck <[email protected]> Signed-off-by: Cornelia Huck <[email protected]>
1 parent 96b1453 commit 79629b2

File tree

1 file changed

+13
-3
lines changed

1 file changed

+13
-3
lines changed

drivers/s390/kvm/virtio_ccw.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ struct virtio_ccw_device {
6161
unsigned long indicators2;
6262
struct vq_config_block *config_block;
6363
bool is_thinint;
64+
bool going_away;
6465
void *airq_info;
6566
};
6667

@@ -995,30 +996,39 @@ static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev)
995996

996997
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
997998
vcdev = dev_get_drvdata(&cdev->dev);
998-
if (!vcdev) {
999+
if (!vcdev || vcdev->going_away) {
9991000
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
10001001
return NULL;
10011002
}
1002-
dev_set_drvdata(&cdev->dev, NULL);
1003+
vcdev->going_away = true;
10031004
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
10041005
return vcdev;
10051006
}
10061007

10071008
static void virtio_ccw_remove(struct ccw_device *cdev)
10081009
{
1010+
unsigned long flags;
10091011
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
10101012

10111013
if (vcdev && cdev->online)
10121014
unregister_virtio_device(&vcdev->vdev);
1015+
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
1016+
dev_set_drvdata(&cdev->dev, NULL);
1017+
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
10131018
cdev->handler = NULL;
10141019
}
10151020

10161021
static int virtio_ccw_offline(struct ccw_device *cdev)
10171022
{
1023+
unsigned long flags;
10181024
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
10191025

1020-
if (vcdev)
1026+
if (vcdev) {
10211027
unregister_virtio_device(&vcdev->vdev);
1028+
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
1029+
dev_set_drvdata(&cdev->dev, NULL);
1030+
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
1031+
}
10221032
return 0;
10231033
}
10241034

0 commit comments

Comments
 (0)