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

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>

authored by

Lu Baolu and committed by
Joerg Roedel
dba9ca9d 293f2564

+28 -2
+28 -2
drivers/iommu/iommu.c
··· 495 495 496 496 void iommu_release_device(struct device *dev) 497 497 { 498 + struct iommu_group *group = dev->iommu_group; 499 + struct group_device *device; 498 500 const struct iommu_ops *ops; 499 501 500 - if (!dev->iommu) 502 + if (!dev->iommu || !group) 501 503 return; 502 504 503 505 iommu_device_unlink(dev->iommu->iommu_dev, dev); 504 506 507 + mutex_lock(&group->mutex); 508 + device = __iommu_group_remove_device(group, dev); 509 + 510 + /* 511 + * If the group has become empty then ownership must have been released, 512 + * and the current domain must be set back to NULL or the default 513 + * domain. 514 + */ 515 + if (list_empty(&group->devices)) 516 + WARN_ON(group->owner_cnt || 517 + group->domain != group->default_domain); 518 + 519 + /* 520 + * release_device() must stop using any attached domain on the device. 521 + * If there are still other devices in the group they are not effected 522 + * by this callback. 523 + * 524 + * The IOMMU driver must set the device to either an identity or 525 + * blocking translation and stop using any domain pointer, as it is 526 + * going to be freed. 527 + */ 505 528 ops = dev_iommu_ops(dev); 506 529 if (ops->release_device) 507 530 ops->release_device(dev); 531 + mutex_unlock(&group->mutex); 508 532 509 - iommu_group_remove_device(dev); 533 + if (device) 534 + __iommu_group_release_device(group, device); 535 + 510 536 module_put(ops->owner); 511 537 dev_iommu_free(dev); 512 538 }