Add an API to create a dynamic domain from an existing domain. A dynamic domain is a special IOMMU domain that is attached to the same device as the parent domain but is backed by separate pagetables. Devices such as GPUs that support asynchronous methods for switching pagetables can create dynamic domains for each individual instance and map memory into them. The hardware can use the physical address of the pagetable (as queried by DOMAIN_ATTR_TTBR0) to asynchronously switch the hardware to the desired pagetable when needed. Dynamic domains must be created from existing attached non-dynamic domains. The domains will share configuration (pagetable format, context bank, etc). Dynamic domains do not modify the hardware directly - they are typically a wrapper for the pagetable memory and facilitate using the other IOMMU APIs to map and unmap buffers. Signed-off-by: Jordan Crouse <jcrouse@xxxxxxxxxxxxxx> --- drivers/iommu/iommu.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 17 ++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 9a2f196..4ba593b 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1079,6 +1079,31 @@ void iommu_domain_free(struct iommu_domain *domain) } EXPORT_SYMBOL_GPL(iommu_domain_free); +struct iommu_domain *iommu_domain_create_dynamic(struct iommu_domain *parent) +{ + struct iommu_domain *child; + int ret; + + if (!parent || !parent->ops || !parent->ops->domain_init_dynamic) + return NULL; + + child = parent->ops->domain_alloc(IOMMU_DOMAIN_DYNAMIC); + if (child == NULL) + return NULL; + + child->ops = parent->ops; + child->type = IOMMU_DOMAIN_DYNAMIC; + child->pgsize_bitmap = parent->pgsize_bitmap; + + ret = child->ops->domain_init_dynamic(parent, child); + if (!ret) + return child; + + child->ops->domain_free(child); + return NULL; +} +EXPORT_SYMBOL_GPL(iommu_domain_create_dynamic); + static int __iommu_attach_device(struct iommu_domain *domain, struct device *dev) { @@ -1097,6 +1122,10 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev) struct iommu_group *group; int ret; + /* Don't try to attach dynamic domains */ + if (!domain || domain->type == IOMMU_DOMAIN_DYNAMIC) + return -EINVAL; + group = iommu_group_get(dev); /* FIXME: Remove this when groups a mandatory for iommu drivers */ if (group == NULL) @@ -1135,6 +1164,10 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) { struct iommu_group *group; + /* Don't try to detach dynamic domains */ + if (!domain || domain->type == IOMMU_DOMAIN_DYNAMIC) + return; + group = iommu_group_get(dev); /* FIXME: Remove this when groups a mandatory for iommu drivers */ if (group == NULL) @@ -1508,6 +1541,10 @@ int iommu_domain_get_attr(struct iommu_domain *domain, ret = -ENODEV; break; + case DOMAIN_ATTR_DYNAMIC: + *((unsigned int *) data) = + !!(domain->type & __IOMMU_DOMAIN_DYNAMIC); + break; default: if (!domain->ops->domain_get_attr) return -EINVAL; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 544cfc6..5b538d0 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -57,7 +57,7 @@ struct iommu_domain_geometry { #define __IOMMU_DOMAIN_DMA_API (1U << 1) /* Domain for use in DMA-API implementation */ #define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */ - +#define __IOMMU_DOMAIN_DYNAMIC (1U << 3) /* Domain is dynamic */ /* * This are the possible domain-types * @@ -69,12 +69,18 @@ struct iommu_domain_geometry { * IOMMU_DOMAIN_DMA - Internally used for DMA-API implementations. * This flag allows IOMMU drivers to implement * certain optimizations for these domains + * IOMMU_DOMAIN_DYNAMIC - The domain is dynamic and bound to a parent + * domain. This allows the driver to implement + * multiple domains on one device with different + * attributes */ #define IOMMU_DOMAIN_BLOCKED (0U) #define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT) #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING) #define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | \ __IOMMU_DOMAIN_DMA_API) +#define IOMMU_DOMAIN_DYNAMIC (__IOMMU_DOMAIN_PAGING | \ + __IOMMU_DOMAIN_DYNAMIC) struct iommu_domain { unsigned type; @@ -116,6 +122,7 @@ enum iommu_attr { DOMAIN_ATTR_NESTING, /* two stages of translation */ DOMAIN_ATTR_ENABLE_TTBR1, DOMAIN_ATTR_TTBR0, + DOMAIN_ATTR_DYNAMIC, DOMAIN_ATTR_MAX, }; @@ -160,6 +167,7 @@ struct iommu_dm_region { * @domain_set_windows: Set the number of windows for a domain * @domain_get_windows: Return the number of windows for a domain * @of_xlate: add OF master IDs to iommu grouping + * @domain_init_dynamic: Initialize a dynamic domain based on a parent domain * @pgsize_bitmap: bitmap of all possible supported page sizes */ struct iommu_ops { @@ -203,6 +211,9 @@ struct iommu_ops { int (*of_xlate)(struct device *dev, struct of_phandle_args *args); + int (*domain_init_dynamic)(struct iommu_domain *parent, + struct iommu_domain *child); + unsigned long pgsize_bitmap; }; @@ -280,6 +291,10 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t offset, u64 size, int prot); extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); + +extern struct iommu_domain *iommu_domain_create_dynamic( + struct iommu_domain *parent); + /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html