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

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>

authored by

Heinz Graalfs and committed by
Christian Borntraeger
2e021043 1ee0bc55

+28 -7
+28 -7
drivers/s390/kvm/virtio_ccw.c
··· 636 636 struct virtqueue *vq; 637 637 struct virtio_driver *drv; 638 638 639 + if (!vcdev) 640 + return; 639 641 /* Check if it's a notification from the host. */ 640 642 if ((intparm == 0) && 641 643 (scsw_stctl(&irb->scsw) == ··· 736 734 return 0; 737 735 } 738 736 737 + static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) 738 + { 739 + unsigned long flags; 740 + struct virtio_ccw_device *vcdev; 741 + 742 + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 743 + vcdev = dev_get_drvdata(&cdev->dev); 744 + if (!vcdev) { 745 + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 746 + return NULL; 747 + } 748 + dev_set_drvdata(&cdev->dev, NULL); 749 + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 750 + return vcdev; 751 + } 752 + 739 753 static void virtio_ccw_remove(struct ccw_device *cdev) 740 754 { 741 - struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 755 + struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 742 756 743 - if (cdev->online) { 757 + if (vcdev && cdev->online) 744 758 unregister_virtio_device(&vcdev->vdev); 745 - dev_set_drvdata(&cdev->dev, NULL); 746 - } 747 759 cdev->handler = NULL; 748 760 } 749 761 750 762 static int virtio_ccw_offline(struct ccw_device *cdev) 751 763 { 752 - struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 764 + struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 753 765 754 - unregister_virtio_device(&vcdev->vdev); 755 - dev_set_drvdata(&cdev->dev, NULL); 766 + if (vcdev) 767 + unregister_virtio_device(&vcdev->vdev); 756 768 return 0; 757 769 } 758 770 ··· 775 759 { 776 760 int ret; 777 761 struct virtio_ccw_device *vcdev; 762 + unsigned long flags; 778 763 779 764 vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); 780 765 if (!vcdev) { ··· 803 786 INIT_LIST_HEAD(&vcdev->virtqueues); 804 787 spin_lock_init(&vcdev->lock); 805 788 789 + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 806 790 dev_set_drvdata(&cdev->dev, vcdev); 791 + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 807 792 vcdev->vdev.id.vendor = cdev->id.cu_type; 808 793 vcdev->vdev.id.device = cdev->id.cu_model; 809 794 ret = register_virtio_device(&vcdev->vdev); ··· 816 797 } 817 798 return 0; 818 799 out_put: 800 + spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 819 801 dev_set_drvdata(&cdev->dev, NULL); 802 + spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 820 803 put_device(&vcdev->vdev.dev); 821 804 return ret; 822 805 out_free: