Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Heinz Graalfs and committed by
Rusty Russell
e75279c4 e2dcdfe9

+37 -12
+37 -12
drivers/s390/kvm/virtio_ccw.c
··· 27 27 #include <linux/module.h> 28 28 #include <linux/io.h> 29 29 #include <linux/kvm_para.h> 30 + #include <linux/notifier.h> 30 31 #include <asm/setup.h> 31 32 #include <asm/irq.h> 32 33 #include <asm/cio.h> ··· 63 62 struct vq_config_block *config_block; 64 63 bool is_thinint; 65 64 bool going_away; 65 + bool device_lost; 66 66 void *airq_info; 67 67 }; 68 68 ··· 1012 1010 unsigned long flags; 1013 1011 struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 1014 1012 1015 - if (vcdev && cdev->online) 1013 + if (vcdev && cdev->online) { 1014 + if (vcdev->device_lost) 1015 + virtio_break_device(&vcdev->vdev); 1016 1016 unregister_virtio_device(&vcdev->vdev); 1017 - spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 1018 - dev_set_drvdata(&cdev->dev, NULL); 1019 - spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 1017 + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 1018 + dev_set_drvdata(&cdev->dev, NULL); 1019 + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 1020 + } 1020 1021 cdev->handler = NULL; 1021 1022 } 1022 1023 ··· 1028 1023 unsigned long flags; 1029 1024 struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 1030 1025 1031 - if (vcdev) { 1032 - unregister_virtio_device(&vcdev->vdev); 1033 - spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 1034 - dev_set_drvdata(&cdev->dev, NULL); 1035 - spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 1036 - } 1026 + if (!vcdev) 1027 + return 0; 1028 + if (vcdev->device_lost) 1029 + virtio_break_device(&vcdev->vdev); 1030 + unregister_virtio_device(&vcdev->vdev); 1031 + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 1032 + dev_set_drvdata(&cdev->dev, NULL); 1033 + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 1037 1034 return 0; 1038 1035 } 1039 1036 ··· 1103 1096 1104 1097 static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) 1105 1098 { 1106 - /* TODO: Check whether we need special handling here. */ 1107 - return 0; 1099 + int rc; 1100 + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 1101 + 1102 + /* 1103 + * Make sure vcdev is set 1104 + * i.e. set_offline/remove callback not already running 1105 + */ 1106 + if (!vcdev) 1107 + return NOTIFY_DONE; 1108 + 1109 + switch (event) { 1110 + case CIO_GONE: 1111 + vcdev->device_lost = true; 1112 + rc = NOTIFY_DONE; 1113 + break; 1114 + default: 1115 + rc = NOTIFY_DONE; 1116 + break; 1117 + } 1118 + return rc; 1108 1119 } 1109 1120 1110 1121 static struct ccw_device_id virtio_ids[] = {