summaryrefslogtreecommitdiff
path: root/drivers/gpu/msm/kgsl_iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/msm/kgsl_iommu.c')
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c64
1 files changed, 56 insertions, 8 deletions
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 978d7ac405aa..95ebe628f823 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -476,6 +476,48 @@ static void _check_if_freed(struct kgsl_iommu_context *ctx,
}
}
+static bool
+kgsl_iommu_uche_overfetch(struct kgsl_process_private *private,
+ uint64_t faultaddr)
+{
+ int id;
+ struct kgsl_mem_entry *entry = NULL;
+
+ spin_lock(&private->mem_lock);
+ idr_for_each_entry(&private->mem_idr, entry, id) {
+ struct kgsl_memdesc *m = &entry->memdesc;
+
+ if ((faultaddr >= (m->gpuaddr + m->size))
+ && (faultaddr < (m->gpuaddr + m->size + 64))) {
+ spin_unlock(&private->mem_lock);
+ return true;
+ }
+ }
+ spin_unlock(&private->mem_lock);
+ return false;
+}
+
+/*
+ * Read pagefaults where the faulting address lies within the first 64 bytes
+ * of a page (UCHE line size is 64 bytes) and the fault page is preceded by a
+ * valid allocation are considered likely due to UCHE overfetch and suppressed.
+ */
+
+static bool kgsl_iommu_suppress_pagefault(uint64_t faultaddr, int write,
+ struct kgsl_context *context)
+{
+ /*
+ * If there is no context associated with the pagefault then this
+ * could be a fault on a global buffer. We do not suppress faults
+ * on global buffers as they are mainly accessed by the CP bypassing
+ * the UCHE. Also, write pagefaults are never suppressed.
+ */
+ if (!context || write)
+ return false;
+
+ return kgsl_iommu_uche_overfetch(context->proc_priv, faultaddr);
+}
+
static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
struct device *dev, unsigned long addr, int flags, void *token)
{
@@ -524,6 +566,18 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
context = kgsl_context_get(device, curr_context_id);
+ write = (flags & IOMMU_FAULT_WRITE) ? 1 : 0;
+ if (flags & IOMMU_FAULT_TRANSLATION)
+ fault_type = "translation";
+ else if (flags & IOMMU_FAULT_PERMISSION)
+ fault_type = "permission";
+
+ if (kgsl_iommu_suppress_pagefault(addr, write, context)) {
+ iommu->pagefault_suppression_count++;
+ kgsl_context_put(context);
+ return ret;
+ }
+
if (context != NULL) {
/* save pagefault timestamp for GFT */
set_bit(KGSL_CONTEXT_PRIV_PAGEFAULT, &context->priv);
@@ -544,12 +598,6 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
mutex_unlock(&device->mutex);
}
- write = (flags & IOMMU_FAULT_WRITE) ? 1 : 0;
- if (flags & IOMMU_FAULT_TRANSLATION)
- fault_type = "translation";
- else if (flags & IOMMU_FAULT_PERMISSION)
- fault_type = "permission";
-
ptbase = KGSL_IOMMU_GET_CTX_REG_Q(ctx, TTBR0);
contextidr = KGSL_IOMMU_GET_CTX_REG(ctx, CONTEXTIDR);
@@ -1178,7 +1226,6 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
int status;
mmu->features |= KGSL_MMU_PAGED;
- mmu->features |= KGSL_MMU_NEED_GUARD_PAGE;
if (ctx->name == NULL) {
KGSL_CORE_ERR("dt: gfx3d0_user context bank not found\n");
@@ -1221,7 +1268,8 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
}
}
- if (kgsl_guard_page == NULL) {
+ if (MMU_FEATURE(mmu, KGSL_MMU_NEED_GUARD_PAGE)
+ && (kgsl_guard_page == NULL)) {
kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
__GFP_HIGHMEM);
if (kgsl_guard_page == NULL) {