Clean up exposure of arch-internal code
Subsystems (6)
cover Clean up exposure of arch-internal code
This series of patches attempts to clean up the use of architecture internal functions in drivers, and removes some functions from view in the asm/ headers. I'm also considering whether we need to add some linker magic to hide symbols when building the built-in.o files.
This was triggered by 3rd party drivers "going under the covers" of the DMA API and calling the dmac_*() functions directly, a practice which I have always refused to allow. This also breaks module building (which is the big hint that they're doing something wrong.)
However, it also came to light that various drivers are using __cpuc_* functions directly, which are non-portable, and is another instance of "going under the covers" and tinkering with what should be kept as architecture implementation details.
This series addresses some of these points. It:
(a) moves dmac_map_area() and dmac_unmap_area() prototypes out of asm/cacheflush.h and into arch/arm/mm.
(b) provide a secure_flush() call for the Qualcomm secure monitor code.
(c) stop tegra smmu driver(s) from using __cpuc_flush_dcache_area, something which necessitates additional complexity to deal with the ARM vs ARM64 differences. It should be using the DMA API. However, the Tegra SMMU driver is in really bad shape, and this isn't a simple conversion - it requires a lot of additional fixes to bring the code up to scratch.
It leaves the Rockchip IOMMU driver for the time being, but that is also something which needs cleaning up in the same way as the Tegra SMMU driver.
arch/arm/include/asm/cacheflush.h | 21 ++- arch/arm/include/asm/glue-cache.h | 2 - arch/arm/mm/dma-mapping.c | 1 + arch/arm/mm/dma.h | 32 ++++ drivers/firmware/qcom_scm-32.c | 4 +- drivers/iommu/tegra-smmu.c | 297 ++++++++++++++++++++++++-------------- drivers/memory/tegra/tegra114.c | 17 --- drivers/memory/tegra/tegra124.c | 30 ---- drivers/memory/tegra/tegra30.c | 17 --- include/soc/tegra/mc.h | 7 - 10 files changed, 242 insertions(+), 186 deletions(-)
Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up according to speedtest.net.
PATCH 1/18 ARM: reduce visibility of dmac_* functions
The dmac_* functions are private to the ARM DMA API implementation, and should not be used by drivers. In order to discourage their use, remove their prototypes and macros from asm/*.h.
We have to leave dmac_flush_range() behind as Exynos and MSM IOMMU code use these; once these sites are fixed, this can be moved also.
--- arch/arm/include/asm/cacheflush.h | 4 ---- arch/arm/include/asm/glue-cache.h | 2 -- arch/arm/mm/dma-mapping.c | 1 + arch/arm/mm/dma.h | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 arch/arm/mm/dma.h
arch/arm/include/asm/cacheflush.h +0 -4
@@ -140,8 +140,6 @@ extern struct cpu_cache_fns cpu_cache;* is visible to DMA, or data written by DMA to system memory is * visible to the CPU. */ -#define dmac_map_area cpu_cache.dma_map_area -#define dmac_unmap_area cpu_cache.dma_unmap_area#define dmac_flush_range cpu_cache.dma_flush_range#else
@@ -161,8 +159,6 @@ extern void __cpuc_flush_dcache_area(void *, size_t);* is visible to DMA, or data written by DMA to system memory is * visible to the CPU. */ -extern void dmac_map_area (const void *,size_t ,int ); -extern void dmac_unmap_area (const void *,size_t ,int );extern void dmac_flush_range (const void *,const void *);#endif
arch/arm/include/asm/glue-cache.h +0 -2
@@ -158,8 +158,6 @@ static inline void nop_dma_unmap_area(const void *s, size_t l, int f) { }#define __cpuc_coherent_user_range __glue(_CACHE,_coherent_user_range)#define __cpuc_flush_dcache_area __glue(_CACHE,_flush_kern_dcache_area) -#define dmac_map_area __glue(_CACHE,_dma_map_area) -#define dmac_unmap_area __glue(_CACHE,_dma_unmap_area)#define dmac_flush_range __glue(_CACHE,_dma_flush_range)#endif
arch/arm/mm/dma-mapping.c +1 -0
@@ -39,6 +39,7 @@#include <asm/system_info.h> #include <asm/dma-contiguous.h> +#include "dma.h" #include "mm.h" /*
arch/arm/mm/dma.h new file +32 -0
@@ -0,0 +1,32 @@ +#ifndef DMA_H +#define DMA_H + +#include <asm/glue-cache.h> + +#ifndef MULTI_CACHE +#define dmac_map_area __glue(_CACHE,_dma_map_area) +#define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) + +/* +* These are private to the dma-mapping API. Do not use directly. +* Their sole purpose is to ensure that data held in the cache +* is visible to DMA, or data written by DMA to system memory is +* visible to the CPU. +*/ +extern void dmac_map_area (const void *,size_t ,int ); +extern void dmac_unmap_area (const void *,size_t ,int ); + +#else + +/* +* These are private to the dma-mapping API. Do not use directly. +* Their sole purpose is to ensure that data held in the cache +* is visible to DMA, or data written by DMA to system memory is +* visible to the CPU. +*/ +#define dmac_map_area cpu_cache.dma_map_area +#define dmac_unmap_area cpu_cache.dma_unmap_area + +#endif + +#endif
Signature · 1 line
2.1.0
PATCH 2/18 firmware: qcom_scm-32: replace open-coded call to __cpuc_flush_dcache_area()
In reply to Russell King - ARM Linux--- arch/arm/include/asm/cacheflush.h | 17 +++++++++++++++++ drivers/firmware/qcom_scm-32.c | 4 +--- 2 files changed, 18 insertions(+), 3 deletions(-)
arch/arm/include/asm/cacheflush.h +17 -0
@@ -502,4 +502,21 @@ static inline void set_kernel_text_ro(void) { }void flush_uprobe_xol_access (struct page *page,unsigned long uaddr,void *kaddr,unsigned long len); +/** +* secure_flush_area - ensure coherency across the secure boundary +* @addr: virtual address +* @size: size of region +* +* Ensure that the specified area of memory is coherent across the secure +* boundary from the non-secure side. This is used when calling secure +* firmware where the secure firmware does not ensure coherency. +*/ +static inline void secure_flush_area (const void *addr,size_t size) +{ +phys_addr_t phys =__pa (addr); + +__cpuc_flush_dcache_area ((void *)addr, size); +outer_flush_range (phys, phys + size); +} +#endif
drivers/firmware/qcom_scm-32.c +1 -3
@@ -24,7 +24,6 @@#include <linux/err.h> #include <linux/qcom_scm.h> -#include <asm/outercache.h> #include <asm/cacheflush.h> #include "qcom_scm.h"
@@ -219,8 +218,7 @@ static int __qcom_scm_call(const struct qcom_scm_command *cmd)* Flush the command buffer so that the secure world sees * the correct data. */ -__cpuc_flush_dcache_area ((void *)cmd, cmd->len); -outer_flush_range (cmd_addr, cmd_addr + cmd->len); +secure_flush_area (cmd, cmd->len); ret =smc (cmd_addr);if (ret <0 )
Signature · 1 line
2.1.0
PATCH 2/18 firmware: qcom_scm-32: replace open-coded call to __cpuc_flush_dcache_area()
Quoted 2 lines · Russell King
Signed-off-by Russell King <[email protected]>---
Signature · 3 lines
Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH 2/18 firmware: qcom_scm-32: replace open-coded call to __cpuc_flush_dcache_area()
In reply to Russell KingQuoted 2 lines · Russell King
Signed-off-by Russell King <[email protected]>---
Thanks!
Quoted 52 lines
arch/arm/include/asm/cacheflush.h | 17 +++++++++++++++++ drivers/firmware/qcom_scm-32.c | 4 +--- 2 files changed, 18 insertions(+), 3 deletions(-)arch/arm/include/asm/cacheflush.h +17
-0@@ -502,4 +502,21 @@ static inline void set_kernel_text_ro(void) { }void flush_uprobe_xol_access (struct page *page,unsigned long uaddr,void *kaddr,unsigned long len); +/** +* secure_flush_area - ensure coherency across the secure boundary +* @addr: virtual address +* @size: size of region +* +* Ensure that the specified area of memory is coherent across the secure +* boundary from the non-secure side. This is used when calling secure +* firmware where the secure firmware does not ensure coherency. +*/ +static inline void secure_flush_area (const void *addr,size_t size) +{ +phys_addr_t phys =__pa (addr); + +__cpuc_flush_dcache_area ((void *)addr, size); +outer_flush_range (phys, phys + size); +} +#endif drivers/firmware/qcom_scm-32.c +1
-3@@ -24,7 +24,6 @@#include <linux/err.h> #include <linux/qcom_scm.h> -#include <asm/outercache.h> #include <asm/cacheflush.h> #include "qcom_scm.h" @@ -219,8 +218,7 @@ static int __qcom_scm_call(const struct qcom_scm_command *cmd)* Flush the command buffer so that the secure world sees * the correct data. */ -__cpuc_flush_dcache_area ((void *)cmd, cmd->len); -outer_flush_range (cmd_addr, cmd_addr + cmd->len); +secure_flush_area (cmd, cmd->len); ret =smc (cmd_addr);if (ret <0 )
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH 3/18 iommu: tegra-smmu: fix iova_to_phys() method
In reply to Russell King - ARM Linuxiova_to_phys() has several problems:
(a) iova_to_phys() is supposed to return 0 if there is no entry present for the iova.
(b) if as_get_pte() fails, we oops the kernel by dereferencing a NULL pointer. Really, we should not even be trying to allocate a page table at all, but should only be returning the presence of the 2nd level page table. This will be fixed in a subsequent patch.
Treat both of these conditions as "no mapping" conditions.
--- drivers/iommu/tegra-smmu.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/iommu/tegra-smmu.c +3 -0
@@ -592,6 +592,9 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,u32 *pte; pte =as_get_pte (as, iova, &page); +if (!pte || !*pte) +return 0 ; + pfn = *pte & as->smmu->pfn_mask;return PFN_PHYS (pfn);
Signature · 1 line
2.1.0
PATCH 4/18 iommu: tegra-smmu: fix unmap() method
In reply to Russell King - ARM LinuxThe Tegra SMMU unmap path has several problems:
1. as_pte_put() can perform a write-after-free
2. tegra_smmu_unmap() can perform cache maintanence on a page we have just freed.
3. when a page table is unmapped, there is no CPU cache maintanence of the write clearing the page directory entry, nor is there any maintanence of the IOMMU to ensure that it sees the page table has gone.
Fix this by getting rid of as_pte_put(), and instead coding the PTE unmap separately from the PDE unmap, placing the PDE unmap after the PTE unmap has been completed.
--- drivers/iommu/tegra-smmu.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-)
drivers/iommu/tegra-smmu.c +23 -14
@@ -509,29 +509,35 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,return &pt[pte]; } -static void as_put_pte (struct tegra_smmu_as *as,dma_addr_t iova) +static void tegra_smmu_pte_put_use (struct tegra_smmu_as *as,unsigned long iova) { +struct tegra_smmu *smmu = as->smmu;u32 pde = (iova >>SMMU_PDE_SHIFT ) &0x3ff ; -u32 pte = (iova >>SMMU_PTE_SHIFT ) &0x3ff ;u32 *count =page_address (as->count); -u32 *pd =page_address (as->pd), *pt; +u32 *pd =page_address (as->pd);struct page *page; - page =pfn_to_page (pd[pde] & as->smmu->pfn_mask); - pt =page_address (page); + page =pfn_to_page (pd[pde] & smmu->pfn_mask);/* * When no entries in this page table are used anymore, return the * memory page to the system. */ -if (pt[pte] !=0 ) { -if (--count[pde] ==0 ) { -ClearPageReserved (page); -__free_page (page); - pd[pde] =0 ; - } +if (--count[pde] ==0 ) { +unsigned int offset = pde *sizeof (*pd); - pt[pte] =0 ; +/* Clear the page directory entry first */ + pd[pde] =0 ; + +/* Flush the page directory entry */ + smmu->soc->ops->flush_dcache (as->pd, offset,sizeof (*pd)); +smmu_flush_ptc (smmu, as->pd, offset); +smmu_flush_tlb_section (smmu, as->id, iova); +smmu_flush (smmu); + +/* Finally, free the page */ +ClearPageReserved (page); +__free_page (page); } }
@@ -569,17 +575,20 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,u32 *pte; pte =as_get_pte (as, iova, &page); -if (!pte) +if (!pte || !*pte)return 0 ; + *pte =0 ; + offset =offset_in_page (pte); -as_put_pte (as, iova); smmu->soc->ops->flush_dcache (page, offset,4 );smmu_flush_ptc (smmu, page, offset);smmu_flush_tlb_group (smmu, as->id, iova);smmu_flush (smmu); +tegra_smmu_pte_put_use (as, iova); +return size; }
Signature · 1 line
2.1.0
PATCH 5/18 iommu: tegra-smmu: factor out common pte setting
In reply to Russell King - ARM LinuxFactor out the common pte setting code into a separate function.
--- drivers/iommu/tegra-smmu.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-)
drivers/iommu/tegra-smmu.c +17 -20
@@ -541,12 +541,24 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova) } } +static void tegra_smmu_set_pte (struct tegra_smmu_as *as,unsigned long iova, +u32 *pte,struct page *pte_page,u32 val) +{ +struct tegra_smmu *smmu = as->smmu; +unsigned long offset =offset_in_page (pte); + + *pte = val; + + smmu->soc->ops->flush_dcache (pte_page, offset,4 ); +smmu_flush_ptc (smmu, pte_page, offset); +smmu_flush_tlb_group (smmu, as->id, iova); +smmu_flush (smmu); +} +static int tegra_smmu_map (struct iommu_domain *domain,unsigned long iova,phys_addr_t paddr,size_t size,int prot) {struct tegra_smmu_as *as =to_smmu_as (domain); -struct tegra_smmu *smmu = as->smmu; -unsigned long offset;struct page *page;u32 *pte;
@@ -554,13 +566,8 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,if (!pte)return -ENOMEM ; - *pte =__phys_to_pfn (paddr) |SMMU_PTE_ATTR ; - offset =offset_in_page (pte); - - smmu->soc->ops->flush_dcache (page, offset,4 ); -smmu_flush_ptc (smmu, page, offset); -smmu_flush_tlb_group (smmu, as->id, iova); -smmu_flush (smmu); +tegra_smmu_set_pte (as, iova, pte, page, +__phys_to_pfn (paddr) |SMMU_PTE_ATTR );return 0 ; }
@@ -569,8 +576,6 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) {struct tegra_smmu_as *as =to_smmu_as (domain); -struct tegra_smmu *smmu = as->smmu; -unsigned long offset;struct page *page;u32 *pte;
@@ -578,15 +583,7 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,if (!pte || !*pte)return 0 ; - *pte =0 ; - - offset =offset_in_page (pte); - - smmu->soc->ops->flush_dcache (page, offset,4 ); -smmu_flush_ptc (smmu, page, offset); -smmu_flush_tlb_group (smmu, as->id, iova); -smmu_flush (smmu); - +tegra_smmu_set_pte (as, iova, pte, page,0 );tegra_smmu_pte_put_use (as, iova);return size;
Signature · 1 line
2.1.0
PATCH 6/18 iommu: tegra-smmu: add iova_pd_index() and iova_pt_index() helpers
In reply to Russell King - ARM LinuxAdd a pair of helpers to get the page directory and page table indexes.
--- drivers/iommu/tegra-smmu.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/iommu/tegra-smmu.c +13 -3
@@ -134,6 +134,16 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)#define SMMU_PTE_ATTR (SMMU_PTE_READABLE | SMMU_PTE_WRITABLE | \ SMMU_PTE_NONSECURE) +static unsigned int iova_pd_index (unsigned long iova) +{ +return (iova >>SMMU_PDE_SHIFT ) & (SMMU_NUM_PDE -1 ); +} + +static unsigned int iova_pt_index (unsigned long iova) +{ +return (iova >>SMMU_PTE_SHIFT ) & (SMMU_NUM_PTE -1 ); +} +static inline void smmu_flush_ptc (struct tegra_smmu *smmu,struct page *page,unsigned long offset) {
@@ -469,8 +479,8 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, struct page **pagep) {u32 *pd =page_address (as->pd), *pt, *count; -u32 pde = (iova >>SMMU_PDE_SHIFT ) &0x3ff ; -u32 pte = (iova >>SMMU_PTE_SHIFT ) &0x3ff ; +unsigned int pde =iova_pd_index (iova); +unsigned int pte =iova_pt_index (iova);struct tegra_smmu *smmu = as->smmu;struct page *page;unsigned int i;
@@ -512,7 +522,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,static void tegra_smmu_pte_put_use (struct tegra_smmu_as *as,unsigned long iova) {struct tegra_smmu *smmu = as->smmu; -u32 pde = (iova >>SMMU_PDE_SHIFT ) &0x3ff ; +unsigned int pde =iova_pd_index (iova);u32 *count =page_address (as->count);u32 *pd =page_address (as->pd);struct page *page;
Signature · 1 line
2.1.0
PATCH 7/18 iommu: tegra-smmu: fix page table lookup in unmap/iova_to_phys methods
In reply to Russell King - ARM LinuxFix the page table lookup in the unmap and iova_to_phys methods. Neither of these methods should allocate a page table; a missing page table should be treated the same as no mapping present.
More importantly, using as_get_pte() for an IOVA corresponding with a non-present page table entry increments the use-count for the page table, on the assumption that the caller of as_get_pte() is going to setup a mapping. This is an incorrect assumption.
Fix both of these bugs by providing a separate helper which only looks up the page table, but never allocates it. This is akin to pte_offset() for CPU page tables.
--- drivers/iommu/tegra-smmu.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-)
drivers/iommu/tegra-smmu.c +34 -9
@@ -475,12 +475,36 @@ static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *de } } +static u32 *tegra_smmu_pte_offset (struct page *pt_page,unsigned long iova) +{ +u32 *pt =page_address (pt_page); + +return pt +iova_pt_index (iova); +} + +static u32 *tegra_smmu_pte_lookup (struct tegra_smmu_as *as,unsigned long iova, +struct page **pagep) +{ +unsigned int pd_index =iova_pd_index (iova); +struct page *pt_page; +u32 *pd; + + pd =page_address (as->pd); + +if (!pd[pd_index]) +return NULL ; + + pt_page =pfn_to_page (pd[pd_index] & as->smmu->pfn_mask); + *pagep = pt_page; + +return tegra_smmu_pte_offset (pt_page, iova); +} +static u32 *as_get_pte (struct tegra_smmu_as *as,dma_addr_t iova,struct page **pagep) {u32 *pd =page_address (as->pd), *pt, *count;unsigned int pde =iova_pd_index (iova); -unsigned int pte =iova_pt_index (iova);struct tegra_smmu *smmu = as->smmu;struct page *page;unsigned int i;
@@ -506,17 +530,18 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,smmu_flush (smmu); }else { page =pfn_to_page (pd[pde] & smmu->pfn_mask); - pt =page_address (page); } *pagep = page; + pt =page_address (page); +/* Keep track of entries in this page table. */ count =page_address (as->count); -if (pt[pte] ==0 ) +if (pt[iova_pt_index (iova)] ==0 ) count[pde]++; -return &pt[pte]; +return tegra_smmu_pte_offset (page, iova); }static void tegra_smmu_pte_put_use (struct tegra_smmu_as *as,unsigned long iova)
@@ -586,14 +611,14 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) {struct tegra_smmu_as *as =to_smmu_as (domain); -struct page *page; +struct page *pte_page;u32 *pte; - pte =as_get_pte (as, iova, &page); + pte =tegra_smmu_pte_lookup (as, iova, &pte_page);if (!pte || !*pte)return 0 ; -tegra_smmu_set_pte (as, iova, pte, page,0 ); +tegra_smmu_set_pte (as, iova, pte, pte_page,0 );tegra_smmu_pte_put_use (as, iova);return size;
@@ -603,11 +628,11 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) {struct tegra_smmu_as *as =to_smmu_as (domain); -struct page *page; +struct page *pte_page;unsigned long pfn;u32 *pte; - pte =as_get_pte (as, iova, &page); + pte =tegra_smmu_pte_lookup (as, iova, &pte_page);if (!pte || !*pte)return 0 ;
Signature · 1 line
2.1.0
PATCH 8/18 iommu: tegra-smmu: store struct page pointer for page tables
In reply to Russell King - ARM LinuxStore the struct page pointer for the second level page tables, rather than working back from the page directory entry. This is necessary as we want to eliminate the use of physical addresses used with arch-private functions, switching instead to use the streaming DMA API.
--- drivers/iommu/tegra-smmu.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
drivers/iommu/tegra-smmu.c +17 -10
@@ -41,6 +41,7 @@ struct tegra_smmu_as {struct tegra_smmu *smmu;unsigned int use_count;struct page *count; +struct page **pts;struct page *pd;unsigned id;u32 attr;
@@ -271,6 +272,14 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)return NULL ; } + as->pts =kcalloc (SMMU_NUM_PDE ,sizeof (*as->pts),GFP_KERNEL ); +if (!as->pts) { +__free_page (as->count); +__free_page (as->pd); +kfree (as); +return NULL ; + } +/* clear PDEs */ pd =page_address (as->pd);SetPageReserved (as->pd);
@@ -487,14 +496,11 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova, {unsigned int pd_index =iova_pd_index (iova);struct page *pt_page; -u32 *pd; - pd =page_address (as->pd); - -if (!pd[pd_index]) + pt_page = as->pts[pd_index]; +if (!pt_page)return NULL ; - pt_page =pfn_to_page (pd[pd_index] & as->smmu->pfn_mask); *pagep = pt_page;return tegra_smmu_pte_offset (pt_page, iova);
@@ -509,7 +515,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,struct page *page;unsigned int i; -if (pd[pde] ==0 ) { +if (!as->pts[pde]) { page =alloc_page (GFP_KERNEL | __GFP_DMA);if (!page)return NULL ;
@@ -520,6 +526,8 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,for (i =0 ; i <SMMU_NUM_PTE ; i++) pt[i] =0 ; + as->pts[pde] = page; + smmu->soc->ops->flush_dcache (page,0 ,SMMU_SIZE_PT ); pd[pde] =SMMU_MK_PDE (page,SMMU_PDE_ATTR |SMMU_PDE_NEXT );
@@ -529,7 +537,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu); }else { - page =pfn_to_page (pd[pde] & smmu->pfn_mask); + page = as->pts[pde]; } *pagep = page;
@@ -550,9 +558,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)unsigned int pde =iova_pd_index (iova);u32 *count =page_address (as->count);u32 *pd =page_address (as->pd); -struct page *page; - - page =pfn_to_page (pd[pde] & smmu->pfn_mask); +struct page *page = as->pts[pde];/* * When no entries in this page table are used anymore, return the
@@ -573,6 +579,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)/* Finally, free the page */ ClearPageReserved (page);__free_page (page); + as->pts[pde] =NULL ; } }
Signature · 1 line
2.1.0
PATCH 9/18 iommu: tegra-smmu: use kcalloc() to allocate counter array
In reply to Russell King - ARM LinuxUse kcalloc() to allocate the use-counter array for the page directory entries/page tables. Using kcalloc() allows us to be provided with zero-initialised memory from the allocators, rather than initialising it ourselves.
--- drivers/iommu/tegra-smmu.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-)
drivers/iommu/tegra-smmu.c +6 -15
@@ -40,7 +40,7 @@ struct tegra_smmu_as {struct iommu_domain domain;struct tegra_smmu *smmu;unsigned int use_count; -struct page *count; +u32 *count;struct page **pts;struct page *pd;unsigned id;
@@ -265,7 +265,7 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)return NULL ; } - as->count =alloc_page (GFP_KERNEL ); + as->count =kcalloc (SMMU_NUM_PDE ,sizeof (u32),GFP_KERNEL );if (!as->count) {__free_page (as->pd);kfree (as);
@@ -274,7 +274,7 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type) as->pts =kcalloc (SMMU_NUM_PDE ,sizeof (*as->pts),GFP_KERNEL );if (!as->pts) { -__free_page (as->count); +kfree (as->count);__free_page (as->pd);kfree (as);return NULL ;
@@ -287,13 +287,6 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)for (i =0 ; i <SMMU_NUM_PDE ; i++) pd[i] =0 ; -/* clear PDE usage counters */ - pd =page_address (as->count); -SetPageReserved (as->count); - -for (i =0 ; i <SMMU_NUM_PDE ; i++) - pd[i] =0 ; -/* setup aperture */ as->domain.geometry.aperture_start =0 ; as->domain.geometry.aperture_end =0xffffffff ;
@@ -509,7 +502,7 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova,static u32 *as_get_pte (struct tegra_smmu_as *as,dma_addr_t iova,struct page **pagep) { -u32 *pd =page_address (as->pd), *pt, *count; +u32 *pd =page_address (as->pd), *pt;unsigned int pde =iova_pd_index (iova);struct tegra_smmu *smmu = as->smmu;struct page *page;
@@ -545,9 +538,8 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, pt =page_address (page);/* Keep track of entries in this page table. */ - count =page_address (as->count);if (pt[iova_pt_index (iova)] ==0 ) - count[pde]++; + as->count[pde]++;return tegra_smmu_pte_offset (page, iova); }
@@ -556,7 +548,6 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova) {struct tegra_smmu *smmu = as->smmu;unsigned int pde =iova_pd_index (iova); -u32 *count =page_address (as->count);u32 *pd =page_address (as->pd);struct page *page = as->pts[pde];
@@ -564,7 +555,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)* When no entries in this page table are used anymore, return the * memory page to the system. */ -if (--count[pde] ==0 ) { +if (--as->count[pde] ==0 ) {unsigned int offset = pde *sizeof (*pd);/* Clear the page directory entry first */
Signature · 1 line
2.1.0
PATCH 10/18 iommu: tegra-smmu: move flush_dcache to tegra-smmu.c
In reply to Russell King - ARM LinuxDrivers should not be using __cpuc_* functions nor outer_cache_flush() directly. This change partly cleans up tegra-smmu.c.
The only difference between cache handling of the tegra variants is Denver, which omits the call to outer_cache_flush(). This is due to Denver being an ARM64 CPU, and the ARM64 architecture does not provide this function. (This, in itself, is a good reason why these should not be used.)
--- drivers/iommu/tegra-smmu.c | 24 +++++++++++++++++++----- drivers/memory/tegra/tegra114.c | 17 ----------------- drivers/memory/tegra/tegra124.c | 30 ------------------------------ drivers/memory/tegra/tegra30.c | 17 ----------------- include/soc/tegra/mc.h | 7 ------- 5 files changed, 19 insertions(+), 76 deletions(-)
drivers/iommu/tegra-smmu.c +19 -5
@@ -16,6 +16,8 @@#include <linux/platform_device.h> #include <linux/slab.h> +#include <asm/cacheflush.h> +#include <soc/tegra/ahb.h> #include <soc/tegra/mc.h>
@@ -145,6 +147,18 @@ static unsigned int iova_pt_index(unsigned long iova)return (iova >>SMMU_PTE_SHIFT ) & (SMMU_NUM_PTE -1 ); } +static void smmu_flush_dcache (struct page *page,unsigned long offset, +size_t size) +{ +phys_addr_t phys =page_to_phys (page) + offset; +void *virt =page_address (page) + offset; + +__cpuc_flush_dcache_area (virt, size); +#ifdef CONFIG_ARM +outer_flush_range (phys, phys + size); +#endif +} +static inline void smmu_flush_ptc (struct tegra_smmu *smmu,struct page *page,unsigned long offset) {
@@ -392,7 +406,7 @@ static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,if (err <0 )return err; - smmu->soc->ops->flush_dcache (as->pd,0 ,SMMU_SIZE_PD ); +smmu_flush_dcache (as->pd,0 ,SMMU_SIZE_PD );smmu_flush_ptc (smmu, as->pd,0 );smmu_flush_tlb_asid (smmu, as->id);
@@ -521,11 +535,11 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, as->pts[pde] = page; - smmu->soc->ops->flush_dcache (page,0 ,SMMU_SIZE_PT ); +smmu_flush_dcache (page,0 ,SMMU_SIZE_PT ); pd[pde] =SMMU_MK_PDE (page,SMMU_PDE_ATTR |SMMU_PDE_NEXT ); - smmu->soc->ops->flush_dcache (as->pd, pde <<2 ,4 ); +smmu_flush_dcache (as->pd, pde <<2 ,4 );smmu_flush_ptc (smmu, as->pd, pde <<2 );smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu);
@@ -562,7 +576,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova) pd[pde] =0 ;/* Flush the page directory entry */ - smmu->soc->ops->flush_dcache (as->pd, offset,sizeof (*pd)); +smmu_flush_dcache (as->pd, offset,sizeof (*pd));smmu_flush_ptc (smmu, as->pd, offset);smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu);
@@ -582,7 +596,7 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova, *pte = val; - smmu->soc->ops->flush_dcache (pte_page, offset,4 ); +smmu_flush_dcache (pte_page, offset,4 );smmu_flush_ptc (smmu, pte_page, offset);smmu_flush_tlb_group (smmu, as->id, iova);smmu_flush (smmu);
drivers/memory/tegra/tegra114.c +0 -17
@@ -9,8 +9,6 @@#include <linux/of.h> #include <linux/mm.h> -#include <asm/cacheflush.h> -#include <dt-bindings/memory/tegra114-mc.h> #include "mc.h"
@@ -914,20 +912,6 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = { { .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, }; -static void tegra114_flush_dcache (struct page *page,unsigned long offset, -size_t size) -{ -phys_addr_t phys =page_to_phys (page) + offset; -void *virt =page_address (page) + offset; - -__cpuc_flush_dcache_area (virt, size); -outer_flush_range (phys, phys + size); -} - -static const struct tegra_smmu_ops tegra114_smmu_ops = { - .flush_dcache = tegra114_flush_dcache, -}; -static const struct tegra_smmu_soc tegra114_smmu_soc = { .clients = tegra114_mc_clients, .num_clients = ARRAY_SIZE(tegra114_mc_clients),
@@ -936,7 +920,6 @@ static const struct tegra_smmu_soc tegra114_smmu_soc = { .supports_round_robin_arbitration = false, .supports_request_limit = false, .num_asids = 4, - .ops = &tegra114_smmu_ops, };const struct tegra_mc_soc tegra114_mc_soc = {
drivers/memory/tegra/tegra124.c +0 -30
@@ -9,8 +9,6 @@#include <linux/of.h> #include <linux/mm.h> -#include <asm/cacheflush.h> -#include <dt-bindings/memory/tegra124-mc.h> #include "mc.h"
@@ -1002,20 +1000,6 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = { };#ifdef CONFIG_ARCH_TEGRA_124_SOC -static void tegra124_flush_dcache (struct page *page,unsigned long offset, -size_t size) -{ -phys_addr_t phys =page_to_phys (page) + offset; -void *virt =page_address (page) + offset; - -__cpuc_flush_dcache_area (virt, size); -outer_flush_range (phys, phys + size); -} - -static const struct tegra_smmu_ops tegra124_smmu_ops = { - .flush_dcache = tegra124_flush_dcache, -}; -static const struct tegra_smmu_soc tegra124_smmu_soc = { .clients = tegra124_mc_clients, .num_clients = ARRAY_SIZE(tegra124_mc_clients),
@@ -1024,7 +1008,6 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = { .supports_round_robin_arbitration = true, .supports_request_limit = true, .num_asids = 128, - .ops = &tegra124_smmu_ops, };const struct tegra_mc_soc tegra124_mc_soc = {
@@ -1039,18 +1022,6 @@ const struct tegra_mc_soc tegra124_mc_soc = {#endif /* CONFIG_ARCH_TEGRA_124_SOC */ #ifdef CONFIG_ARCH_TEGRA_132_SOC -static void tegra132_flush_dcache (struct page *page,unsigned long offset, -size_t size) -{ -void *virt =page_address (page) + offset; - -__flush_dcache_area (virt, size); -} - -static const struct tegra_smmu_ops tegra132_smmu_ops = { - .flush_dcache = tegra132_flush_dcache, -}; -static const struct tegra_smmu_soc tegra132_smmu_soc = { .clients = tegra124_mc_clients, .num_clients = ARRAY_SIZE(tegra124_mc_clients),
@@ -1059,7 +1030,6 @@ static const struct tegra_smmu_soc tegra132_smmu_soc = { .supports_round_robin_arbitration = true, .supports_request_limit = true, .num_asids = 128, - .ops = &tegra132_smmu_ops, };const struct tegra_mc_soc tegra132_mc_soc = {
drivers/memory/tegra/tegra30.c +0 -17
@@ -9,8 +9,6 @@#include <linux/of.h> #include <linux/mm.h> -#include <asm/cacheflush.h> -#include <dt-bindings/memory/tegra30-mc.h> #include "mc.h"
@@ -936,20 +934,6 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, }; -static void tegra30_flush_dcache (struct page *page,unsigned long offset, -size_t size) -{ -phys_addr_t phys =page_to_phys (page) + offset; -void *virt =page_address (page) + offset; - -__cpuc_flush_dcache_area (virt, size); -outer_flush_range (phys, phys + size); -} - -static const struct tegra_smmu_ops tegra30_smmu_ops = { - .flush_dcache = tegra30_flush_dcache, -}; -static const struct tegra_smmu_soc tegra30_smmu_soc = { .clients = tegra30_mc_clients, .num_clients = ARRAY_SIZE(tegra30_mc_clients),
@@ -958,7 +942,6 @@ static const struct tegra_smmu_soc tegra30_smmu_soc = { .supports_round_robin_arbitration = false, .supports_request_limit = false, .num_asids = 4, - .ops = &tegra30_smmu_ops, };const struct tegra_mc_soc tegra30_mc_soc = {
include/soc/tegra/mc.h +0 -7
@@ -51,11 +51,6 @@ struct tegra_smmu_swgroup {unsigned int reg; }; -struct tegra_smmu_ops { -void (*flush_dcache)(struct page *page,unsigned long offset, -size_t size); -}; -struct tegra_smmu_soc {const struct tegra_mc_client *clients;unsigned int num_clients;
@@ -67,8 +62,6 @@ struct tegra_smmu_soc {bool supports_request_limit;unsigned int num_asids; - -const struct tegra_smmu_ops *ops; };struct tegra_mc ;
Signature · 1 line
2.1.0
PATCH 11/18 iommu: tegra-smmu: split smmu_flush_ptc()
In reply to Russell King - ARM Linuxsmmu_flush_ptc() is used in two modes: one is to flush an individual entry, the other is to flush all entries. We know at the call site which we require. Split the function into smmu_flush_ptc_all() and smmu_flush_ptc().
--- drivers/iommu/tegra-smmu.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
drivers/iommu/tegra-smmu.c +13 -13
@@ -159,29 +159,29 @@ static void smmu_flush_dcache(struct page *page, unsigned long offset,#endif } +static void smmu_flush_ptc_all (struct tegra_smmu *smmu) +{ +smmu_writel (smmu,SMMU_PTC_FLUSH_TYPE_ALL ,SMMU_PTC_FLUSH ); +} +static inline void smmu_flush_ptc (struct tegra_smmu *smmu,struct page *page,unsigned long offset) { -phys_addr_t phys = page ?page_to_phys (page) :0 ; +phys_addr_t phys =page_to_phys (page);u32 value; -if (page) { - offset &= ~(smmu->mc->soc->atom_size -1 ); + offset &= ~(smmu->mc->soc->atom_size -1 ); -if (smmu->mc->soc->num_address_bits >32 ) { +if (smmu->mc->soc->num_address_bits >32 ) {#ifdef CONFIG_PHYS_ADDR_T_64BIT - value = (phys >>32 ) &SMMU_PTC_FLUSH_HI_MASK ; + value = (phys >>32 ) &SMMU_PTC_FLUSH_HI_MASK ;#else - value =0 ; + value =0 ;#endif -smmu_writel (smmu, value,SMMU_PTC_FLUSH_HI ); - } - - value = (phys + offset) |SMMU_PTC_FLUSH_TYPE_ADR ; - }else { - value =SMMU_PTC_FLUSH_TYPE_ALL ; +smmu_writel (smmu, value,SMMU_PTC_FLUSH_HI ); } + value = (phys + offset) |SMMU_PTC_FLUSH_TYPE_ADR ;smmu_writel (smmu, value,SMMU_PTC_FLUSH ); }
@@ -888,7 +888,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,smmu_writel (smmu, value,SMMU_TLB_CONFIG ); -smmu_flush_ptc (smmu,NULL ,0 ); +smmu_flush_ptc_all (smmu);smmu_flush_tlb (smmu);smmu_writel (smmu,SMMU_CONFIG_ENABLE ,SMMU_CONFIG );smmu_flush (smmu);
Signature · 1 line
2.1.0
PATCH 12/18 iommu: tegra-smmu: smmu_flush_ptc() wants device address
In reply to Russell King - ARM LinuxPass smmu_flush_ptc() the device address rather than struct page poiner.
--- drivers/iommu/tegra-smmu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
drivers/iommu/tegra-smmu.c +5 -6
@@ -164,10 +164,9 @@ static void smmu_flush_ptc_all(struct tegra_smmu *smmu)smmu_writel (smmu,SMMU_PTC_FLUSH_TYPE_ALL ,SMMU_PTC_FLUSH ); } -static inline void smmu_flush_ptc (struct tegra_smmu *smmu,struct page *page, +static inline void smmu_flush_ptc (struct tegra_smmu *smmu,phys_addr_t phys,unsigned long offset) { -phys_addr_t phys =page_to_phys (page);u32 value; offset &= ~(smmu->mc->soc->atom_size -1 );
@@ -407,7 +406,7 @@ static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,return err;smmu_flush_dcache (as->pd,0 ,SMMU_SIZE_PD ); -smmu_flush_ptc (smmu, as->pd,0 ); +smmu_flush_ptc (smmu,page_to_phys (as->pd),0 );smmu_flush_tlb_asid (smmu, as->id);smmu_writel (smmu, as->id &0x7f ,SMMU_PTB_ASID );
@@ -540,7 +539,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, pd[pde] =SMMU_MK_PDE (page,SMMU_PDE_ATTR |SMMU_PDE_NEXT );smmu_flush_dcache (as->pd, pde <<2 ,4 ); -smmu_flush_ptc (smmu, as->pd, pde <<2 ); +smmu_flush_ptc (smmu,page_to_phys (as->pd), pde <<2 );smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu); }else {
@@ -577,7 +576,7 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)/* Flush the page directory entry */ smmu_flush_dcache (as->pd, offset,sizeof (*pd)); -smmu_flush_ptc (smmu, as->pd, offset); +smmu_flush_ptc (smmu,page_to_phys (as->pd), offset);smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu);
@@ -597,7 +596,7 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova, *pte = val;smmu_flush_dcache (pte_page, offset,4 ); -smmu_flush_ptc (smmu, pte_page, offset); +smmu_flush_ptc (smmu,page_to_phys (pte_page), offset);smmu_flush_tlb_group (smmu, as->id, iova);smmu_flush (smmu); }
Signature · 1 line
2.1.0
PATCH 13/18 iommu: tegra-smmu: convert to use DMA API
In reply to Russell King - ARM LinuxUse the DMA API instead of calling architecture internal functions in the Tegra SMMU driver.
--- drivers/iommu/tegra-smmu.c | 129 +++++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 45 deletions(-)
drivers/iommu/tegra-smmu.c +84 -45
@@ -45,6 +45,7 @@ struct tegra_smmu_as {u32 *count;struct page **pts;struct page *pd; +dma_addr_t pd_dma;unsigned id;u32 attr; };
@@ -82,9 +83,9 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)#define SMMU_PTB_ASID_VALUE (x) ((x) & 0x7f)#define SMMU_PTB_DATA 0x020 -#define SMMU_PTB_DATA_VALUE (page, attr) (page_to_phys(page) >> 12 | (attr)) +#define SMMU_PTB_DATA_VALUE (dma, attr) ((dma) >> 12 | (attr)) -#define SMMU_MK_PDE (page, attr) (page_to_phys(page) >> SMMU_PTE_SHIFT | (attr)) +#define SMMU_MK_PDE (dma, attr) ((dma) >> SMMU_PTE_SHIFT | (attr))#define SMMU_TLB_FLUSH 0x030#define SMMU_TLB_FLUSH_VA_MATCH_ALL (0 << 0)
@@ -147,16 +148,15 @@ static unsigned int iova_pt_index(unsigned long iova)return (iova >>SMMU_PTE_SHIFT ) & (SMMU_NUM_PTE -1 ); } -static void smmu_flush_dcache (struct page *page,unsigned long offset, -size_t size) +static bool smmu_dma_addr_valid (struct tegra_smmu *smmu,dma_addr_t addr) { -phys_addr_t phys =page_to_phys (page) + offset; -void *virt =page_address (page) + offset; + addr >>=12 ; +return (addr & smmu->pfn_mask) == addr; +} -__cpuc_flush_dcache_area (virt, size); -#ifdef CONFIG_ARM -outer_flush_range (phys, phys + size); -#endif +static dma_addr_t smmu_pde_to_dma (u32 pde) +{ +return pde <<12 ; }static void smmu_flush_ptc_all (struct tegra_smmu *smmu)
@@ -164,7 +164,7 @@ static void smmu_flush_ptc_all(struct tegra_smmu *smmu)smmu_writel (smmu,SMMU_PTC_FLUSH_TYPE_ALL ,SMMU_PTC_FLUSH ); } -static inline void smmu_flush_ptc (struct tegra_smmu *smmu,phys_addr_t phys, +static inline void smmu_flush_ptc (struct tegra_smmu *smmu,dma_addr_t dma,unsigned long offset) {u32 value;
@@ -172,15 +172,15 @@ static inline void smmu_flush_ptc(struct tegra_smmu *smmu, phys_addr_t phys, offset &= ~(smmu->mc->soc->atom_size -1 );if (smmu->mc->soc->num_address_bits >32 ) { -#ifdef CONFIG_PHYS_ADDR_T_64BIT - value = (phys >>32 ) &SMMU_PTC_FLUSH_HI_MASK ; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + value = (dma >>32 ) &SMMU_PTC_FLUSH_HI_MASK ;#else value =0 ;#endif smmu_writel (smmu, value,SMMU_PTC_FLUSH_HI ); } - value = (phys + offset) |SMMU_PTC_FLUSH_TYPE_ADR ; + value = (dma + offset) |SMMU_PTC_FLUSH_TYPE_ADR ;smmu_writel (smmu, value,SMMU_PTC_FLUSH ); }
@@ -401,16 +401,26 @@ static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,return 0 ; } + as->pd_dma =dma_map_page (smmu->dev, as->pd,0 ,SMMU_SIZE_PD , +DMA_TO_DEVICE ); +if (dma_mapping_error (smmu->dev, as->pd_dma)) +return -ENOMEM ; + +/* We can't handle 64-bit DMA addresses */ +if (!smmu_dma_addr_valid (smmu, as->pd_dma)) { + err = -ENOMEM ; + goto err_unmap; + } + err =tegra_smmu_alloc_asid (smmu, &as->id);if (err <0 ) -return err; + goto err_unmap; -smmu_flush_dcache (as->pd,0 ,SMMU_SIZE_PD ); -smmu_flush_ptc (smmu,page_to_phys (as->pd),0 ); +smmu_flush_ptc (smmu, as->pd_dma,0 );smmu_flush_tlb_asid (smmu, as->id);smmu_writel (smmu, as->id &0x7f ,SMMU_PTB_ASID ); - value =SMMU_PTB_DATA_VALUE (as->pd, as->attr); + value =SMMU_PTB_DATA_VALUE (as->pd_dma, as->attr);smmu_writel (smmu, value,SMMU_PTB_DATA );smmu_flush (smmu);
@@ -418,6 +428,10 @@ static int tegra_smmu_as_prepare(struct tegra_smmu *smmu, as->use_count++;return 0 ; + +err_unmap: +dma_unmap_page (smmu->dev, as->pd_dma,SMMU_SIZE_PD ,DMA_TO_DEVICE ); +return err; } static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,
@@ -427,6 +441,9 @@ static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,return ;tegra_smmu_free_asid (smmu, as->id); + +dma_unmap_page (smmu->dev, as->pd_dma,SMMU_SIZE_PD ,DMA_TO_DEVICE ); + as->smmu =NULL ; }
@@ -498,63 +515,81 @@ static u32 *tegra_smmu_pte_offset(struct page *pt_page, unsigned long iova) }static u32 *tegra_smmu_pte_lookup (struct tegra_smmu_as *as,unsigned long iova, -struct page **pagep) +dma_addr_t *dmap) {unsigned int pd_index =iova_pd_index (iova);struct page *pt_page; +u32 *pd; pt_page = as->pts[pd_index];if (!pt_page)return NULL ; - *pagep = pt_page; + pd =page_address (as->pd); + *dmap =smmu_pde_to_dma (pd[pd_index]);return tegra_smmu_pte_offset (pt_page, iova); }static u32 *as_get_pte (struct tegra_smmu_as *as,dma_addr_t iova, -struct page **pagep) +dma_addr_t *dmap) {u32 *pd =page_address (as->pd), *pt;unsigned int pde =iova_pd_index (iova);struct tegra_smmu *smmu = as->smmu; -struct page *page;unsigned int i;if (!as->pts[pde]) { +struct page *page; +dma_addr_t dma; + page =alloc_page (GFP_KERNEL | __GFP_DMA);if (!page)return NULL ; pt =page_address (page); -SetPageReserved (page);for (i =0 ; i <SMMU_NUM_PTE ; i++) pt[i] =0 ; + dma =dma_map_page (smmu->dev, page,0 ,SMMU_SIZE_PT , +DMA_TO_DEVICE ); +if (dma_mapping_error (smmu->dev, dma)) { +__free_page (page); +return NULL ; + } + +if (!smmu_dma_addr_valid (smmu, dma)) { +dma_unmap_page (smmu->dev, dma,SMMU_SIZE_PT , +DMA_TO_DEVICE ); +__free_page (page); +return NULL ; + } + as->pts[pde] = page; -smmu_flush_dcache (page,0 ,SMMU_SIZE_PT ); +SetPageReserved (page); - pd[pde] =SMMU_MK_PDE (page,SMMU_PDE_ATTR |SMMU_PDE_NEXT ); + pd[pde] =SMMU_MK_PDE (dma,SMMU_PDE_ATTR |SMMU_PDE_NEXT ); -smmu_flush_dcache (as->pd, pde <<2 ,4 ); -smmu_flush_ptc (smmu,page_to_phys (as->pd), pde <<2 ); +dma_sync_single_range_for_device (smmu->dev, as->pd_dma, + pde <<2 ,4 ,DMA_TO_DEVICE ); +smmu_flush_ptc (smmu, as->pd_dma, pde <<2 );smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu); + + *dmap = dma; }else { - page = as->pts[pde]; + *dmap =smmu_pde_to_dma (pd[pde]); } - *pagep = page; - - pt =page_address (page); + pt =page_address (as->pts[pde]);/* Keep track of entries in this page table. */ if (pt[iova_pt_index (iova)] ==0 ) as->count[pde]++; -return tegra_smmu_pte_offset (page, iova); +return tegra_smmu_pte_offset (as->pts[pde], iova); }static void tegra_smmu_pte_put_use (struct tegra_smmu_as *as,unsigned long iova)
@@ -570,17 +605,20 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)*/ if (--as->count[pde] ==0 ) {unsigned int offset = pde *sizeof (*pd); +dma_addr_t pte_dma =smmu_pde_to_dma (pd[pde]);/* Clear the page directory entry first */ pd[pde] =0 ;/* Flush the page directory entry */ -smmu_flush_dcache (as->pd, offset,sizeof (*pd)); -smmu_flush_ptc (smmu,page_to_phys (as->pd), offset); +dma_sync_single_range_for_device (smmu->dev, as->pd_dma, offset, +sizeof (*pd),DMA_TO_DEVICE ); +smmu_flush_ptc (smmu, as->pd_dma, offset);smmu_flush_tlb_section (smmu, as->id, iova);smmu_flush (smmu);/* Finally, free the page */ +dma_unmap_page (smmu->dev, pte_dma,SMMU_SIZE_PT ,DMA_TO_DEVICE );ClearPageReserved (page);__free_page (page); as->pts[pde] =NULL ;
@@ -588,15 +626,16 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova) }static void tegra_smmu_set_pte (struct tegra_smmu_as *as,unsigned long iova, -u32 *pte,struct page *pte_page,u32 val) +u32 *pte,dma_addr_t pte_dma,u32 val) {struct tegra_smmu *smmu = as->smmu;unsigned long offset =offset_in_page (pte); *pte = val; -smmu_flush_dcache (pte_page, offset,4 ); -smmu_flush_ptc (smmu,page_to_phys (pte_page), offset); +dma_sync_single_range_for_device (smmu->dev, pte_dma, offset, +4 ,DMA_TO_DEVICE ); +smmu_flush_ptc (smmu, pte_dma, offset);smmu_flush_tlb_group (smmu, as->id, iova);smmu_flush (smmu); }
@@ -605,14 +644,14 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { struct tegra_smmu_as *as = to_smmu_as(domain); -struct page *page; +dma_addr_t pte_dma;u32 *pte; - pte =as_get_pte (as, iova, &page); + pte =as_get_pte (as, iova, &pte_dma);if (!pte)return -ENOMEM ; -tegra_smmu_set_pte (as, iova, pte, page, +tegra_smmu_set_pte (as, iova, pte, pte_dma,__phys_to_pfn (paddr) |SMMU_PTE_ATTR );return 0 ;
@@ -622,14 +661,14 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) {struct tegra_smmu_as *as =to_smmu_as (domain); -struct page *pte_page; +dma_addr_t pte_dma;u32 *pte; - pte =tegra_smmu_pte_lookup (as, iova, &pte_page); + pte =tegra_smmu_pte_lookup (as, iova, &pte_dma);if (!pte || !*pte)return 0 ; -tegra_smmu_set_pte (as, iova, pte, pte_page,0 ); +tegra_smmu_set_pte (as, iova, pte, pte_dma,0 );tegra_smmu_pte_put_use (as, iova);return size;
@@ -639,11 +678,11 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) {struct tegra_smmu_as *as =to_smmu_as (domain); -struct page *pte_page;unsigned long pfn; +dma_addr_t pte_dma;u32 *pte; - pte =tegra_smmu_pte_lookup (as, iova, &pte_page); + pte =tegra_smmu_pte_lookup (as, iova, &pte_dma);if (!pte || !*pte)return 0 ;
Signature · 1 line
2.1.0
PATCH 14/18 iommu: tegra-smmu: remove PageReserved manipulation
In reply to Russell King - ARM LinuxRemove the unnecessary manipulation of the PageReserved flags in the Tegra SMMU driver. None of this is required as the page(s) remain private to the SMMU driver.
--- drivers/iommu/tegra-smmu.c | 5 ----- 1 file changed, 5 deletions(-)
drivers/iommu/tegra-smmu.c +0 -5
@@ -295,7 +295,6 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)/* clear PDEs */ pd =page_address (as->pd); -SetPageReserved (as->pd);for (i =0 ; i <SMMU_NUM_PDE ; i++) pd[i] =0 ;
@@ -313,7 +312,6 @@ static void tegra_smmu_domain_free(struct iommu_domain *domain)struct tegra_smmu_as *as =to_smmu_as (domain);/* TODO: free page directory and page tables */ -ClearPageReserved (as->pd);kfree (as); }
@@ -568,8 +566,6 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, as->pts[pde] = page; -SetPageReserved (page); - pd[pde] =SMMU_MK_PDE (dma,SMMU_PDE_ATTR |SMMU_PDE_NEXT ); dma_sync_single_range_for_device(smmu->dev, as->pd_dma,
@@ -619,7 +615,6 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)/* Finally, free the page */ dma_unmap_page (smmu->dev, pte_dma,SMMU_SIZE_PT ,DMA_TO_DEVICE ); -ClearPageReserved (page);__free_page (page); as->pts[pde] =NULL ; }
Signature · 1 line
2.1.0
PATCH 15/18 iommu: tegra-smmu: use __GFP_ZERO to allocate zeroed pages
In reply to Russell King - ARM LinuxRather than explicitly zeroing pages allocated via alloc_page(), add __GFP_ZERO to the gfp mask to ask the allocator for zeroed pages.
--- drivers/iommu/tegra-smmu.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-)
drivers/iommu/tegra-smmu.c +2 -16
@@ -260,8 +260,6 @@ static bool tegra_smmu_capable(enum iommu_cap cap)static struct iommu_domain *tegra_smmu_domain_alloc (unsigned type) {struct tegra_smmu_as *as; -unsigned int i; -uint32_t *pd;if (type !=IOMMU_DOMAIN_UNMANAGED )return NULL ;
@@ -272,7 +270,7 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type) as->attr =SMMU_PD_READABLE |SMMU_PD_WRITABLE |SMMU_PD_NONSECURE ; - as->pd =alloc_page (GFP_KERNEL | __GFP_DMA); + as->pd =alloc_page (GFP_KERNEL | __GFP_DMA | __GFP_ZERO);if (!as->pd) {kfree (as);return NULL ;
@@ -293,12 +291,6 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)return NULL ; } -/* clear PDEs */ - pd =page_address (as->pd); - -for (i =0 ; i <SMMU_NUM_PDE ; i++) - pd[i] =0 ; -/* setup aperture */ as->domain.geometry.aperture_start =0 ; as->domain.geometry.aperture_end =0xffffffff ;
@@ -535,21 +527,15 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,u32 *pd =page_address (as->pd), *pt;unsigned int pde =iova_pd_index (iova);struct tegra_smmu *smmu = as->smmu; -unsigned int i;if (!as->pts[pde]) {struct page *page;dma_addr_t dma; - page =alloc_page (GFP_KERNEL | __GFP_DMA); + page =alloc_page (GFP_KERNEL | __GFP_DMA | __GFP_ZERO);if (!page)return NULL ; - pt =page_address (page); - -for (i =0 ; i <SMMU_NUM_PTE ; i++) - pt[i] =0 ; - dma =dma_map_page (smmu->dev, page,0 ,SMMU_SIZE_PT ,DMA_TO_DEVICE );if (dma_mapping_error (smmu->dev, dma)) {
Signature · 1 line
2.1.0
PATCH 16/18 iommu: tegra-smmu: get_use
In reply to Russell King - ARM Linux--- drivers/iommu/tegra-smmu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
drivers/iommu/tegra-smmu.c +11 -6
@@ -524,7 +524,7 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova,static u32 *as_get_pte (struct tegra_smmu_as *as,dma_addr_t iova,dma_addr_t *dmap) { -u32 *pd =page_address (as->pd), *pt; +u32 *pd =page_address (as->pd);unsigned int pde =iova_pd_index (iova);struct tegra_smmu *smmu = as->smmu;
@@ -565,13 +565,14 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, *dmap =smmu_pde_to_dma (pd[pde]); } - pt =page_address (as->pts[pde]); +return tegra_smmu_pte_offset (as->pts[pde], iova); +} -/* Keep track of entries in this page table. */ -if (pt[iova_pt_index (iova)] ==0 ) - as->count[pde]++; +static void tegra_smmu_pte_get_use (struct tegra_smmu_as *as,unsigned long iova) +{ +unsigned int pd_index =iova_pd_index (iova); -return tegra_smmu_pte_offset (as->pts[pde], iova); + as->count[pd_index]++; }static void tegra_smmu_pte_put_use (struct tegra_smmu_as *as,unsigned long iova)
@@ -632,6 +633,10 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,if (!pte)return -ENOMEM ; +/* If we aren't overwriting a pre-existing entry, increment use */ +if (!*pte) +tegra_smmu_pte_get_use (as, iova); +tegra_smmu_set_pte (as, iova, pte, pte_dma,__phys_to_pfn (paddr) |SMMU_PTE_ATTR );
Signature · 1 line
2.1.0
PATCH 16/18 iommu: tegra-smmu: get_use
Quoted 4 lines · Russell King
Signed-off-by Russell King <[email protected]>--- drivers/iommu/tegra-smmu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
Joerg may want to have a proper commit message here. If you're too busy I can probably come up with something.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150727/de180f7a/attachment.sig>
PATCH 17/18 iommu: tegra-smmu: more cleanups
In reply to Russell King - ARM Linux--- drivers/iommu/tegra-smmu.c | 49 ++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 21 deletions(-)
drivers/iommu/tegra-smmu.c +28 -21
@@ -497,6 +497,27 @@ static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *de } } +static void tegra_smmu_set_pge (struct tegra_smmu_as *as,unsigned long iova, +u32 val) +{ +struct tegra_smmu *smmu = as->smmu; +unsigned int pd_index =iova_pd_index (iova); +u32 *pd =page_address (as->pd); +unsigned long offset = pd_index *sizeof (*pd); + +/* Set the page directory entry first */ + pd[pd_index] = val; + +/* The flush the page directory entry from caches */ +dma_sync_single_range_for_device (smmu->dev, as->pd_dma, offset, +sizeof (*pd),DMA_TO_DEVICE ); + +/* And flush the iommu */ +smmu_flush_ptc (smmu, as->pd_dma, offset); +smmu_flush_tlb_section (smmu, as->id, iova); +smmu_flush (smmu); +} +static u32 *tegra_smmu_pte_offset (struct page *pt_page,unsigned long iova) {u32 *pt =page_address (pt_page);
@@ -524,7 +545,6 @@ static u32 *tegra_smmu_pte_lookup(struct tegra_smmu_as *as, unsigned long iova,static u32 *as_get_pte (struct tegra_smmu_as *as,dma_addr_t iova,dma_addr_t *dmap) { -u32 *pd =page_address (as->pd);unsigned int pde =iova_pd_index (iova);struct tegra_smmu *smmu = as->smmu;
@@ -552,16 +572,13 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, as->pts[pde] = page; - pd[pde] =SMMU_MK_PDE (dma,SMMU_PDE_ATTR |SMMU_PDE_NEXT ); - -dma_sync_single_range_for_device (smmu->dev, as->pd_dma, - pde <<2 ,4 ,DMA_TO_DEVICE ); -smmu_flush_ptc (smmu, as->pd_dma, pde <<2 ); -smmu_flush_tlb_section (smmu, as->id, iova); -smmu_flush (smmu); +tegra_smmu_set_pge (as, iova,SMMU_MK_PDE (dma,SMMU_PDE_ATTR | +SMMU_PDE_NEXT )); *dmap = dma; }else { +u32 *pd =page_address (as->pd); + *dmap =smmu_pde_to_dma (pd[pde]); }
@@ -577,9 +594,7 @@ static void tegra_smmu_pte_get_use(struct tegra_smmu_as *as, unsigned long iova)static void tegra_smmu_pte_put_use (struct tegra_smmu_as *as,unsigned long iova) { -struct tegra_smmu *smmu = as->smmu;unsigned int pde =iova_pd_index (iova); -u32 *pd =page_address (as->pd);struct page *page = as->pts[pde];/*
@@ -587,20 +602,12 @@ static void tegra_smmu_pte_put_use(struct tegra_smmu_as *as, unsigned long iova)* memory page to the system. */ if (--as->count[pde] ==0 ) { -unsigned int offset = pde *sizeof (*pd); +struct tegra_smmu *smmu = as->smmu; +u32 *pd =page_address (as->pd);dma_addr_t pte_dma =smmu_pde_to_dma (pd[pde]); -/* Clear the page directory entry first */ - pd[pde] =0 ; - -/* Flush the page directory entry */ -dma_sync_single_range_for_device (smmu->dev, as->pd_dma, offset, -sizeof (*pd),DMA_TO_DEVICE ); -smmu_flush_ptc (smmu, as->pd_dma, offset); -smmu_flush_tlb_section (smmu, as->id, iova); -smmu_flush (smmu); +tegra_smmu_set_pge (as, iova,0 ); -/* Finally, free the page */ dma_unmap_page (smmu->dev, pte_dma,SMMU_SIZE_PT ,DMA_TO_DEVICE );__free_page (page); as->pts[pde] =NULL ;
Signature · 1 line
2.1.0
PATCH 17/18 iommu: tegra-smmu: more cleanups
Quoted 4 lines · Russell King
Signed-off-by Russell King <[email protected]>--- drivers/iommu/tegra-smmu.c | 49 ++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 21 deletions(-)
Same here.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150727/09eb379e/attachment-0001.sig>
PATCH 18/18 iommu: tegra-smmu: remove cacheflush.h
In reply to Russell King - ARM Linux--- drivers/iommu/tegra-smmu.c | 2 -- 1 file changed, 2 deletions(-)
drivers/iommu/tegra-smmu.c +0 -2
@@ -16,8 +16,6 @@#include <linux/platform_device.h> #include <linux/slab.h> -#include <asm/cacheflush.h> -#include <soc/tegra/ahb.h> #include <soc/tegra/mc.h>
Signature · 1 line
2.1.0
PATCH 18/18 iommu: tegra-smmu: remove cacheflush.h
Quoted 10 lines · Russell King
Signed-off-by Russell King <[email protected]>--- drivers/iommu/tegra-smmu.c | 2 -- 1 file changed, 2 deletions(-)drivers/iommu/tegra-smmu.c +0
-0
drivers/iommu/tegra-smmu.c line 20
Quoted 5 lines
drivers/iommu/tegra-smmu.c +0
-2@@ -16,8 +16,6 @@#include <linux/platform_device.h> #include <linux/slab.h> -#include <asm/cacheflush.h> -
Doesn't this logically belong in patch 13/18, along with the DMA API conversion that drops the last reference to these functions?
Again, I can take care of squashing this into 13/18 if you don't have any objections and send a pull request to Joerg.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150727/17e208af/attachment.sig>
Joerg Roedel
maintainercover Clean up exposure of arch-internal code
In reply to Russell King - ARM Linux[+ Heiko for comment on Rockchip IOMMU]
Quoted 47 lines · Russell King - ARM Linux
This series of patches attempts to clean up the use of architecture internal functions in drivers, and removes some functions from view in the asm/ headers. I'm also considering whether we need to add some linker magic to hide symbols when building the built-in.o files.
This was triggered by 3rd party drivers "going under the covers" of the DMA API and calling the dmac_*() functions directly, a practice which I have always refused to allow. This also breaks module building (which is the big hint that they're doing something wrong.)
However, it also came to light that various drivers are using __cpuc_* functions directly, which are non-portable, and is another instance of "going under the covers" and tinkering with what should be kept as architecture implementation details.
This series addresses some of these points. It:
(a) moves
dmac_map_area()anddmac_unmap_area()prototypes out ofasm/cacheflush.hand intoarch/arm/mm.
(b) provide asecure_flush()call for the Qualcomm secure monitor code.
(c) stop tegra smmu driver(s) from using__cpuc_flush_dcache_area, something which necessitates additional complexity to deal with the ARM vs ARM64 differences. It should be using the DMA API. However, the Tegra SMMU driver is in really bad shape, and this isn't a simple conversion - it requires a lot of additional fixes to bring the code up to scratch.It leaves the Rockchip IOMMU driver for the time being, but that is also something which needs cleaning up in the same way as the Tegra SMMU driver.
arch/arm/include/asm/cacheflush.h | 21 ++- arch/arm/include/asm/glue-cache.h | 2 - arch/arm/mm/dma-mapping.c | 1 + arch/arm/mm/dma.h | 32 ++++ drivers/firmware/qcom_scm-32.c | 4 +- drivers/iommu/tegra-smmu.c | 297 ++++++++++++++++++++++++-------------- drivers/memory/tegra/tegra114.c | 17 --- drivers/memory/tegra/tegra124.c | 30 ---- drivers/memory/tegra/tegra30.c | 17 --- include/soc/tegra/mc.h | 7 - 10 files changed, 242 insertions(+), 186 deletions(-)Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up according to speedtest.net.
cover Clean up exposure of arch-internal code
Am Montag, 27. Juli 2015, 15:13:13 schrieb Joerg Roedel:
Quoted 1 line
[+ Heiko for comment on Rockchip IOMMU]
adding also Daniel Kurtz and Tomasz Figa who did most of the iommu stuff if I remember correctly.
While I'm not this versed in the iommu world I see the issue Russell pointed out, as especially the cache flushes won't work like this on arm64, which uses the same iommu on the rk3368.
I'll also try to understand what this does and what needs to be done to the Rockchip iommu, but I guess that'll still take a bit.
Quoted 50 lines · Russell King - ARM Linux
Quoted 49 lines
This series of patches attempts to clean up the use of architecture internal functions in drivers, and removes some functions from view in the asm/ headers. I'm also considering whether we need to add some linker magic to hide symbols when building the built-in.o files.
This was triggered by 3rd party drivers "going under the covers" of the DMA API and calling the dmac_*() functions directly, a practice which I have always refused to allow. This also breaks module building (which is the big hint that they're doing something wrong.)
However, it also came to light that various drivers are using __cpuc_* functions directly, which are non-portable, and is another instance of "going under the covers" and tinkering with what should be kept as architecture implementation details.
This series addresses some of these points. It:
(a) moves
dmac_map_area()anddmac_unmap_area()prototypes out of
asm/cacheflush.hand intoarch/arm/mm.(b) provide a
secure_flush()call for the Qualcomm secure monitorcode.
(c) stop tegra smmu driver(s) from using
__cpuc_flush_dcache_area,something which necessitates additional complexity to deal with the ARM vs ARM64 differences. It should be using the DMA API. However, the Tegra SMMU driver is in really bad shape, and this isn't a simple conversion - it requires a lot of additional fixes to bring the code up to scratch.
It leaves the Rockchip IOMMU driver for the time being, but that is also something which needs cleaning up in the same way as the Tegra SMMU driver.
arch/arm/include/asm/cacheflush.h | 21 ++- arch/arm/include/asm/glue-cache.h | 2 - arch/arm/mm/dma-mapping.c | 1 + arch/arm/mm/dma.h | 32 ++++ drivers/firmware/qcom_scm-32.c | 4 +- drivers/iommu/tegra-smmu.c | 297 ++++++++++++++++++++++++-------------- drivers/memory/tegra/tegra114.c | 17 --- drivers/memory/tegra/tegra124.c | 30 ---- drivers/memory/tegra/tegra30.c | 17 --- include/soc/tegra/mc.h | 7 - 10 files changed, 242 insertions(+), 186 deletions(-)
cover Clean up exposure of arch-internal code
In reply to Russell King - ARM LinuxQuoted 17 lines · Russell King - ARM Linux
This series of patches attempts to clean up the use of architecture internal functions in drivers, and removes some functions from view in the asm/ headers. I'm also considering whether we need to add some linker magic to hide symbols when building the built-in.o files.
This was triggered by 3rd party drivers "going under the covers" of the DMA API and calling the dmac_*() functions directly, a practice which I have always refused to allow. This also breaks module building (which is the big hint that they're doing something wrong.)
However, it also came to light that various drivers are using __cpuc_* functions directly, which are non-portable, and is another instance of "going under the covers" and tinkering with what should be kept as architecture implementation details.
This series addresses some of these points. It:
Quoted 10 lines
(a) moves
dmac_map_area()anddmac_unmap_area()prototypes out ofasm/cacheflush.hand intoarch/arm/mm.
(b) provide asecure_flush()call for the Qualcomm secure monitor code.
(c) stop tegra smmu driver(s) from using__cpuc_flush_dcache_area, something which necessitates additional complexity to deal with the ARM vs ARM64 differences. It should be using the DMA API. However, the Tegra SMMU driver is in really bad shape, and this isn't a simple conversion - it requires a lot of additional fixes to bring the code up to scratch.
Out of curiosity, did you have any hardware to test this on? I've given it a quick spin and haven't seen anything out of the ordinary. I have a couple of nitpicks towards the end of the series, but other than that, very nice work.
What's the plan for merging this? Should it all go in through Joerg's tree so that potentially other driver conversions can go in on top of this, or would you prefer to take it through the ARM tree?
Quoted 5 lines
It leaves the Rockchip IOMMU driver for the time being, but that is also something which needs cleaning up in the same way as the Tegra SMMU driver.
From a cursory look, MSM, OMAP and Exynos are in the same boat as well.
Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150727/e1d4912f/attachment-0001.sig>
cover Clean up exposure of arch-internal code
Quoted 29 lines · Thierry Reding
Quoted 17 lines · Russell King - ARM Linux
This series of patches attempts to clean up the use of architecture internal functions in drivers, and removes some functions from view in the asm/ headers. I'm also considering whether we need to add some linker magic to hide symbols when building the built-in.o files.
This was triggered by 3rd party drivers "going under the covers" of the DMA API and calling the dmac_*() functions directly, a practice which I have always refused to allow. This also breaks module building (which is the big hint that they're doing something wrong.)
However, it also came to light that various drivers are using __cpuc_* functions directly, which are non-portable, and is another instance of "going under the covers" and tinkering with what should be kept as architecture implementation details.
This series addresses some of these points. It:
Quoted 10 lines
(a) moves
dmac_map_area()anddmac_unmap_area()prototypes out ofasm/cacheflush.hand intoarch/arm/mm.
(b) provide asecure_flush()call for the Qualcomm secure monitor code.
(c) stop tegra smmu driver(s) from using__cpuc_flush_dcache_area, something which necessitates additional complexity to deal with the ARM vs ARM64 differences. It should be using the DMA API. However, the Tegra SMMU driver is in really bad shape, and this isn't a simple conversion - it requires a lot of additional fixes to bring the code up to scratch.
Quoted 1 line
Out of curiosity, did you have any hardware to test this on?
Nope - it only got build-tested, and it's partly why there's soo many incremental small patches.
Quoted 4 lines
I've given
it a quick spin and haven't seen anything out of the ordinary. I have a couple of nitpicks towards the end of the series, but other than that, very nice work.
Good news.
Quoted 3 lines
What's the plan for merging this? Should it all go in through Joerg's tree so that potentially other driver conversions can go in on top of this, or would you prefer to take it through the ARM tree?
I don't mind the series being split - there's nothing dependent between the individual drivers, so all the Tegra SMMU stuff can go through Joerg's tree if that'll be easiest.
Quoted 5 lines
Quoted 3 lines
It leaves the Rockchip IOMMU driver for the time being, but that is also something which needs cleaning up in the same way as the Tegra SMMU driver.
From a cursory look, MSM, OMAP and Exynos are in the same boat as well.
Yes. There is a question here though:
Is it a good idea to use normal cacheable memory for the IOMMU page table entries, or would DMA coherent memory be better?
If the latter, then we don't need to do any cache maintanence of the tables, and all the hacks can go. If the former, using the streaming DMA API in all IOMMU drivers would be a good idea, like Tegra SMMU now does as a result of these patches.
Signature · 2 lines
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up according to speedtest.net.
cover Clean up exposure of arch-internal code
Quoted 30 lines · Russell King - ARM Linux
Quoted 29 lines · Thierry Reding
Quoted 27 lines · Russell King - ARM Linux
This series of patches attempts to clean up the use of architecture internal functions in drivers, and removes some functions from view in the asm/ headers. I'm also considering whether we need to add some linker magic to hide symbols when building the built-in.o files.
This was triggered by 3rd party drivers "going under the covers" of the DMA API and calling the dmac_*() functions directly, a practice which I have always refused to allow. This also breaks module building (which is the big hint that they're doing something wrong.)
However, it also came to light that various drivers are using __cpuc_* functions directly, which are non-portable, and is another instance of "going under the covers" and tinkering with what should be kept as architecture implementation details.
This series addresses some of these points. It:
(a) moves
dmac_map_area()anddmac_unmap_area()prototypes out ofasm/cacheflush.hand intoarch/arm/mm.
(b) provide asecure_flush()call for the Qualcomm secure monitor code.
(c) stop tegra smmu driver(s) from using__cpuc_flush_dcache_area, something which necessitates additional complexity to deal with the ARM vs ARM64 differences. It should be using the DMA API. However, the Tegra SMMU driver is in really bad shape, and this isn't a simple conversion - it requires a lot of additional fixes to bring the code up to scratch.
Quoted 4 lines
Quoted 1 line
Out of curiosity, did you have any hardware to test this on?
Nope - it only got build-tested, and it's partly why there's soo many incremental small patches.
All the more impressive. =)
Quoted 10 lines
Quoted 5 lines
Quoted 3 lines
It leaves the Rockchip IOMMU driver for the time being, but that is also something which needs cleaning up in the same way as the Tegra SMMU driver.
From a cursory look, MSM, OMAP and Exynos are in the same boat as well.
Yes. There is a question here though:
Is it a good idea to use normal cacheable memory for the IOMMU page table entries, or would DMA coherent memory be better?
Currently I think coherent memory would do just as well as cacheable memory, maybe even better given that we wouldn't need the cache maintenance. However, I think cacheable memory might come in handy if ever the ->map_sg() callback gets implemented, because it would allow us to batch up a bunch of PTE writes and flush whole pages in one go.
Unfortunately I don't have the tools handy to provide any numbers, so the above is really just guesswork. Cc'ing Rob Clark who, I think, did performance measurements a while ago. Not sure if those included coherent vs. cacheable memory, though.
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150727/3cbb9673/attachment.sig>
Joerg Roedel
maintainercover Clean up exposure of arch-internal code
In reply to Thierry RedingHey Thierry,
Quoted 3 lines · Thierry Reding
What's the plan for merging this? Should it all go in through Joerg's tree so that potentially other driver conversions can go in on top of this, or would you prefer to take it through the ARM tree?
So it would be great if you can work in your review comments and send me a pull request with the result. I'll put it into the iommu-tree for v4.3 then.
Joerg