Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c8216586 authored by Akhil P Oommen's avatar Akhil P Oommen
Browse files

msm: kgsl: Correctly identify page faulting process



Currently, we rely on the global current_context entry in memstore
to identify the faulting context in the pagefault handler. This entry
could be wrong during preemption from higher priority RB to a lower
one. This happens because the global entry in memstore is updated
using inline PM4 packets in the RB. So after resuming a preempted RB,
the global current_context value will be wrong until there is a
context switch in the new RB.

We can avoid identifying the faulting context. It is enough to identify
the faulting process and we can do that from pagetable base address
from the TTBR0 register.

Change-Id: Ib3da7b55f8245350a4430ff97ec7c34804a41b07
Signed-off-by: default avatarAkhil P Oommen <akhilpo@codeaurora.org>
parent 29444f60
Loading
Loading
Loading
Loading
+43 −62
Original line number Diff line number Diff line
@@ -647,10 +647,8 @@ static void _get_entries(struct kgsl_process_private *private,

static void _find_mem_entries(struct kgsl_mmu *mmu, uint64_t faultaddr,
		struct _mem_entry *preventry, struct _mem_entry *nextentry,
		struct kgsl_context *context)
		struct kgsl_process_private *private)
{
	struct kgsl_process_private *private;

	memset(preventry, 0, sizeof(*preventry));
	memset(nextentry, 0, sizeof(*nextentry));

@@ -659,8 +657,7 @@ static void _find_mem_entries(struct kgsl_mmu *mmu, uint64_t faultaddr,

	if (ADDR_IN_GLOBAL(mmu, faultaddr)) {
		_get_global_entries(faultaddr, preventry, nextentry);
	} else if (context) {
		private = context->proc_priv;
	} else if (private) {
		spin_lock(&private->mem_lock);
		_get_entries(private, faultaddr, preventry, nextentry);
		spin_unlock(&private->mem_lock);
@@ -727,7 +724,7 @@ kgsl_iommu_uche_overfetch(struct kgsl_process_private *private,
 */

static bool kgsl_iommu_suppress_pagefault(uint64_t faultaddr, int write,
					struct kgsl_context *context)
					struct kgsl_process_private *private)
{
	/*
	 * If there is no context associated with the pagefault then this
@@ -735,10 +732,28 @@ static bool kgsl_iommu_suppress_pagefault(uint64_t faultaddr, int write,
	 * on global buffers as they are mainly accessed by the CP bypassing
	 * the UCHE. Also, write pagefaults are never suppressed.
	 */
	if (!context || write)
	if (!private || write)
		return false;

	return kgsl_iommu_uche_overfetch(context->proc_priv, faultaddr);
	return kgsl_iommu_uche_overfetch(private, faultaddr);
}

static struct kgsl_process_private *kgsl_iommu_identify_process(u64 ptbase)
{
	struct kgsl_process_private *p = NULL;
	struct kgsl_iommu_pt *iommu_pt;

	mutex_lock(&kgsl_driver.process_mutex);
	list_for_each_entry(p, &kgsl_driver.process_list, list) {
		iommu_pt = p->pagetable->priv;
		if (iommu_pt->ttbr0 == ptbase) {
			mutex_unlock(&kgsl_driver.process_mutex);
			return p;
		}
	}

	mutex_unlock(&kgsl_driver.process_mutex);
	return p;
}

static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
@@ -749,7 +764,7 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	struct kgsl_mmu *mmu = pt->mmu;
	struct kgsl_iommu *iommu;
	struct kgsl_iommu_context *ctx;
	u64 ptbase, proc_ptbase;
	u64 ptbase;
	u32 contextidr;
	pid_t pid = 0;
	pid_t ptname;
@@ -759,9 +774,8 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	struct adreno_device *adreno_dev;
	struct adreno_gpudev *gpudev;
	unsigned int no_page_fault_log = 0;
	unsigned int curr_context_id = 0;
	struct kgsl_context *context;
	char *fault_type = "unknown";
	struct kgsl_process_private *private;

	static DEFINE_RATELIMIT_STATE(_rs,
					DEFAULT_RATELIMIT_INTERVAL,
@@ -776,21 +790,6 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	adreno_dev = ADRENO_DEVICE(device);
	gpudev = ADRENO_GPU_DEVICE(adreno_dev);

	if (pt->name == KGSL_MMU_SECURE_PT)
		ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE];

	/*
	 * set the fault bits and stuff before any printks so that if fault
	 * handler runs then it will know it's dealing with a pagefault.
	 * Read the global current timestamp because we could be in middle of
	 * RB switch and hence the cur RB may not be reliable but global
	 * one will always be reliable
	 */
	kgsl_sharedmem_readl(&device->memstore, &curr_context_id,
		KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL, current_context));

	context = kgsl_context_get(device, curr_context_id);

	write = (flags & IOMMU_FAULT_WRITE) ? 1 : 0;
	if (flags & IOMMU_FAULT_TRANSLATION)
		fault_type = "translation";
@@ -801,17 +800,21 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
		fault_type = "transaction stalled";

	if (kgsl_iommu_suppress_pagefault(addr, write, context)) {
	ptbase = KGSL_IOMMU_GET_CTX_REG_Q(ctx, TTBR0);
	private = kgsl_iommu_identify_process(ptbase);

	if (!kgsl_process_private_get(private))
		private = NULL;
	else
		pid = private->pid;

	if (kgsl_iommu_suppress_pagefault(addr, write, private)) {
		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);
		pid = context->proc_priv->pid;
	}
	if (pt->name == KGSL_MMU_SECURE_PT)
		ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE];

