From 767700e13df25f9ab6a9b25d1aa1c353fb50376e Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 12 Apr 2019 13:13:43 -0700 Subject: [PATCH] platform: nvadsp: Fix iova reservation There is no point in managing iovad that's not binded to device's IOMMU domain. So this patch just removes iovad managing code and does a correct iommu_dma_alloc_iova() to reserve the iova. A difficulty of using iommu_dma_alloc_iova() is that we mostly do not use aligned addresses for the target iova, but IOMMU core aligns the iova address with the size by power of two due to cache friendly. So the trick here is to align the start address and to allocate a larger iova region that would cover the target iova -- the excessive part will be freed during iommu_dma_free_iova() by IOMMU core. Note that the dma_free_coherent() does both iommu_unmap and iommu_dma_free_iova, so there's no need of an extra free_iova() in the exit routine. However, for the remap part, the tmp_iova is allocated and mapped, so it should be unmapped and freed. So this patch also adds a free() of tmp_iova in the nvadsp_dma_alloc_at_map() function. Bug 200444660 Change-Id: I54126c2bd9ff3dd688aaa5c6f9befaad620ab548 Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2096704 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.h | 4 --- drivers/platform/tegra/nvadsp/os.c | 50 ++++++++++++----------------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index ac68a480..dbdcbdbf 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -215,9 +214,6 @@ struct nvadsp_drv_data { struct tegra_bwmgr_client *bwmgr; u32 evp_base[ADSP_EVP_END]; - /* Used to reserve iova */ - struct iova_domain iovad; - const struct nvadsp_chipdata *chip_data; }; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index fd402397..73deff89 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -620,26 +621,35 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, gfp_t flags) { struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev); - struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + unsigned long align_mask = ~0UL << fls_long(size - 1); unsigned long shift = __ffs(domain->pgsize_bitmap); unsigned long pg_size = 1UL << shift; unsigned long mp_size = pg_size; struct device *dev = &pdev->dev; + dma_addr_t aligned_iova = iova & align_mask; + dma_addr_t end = iova + size; dma_addr_t tmp_iova, offset; phys_addr_t pa, pa_new; - struct iova *iova_pfn; void *cpu_va; int ret; - /* Reserve iova range */ - iova_pfn = alloc_iova(&drv_data->iovad, size >> shift, - (iova + size - pg_size) >> shift, false); - if (!iova_pfn || (iova_pfn->pfn_lo << shift) != iova) { - dev_err(dev, "failed to reserve iova at 0x%llx size 0x%lx\n", - iova, size); + /* + * Reserve iova range using aligned size: adsp memory might not start + * from an aligned address by power of 2, while iommu_dma_alloc_iova() + * would shift the allocation off the target iova so as to align start + * address by power of 2. To prevent this shifting, use aligned size. + * It might allocate an excessive iova region but it would be handled + * by IOMMU core during iommu_dma_free_iova(). + */ + tmp_iova = iommu_dma_alloc_iova(dev, end - aligned_iova, end - pg_size); + if (tmp_iova != aligned_iova) { + dev_err(dev, "failed to reserve iova range [%llx, %llx]\n", + aligned_iova, end); return NULL; } + dev_dbg(dev, "Reserved iova region [%llx, %llx]\n", aligned_iova, end); + /* Allocate a memory first and get a tmp_iova */ cpu_va = dma_alloc_coherent(dev, size, &tmp_iova, flags); if (!cpu_va) @@ -675,8 +685,9 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, } } - /* Unmap the tmp_iova since target iova is linked */ + /* Unmap and free the tmp_iova since target iova is linked */ iommu_unmap(domain, tmp_iova, size); + iommu_dma_free_iova(dev, tmp_iova, size); return cpu_va; @@ -684,7 +695,7 @@ fail_map: iommu_unmap(domain, iova, offset); dma_free_coherent(dev, size, cpu_va, tmp_iova); fail_dma_alloc: - __free_iova(&drv_data->iovad, iova_pfn); + iommu_dma_free_iova(dev, end - aligned_iova, end - pg_size); return NULL; } @@ -727,13 +738,9 @@ end: static void deallocate_memory_for_adsp_os(struct device *dev) { #if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) - struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); - struct iova_domain *iovad = &drv_data->iovad; void *va = nvadsp_da_to_va_mappings(priv.adsp_os_addr, priv.adsp_os_size); dma_free_coherent(dev, priv.adsp_os_size, va, priv.adsp_os_addr); - free_iova(iovad, iova_pfn(iovad, priv.adsp_os_addr)); - put_iova_domain(iovad); #endif } @@ -839,9 +846,6 @@ int nvadsp_os_load(void) struct nvadsp_drv_data *drv_data; struct device *dev; int ret = 0; -#ifdef CONFIG_TEGRA_NVADSP_ON_SMMU - u32 addr, size; -#endif if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); @@ -855,18 +859,6 @@ int nvadsp_os_load(void) drv_data = platform_get_drvdata(priv.pdev); dev = &priv.pdev->dev; -#ifdef CONFIG_TEGRA_NVADSP_ON_SMMU - if (drv_data->adsp_os_secload) { - addr = drv_data->adsp_mem[ACSR_ADDR]; - size = drv_data->adsp_mem[ACSR_SIZE]; - } else { - addr = drv_data->adsp_mem[ADSP_OS_ADDR]; - size = drv_data->adsp_mem[ADSP_OS_SIZE]; - } - init_iova_domain(&drv_data->iovad, PAGE_SIZE, - addr >> PAGE_SHIFT, (addr + size) >> PAGE_SHIFT); -#endif - if (drv_data->adsp_os_secload) { dev_info(dev, "ADSP OS firmware already loaded\n"); ret = __nvadsp_os_secload(priv.pdev);