diff options
| author | Heinz Graalfs <graalfs@linux.vnet.ibm.com> | 2014-03-05 15:23:54 +0100 | 
|---|---|---|
| committer | Cornelia Huck <cornelia.huck@de.ibm.com> | 2014-03-06 10:22:40 +0100 | 
| 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 {  	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;  } | 
