From: Heinz Graalfs <graalfs@xxxxxxxxxxxxxxxxxx> 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 <graalfs@xxxxxxxxxxxxxxxxxx> Reviewed-by: Cornelia Huck <cornelia.huck@xxxxxxxxxx> Signed-off-by: Cornelia Huck <cornelia.huck@xxxxxxxxxx> --- drivers/s390/kvm/virtio_ccw.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 6a2b5fd..1e1fc67 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c @@ -61,6 +61,7 @@ struct virtio_ccw_device { unsigned long indicators2; struct vq_config_block *config_block; bool is_thinint; + bool going_away; void *airq_info; }; @@ -995,30 +996,39 @@ static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) spin_lock_irqsave(get_ccwdev_lock(cdev), flags); vcdev = dev_get_drvdata(&cdev->dev); - if (!vcdev) { + if (!vcdev || vcdev->going_away) { spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); return NULL; } - dev_set_drvdata(&cdev->dev, NULL); + vcdev->going_away = true; spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); return vcdev; } static void virtio_ccw_remove(struct ccw_device *cdev) { + unsigned long flags; struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); if (vcdev && cdev->online) unregister_virtio_device(&vcdev->vdev); + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + dev_set_drvdata(&cdev->dev, NULL); + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); cdev->handler = NULL; } static int virtio_ccw_offline(struct ccw_device *cdev) { + unsigned long flags; struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); - if (vcdev) + if (vcdev) { unregister_virtio_device(&vcdev->vdev); + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); + dev_set_drvdata(&cdev->dev, NULL); + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); + } return 0; } -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html