summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
authorHeinz Graalfs <graalfs@linux.vnet.ibm.com>2014-04-28 11:24:05 +0930
committerRusty Russell <rusty@rustcorp.com.au>2014-04-28 11:34:13 +0930
commite75279c4fb853f42004cbabb6dbf6b23188dc163 (patch)
tree623a5644aa98b17d49328acb8e260d39f7f43ab2 /drivers/s390
parente2dcdfe95c0bd67e37db6057edd9c4ee1f1c7b17 (diff)
virtio_ccw: introduce device_lost in virtio_ccw_device
When a device is lost, the common I/O layer calls the notification handler with CIO_GONE: In that event, flag device_lost as true. In case the device had been flagged as lost when the remove/offline callbacks are called, call the new virtio_break_device() function prior to invoking device_unregister(). This avoids hangs of I/O triggered via the device unregistration callbacks. Signed-off-by: Heinz Graalfs <graalfs@linux.vnet.ibm.com> Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/kvm/virtio_ccw.c49
1 files changed, 37 insertions, 12 deletions
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 1e1fc671f89a..d2c0b442bce5 100644
--- a/drivers/s390/kvm/virtio_ccw.c
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/kvm_para.h>
+#include <linux/notifier.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/cio.h>
@@ -62,6 +63,7 @@ struct virtio_ccw_device {
struct vq_config_block *config_block;
bool is_thinint;
bool going_away;
+ bool device_lost;
void *airq_info;
};
@@ -1010,11 +1012,14 @@ 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)
+ if (vcdev && cdev->online) {
+ if (vcdev->device_lost)
+ virtio_break_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);
+ 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;
}
@@ -1023,12 +1028,14 @@ static int virtio_ccw_offline(struct ccw_device *cdev)
unsigned long flags;
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
- 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);
- }
+ if (!vcdev)
+ return 0;
+ if (vcdev->device_lost)
+ virtio_break_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;
}
@@ -1096,8 +1103,26 @@ out_free:
static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event)
{
- /* TODO: Check whether we need special handling here. */
- return 0;
+ int rc;
+ struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
+
+ /*
+ * Make sure vcdev is set
+ * i.e. set_offline/remove callback not already running
+ */
+ if (!vcdev)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case CIO_GONE:
+ vcdev->device_lost = true;
+ rc = NOTIFY_DONE;
+ break;
+ default:
+ rc = NOTIFY_DONE;
+ break;
+ }
+ return rc;
}
static struct ccw_device_id virtio_ids[] = {