aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeinz Graalfs <graalfs@linux.vnet.ibm.com>2014-03-05 09:23:54 -0500
committerCornelia Huck <cornelia.huck@de.ibm.com>2014-03-06 04:22:40 -0500
commit79629b208fc0484ee448c4acfa3762f0350e97ce (patch)
treec9a706ec6d539aad6f1aacd4201577ddcc2a8d24
parent96b14536d935848cffd904f583f67c66169002d8 (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.c16
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
1007static void virtio_ccw_remove(struct ccw_device *cdev) 1008static 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
1016static int virtio_ccw_offline(struct ccw_device *cdev) 1021static 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