Tegra GART fixes and improvements
Subsystems (3)
cover Tegra GART fixes and improvements
GART driver wasn't ever been utilized in upstream, but finally this should change sometime soon with Tegra's DRM driver rework. In general GART driver works fine, though there are couple things that could be improved.
Dmitry Osipenko (4):
iommu/tegra: gart: Add debugging facility iommu/tegra: gart: Fix gart_iommu_unmap() iommu/tegra: gart: Constify number of GART pages iommu/tegra: gart: Optimize map/unmap
drivers/iommu/tegra-gart.c | 90 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 21 deletions(-)
Signature · 1 line
2.16.3
PATCH 1/4 iommu/tegra: gart: Add debugging facility
Page mapping could overwritten by an accident (a bug). We can catch this case by checking 'VALID' bit of GART's page entry prior to mapping of a page. Since that check introduces a small performance impact, it should be enabled explicitly using new GART's kernel module 'debug' parameter.
--- drivers/iommu/tegra-gart.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/iommu/tegra-gart.c +13 -0
@@ -72,6 +72,8 @@ struct gart_domain {static struct gart_device *gart_handle;/* unique for a system */ +static bool gart_debug; +#define GART_PTE (_pfn) \ (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT))
@@ -271,6 +273,7 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,struct gart_device *gart = gart_domain->gart;unsigned long flags;unsigned long pfn; +unsigned long pte;if (!gart_iova_range_valid (gart, iova, bytes))return -EINVAL ;
@@ -282,6 +285,14 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,spin_unlock_irqrestore (&gart->pte_lock, flags);return -EINVAL ; } +if (gart_debug) { + pte =gart_read_pte (gart, iova); +if (pte &GART_ENTRY_PHYS_ADDR_VALID ) { +spin_unlock_irqrestore (&gart->pte_lock, flags); +dev_err (gart->dev,"Page entry is in-use\n" ); +return -EBUSY ; + } + }gart_set_pte (gart, iova,GART_PTE (pfn));FLUSH_GART_REGS (gart);spin_unlock_irqrestore (&gart->pte_lock, flags);
@@ -515,7 +526,9 @@ static void __exit tegra_gart_exit(void)subsys_initcall (tegra_gart_init);module_exit (tegra_gart_exit); +module_param (gart_debug, bool,0644 ); +MODULE_PARM_DESC (gart_debug,"Enable GART debugging" );MODULE_DESCRIPTION ("IOMMU API for GART in Tegra20" );MODULE_AUTHOR ("Hiroshi DOYU <[email protected]>" );MODULE_ALIAS ("platform:tegra-gart" );
Signature · 1 line
2.16.3
PATCH 2/4 iommu/tegra: gart: Fix gart_iommu_unmap()
In reply to Dmitry OsipenkoIt must return the number of unmapped bytes on success, returning 0 means that unmapping failed and in result only one page is unmapped.
--- drivers/iommu/tegra-gart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/tegra-gart.c +1 -1
@@ -313,7 +313,7 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,gart_set_pte (gart, iova,0 );FLUSH_GART_REGS (gart);spin_unlock_irqrestore (&gart->pte_lock, flags); -return 0 ; +return bytes; } static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
Signature · 1 line
2.16.3
PATCH 3/4 iommu/tegra: gart: Constify number of GART pages
In reply to Dmitry OsipenkoGART has a fixed aperture size, hence the number of pages is constant.
--- drivers/iommu/tegra-gart.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
drivers/iommu/tegra-gart.c +8 -6
@@ -33,6 +33,8 @@#include <asm/cacheflush.h> +#define GART_APERTURE_SIZE SZ_32M +/* bitmap of the page sizes currently supported */ #define GART_IOMMU_PGSIZES (SZ_4K)
@@ -47,6 +49,8 @@#define GART_PAGE_MASK \ (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) +#define GART_PAGECOUNT (GART_APERTURE_SIZE >> GART_PAGE_SHIFT) +struct gart_client {struct device *dev;struct list_head list;
@@ -55,7 +59,6 @@ struct gart_client {struct gart_device {void __iomem *regs;u32 *savedata; -u32 page_count;/* total remappable size */ dma_addr_t iovmm_base;/* offset to vmm_area */ spinlock_t pte_lock;/* for pagetable */ struct list_head client;
@@ -91,7 +94,7 @@ static struct gart_domain *to_gart_domain(struct iommu_domain *dom)#define for_each_gart_pte (gart, iova) \ for (iova = gart->iovmm_base; \ - iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \ + iova < gart->iovmm_base + GART_APERTURE_SIZE; \ iova += GART_PAGE_SIZE) static inline void gart_set_pte(struct gart_device *gart,
@@ -158,7 +161,7 @@ static inline bool gart_iova_range_valid(struct gart_device *gart, iova_start = iova; iova_end = iova_start + bytes -1 ; gart_start = gart->iovmm_base; - gart_end = gart_start + gart->page_count *GART_PAGE_SIZE -1 ; + gart_end = gart_start +GART_APERTURE_SIZE -1 ;if (iova_start < gart_start)return false;
@@ -241,7 +244,7 @@ static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) gart_domain->gart = gart; gart_domain->domain.geometry.aperture_start = gart->iovmm_base; gart_domain->domain.geometry.aperture_end = gart->iovmm_base + - gart->page_count *GART_PAGE_SIZE -1 ; +GART_APERTURE_SIZE -1 ; gart_domain->domain.geometry.force_aperture = true;return &gart_domain->domain;
@@ -463,9 +466,8 @@ static int tegra_gart_probe(struct platform_device *pdev)INIT_LIST_HEAD (&gart->client); gart->regs = gart_regs; gart->iovmm_base = (dma_addr_t )res_remap->start; - gart->page_count = (resource_size (res_remap) >>GART_PAGE_SHIFT ); - gart->savedata =vmalloc (sizeof (u32) * gart->page_count); + gart->savedata =vmalloc (sizeof (u32) *GART_PAGECOUNT );if (!gart->savedata) {dev_err (dev,"failed to allocate context save area\n" );return -ENOMEM ;
Signature · 1 line
2.16.3
PATCH 4/4 iommu/tegra: gart: Optimize map/unmap
In reply to Dmitry OsipenkoCurrently GART writes one page entry at a time. More optimal would be to aggregate the writes and flush BUS buffer in the end, this gives map/unmap 10-40% (depending on size of mapping) performance boost compared to a flushing after each entry update.
--- drivers/iommu/tegra-gart.c | 63 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-)
drivers/iommu/tegra-gart.c +48 -15
@@ -36,7 +36,7 @@#define GART_APERTURE_SIZE SZ_32M/* bitmap of the page sizes currently supported */ -#define GART_IOMMU_PGSIZES (SZ_4K) +#define GART_IOMMU_PGSIZES GENMASK(24, 12)#define GART_REG_BASE 0x24#define GART_CONFIG (0x24 - GART_REG_BASE)
@@ -269,25 +269,21 @@ static void gart_iommu_domain_free(struct iommu_domain *domain)kfree (gart_domain); } -static int gart_iommu_map (struct iommu_domain *domain,unsigned long iova, -phys_addr_t pa,size_t bytes,int prot) +static int gart_iommu_map_page (struct gart_device *gart, +unsigned long iova, +phys_addr_t pa) { -struct gart_domain *gart_domain =to_gart_domain (domain); -struct gart_device *gart = gart_domain->gart;unsigned long flags;unsigned long pfn;unsigned long pte; -if (!gart_iova_range_valid (gart, iova, bytes)) -return -EINVAL ; - -spin_lock_irqsave (&gart->pte_lock, flags); pfn =__phys_to_pfn (pa);if (!pfn_valid (pfn)) {dev_err (gart->dev,"Invalid page: %pa\n" , &pa); -spin_unlock_irqrestore (&gart->pte_lock, flags);return -EINVAL ; } + +spin_lock_irqsave (&gart->pte_lock, flags);if (gart_debug) { pte =gart_read_pte (gart, iova);if (pte &GART_ENTRY_PHYS_ADDR_VALID ) {
@@ -297,8 +293,41 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, } }gart_set_pte (gart, iova,GART_PTE (pfn)); +spin_unlock_irqrestore (&gart->pte_lock, flags); + +return 0 ; +} + +static int gart_iommu_map (struct iommu_domain *domain,unsigned long iova, +phys_addr_t pa,size_t bytes,int prot) +{ +struct gart_domain *gart_domain =to_gart_domain (domain); +struct gart_device *gart = gart_domain->gart; +size_t mapped; +int ret =-1 ; + +if (!gart_iova_range_valid (gart, iova, bytes)) +return -EINVAL ; + +for (mapped =0 ; mapped < bytes; mapped +=GART_PAGE_SIZE ) { + ret =gart_iommu_map_page (gart, iova + mapped, pa + mapped); +if (ret) +break ; + } +FLUSH_GART_REGS (gart); +return ret; +} + +static int gart_iommu_unmap_page (struct gart_device *gart, +unsigned long iova) +{ +unsigned long flags; + +spin_lock_irqsave (&gart->pte_lock, flags); +gart_set_pte (gart, iova,0 );spin_unlock_irqrestore (&gart->pte_lock, flags); +return 0 ; }
@@ -307,16 +336,20 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, {struct gart_domain *gart_domain =to_gart_domain (domain);struct gart_device *gart = gart_domain->gart; -unsigned long flags; +size_t unmapped; +int ret;if (!gart_iova_range_valid (gart, iova, bytes))return 0 ; -spin_lock_irqsave (&gart->pte_lock, flags); -gart_set_pte (gart, iova,0 ); +for (unmapped =0 ; unmapped < bytes; unmapped +=GART_PAGE_SIZE ) { + ret =gart_iommu_unmap_page (gart, iova + unmapped); +if (ret) +break ; + } +FLUSH_GART_REGS (gart); -spin_unlock_irqrestore (&gart->pte_lock, flags); -return bytes; +return unmapped; } static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
Signature · 1 line
2.16.3
Re: [PATCH v1 2/4] iommu/tegra: gart: Fix gart_iommu_unmap()
In reply to Dmitry OsipenkoQuoted 7 lines · Dmitry Osipenko
It must return the number of unmapped bytes on success, returning 0 means that unmapping failed and in result only one page is unmapped.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/iommu/tegra-gart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Re: [PATCH v1 1/4] iommu/tegra: gart: Add debugging facility
In reply to Dmitry OsipenkoQuoted 9 lines · Dmitry Osipenko
Page mapping could overwritten by an accident (a bug). We can catch this case by checking 'VALID' bit of GART's page entry prior to mapping of a page. Since that check introduces a small performance impact, it should be enabled explicitly using new GART's kernel module 'debug' parameter.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/iommu/tegra-gart.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
I think this was discussed before, but this really shouldn't be needed unless the IOVA allocator or GART driver are buggy.
That said, I think this has some usefulness to find such bugs, so:
Re: [PATCH v1 3/4] iommu/tegra: gart: Constify number of GART pages
In reply to Dmitry OsipenkoQuoted 6 lines · Dmitry Osipenko
GART has a fixed aperture size, hence the number of pages is constant.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/iommu/tegra-gart.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
This doesn't really give a good explanation of why you want to do this. While it is certainly true that the aperture is fixed in size, I can imagine cases where somebody would want to restrict the aperture size (perhaps there's a fixed mapping somewhere near the end of the mapping that Linux shouldn't touch, or the GART aperture could be reduced to simulate out of IOVA memory situations).
Without a good explanation why this is necessary I don't see why the current code should be changed.
Thierry
Re: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Dmitry OsipenkoQuoted 15 lines · Dmitry Osipenko
Currently GART writes one page entry at a time. More optimal would be to aggregate the writes and flush BUS buffer in the end, this gives map/unmap 10-40% (depending on size of mapping) performance boost compared to a flushing after each entry update.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/iommu/tegra-gart.c | 63 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-)drivers/iommu/tegra-gart.c +0
-0
drivers/iommu/tegra-gart.c line 39
Quoted 5 lines
drivers/iommu/tegra-gart.c +1
-1@@ -36,7 +36,7 @@#define GART_APERTURE_SIZE SZ_32M/* bitmap of the page sizes currently supported */ -#define GART_IOMMU_PGSIZES (SZ_4K) +#define GART_IOMMU_PGSIZES GENMASK(24, 12)
That doesn't look right. The GART really only supports 4 KiB pages. You seem to be "emulating" more page sizes here in order to improve mapping performance. That seems wrong to me. I'm wondering if this couldn't be improved by a similar factor by simply moving the flushing into an implementation of ->iotlb_sync().
That said, it seems like ->iotlb_sync() is only used for unmapping, but I don't see a reason why iommu_map() wouldn't need to call it as well after going through several calls to ->map(). It seems to me like a driver that implements ->iotlb_sync() would want to use it to optimize for both the mapping and unmapping cases.
Joerg, I've gone over the git log and header files and I see no mention of why the TLB flush interface isn't used for mapping. Do you recall any special reasons why the same shouldn't be applied for mapping? Would you accept any patches doing this?
Thierry
Quoted 105 lines
drivers/iommu/tegra-gart.c +47
-14#define GART_REG_BASE 0x24#define GART_CONFIG (0x24 - GART_REG_BASE)@@ -269,25 +269,21 @@ static void gart_iommu_domain_free(struct iommu_domain *domain)kfree (gart_domain); } -static int gart_iommu_map (struct iommu_domain *domain,unsigned long iova, -phys_addr_t pa,size_t bytes,int prot) +static int gart_iommu_map_page (struct gart_device *gart, +unsigned long iova, +phys_addr_t pa) { -struct gart_domain *gart_domain =to_gart_domain (domain); -struct gart_device *gart = gart_domain->gart;unsigned long flags;unsigned long pfn;unsigned long pte; -if (!gart_iova_range_valid (gart, iova, bytes)) -return -EINVAL ; - -spin_lock_irqsave (&gart->pte_lock, flags); pfn =__phys_to_pfn (pa);if (!pfn_valid (pfn)) {dev_err (gart->dev,"Invalid page: %pa\n" , &pa); -spin_unlock_irqrestore (&gart->pte_lock, flags);return -EINVAL ; } + +spin_lock_irqsave (&gart->pte_lock, flags);if (gart_debug) { pte =gart_read_pte (gart, iova);if (pte &GART_ENTRY_PHYS_ADDR_VALID ) {@@ -297,8 +293,41 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, } }gart_set_pte (gart, iova,GART_PTE (pfn)); +spin_unlock_irqrestore (&gart->pte_lock, flags); + +return 0 ; +} + +static int gart_iommu_map (struct iommu_domain *domain,unsigned long iova, +phys_addr_t pa,size_t bytes,int prot) +{ +struct gart_domain *gart_domain =to_gart_domain (domain); +struct gart_device *gart = gart_domain->gart; +size_t mapped; +int ret =-1 ; + +if (!gart_iova_range_valid (gart, iova, bytes)) +return -EINVAL ; + +for (mapped =0 ; mapped < bytes; mapped +=GART_PAGE_SIZE ) { + ret =gart_iommu_map_page (gart, iova + mapped, pa + mapped); +if (ret) +break ; + } +FLUSH_GART_REGS (gart); +return ret; +} + +static int gart_iommu_unmap_page (struct gart_device *gart, +unsigned long iova) +{ +unsigned long flags; + +spin_lock_irqsave (&gart->pte_lock, flags); +gart_set_pte (gart, iova,0 );spin_unlock_irqrestore (&gart->pte_lock, flags); +return 0 ; }@@ -307,16 +336,20 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, {struct gart_domain *gart_domain =to_gart_domain (domain);struct gart_device *gart = gart_domain->gart; -unsigned long flags; +size_t unmapped; +int ret;if (!gart_iova_range_valid (gart, iova, bytes))return 0 ; -spin_lock_irqsave (&gart->pte_lock, flags); -gart_set_pte (gart, iova,0 ); +for (unmapped =0 ; unmapped < bytes; unmapped +=GART_PAGE_SIZE ) { + ret =gart_iommu_unmap_page (gart, iova + unmapped); +if (ret) +break ; + } +FLUSH_GART_REGS (gart); -spin_unlock_irqrestore (&gart->pte_lock, flags); -return bytes; +return unmapped; } static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,Signature · 1 line
2.16.3
Re: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
Quoted 28 lines · Thierry Reding
Quoted 15 lines · Dmitry Osipenko
Currently GART writes one page entry at a time. More optimal would be to aggregate the writes and flush BUS buffer in the end, this gives map/unmap 10-40% (depending on size of mapping) performance boost compared to a flushing after each entry update.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/iommu/tegra-gart.c | 63 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-)drivers/iommu/tegra-gart.c +0
-0drivers/iommu/tegra-gart.c line 39
Quoted 5 lines
drivers/iommu/tegra-gart.c +1
-1@@ -36,7 +36,7 @@#define GART_APERTURE_SIZE SZ_32M/* bitmap of the page sizes currently supported */ -#define GART_IOMMU_PGSIZES (SZ_4K) +#define GART_IOMMU_PGSIZES GENMASK(24, 12)That doesn't look right. The GART really only supports 4 KiB pages. You seem to be "emulating" more page sizes here in order to improve mapping performance. That seems wrong to me. I'm wondering if this couldn't be improved by a similar factor by simply moving the flushing into an implementation of ->iotlb_sync().
Quoted 5 lines
That said, it seems like ->iotlb_sync() is only used for unmapping, but I don't see a reason why
iommu_map()wouldn't need to call it as well after going through several calls to ->map(). It seems to me like a driver that implements ->iotlb_sync() would want to use it to optimize for both the mapping and unmapping cases.
I vaguely remember looking at the IOMMU syncing, but decided to try at first the way this patch is. I'll be happy to switch to map/unmap syncing, let's see what Joerg would suggest.
Quoted 4 lines
Joerg, I've gone over the git log and header files and I see no mention of why the TLB flush interface isn't used for mapping. Do you recall any special reasons why the same shouldn't be applied for mapping? Would you accept any patches doing this?
Robin Murphy
reviewerRe: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Thierry RedingHi Thierry,
Quoted 34 lines · Thierry Reding
Quoted 15 lines · Dmitry Osipenko
Currently GART writes one page entry at a time. More optimal would be to aggregate the writes and flush BUS buffer in the end, this gives map/unmap 10-40% (depending on size of mapping) performance boost compared to a flushing after each entry update.
Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/iommu/tegra-gart.c | 63 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-)drivers/iommu/tegra-gart.c +0
-0drivers/iommu/tegra-gart.c line 39
Quoted 5 lines
drivers/iommu/tegra-gart.c +1
-1@@ -36,7 +36,7 @@#define GART_APERTURE_SIZE SZ_32M/* bitmap of the page sizes currently supported */ -#define GART_IOMMU_PGSIZES (SZ_4K) +#define GART_IOMMU_PGSIZES GENMASK(24, 12)That doesn't look right. The GART really only supports 4 KiB pages. You seem to be "emulating" more page sizes here in order to improve mapping performance. That seems wrong to me. I'm wondering if this couldn't be improved by a similar factor by simply moving the flushing into an implementation of ->iotlb_sync().
That said, it seems like ->iotlb_sync() is only used for unmapping, but I don't see a reason why
iommu_map()wouldn't need to call it as well after going through several calls to ->map(). It seems to me like a driver that implements ->iotlb_sync() would want to use it to optimize for both the mapping and unmapping cases.
Quoted 4 lines
Joerg, I've gone over the git log and header files and I see no mention of why the TLB flush interface isn't used for mapping. Do you recall any special reasons why the same shouldn't be applied for mapping? Would you accept any patches doing this?
In general, requiring TLB maintenance when transitioning from an invalid entry to a valid one tends to be the exception rather than the norm, and I think we ended up at the consensus that it wasn't worth the complication of trying to cater for this in the generic iotlb API.
To be fair, on simple hardware which doesn't implement multiple page sizes with associated walk depth/TLB pressure benefits for larger ones, there's no need for the IOMMU API (and/or the owner of the domain) to try harder to use them, so handling "compound" page sizes within the driver is a more reasonable thing to do. There is already some precedent for this in other drivers (e.g. mtk_iommu_v1).
Robin.
Quoted 114 lines
Thierry
Quoted 111 lines
drivers/iommu/tegra-gart.c +47
-14#define GART_REG_BASE 0x24#define GART_CONFIG (0x24 - GART_REG_BASE)@@ -269,25 +269,21 @@ static void gart_iommu_domain_free(struct iommu_domain *domain)kfree (gart_domain); } -static int gart_iommu_map (struct iommu_domain *domain,unsigned long iova, -phys_addr_t pa,size_t bytes,int prot) +static int gart_iommu_map_page (struct gart_device *gart, +unsigned long iova, +phys_addr_t pa) { -struct gart_domain *gart_domain =to_gart_domain (domain); -struct gart_device *gart = gart_domain->gart;unsigned long flags;unsigned long pfn;unsigned long pte; -if (!gart_iova_range_valid (gart, iova, bytes)) -return -EINVAL ; - -spin_lock_irqsave (&gart->pte_lock, flags); pfn =__phys_to_pfn (pa);if (!pfn_valid (pfn)) {dev_err (gart->dev,"Invalid page: %pa\n" , &pa); -spin_unlock_irqrestore (&gart->pte_lock, flags);return -EINVAL ; } + +spin_lock_irqsave (&gart->pte_lock, flags);if (gart_debug) { pte =gart_read_pte (gart, iova);if (pte &GART_ENTRY_PHYS_ADDR_VALID ) {@@ -297,8 +293,41 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, } }gart_set_pte (gart, iova,GART_PTE (pfn)); +spin_unlock_irqrestore (&gart->pte_lock, flags); + +return 0 ; +} + +static int gart_iommu_map (struct iommu_domain *domain,unsigned long iova, +phys_addr_t pa,size_t bytes,int prot) +{ +struct gart_domain *gart_domain =to_gart_domain (domain); +struct gart_device *gart = gart_domain->gart; +size_t mapped; +int ret =-1 ; + +if (!gart_iova_range_valid (gart, iova, bytes)) +return -EINVAL ; + +for (mapped =0 ; mapped < bytes; mapped +=GART_PAGE_SIZE ) { + ret =gart_iommu_map_page (gart, iova + mapped, pa + mapped); +if (ret) +break ; + } +FLUSH_GART_REGS (gart); +return ret; +} + +static int gart_iommu_unmap_page (struct gart_device *gart, +unsigned long iova) +{ +unsigned long flags; + +spin_lock_irqsave (&gart->pte_lock, flags); +gart_set_pte (gart, iova,0 );spin_unlock_irqrestore (&gart->pte_lock, flags); +return 0 ; }@@ -307,16 +336,20 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, {struct gart_domain *gart_domain =to_gart_domain (domain);struct gart_device *gart = gart_domain->gart; -unsigned long flags; +size_t unmapped; +int ret;if (!gart_iova_range_valid (gart, iova, bytes))return 0 ; -spin_lock_irqsave (&gart->pte_lock, flags); -gart_set_pte (gart, iova,0 ); +for (unmapped =0 ; unmapped < bytes; unmapped +=GART_PAGE_SIZE ) { + ret =gart_iommu_unmap_page (gart, iova + unmapped); +if (ret) +break ; + } +FLUSH_GART_REGS (gart); -spin_unlock_irqrestore (&gart->pte_lock, flags); -return bytes; +return unmapped; } static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,Signature · 8 lines
2.16.3 _______________________________________________ iommu mailing list iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Quoted 7 lines · Dmitry Osipenko
GART driver wasn't ever been utilized in upstream, but finally this should change sometime soon with Tegra's DRM driver rework. In general GART driver works fine, though there are couple things that could be improved.
Dmitry Osipenko (4):
iommu/tegra: gart: Add debugging facility
iommu/tegra: gart: Fixgart_iommu_unmap()
Applied these two patches, thanks.
Re: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Robin MurphyQuoted 46 lines · Robin Murphy
Hi Thierry,
Quoted 33 lines · Thierry Reding
Quoted 15 lines · Dmitry Osipenko
Currently GART writes one page entry at a time. More optimal would be to aggregate the writes and flush BUS buffer in the end, this gives map/unmap 10-40% (depending on size of mapping) performance boost compared to a flushing after each entry update.
Signed-off-by Dmitry Osipenko <[email protected]>---
drivers/iommu/tegra-gart.c| 63 +++++++++++++++++++++++++++++++++++-----------1 file changed, 48 insertions(+), 15 deletions(-)drivers/iommu/tegra-gart.c +0
-0drivers/iommu/tegra-gart.c line 38
Quoted 4 lines
drivers/iommu/tegra-gart.c +1
-1@@ -36,7 +36,7 @@ #define GART_APERTURE_SIZE SZ_32M /* bitmap of the page sizes currently supported */ -#define GART_IOMMU_PGSIZES (SZ_4K) +#define GART_IOMMU_PGSIZES GENMASK(24, 12)That doesn't look right. The GART really only supports 4 KiB pages. You seem to be "emulating" more page sizes here in order to improve mapping performance. That seems wrong to me. I'm wondering if this couldn't be improved by a similar factor by simply moving the flushing into an implementation of ->iotlb_sync().
That said, it seems like ->iotlb_sync() is only used for unmapping, but I don't see a reason why
iommu_map()wouldn't need to call it as well after going through several calls to ->map(). It seems to me like a driver that implements ->iotlb_sync() would want to use it to optimize for both the mapping and unmapping cases.Quoted 4 lines
Joerg, I've gone over the git log and header files and I see no mention of why the TLB flush interface isn't used for mapping. Do you recall any special reasons why the same shouldn't be applied for mapping? Would you accept any patches doing this?
In general, requiring TLB maintenance when transitioning from an invalid entry to a valid one tends to be the exception rather than the norm, and I think we ended up at the consensus that it wasn't worth the complication of trying to cater for this in the generic iotlb API.
Quoted 5 lines
To be fair, on simple hardware which doesn't implement multiple page sizes with associated walk depth/TLB pressure benefits for larger ones, there's no need for the IOMMU API (and/or the owner of the domain) to try harder to use them, so handling "compound" page sizes within the driver is a more reasonable thing to do. There is already some precedent for this in other drivers (e.g.
mtk_iommu_v1).
Probably the best variant would be to give an explicit control over syncing to a user of the IOMMU API, like for example device driver may perform multiple mappings / unmappings and then sync/flush in the end. I'm not sure that it's really worth the hassle to shuffle the API right now, maybe we can implement it later if needed. Joerg, do you have objections to a 'compound page' approach?
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Joerg Roedel
maintainerRe: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Thierry RedingQuoted 4 lines · Thierry Reding
Joerg, I've gone over the git log and header files and I see no mention of why the TLB flush interface isn't used for mapping. Do you recall any special reasons why the same shouldn't be applied for mapping? Would you accept any patches doing this?
Yeah, the reason was that we assumed that only emulated IOMMUs ever need flushing in the mapping path, and there is no reason to optimize for that. Especially when a call to iotlb_sync() in the mapping path would hurt most other users.
Does the tegra-gart actually need it too?
Joerg
Joerg Roedel
maintainerRe: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Dmitry OsipenkoQuoted 5 lines · Dmitry Osipenko
Probably the best variant would be to give an explicit control over syncing to a user of the IOMMU API, like for example device driver may perform multiple mappings / unmappings and then sync/flush in the end. I'm not sure that it's really worth the hassle to shuffle the API right now, maybe we can implement it later if needed. Joerg, do you have objections to a 'compound page' approach?
Have you measured the performance difference on both variants? The compound-page approach only works for cases when the physical memory you map contiguous and correctly aligned.
If it is really needed I would prefer a separate iotlb_sync_map() call-back that is just NULL when not needed. This way all users that don't need it only get a minimal penalty in the mapping path and you don't have any requirements on the physical memory you map to get good performance.
Joerg
Re: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Joerg RoedelQuoted 6 lines · Joerg Roedel
Quoted 4 lines · Thierry Reding
Joerg, I've gone over the git log and header files and I see no mention of why the TLB flush interface isn't used for mapping. Do you recall any special reasons why the same shouldn't be applied for mapping? Would you accept any patches doing this?
Quoted 6 lines
Yeah, the reason was that we assumed that only emulated IOMMUs ever need flushing in the mapping path, and there is no reason to optimize for that. Especially when a call to
iotlb_sync()in the mapping path would hurt most other users.Does the tegra-gart actually need it too?
Tegra-GART is exactly an emulated IOMMU, it doesn't have anything like TLB and it's simply a remapping table. What is actually needed for the GART - is to make sure that the remapping table modifications reach GART HW before anything tries to touch the modified page entries. Presumable there is some kind of a HW buffer that accumulates multiple registers writes and issues them in bursts to improve performance, read-after-write is the way to flush that buffer.
Re: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
In reply to Joerg RoedelQuoted 10 lines · Joerg Roedel
Quoted 5 lines · Dmitry Osipenko
Probably the best variant would be to give an explicit control over syncing to a user of the IOMMU API, like for example device driver may perform multiple mappings / unmappings and then sync/flush in the end. I'm not sure that it's really worth the hassle to shuffle the API right now, maybe we can implement it later if needed. Joerg, do you have objections to a 'compound page' approach?
Have you measured the performance difference on both variants? The compound-page approach only works for cases when the physical memory you map contiguous and correctly aligned.
Yes, previously I actually only tested mapping of the contiguous allocations (used for memory isolation purposes). But now I've re-tested all variants and got somewhat interesting results.
Firstly it is not that easy to test a really sparse mapping simply because memory allocator produces sparse allocation only when memory is _really_ fragmented. Pretty much all of the time the sparse allocations are contiguous or they consist of a very few chunks that do not impose any noticeable performance impact.
Secondly, the interesting part is that mapping / unmapping of a contiguous allocation (CMA using DMA API) is slower by ~50% then doing it for a sparse allocation (get_pages using bare IOMMU API). /I think/ it's a shortcoming of the arch/arm/mm/dma-mapping.c, which also suffers from other inflexibilities that Thierry faced recently. Though I haven't really tried to figure out what is the bottleneck yet and Thierry was going to re-write ARM's dma-mapping implementation anyway, I'll take a closer look at this issue a bit later.
I've implemented the iotlb_sync_map() and tested things with it. The end result is the same as for the compound page approach, simply because actual allocations are pretty much always contiguous.
Quoted 5 lines
If it is really needed I would prefer a separate
iotlb_sync_map()call-back that is just NULL when not needed. This way all users that don't need it only get a minimal penalty in the mapping path and you don't have any requirements on the physical memory you map to get good performance.
Summarizing, the iotlb_sync_map() is indeed better way. As you rightly noticed, that approach is also optimal for the non-contiguous cases as we won't have to flush on mapping of each contiguous chunk of the sparse allocation, but after the whole mapping is done.
Thierry, Robin and Joerg - thanks for your input, I'll prepare patches implementing the iotlb_sync_map.
Re: [PATCH v1 4/4] iommu/tegra: gart: Optimize map/unmap
On 07.05.2018 18:51, Dmitry Osipenko wrote:
[snip]
Quoted 7 lines
Secondly, the interesting part is that mapping / unmapping of a contiguous allocation (CMA using DMA API) is slower by ~50% then doing it for a sparse allocation (
get_pagesusing bare IOMMU API). /I think/ it's a shortcoming of thearch/arm/mm/dma-mapping.c, which also suffers from other inflexibilities that Thierry faced recently. Though I haven't really tried to figure out what is the bottleneck yet and Thierry was going to re-write ARM's dma-mapping implementation anyway, I'll take a closer look at this issue a bit later.
Please scratch my accusation of ARM's dma-mapping, it's not the culprit at all. I completely forgot that in a case of sparse allocation displays framebuffer IOMMU mapping is "pinned" to the GART and hence it's not getting dynamically mapped / unmapped during of my testing. I also forgot to set CPU freq governor to "perfomance", that reduced 50% to 20% of the above perf difference. The rest of the testing is unaffected, flushing after whole mapping is still much more efficient than flushing after modification of each page entry. And yet again, performance of sparse mapping is nearly the same as of contiguous mapping unless sparse allocation is large and _very_ fragmented.