From 6d17c7e0e2334b931623dfc34c2eb74a0376d272 Mon Sep 17 00:00:00 2001 From: Sudarshan Rajagopalan Date: Wed, 18 Dec 2019 17:35:55 -0800 Subject: [PATCH] iommu: arm-smmu: fix check for need for preallocate memory commit b312b4f0e2f9 ("iommu: arm-smmu: Preallocate memory for map only on failure") had the following two errors: 1. The return code we checking when map_sg fails and we preallocte is wrong. The check should be for 0 and not -ENOMEM. So the preallocate is never happening when map_sg fails. 2. map_sg could've have mapped certain elements in sglist and later had got failed. With proper check, we are trying to call map_sg on the same size again, which would leave to double map of previously mapped elements in sglist. Fix this by returning the actual ret code from arm_lpae_map_sg() and check it against -ENOMEM if we need to preallocate or not. Also, unmap any partial iovas that was mapped previously. Change-Id: Ifee7c0bed6b9cf1c35ebb4a03d51a1a80ab0ed58 Signed-off-by: Sudarshan Rajagopalan [isaacm@codeaurora.org: resolve trivial merge conflicts] Signed-off-by: Isaac J. Manjarres --- drivers/iommu/arm-smmu.c | 12 +++++++++--- drivers/iommu/io-pgtable-arm.c | 5 +++-- include/linux/io-pgtable.h | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 3b6086fc0746..1dc8f5965ed1 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -3065,8 +3065,13 @@ static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova, idx_end - idx_start, prot, &size); spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); - if (ret == -ENOMEM) { + /* unmap any partially mapped iova */ + if (size) { + arm_smmu_secure_domain_unlock(smmu_domain); + arm_smmu_unmap(domain, iova, size, NULL); + arm_smmu_secure_domain_lock(smmu_domain); + } arm_smmu_prealloc_memory(smmu_domain, batch_size, &nonsecure_pool); spin_lock_irqsave(&smmu_domain->cb_lock, flags); @@ -3082,8 +3087,8 @@ static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova, &nonsecure_pool); } - /* Returns 0 on error */ - if (!ret) { + /* Returns -ve val on error */ + if (ret < 0) { size_to_unmap = iova + size - __saved_iova_start; goto out; } @@ -3091,6 +3096,7 @@ static size_t arm_smmu_map_sg(struct iommu_domain *domain, unsigned long iova, iova += batch_size; idx_start = idx_end; sg_start = sg_end; + size = 0; } out: diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 0fa53d3035eb..959c55e755ff 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -642,7 +642,8 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, arm_lpae_iopte prot; struct scatterlist *s; size_t mapped = 0; - int i, ret; + int i; + int ret = -EINVAL; unsigned int min_pagesz; struct io_pgtable_cfg *cfg = &data->iop.cfg; struct map_state ms; @@ -713,7 +714,7 @@ static int arm_lpae_map_sg(struct io_pgtable_ops *ops, unsigned long iova, out_err: /* Return the size of the partial mapping so that they can be undone */ *size = mapped; - return 0; + return ret; } static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h index 9ea96a64d380..00fd7388708a 100644 --- a/include/linux/io-pgtable.h +++ b/include/linux/io-pgtable.h @@ -193,8 +193,8 @@ struct io_pgtable_ops { * IOMMU drivers, and page table configuration. * * @map_sg: Map a scatterlist. Returns the number of bytes mapped, - * or 0 on failure. The size parameter contains the size - * of the partial mapping in case of failure. + * or -ve val on failure. The size parameter contains the + * size of the partial mapping in case of failure. * @is_iova_coherent: Checks coherency of given IOVA. Returns True if coherent * and False if non-coherent. * @iova_to_pte: Translate iova to Page Table Entry (PTE).