Commit 79629b20 authored by Heinz Graalfs's avatar Heinz Graalfs Committed by Cornelia Huck

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: default avatarHeinz Graalfs <graalfs@linux.vnet.ibm.com>
Reviewed-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
parent 96b14536
...@@ -61,6 +61,7 @@ struct virtio_ccw_device { ...@@ -61,6 +61,7 @@ struct virtio_ccw_device {
unsigned long indicators2; unsigned long indicators2;
struct vq_config_block *config_block; struct vq_config_block *config_block;
bool is_thinint; bool is_thinint;
bool going_away;
void *airq_info; void *airq_info;
}; };
...@@ -995,30 +996,39 @@ static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) ...@@ -995,30 +996,39 @@ static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev)
spin_lock_irqsave(get_ccwdev_lock(cdev), flags); spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
vcdev = dev_get_drvdata(&cdev->dev); vcdev = dev_get_drvdata(&cdev->dev);
if (!vcdev) { if (!vcdev || vcdev->going_away) {
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return NULL; return NULL;
} }
dev_set_drvdata(&cdev->dev, NULL); vcdev->going_away = true;
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return vcdev; return vcdev;
} }
static void virtio_ccw_remove(struct ccw_device *cdev) static void virtio_ccw_remove(struct ccw_device *cdev)
{ {
unsigned long flags;
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
if (vcdev && cdev->online) if (vcdev && cdev->online)
unregister_virtio_device(&vcdev->vdev); 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; cdev->handler = NULL;
} }
static int virtio_ccw_offline(struct ccw_device *cdev) static int virtio_ccw_offline(struct ccw_device *cdev)
{ {
unsigned long flags;
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
if (vcdev) if (vcdev) {
unregister_virtio_device(&vcdev->vdev); 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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment