Commit 65d4418c authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Joerg Roedel

iommu/sva: Restore SVA handle sharing

Prior to commit 092edadd ("iommu: Support mm PASID 1:n with sva
domains") the code allowed a SVA handle to be bound multiple times to the
same (mm, device) pair. This was alluded to in the kdoc comment, but we
had understood this to be more a remark about allowing multiple devices,
not a literal same-driver re-opening the same SVA.

It turns out uacce and idxd were both relying on the core code to handle
reference counting for same-device same-mm scenarios. As this looks hard
to resolve in the drivers bring it back to the core code.

The new design has changed the meaning of the domain->users refcount to
refer to the number of devices that are sharing that domain for the same
mm. This is part of the design to lift the SVA domain de-duplication out
of the drivers.

Return the old behavior by explicitly de-duplicating the struct iommu_sva
handle. The same (mm, device) will return the same handle pointer and the
core code will handle tracking this. The last unbind of the handle will
destroy it.

Fixes: 092edadd ("iommu: Support mm PASID 1:n with sva domains")
Reported-by: default avatarZhangfei Gao <zhangfei.gao@linaro.org>
Closes: https://lore.kernel.org/all/20240221110658.529-1-zhangfei.gao@linaro.org/Tested-by: default avatarZhangfei Gao <zhangfei.gao@linaro.org>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Link: https://lore.kernel.org/r/0-v1-9455fc497a6f+3b4-iommu_sva_sharing_jgg@nvidia.comSigned-off-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 16b1b391
...@@ -41,6 +41,7 @@ static struct iommu_mm_data *iommu_alloc_mm_data(struct mm_struct *mm, struct de ...@@ -41,6 +41,7 @@ static struct iommu_mm_data *iommu_alloc_mm_data(struct mm_struct *mm, struct de
} }
iommu_mm->pasid = pasid; iommu_mm->pasid = pasid;
INIT_LIST_HEAD(&iommu_mm->sva_domains); INIT_LIST_HEAD(&iommu_mm->sva_domains);
INIT_LIST_HEAD(&iommu_mm->sva_handles);
/* /*
* Make sure the write to mm->iommu_mm is not reordered in front of * Make sure the write to mm->iommu_mm is not reordered in front of
* initialization to iommu_mm fields. If it does, readers may see a * initialization to iommu_mm fields. If it does, readers may see a
...@@ -82,6 +83,14 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm ...@@ -82,6 +83,14 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
goto out_unlock; goto out_unlock;
} }
list_for_each_entry(handle, &mm->iommu_mm->sva_handles, handle_item) {
if (handle->dev == dev) {
refcount_inc(&handle->users);
mutex_unlock(&iommu_sva_lock);
return handle;
}
}
handle = kzalloc(sizeof(*handle), GFP_KERNEL); handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (!handle) { if (!handle) {
ret = -ENOMEM; ret = -ENOMEM;
...@@ -108,7 +117,9 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm ...@@ -108,7 +117,9 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm
if (ret) if (ret)
goto out_free_domain; goto out_free_domain;
domain->users = 1; domain->users = 1;
refcount_set(&handle->users, 1);
list_add(&domain->next, &mm->iommu_mm->sva_domains); list_add(&domain->next, &mm->iommu_mm->sva_domains);
list_add(&handle->handle_item, &mm->iommu_mm->sva_handles);
out: out:
mutex_unlock(&iommu_sva_lock); mutex_unlock(&iommu_sva_lock);
...@@ -141,6 +152,12 @@ void iommu_sva_unbind_device(struct iommu_sva *handle) ...@@ -141,6 +152,12 @@ void iommu_sva_unbind_device(struct iommu_sva *handle)
struct device *dev = handle->dev; struct device *dev = handle->dev;
mutex_lock(&iommu_sva_lock); mutex_lock(&iommu_sva_lock);
if (!refcount_dec_and_test(&handle->users)) {
mutex_unlock(&iommu_sva_lock);
return;
}
list_del(&handle->handle_item);
iommu_detach_device_pasid(domain, dev, iommu_mm->pasid); iommu_detach_device_pasid(domain, dev, iommu_mm->pasid);
if (--domain->users == 0) { if (--domain->users == 0) {
list_del(&domain->next); list_del(&domain->next);
......
...@@ -892,11 +892,14 @@ struct iommu_fwspec { ...@@ -892,11 +892,14 @@ struct iommu_fwspec {
struct iommu_sva { struct iommu_sva {
struct device *dev; struct device *dev;
struct iommu_domain *domain; struct iommu_domain *domain;
struct list_head handle_item;
refcount_t users;
}; };
struct iommu_mm_data { struct iommu_mm_data {
u32 pasid; u32 pasid;
struct list_head sva_domains; struct list_head sva_domains;
struct list_head sva_handles;
}; };
int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment