arm64: dma-mapping: Support attach after detach case
Iommu client drivers which use DOMAIN_ATTR_ATOMIC need to call
arm_iommu_detach_device() when their driver enters low power mode to
disable their clock/power votes. On wakeup they call
arm_iommu_attach_device() to reenable the iommu. To support this
usecase, ensure dma allocator state is freed as part of
arm_iommu_release_mapping().
Additionally, move memory allocations for dma allocator state to the
first arm_iommu_attach_device() call to allow clients to set iommu
domain attributes first. This is expected to reduce memory use.
Change-Id: I0df29ee0de52c6f9b6621ec6f0105201c6ee5996
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index b5e817b..04a5c09 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -855,29 +855,28 @@
spin_unlock_irqrestore(&mapping->lock, flags);
}
-
/**
- * fast_smmu_attach_device
+ * fast_smmu_init_mapping
* @dev: valid struct device pointer
* @mapping: io address space mapping structure (returned from
- * fast_smmu_create_mapping)
+ * arm_iommu_create_mapping)
*
- * Attaches specified io address space mapping to the provided device,
- * this replaces the dma operations (dma_map_ops pointer) with the
- * IOMMU aware version. More than one client might be attached to
- * the same io address space mapping.
+ * Called the first time a device is attached to this mapping.
+ * Not for dma client use.
*/
-int fast_smmu_attach_device(struct device *dev,
+int fast_smmu_init_mapping(struct device *dev,
struct dma_iommu_mapping *mapping)
{
- int atomic_domain = 1;
+ int err, atomic_domain = 1;
struct iommu_domain *domain = mapping->domain;
struct iommu_group *group;
struct iommu_pgtbl_info info;
u64 size = (u64)mapping->bits << PAGE_SHIFT;
- if (mapping->base + size > (SZ_1G * 4ULL))
+ if (mapping->base + size > (SZ_1G * 4ULL)) {
+ dev_err(dev, "Iova end address too large\n");
return -EINVAL;
+ }
if (iommu_domain_set_attr(domain, DOMAIN_ATTR_ATOMIC,
&atomic_domain))
@@ -894,54 +893,67 @@
group = dev->iommu_group;
if (!group) {
dev_err(dev, "No iommu associated with device\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto release_mapping;
}
if (iommu_get_domain_for_dev(dev)) {
dev_err(dev, "Device already attached to other iommu_domain\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto release_mapping;
}
- if (iommu_attach_group(mapping->domain, group))
- return -EINVAL;
+ /*
+ * Need to attach prior to calling DOMAIN_ATTR_PGTBL_INFO and then
+ * detach to be in the expected state. Its a bit messy.
+ */
+ if (iommu_attach_group(mapping->domain, group)) {
+ err = -EINVAL;
+ goto release_mapping;
+ }
if (iommu_domain_get_attr(domain, DOMAIN_ATTR_PGTBL_INFO,
&info)) {
dev_err(dev, "Couldn't get page table info\n");
- fast_smmu_detach_device(dev, mapping);
- return -EINVAL;
+ err = -EINVAL;
+ goto detach_group;
}
mapping->fast->pgtbl_pmds = info.pmds;
if (iommu_domain_get_attr(domain, DOMAIN_ATTR_PAGE_TABLE_IS_COHERENT,
- &mapping->fast->is_smmu_pt_coherent))
- return -EINVAL;
+ &mapping->fast->is_smmu_pt_coherent)) {
+ err = -EINVAL;
+ goto detach_group;
+ }
mapping->fast->notifier.notifier_call = fast_smmu_notify;
av8l_register_notify(&mapping->fast->notifier);
- dev->archdata.mapping = mapping;
- set_dma_ops(dev, &fast_smmu_dma_ops);
-
+ iommu_detach_group(mapping->domain, group);
+ mapping->ops = &fast_smmu_dma_ops;
return 0;
+
+detach_group:
+ iommu_detach_group(mapping->domain, group);
+release_mapping:
+ kfree(mapping->fast->bitmap);
+ kfree(mapping->fast);
+ return err;
}
-EXPORT_SYMBOL(fast_smmu_attach_device);
/**
- * fast_smmu_detach_device
- * @dev: valid struct device pointer
+ * fast_smmu_release_mapping
+ * @kref: dma_iommu_mapping->kref
*
- * Detaches the provided device from a previously attached map.
- * This voids the dma operations (dma_map_ops pointer)
+ * Cleans up the given iommu mapping.
*/
-void fast_smmu_detach_device(struct device *dev,
- struct dma_iommu_mapping *mapping)
+void fast_smmu_release_mapping(struct kref *kref)
{
- iommu_detach_group(mapping->domain, dev->iommu_group);
- dev->archdata.mapping = NULL;
- set_dma_ops(dev, NULL);
+ struct dma_iommu_mapping *mapping =
+ container_of(kref, struct dma_iommu_mapping, kref);
kvfree(mapping->fast->bitmap);
kfree(mapping->fast);
+ iommu_domain_free(mapping->domain);
+ kfree(mapping);
}
-EXPORT_SYMBOL(fast_smmu_detach_device);