diff options
author | Heinz Graalfs <graalfs@linux.vnet.ibm.com> | 2014-03-05 09:23:54 -0500 |
---|---|---|
committer | Cornelia Huck <cornelia.huck@de.ibm.com> | 2014-03-06 04:22:40 -0500 |
commit | 79629b208fc0484ee448c4acfa3762f0350e97ce (patch) | |
tree | c9a706ec6d539aad6f1aacd4201577ddcc2a8d24 | |
parent | 96b14536d935848cffd904f583f67c66169002d8 (diff) |
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 <graalfs@linux.vnet.ibm.com>
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 6a2b5fdcd552..1e1fc671f89a 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c | |||
@@ -61,6 +61,7 @@ struct virtio_ccw_device { | |||
61 | unsigned long indicators2; | 61 | unsigned long indicators2; |
62 | struct vq_config_block *config_block; | 62 | struct vq_config_block *config_block; |
63 | bool is_thinint; | 63 | bool is_thinint; |
64 | bool going_away; | ||
64 | void *airq_info; | 65 | void *airq_info; |
65 | }; | 66 | }; |
66 | 67 | ||
@@ -995,30 +996,39 @@ static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) | |||
995 | 996 | ||
996 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | 997 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); |
997 | vcdev = dev_get_drvdata(&cdev->dev); | 998 | vcdev = dev_get_drvdata(&cdev->dev); |
998 | if (!vcdev) { | 999 | if (!vcdev || vcdev->going_away) { |
999 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | 1000 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); |
1000 | return NULL; | 1001 | return NULL; |
1001 | } | 1002 | } |
1002 | dev_set_drvdata(&cdev->dev, NULL); | 1003 | vcdev->going_away = true; |
1003 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | 1004 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); |
1004 | return vcdev; | 1005 | return vcdev; |
1005 | } | 1006 | } |
1006 | 1007 | ||
1007 | static void virtio_ccw_remove(struct ccw_device *cdev) | 1008 | static void virtio_ccw_remove(struct ccw_device *cdev) |
1008 | { | 1009 | { |
1010 | unsigned long flags; | ||
1009 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); | 1011 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
1010 | 1012 | ||
1011 | if (vcdev && cdev->online) | 1013 | if (vcdev && cdev->online) |
1012 | unregister_virtio_device(&vcdev->vdev); | 1014 | 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); | ||
1013 | cdev->handler = NULL; | 1018 | cdev->handler = NULL; |
1014 | } | 1019 | } |
1015 | 1020 | ||
1016 | static int virtio_ccw_offline(struct ccw_device *cdev) | 1021 | static int virtio_ccw_offline(struct ccw_device *cdev) |
1017 | { | 1022 | { |
1023 | unsigned long flags; | ||
1018 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); | 1024 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
1019 | 1025 | ||
1020 | if (vcdev) | 1026 | if (vcdev) { |
1021 | unregister_virtio_device(&vcdev->vdev); | 1027 | 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 | } | ||
1022 | return 0; | 1032 | return 0; |
1023 | } | 1033 | } |
1024 | 1034 | ||