[RFC PATCH v2 57/58] iommu/arm-smmu-v3-kvm: Implement sg operations

From: Mostafa Saleh
Date: Thu Dec 12 2024 - 13:16:40 EST


Implement the new map_sg ops which mainly populate the kvm_iommu_sg
and pass it in the hypervisor.

Signed-off-by: Mostafa Saleh <smostafa@xxxxxxxxxx>
---
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c | 93 +++++++++++++++++++
1 file changed, 93 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c
index e987c273ff3c..ac45455b384d 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c
@@ -445,6 +445,96 @@ static phys_addr_t kvm_arm_smmu_iova_to_phys(struct iommu_domain *domain,
return kvm_call_hyp_nvhe(__pkvm_host_iommu_iova_to_phys, kvm_smmu_domain->id, iova);
}

+struct kvm_arm_smmu_map_sg {
+ struct iommu_map_cookie_sg cookie;
+ struct kvm_iommu_sg *sg;
+ unsigned int ptr;
+ unsigned long iova;
+ int prot;
+ gfp_t gfp;
+ unsigned int nents;
+};
+
+static struct iommu_map_cookie_sg *kvm_arm_smmu_alloc_cookie_sg(unsigned long iova,
+ int prot,
+ unsigned int nents,
+ gfp_t gfp)
+{
+ int ret;
+ struct kvm_arm_smmu_map_sg *map_sg = kzalloc(sizeof(*map_sg), gfp);
+
+ if (!map_sg)
+ return NULL;
+
+ map_sg->sg = kvm_iommu_sg_alloc(nents, gfp);
+ if (!map_sg->sg)
+ return NULL;
+ map_sg->iova = iova;
+ map_sg->prot = prot;
+ map_sg->gfp = gfp;
+ map_sg->nents = nents;
+ ret = kvm_iommu_share_hyp_sg(map_sg->sg, nents);
+ if (ret) {
+ kvm_iommu_sg_free(map_sg->sg, nents);
+ kfree(map_sg);
+ return NULL;
+ }
+
+ return &map_sg->cookie;
+}
+
+static int kvm_arm_smmu_add_deferred_map_sg(struct iommu_map_cookie_sg *cookie,
+ phys_addr_t paddr, size_t pgsize, size_t pgcount)
+{
+ struct kvm_arm_smmu_map_sg *map_sg = container_of(cookie, struct kvm_arm_smmu_map_sg,
+ cookie);
+ struct kvm_iommu_sg *sg = map_sg->sg;
+
+ sg[map_sg->ptr].phys = paddr;
+ sg[map_sg->ptr].pgsize = pgsize;
+ sg[map_sg->ptr].pgcount = pgcount;
+ map_sg->ptr++;
+ return 0;
+}
+
+static int kvm_arm_smmu_consume_deferred_map_sg(struct iommu_map_cookie_sg *cookie)
+{
+ struct kvm_arm_smmu_map_sg *map_sg = container_of(cookie, struct kvm_arm_smmu_map_sg,
+ cookie);
+ struct kvm_iommu_sg *sg = map_sg->sg;
+ size_t mapped, total_mapped = 0;
+ struct arm_smccc_res res;
+ struct kvm_arm_smmu_domain *kvm_smmu_domain = to_kvm_smmu_domain(map_sg->cookie.domain);
+
+ do {
+ res = kvm_call_hyp_nvhe_smccc(__pkvm_host_iommu_map_sg,
+ kvm_smmu_domain->id,
+ map_sg->iova, sg, map_sg->ptr, map_sg->prot);
+ mapped = res.a1;
+ map_sg->iova += mapped;
+ total_mapped += mapped;
+ /* Skip mapped */
+ while (mapped) {
+ if (mapped < (sg->pgsize * sg->pgcount)) {
+ sg->phys += mapped;
+ sg->pgcount -= mapped / sg->pgsize;
+ mapped = 0;
+ } else {
+ mapped -= sg->pgsize * sg->pgcount;
+ sg++;
+ map_sg->ptr--;
+ }
+ }
+
+ kvm_arm_smmu_topup_memcache(&res, map_sg->gfp);
+ } while (map_sg->ptr);
+
+ kvm_iommu_unshare_hyp_sg(sg, map_sg->nents);
+ kvm_iommu_sg_free(sg, map_sg->nents);
+ kfree(map_sg);
+ return 0;
+}
+
static struct iommu_ops kvm_arm_smmu_ops = {
.capable = kvm_arm_smmu_capable,
.device_group = arm_smmu_device_group,
@@ -463,6 +553,9 @@ static struct iommu_ops kvm_arm_smmu_ops = {
.unmap_pages = kvm_arm_smmu_unmap_pages,
.iova_to_phys = kvm_arm_smmu_iova_to_phys,
.set_dev_pasid = kvm_arm_smmu_set_dev_pasid,
+ .alloc_cookie_sg = kvm_arm_smmu_alloc_cookie_sg,
+ .add_deferred_map_sg = kvm_arm_smmu_add_deferred_map_sg,
+ .consume_deferred_map_sg = kvm_arm_smmu_consume_deferred_map_sg,
}
};

--
2.47.0.338.g60cca15819-goog