msm: iommu: re-use existing buffers for `extra' mappings

There is a bug on some hardware that requires us to overmap Iommu
mappings by 2x. Currently, we set aside a dummy buffer onto which we
map all of these dummy mappings. In general, for large mappings it's
nice to use the more efficient iommu_map_range instead of calling
iommu_map repeatedly. However, with our current approach in
msm_iommu_map_extra we can't use iommu_map_range for page_sizes larger
than the dummy buffer. To avoid wasting memory by increasing the size
of the dummy buffer, we can simply remap on top of the the buffer
being mapped in the first place. Since the second mapping should never
be used (besides by the buggy hardware) this should not be a problem.

Re-use existing buffers for all `extra' mappings. Essentially, map the
same physical address range twice. To be extra safe, make the second
mapping read-only.

Change-Id: I35462ad50de8da1f2befa3e3f0895925535cdc98
Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
This commit is contained in:
Mitchel Humpherys 2013-01-15 15:38:52 -08:00 committed by Artem Borisov
parent 32c723c2da
commit 0423b88e29
8 changed files with 45 additions and 27 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -90,6 +90,7 @@ extern int msm_use_iommu(void);
extern int msm_iommu_map_extra(struct iommu_domain *domain,
unsigned long start_iova,
unsigned long phys_addr,
unsigned long size,
unsigned long page_size,
int cached);
@ -140,6 +141,7 @@ static inline int msm_use_iommu(void)
static inline int msm_iommu_map_extra(struct iommu_domain *domain,
unsigned long start_iova,
unsigned long phys_addr,
unsigned long size,
unsigned long page_size,
int cached)

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -26,9 +26,6 @@
#include <mach/socinfo.h>
#include <mach/msm_subsystem_map.h>
/* dummy 64K for overmapping */
char iommu_dummy[2*SZ_64K-4];
struct msm_iova_data {
struct rb_node node;
struct mem_pool *pools;
@ -51,17 +48,29 @@ int msm_use_iommu()
return iommu_present(&platform_bus_type);
}
bool msm_iommu_page_size_is_supported(unsigned long page_size)
{
return page_size == SZ_4K
|| page_size == SZ_64K
|| page_size == SZ_1M
|| page_size == SZ_16M;
}
int msm_iommu_map_extra(struct iommu_domain *domain,
unsigned long start_iova,
unsigned long phy_addr,
unsigned long size,
unsigned long page_size,
int cached)
int prot)
{
int ret = 0;
int i = 0;
unsigned long phy_addr = ALIGN(virt_to_phys(iommu_dummy), page_size);
unsigned long temp_iova = start_iova;
if (page_size == SZ_4K) {
/* the extra "padding" should never be written to. map it
* read-only. */
prot &= ~IOMMU_WRITE;
if (msm_iommu_page_size_is_supported(page_size)) {
struct scatterlist *sglist;
unsigned int nrpages = PFN_ALIGN(size) >> PAGE_SHIFT;
struct page *dummy_page = phys_to_page(phy_addr);
@ -77,7 +86,7 @@ int msm_iommu_map_extra(struct iommu_domain *domain,
for (i = 0; i < nrpages; i++)
sg_set_page(&sglist[i], dummy_page, PAGE_SIZE, 0);
ret = iommu_map_range(domain, temp_iova, sglist, size, cached);
ret = iommu_map_range(domain, temp_iova, sglist, size, prot);
if (ret) {
pr_err("%s: could not map extra %lx in domain %p\n",
__func__, start_iova, domain);
@ -91,7 +100,7 @@ int msm_iommu_map_extra(struct iommu_domain *domain,
for (i = 0; i < nrpages; i++) {
ret = iommu_map(domain, temp_iova, phy_addr, page_size,
cached);
prot);
if (ret) {
pr_err("%s: could not map %lx in domain %p, error: %d\n",
__func__, start_iova, domain, ret);

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -412,8 +412,8 @@ struct msm_mapped_buffer *msm_subsystem_map_buffer(unsigned long phys,
if (flags & MSM_SUBSYSTEM_MAP_IOMMU_2X)
msm_iommu_map_extra
(d, temp_va, length, SZ_4K,
(IOMMU_READ | IOMMU_WRITE));
(d, temp_va, phys, length, SZ_4K,
IOMMU_READ);
}
}

View file

@ -2,7 +2,7 @@
* drivers/gpu/ion/ion_carveout_heap.c
*
* Copyright (C) 2011 Google, Inc.
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -430,8 +430,9 @@ int ion_carveout_heap_map_iommu(struct ion_buffer *buffer,
if (extra) {
unsigned long extra_iova_addr = data->iova_addr + buffer->size;
ret = msm_iommu_map_extra(domain, extra_iova_addr, extra,
SZ_4K, prot);
unsigned long phys_addr = sg_phys(sglist);
ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
extra, SZ_4K, prot);
if (ret)
goto out2;
}

View file

@ -228,8 +228,9 @@ int ion_cma_map_iommu(struct ion_buffer *buffer,
extra_iova_addr = data->iova_addr + buffer->size;
if (extra) {
ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
prot);
unsigned long phys_addr = sg_phys(table->sgl);
ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
extra, SZ_4K, prot);
if (ret)
goto out2;
}

View file

@ -926,6 +926,7 @@ static int iommu_map_all(unsigned long domain_num, struct ion_cp_heap *cp_heap,
}
if (domain_num == cp_heap->iommu_2x_map_domain)
ret_value = msm_iommu_map_extra(domain, temp_iova,
cp_heap->base,
cp_heap->total_size,
SZ_64K, prot);
if (ret_value)
@ -1018,8 +1019,9 @@ static int ion_cp_heap_map_iommu(struct ion_buffer *buffer,
if (extra) {
unsigned long extra_iova_addr = data->iova_addr + buffer->size;
ret = msm_iommu_map_extra(domain, extra_iova_addr, extra,
SZ_4K, prot);
unsigned long phys_addr = sg_phys(buffer->sg_table->sgl);
ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
extra, SZ_4K, prot);
if (ret)
goto out2;
}

View file

@ -354,8 +354,9 @@ int ion_iommu_heap_map_iommu(struct ion_buffer *buffer,
if (extra) {
unsigned long extra_iova_addr = data->iova_addr + buffer->size;
ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
prot);
unsigned long phys_addr = sg_phys(buffer->sg_table->sgl);
ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
extra, SZ_4K, prot);
if (ret)
goto out2;
}

View file

@ -2,7 +2,7 @@
* drivers/gpu/ion/ion_system_heap.c
*
* Copyright (C) 2011 Google, Inc.
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@ -304,8 +304,9 @@ int ion_system_heap_map_iommu(struct ion_buffer *buffer,
extra_iova_addr = data->iova_addr + buffer->size;
if (extra) {
ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
prot);
unsigned long phys_addr = sg_phys(table->sgl);
ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
extra, SZ_4K, prot);
if (ret)
goto out2;
}
@ -524,8 +525,9 @@ int ion_system_contig_heap_map_iommu(struct ion_buffer *buffer,
if (extra) {
unsigned long extra_iova_addr = data->iova_addr + buffer->size;
ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K,
prot);
unsigned long phys_addr = sg_phys(sglist);
ret = msm_iommu_map_extra(domain, extra_iova_addr, phys_addr,
extra, SZ_4K, prot);
if (ret)
goto out2;
}