BACKPORT: FROMLIST: iommu/arm-smmu-v3-kvm: Implement sg operations
Implement the new map_sg ops which mainly populate the kvm_iommu_sg and pass it in the hypervisor. Link: https://lore.kernel.org/all/20241212180423.1578358-58-smostafa@google.com/ Bug: 357781595 Bug: 384432312 Change-Id: I68d5ed71e222900e38a2212661c425b4dde2444d Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
@@ -485,6 +485,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 size_t 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 total_mapped;
|
||||
}
|
||||
|
||||
static struct iommu_ops kvm_arm_smmu_ops = {
|
||||
.capable = kvm_arm_smmu_capable,
|
||||
.device_group = arm_smmu_device_group,
|
||||
@@ -504,6 +594,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,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user