summaryrefslogtreecommitdiff
path: root/drivers/iommu
diff options
context:
space:
mode:
authorLu Baolu <baolu.lu@linux.intel.com>2023-03-22 14:49:53 +0800
committerJoerg Roedel <jroedel@suse.de>2023-03-22 15:45:16 +0100
commitdba9ca9d41f5266194a5e8007db50985d271d0a4 (patch)
tree93e7e2fcca587255b62de0376d7485367889e730 /drivers/iommu
parent293f2564f3dd76b749fc31ae4226bd818f860e4f (diff)
downloadlinux-stable-dba9ca9d41f5266194a5e8007db50985d271d0a4.tar.gz
iommu: Same critical region for device release and removal
In a non-driver context, it is crucial to ensure the consistency of a device's iommu ops. Otherwise, it may result in a situation where a device is released but it's iommu ops are still used. Put the ops->release_device and __iommu_group_remove_device() in a same group->mutext critical region, so that, as long as group->mutex is held and the device is in its group's device list, its iommu ops are always consistent. Add check of group ownership if the released device is the last one. Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Link: https://lore.kernel.org/r/20230322064956.263419-4-baolu.lu@linux.intel.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
Diffstat (limited to 'drivers/iommu')
-rw-r--r--drivers/iommu/iommu.c30
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);
}