The arm_smmu_domain_alloc_user callback function is used for userspace to allocate iommu_domains, such as standalone stage-1 domain, nested stage-1 domain, and nested stage-2 domain. The input user_data is in the type of struct iommu_hwpt_arm_smmuv3 that contains the configurations of a nested stage-1 or a nested stage-2 iommu_domain. A NULL user_data will just opt in a standalone stage-1 domain allocation. Add a constitutive function __arm_smmu_domain_alloc to support that. Since ops->domain_alloc_user has a valid dev pointer, the master pointer is available when calling __arm_smmu_domain_alloc() in this case, meaning that arm_smmu_domain_finalise() can be done at the allocation stage. This allows IOMMUFD to initialize the hw_pagetable for the domain. Signed-off-by: Nicolin Chen <nicolinc@xxxxxxxxxx> --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 117 +++++++++++++++----- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index b8c189b732ba..75ee928c2390 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -2051,36 +2051,6 @@ static void *arm_smmu_hw_info(struct device *dev, u32 *length) return info; } -static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) -{ - struct arm_smmu_domain *smmu_domain; - - if (type == IOMMU_DOMAIN_SVA) - return arm_smmu_sva_domain_alloc(); - - if (type != IOMMU_DOMAIN_UNMANAGED && - type != IOMMU_DOMAIN_DMA && - type != IOMMU_DOMAIN_DMA_FQ && - type != IOMMU_DOMAIN_IDENTITY) - return NULL; - - /* - * Allocate the domain and initialise some of its data structures. - * We can't really do anything meaningful until we've added a - * master. - */ - smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); - if (!smmu_domain) - return NULL; - - mutex_init(&smmu_domain->init_mutex); - INIT_LIST_HEAD(&smmu_domain->devices); - spin_lock_init(&smmu_domain->devices_lock); - INIT_LIST_HEAD(&smmu_domain->mmu_notifiers); - - return &smmu_domain->domain; -} - static int arm_smmu_bitmap_alloc(unsigned long *map, int span) { int idx, size = 1 << span; @@ -2928,10 +2898,97 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid) arm_smmu_sva_remove_dev_pasid(domain, dev, pasid); } +/** + * __arm_smmu_domain_alloc - Allocate a customizable iommu_domain + * @type: Type of the new iommu_domain, in form of IOMMU_DOMAIN_* + * @master: Optional master pointer for the allocation. If given, this will be + * used to call arm_smmu_domain_finalise at the end of the allocation. + * Otherwise, arm_smmu_domain_finalise will be done when the domain is + * attached to a device. + * @user_cfg: Optional user configuration for the allocation. This allows the + * caller, mainly user space, to customize the iommu_domain in the + * arm_smmu_domain_finalise function that decodes the user_cfg data. + * + * This helper function is shared by domain_alloc_user() and domain_alloc(). + * Unlike ops->domain_alloc has been so far only called by the iommu core that + * sets default values of domain->type, domain->ops and domain->pgsize_bitmap, + * the ops->domain_alloc_user could be directly called by the iommufd core. So, + * this function should set those default values for an ops->domain_alloc_user + * call. Note that domain->pgsize_bitmap is set in arm_smmu_domain_finalise(). + */ +static struct iommu_domain * +__arm_smmu_domain_alloc(unsigned type, + struct arm_smmu_master *master, + const struct iommu_hwpt_arm_smmuv3 *user_cfg) +{ + struct arm_smmu_domain *smmu_domain; + struct iommu_domain *domain; + int ret = 0; + + if (type == IOMMU_DOMAIN_SVA) + return arm_smmu_sva_domain_alloc(); + + if (type != IOMMU_DOMAIN_UNMANAGED && + type != IOMMU_DOMAIN_DMA && + type != IOMMU_DOMAIN_DMA_FQ && + type != IOMMU_DOMAIN_IDENTITY) + return NULL; + + /* + * Allocate the domain and initialise some of its data structures. + * We can't really finalise the domain unless a master is given. + */ + smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); + if (!smmu_domain) + return NULL; + domain = &smmu_domain->domain; + + domain->type = type; + domain->ops = arm_smmu_ops.default_domain_ops; + + mutex_init(&smmu_domain->init_mutex); + INIT_LIST_HEAD(&smmu_domain->devices); + spin_lock_init(&smmu_domain->devices_lock); + INIT_LIST_HEAD(&smmu_domain->mmu_notifiers); + + if (master) { + smmu_domain->smmu = master->smmu; + ret = arm_smmu_domain_finalise(domain, master, user_cfg); + if (ret) { + kfree(smmu_domain); + return NULL; + } + } + + return domain; +} + +static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) +{ + return __arm_smmu_domain_alloc(type, NULL, NULL); +} + +static struct iommu_domain * +arm_smmu_domain_alloc_user(struct device *dev, struct iommu_domain *parent, + const void *user_data) +{ + const struct iommu_hwpt_arm_smmuv3 *user_cfg = user_data; + struct arm_smmu_master *master = dev_iommu_priv_get(dev); + unsigned type = IOMMU_DOMAIN_UNMANAGED; + + return __arm_smmu_domain_alloc(type, master, user_cfg); +} + +static const size_t arm_smmu_domain_user_data_len[] = { + [IOMMU_HWPT_TYPE_ARM_SMMUV3] = sizeof(struct iommu_hwpt_arm_smmuv3), +}; + static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .hw_info = arm_smmu_hw_info, .domain_alloc = arm_smmu_domain_alloc, + .domain_alloc_user = arm_smmu_domain_alloc_user, + .domain_alloc_user_data_len = arm_smmu_domain_user_data_len, .probe_device = arm_smmu_probe_device, .release_device = arm_smmu_release_device, .set_dev_user_data = arm_smmu_set_dev_user_data, -- 2.40.1