diff --git a/drivers/gpu/msm/adreno_iommu.c b/drivers/gpu/msm/adreno_iommu.c index f1168d96b5de..2eeda01b3c4d 100644 --- a/drivers/gpu/msm/adreno_iommu.c +++ b/drivers/gpu/msm/adreno_iommu.c @@ -867,6 +867,10 @@ int adreno_iommu_init(struct adreno_device *adreno_dev) } } + /* Enable guard page MMU feature for A3xx and A4xx targets only */ + if (adreno_is_a3xx(adreno_dev) || adreno_is_a4xx(adreno_dev)) + device->mmu.features |= KGSL_MMU_NEED_GUARD_PAGE; + return 0; } 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) { diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h index c8875bb975a9..f82452dd6080 100644 --- a/drivers/gpu/msm/kgsl_iommu.h +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2016, 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 @@ -137,6 +137,8 @@ struct kgsl_iommu_context { * @micro_mmu_ctrl: GPU register offset of this glob al register * @smmu_info: smmu info used in a5xx preemption * @protect: register protection settings for the iommu. + * @pagefault_suppression_count: Total number of pagefaults + * suppressed since boot. */ struct kgsl_iommu { struct kgsl_iommu_context ctx[KGSL_IOMMU_CONTEXT_MAX]; @@ -150,6 +152,7 @@ struct kgsl_iommu { struct kgsl_memdesc smmu_info; unsigned int version; struct kgsl_protected_registers protect; + u32 pagefault_suppression_count; }; /*