	ctx->fault = 1;

@@ -827,9 +830,7 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
		mutex_unlock(&device->mutex);
	}

	ptbase = KGSL_IOMMU_GET_CTX_REG_Q(ctx, TTBR0);
	contextidr = KGSL_IOMMU_GET_CTX_REG(ctx, CONTEXTIDR);

	ptname = MMU_FEATURE(mmu, KGSL_MMU_GLOBAL_PAGETABLE) ?
		KGSL_MMU_GLOBAL_PT : pid;
	/*
@@ -839,7 +840,7 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	 */
	trace_kgsl_mmu_pagefault(ctx->kgsldev, addr,
			ptname,
			context != NULL ? context->proc_priv->comm : "unknown",
			private != NULL ? private->comm : "unknown",
			write ? "write" : "read");

	if (test_bit(KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE,
@@ -847,34 +848,13 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
		no_page_fault_log = kgsl_mmu_log_fault_addr(mmu, ptbase, addr);

	if (!no_page_fault_log && __ratelimit(&_rs)) {
		const char *api_str;

		if (context != NULL) {
			struct adreno_context *drawctxt =
					ADRENO_CONTEXT(context);

			api_str = get_api_type_str(drawctxt->type);
		} else
			api_str = "UNKNOWN";

		KGSL_MEM_CRIT(ctx->kgsldev,
			"GPU PAGE FAULT: addr = %lX pid= %d name=%s\n", addr,
			ptname,
			context != NULL ? context->proc_priv->comm : "unknown");

		if (context != NULL) {
			proc_ptbase = kgsl_mmu_pagetable_get_ttbr0(
					context->proc_priv->pagetable);

			if (ptbase != proc_ptbase)
			private != NULL ? private->comm : "unknown");
		KGSL_MEM_CRIT(ctx->kgsldev,
				"Pagetable address mismatch: HW address is 0x%llx but SW expected 0x%llx\n",
				ptbase, proc_ptbase);
		}

		KGSL_MEM_CRIT(ctx->kgsldev,
			"context=%s ctx_type=%s TTBR0=0x%llx CIDR=0x%x (%s %s fault)\n",
			ctx->name, api_str, ptbase, contextidr,
			"context=%s TTBR0=0x%llx CIDR=0x%x (%s %s fault)\n",
			ctx->name, ptbase, contextidr,
			write ? "write" : "read", fault_type);

		if (gpudev->iommu_fault_block) {
@@ -893,7 +873,7 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
			KGSL_LOG_DUMP(ctx->kgsldev,
				"---- nearby memory ----\n");

			_find_mem_entries(mmu, addr, &prev, &next, context);
			_find_mem_entries(mmu, addr, &prev, &next, private);
			if (prev.gpuaddr)
				_print_entry(ctx->kgsldev, &prev);
			else
@@ -935,7 +915,8 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
		adreno_dispatcher_schedule(device);
	}

	kgsl_context_put(context);
	kgsl_process_private_put(private);

	return ret;
}