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>
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 07f82ec..a104a42 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -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
@@ -94,6 +94,7 @@
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);
@@ -147,6 +148,7 @@
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)
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index dc67403..b8c1ecf 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -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
@@ -29,9 +29,6 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
-/* dummy 64K for overmapping */
-char iommu_dummy[2*SZ_64K-4];
-
struct msm_iova_data {
struct rb_node node;
struct mem_pool *pools;
@@ -49,17 +46,29 @@
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);
@@ -75,7 +84,7 @@
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);
@@ -89,7 +98,7 @@
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);
diff --git a/arch/arm/mach-msm/subsystem_map.c b/arch/arm/mach-msm/subsystem_map.c
index d638817..3269e50 100644
--- a/arch/arm/mach-msm/subsystem_map.c
+++ b/arch/arm/mach-msm/subsystem_map.c
@@ -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
@@ -410,8 +410,8 @@
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);
}
}
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index 849d72e..3e55a57 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -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
@@ -377,8 +377,9 @@
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;
}
diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
index bef6b6f..da5d22f 100644
--- a/drivers/gpu/ion/ion_cma_heap.c
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -228,8 +228,9 @@
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;
}
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index 4649606..94e0ad8 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -1086,6 +1086,7 @@
}
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)
@@ -1179,8 +1180,9 @@
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;
}
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index 3834f80..304a39e 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -351,8 +351,9 @@
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;
}
diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c
index 566286c..f33fc18 100644
--- a/drivers/gpu/ion/ion_system_heap.c
+++ b/drivers/gpu/ion/ion_system_heap.c
@@ -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
@@ -280,8 +280,9 @@
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;
}
@@ -500,8 +501,9 @@
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;
}