diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2013-02-14 16:02:13 -0500 |
---|---|---|
committer | Alex Williamson <alex.williamson@redhat.com> | 2013-02-14 16:02:13 -0500 |
commit | e014e9444aedc365742d533e1443b22470cc67b9 (patch) | |
tree | 5647eca5f1a28297b023fd1a7b9b1c37030de9a6 /drivers/vfio | |
parent | 906ee99dd2a5c819c1171ce5eaf6c080c027e58c (diff) |
vfio: Protect vfio_dev_present against device_del
vfio_dev_present is meant to give us a wait_event callback so that we
can block removing a device from vfio until it becomes unused. The
root of this check depends on being able to get the iommu group from
the device. Unfortunately if the BUS_NOTIFY_DEL_DEVICE notifier has
fired then the device-group reference is no longer searchable and we
fail the lookup.
We don't need to go to such extents for this though. We have a
reference to the device, from which we can acquire a reference to the
group. We can then use the group reference to search for the device
and properly block removal.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r-- | drivers/vfio/vfio.c | 33 |
1 files changed, 12 insertions, 21 deletions
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 12c264d3b058..8e6dcecbc407 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c | |||
@@ -642,33 +642,16 @@ int vfio_add_group_dev(struct device *dev, | |||
642 | } | 642 | } |
643 | EXPORT_SYMBOL_GPL(vfio_add_group_dev); | 643 | EXPORT_SYMBOL_GPL(vfio_add_group_dev); |
644 | 644 | ||
645 | /* Test whether a struct device is present in our tracking */ | 645 | /* Given a referenced group, check if it contains the device */ |
646 | static bool vfio_dev_present(struct device *dev) | 646 | static bool vfio_dev_present(struct vfio_group *group, struct device *dev) |
647 | { | 647 | { |
648 | struct iommu_group *iommu_group; | ||
649 | struct vfio_group *group; | ||
650 | struct vfio_device *device; | 648 | struct vfio_device *device; |
651 | 649 | ||
652 | iommu_group = iommu_group_get(dev); | ||
653 | if (!iommu_group) | ||
654 | return false; | ||
655 | |||
656 | group = vfio_group_get_from_iommu(iommu_group); | ||
657 | if (!group) { | ||
658 | iommu_group_put(iommu_group); | ||
659 | return false; | ||
660 | } | ||
661 | |||
662 | device = vfio_group_get_device(group, dev); | 650 | device = vfio_group_get_device(group, dev); |
663 | if (!device) { | 651 | if (!device) |
664 | vfio_group_put(group); | ||
665 | iommu_group_put(iommu_group); | ||
666 | return false; | 652 | return false; |
667 | } | ||
668 | 653 | ||
669 | vfio_device_put(device); | 654 | vfio_device_put(device); |
670 | vfio_group_put(group); | ||
671 | iommu_group_put(iommu_group); | ||
672 | return true; | 655 | return true; |
673 | } | 656 | } |
674 | 657 | ||
@@ -682,10 +665,18 @@ void *vfio_del_group_dev(struct device *dev) | |||
682 | struct iommu_group *iommu_group = group->iommu_group; | 665 | struct iommu_group *iommu_group = group->iommu_group; |
683 | void *device_data = device->device_data; | 666 | void *device_data = device->device_data; |
684 | 667 | ||
668 | /* | ||
669 | * The group exists so long as we have a device reference. Get | ||
670 | * a group reference and use it to scan for the device going away. | ||
671 | */ | ||
672 | vfio_group_get(group); | ||
673 | |||
685 | vfio_device_put(device); | 674 | vfio_device_put(device); |
686 | 675 | ||
687 | /* TODO send a signal to encourage this to be released */ | 676 | /* TODO send a signal to encourage this to be released */ |
688 | wait_event(vfio.release_q, !vfio_dev_present(dev)); | 677 | wait_event(vfio.release_q, !vfio_dev_present(group, dev)); |
678 | |||
679 | vfio_group_put(group); | ||
689 | 680 | ||
690 | iommu_group_put(iommu_group); | 681 | iommu_group_put(iommu_group); |
691 | 682 | ||