diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/iommu/iommu.c | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 942bab8b9798..419d5655ce61 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -495,18 +495,44 @@ static void __iommu_group_release_device(struct iommu_group *group, void iommu_release_device(struct device *dev) { + struct iommu_group *group = dev->iommu_group; + struct group_device *device; const struct iommu_ops *ops; - if (!dev->iommu) + if (!dev->iommu || !group) return; iommu_device_unlink(dev->iommu->iommu_dev, dev); + mutex_lock(&group->mutex); + device = __iommu_group_remove_device(group, dev); + + /* + * If the group has become empty then ownership must have been released, + * and the current domain must be set back to NULL or the default + * domain. + */ + if (list_empty(&group->devices)) + WARN_ON(group->owner_cnt || + group->domain != group->default_domain); + + /* + * release_device() must stop using any attached domain on the device. + * If there are still other devices in the group they are not effected + * by this callback. + * + * The IOMMU driver must set the device to either an identity or + * blocking translation and stop using any domain pointer, as it is + * going to be freed. + */ ops = dev_iommu_ops(dev); if (ops->release_device) ops->release_device(dev); + mutex_unlock(&group->mutex); + + if (device) + __iommu_group_release_device(group, device); - iommu_group_remove_device(dev); module_put(ops->owner); dev_iommu_free(dev); } |