From: Nicolin Chen nicolinc@nvidia.com
A nested_domain is per user Stream Table Entry in SMMUv3 case. So, user space must provide the user Stream ID to the user Stream Table Entry in the user data together, to link the user Stream ID to the corresponding physical Stream ID. It will be used to replace user Stream IDs in cache invalidation commands with physical Stream IDs in the following patch.
Add a streams_user xarray to log all the links between user Stream IDs and physical Stream IDs. Add id_user in struct arm_smmu_nested_domain, so its free op can unlink the id_user and clean it up. Also, store the same id_user in struct arm_smmu_stream to verify the ledger, since the master pointer is no longer available in arm_smmu_domain_nested_free().
Signed-off-by: Nicolin Chen nicolinc@nvidia.com Signed-off-by: Kunkun Jiang jiangkunkun@huawei.com --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 39 ++++++++++++++++++++- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 ++ include/uapi/linux/iommufd.h | 2 ++ 3 files changed, 43 insertions(+), 1 deletion(-)
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 0c858ab70d30..4ddb6d48dde7 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -3340,7 +3340,30 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
static void arm_smmu_domain_nested_free(struct iommu_domain *domain) { - kfree(container_of(domain, struct arm_smmu_nested_domain, domain)); + struct arm_smmu_nested_domain *nested_domain = + container_of(domain, struct arm_smmu_nested_domain, domain); + u32 id_user = nested_domain->sid_user; + + if (!WARN_ON(!id_user)) { + struct arm_smmu_device *smmu = nested_domain->s2_parent->smmu; + struct arm_smmu_stream *stream; + + xa_lock(&smmu->streams_user); + stream = __xa_erase(&smmu->streams_user, id_user); + /* + * A mismatched id_user is unlikely to happen, but restore the + * stream back to the xarray for its actual owner to carray on. + */ + if (unlikely(stream->id_user != id_user)) { + WARN_ON(__xa_alloc(&smmu->streams_user, &id_user, + stream, XA_LIMIT(id_user, id_user), + GFP_KERNEL_ACCOUNT)); + } else + stream->id_user = 0; + xa_unlock(&smmu->streams_user); + } + + kfree(nested_domain); }
static struct iommu_domain * @@ -3373,6 +3396,9 @@ arm_smmu_domain_alloc_nesting(struct device *dev, u32 flags, if (!(master->smmu->features & ARM_SMMU_FEAT_NESTING)) return ERR_PTR(-EOPNOTSUPP);
+ if (master->streams[0].id_user) + return ERR_PTR(-EEXIST); + ret = iommu_copy_struct_from_user(&arg, user_data, IOMMU_HWPT_DATA_ARM_SMMUV3, ste); if (ret) @@ -3403,12 +3429,22 @@ arm_smmu_domain_alloc_nesting(struct device *dev, u32 flags, if (!nested_domain) return ERR_PTR(-ENOMEM);
+ ret = xa_alloc(&smmu_parent->smmu->streams_user, &arg.sid, + &master->streams[0], XA_LIMIT(arg.sid, arg.sid), + GFP_KERNEL_ACCOUNT); + if (ret) { + kfree(nested_domain); + return ERR_PTR(ret); + } + master->streams[0].id_user = arg.sid; + nested_domain->domain.type = IOMMU_DOMAIN_NESTED; nested_domain->domain.ops = &arm_smmu_nested_ops; nested_domain->s2_parent = smmu_parent; nested_domain->enable_ats = eats == STRTAB_STE_1_EATS_TRANS; nested_domain->ste[0] = arg.ste[0]; nested_domain->ste[1] = arg.ste[1] & ~cpu_to_le64(STRTAB_STE_1_EATS); + nested_domain->sid_user = arg.sid;
return &nested_domain->domain; } @@ -4358,6 +4394,7 @@ static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
mutex_init(&smmu->streams_mutex); smmu->streams = RB_ROOT; + xa_init_flags(&smmu->streams_user, XA_FLAGS_ALLOC1 | XA_FLAGS_ACCOUNT);
ret = arm_smmu_init_queues(smmu); if (ret) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 6856dde2273d..c68779303b0d 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -772,10 +772,12 @@ struct arm_smmu_device { struct mutex streams_mutex;
bool bypass; + struct xarray streams_user; };
struct arm_smmu_stream { u32 id; + u32 id_user; struct arm_smmu_master *master; struct rb_node node; }; @@ -832,6 +834,7 @@ struct arm_smmu_nested_domain { u8 enable_ats : 1;
__le64 ste[2]; + u32 sid_user; };
struct arm_smmu_master_domain { diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 79b7d1558e11..cf13ab623b4a 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -403,11 +403,13 @@ struct iommu_hwpt_vtd_s1 { * Allowed fields: (Refer to "5.2 Stream Table Entry" in SMMUv3 HW Spec) * - word-0: V, S1Fmt, S1ContextPtr, S1CDMax * - word-1: S1DSS, S1CIR, S1COR, S1CSH, S1STALLD + * @sid: The user space Stream ID to index the user Stream Table Entry @ste * * -EIO will be returned if @ste is not legal or contains any non-allowed field. */ struct iommu_hwpt_arm_smmuv3 { __aligned_le64 ste[2]; + __u32 sid; };
/**