diff options
author | Heinz Graalfs <graalfs@linux.vnet.ibm.com> | 2014-02-27 08:34:35 -0500 |
---|---|---|
committer | Christian Borntraeger <borntraeger@de.ibm.com> | 2014-03-04 04:41:03 -0500 |
commit | 2e0210432d34bc7f01644905c2bb2d5d9be5b6ac (patch) | |
tree | ecc95786e82a53aeff50efb50913aeb4bb9b9f32 | |
parent | 1ee0bc559dc34fe36a29494faf7b7c91533bd31c (diff) |
virtio_ccw: fix vcdev pointer handling issues
The interrupt handler virtio_ccw_int_handler() using the vcdev pointer
is protected by the ccw_device lock. Resetting the pointer within the
ccw_device structure should be done when holding this lock.
Also resetting the vcdev pointer (under the ccw_device lock) prior to
freeing the vcdev pointer memory removes a critical path.
Signed-off-by: Heinz Graalfs <graalfs@linux.vnet.ibm.com>
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 0fc584832001..413c6304d511 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c | |||
@@ -636,6 +636,8 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev, | |||
636 | struct virtqueue *vq; | 636 | struct virtqueue *vq; |
637 | struct virtio_driver *drv; | 637 | struct virtio_driver *drv; |
638 | 638 | ||
639 | if (!vcdev) | ||
640 | return; | ||
639 | /* Check if it's a notification from the host. */ | 641 | /* Check if it's a notification from the host. */ |
640 | if ((intparm == 0) && | 642 | if ((intparm == 0) && |
641 | (scsw_stctl(&irb->scsw) == | 643 | (scsw_stctl(&irb->scsw) == |
@@ -734,23 +736,37 @@ static int virtio_ccw_probe(struct ccw_device *cdev) | |||
734 | return 0; | 736 | return 0; |
735 | } | 737 | } |
736 | 738 | ||
739 | static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) | ||
740 | { | ||
741 | unsigned long flags; | ||
742 | struct virtio_ccw_device *vcdev; | ||
743 | |||
744 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
745 | vcdev = dev_get_drvdata(&cdev->dev); | ||
746 | if (!vcdev) { | ||
747 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
748 | return NULL; | ||
749 | } | ||
750 | dev_set_drvdata(&cdev->dev, NULL); | ||
751 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
752 | return vcdev; | ||
753 | } | ||
754 | |||
737 | static void virtio_ccw_remove(struct ccw_device *cdev) | 755 | static void virtio_ccw_remove(struct ccw_device *cdev) |
738 | { | 756 | { |
739 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | 757 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
740 | 758 | ||
741 | if (cdev->online) { | 759 | if (vcdev && cdev->online) |
742 | unregister_virtio_device(&vcdev->vdev); | 760 | unregister_virtio_device(&vcdev->vdev); |
743 | dev_set_drvdata(&cdev->dev, NULL); | ||
744 | } | ||
745 | cdev->handler = NULL; | 761 | cdev->handler = NULL; |
746 | } | 762 | } |
747 | 763 | ||
748 | static int virtio_ccw_offline(struct ccw_device *cdev) | 764 | static int virtio_ccw_offline(struct ccw_device *cdev) |
749 | { | 765 | { |
750 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | 766 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
751 | 767 | ||
752 | unregister_virtio_device(&vcdev->vdev); | 768 | if (vcdev) |
753 | dev_set_drvdata(&cdev->dev, NULL); | 769 | unregister_virtio_device(&vcdev->vdev); |
754 | return 0; | 770 | return 0; |
755 | } | 771 | } |
756 | 772 | ||
@@ -759,6 +775,7 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
759 | { | 775 | { |
760 | int ret; | 776 | int ret; |
761 | struct virtio_ccw_device *vcdev; | 777 | struct virtio_ccw_device *vcdev; |
778 | unsigned long flags; | ||
762 | 779 | ||
763 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); | 780 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); |
764 | if (!vcdev) { | 781 | if (!vcdev) { |
@@ -786,7 +803,9 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
786 | INIT_LIST_HEAD(&vcdev->virtqueues); | 803 | INIT_LIST_HEAD(&vcdev->virtqueues); |
787 | spin_lock_init(&vcdev->lock); | 804 | spin_lock_init(&vcdev->lock); |
788 | 805 | ||
806 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
789 | dev_set_drvdata(&cdev->dev, vcdev); | 807 | dev_set_drvdata(&cdev->dev, vcdev); |
808 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
790 | vcdev->vdev.id.vendor = cdev->id.cu_type; | 809 | vcdev->vdev.id.vendor = cdev->id.cu_type; |
791 | vcdev->vdev.id.device = cdev->id.cu_model; | 810 | vcdev->vdev.id.device = cdev->id.cu_model; |
792 | ret = register_virtio_device(&vcdev->vdev); | 811 | ret = register_virtio_device(&vcdev->vdev); |
@@ -797,7 +816,9 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
797 | } | 816 | } |
798 | return 0; | 817 | return 0; |
799 | out_put: | 818 | out_put: |
819 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
800 | dev_set_drvdata(&cdev->dev, NULL); | 820 | dev_set_drvdata(&cdev->dev, NULL); |
821 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
801 | put_device(&vcdev->vdev.dev); | 822 | put_device(&vcdev->vdev.dev); |
802 | return ret; | 823 | return ret; |
803 | out_free: | 824 | out_free: |