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

Commit 4a1b1549 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 f074d73b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -738,7 +738,7 @@ static void setup_fault_process(struct kgsl_device *device,
	if (kgsl_mmu_is_perprocess(&device->mmu)) {
		struct kgsl_process_private *tmp;

		mutex_lock(&kgsl_driver.process_mutex);
		spin_lock(&kgsl_driver.proclist_lock);
		list_for_each_entry(tmp, &kgsl_driver.process_list, list) {
			u64 pt_ttbr0;

@@ -749,7 +749,7 @@ static void setup_fault_process(struct kgsl_device *device,
				break;
			}
		}
		mutex_unlock(&kgsl_driver.process_mutex);
		spin_unlock(&kgsl_driver.proclist_lock);
	}
done:
	snapshot->process = process;
+8 −2
Original line number Diff line number Diff line
@@ -921,7 +921,7 @@ struct kgsl_process_private *kgsl_process_private_find(pid_t pid)
{
	struct kgsl_process_private *p, *private = NULL;

	mutex_lock(&kgsl_driver.process_mutex);
	spin_lock(&kgsl_driver.proclist_lock);
	list_for_each_entry(p, &kgsl_driver.process_list, list) {
		if (p->pid == pid) {
			if (kgsl_process_private_get(p))
@@ -929,7 +929,8 @@ struct kgsl_process_private *kgsl_process_private_find(pid_t pid)
			break;
		}
	}
	mutex_unlock(&kgsl_driver.process_mutex);
	spin_unlock(&kgsl_driver.proclist_lock);

	return private;
}

@@ -1035,7 +1036,9 @@ static void kgsl_process_private_close(struct kgsl_device_private *dev_priv,
		kgsl_mmu_detach_pagetable(private->pagetable);

	/* Remove the process struct from the master list */
	spin_lock(&kgsl_driver.proclist_lock);
	list_del(&private->list);
	spin_unlock(&kgsl_driver.proclist_lock);

	/*
	 * Unlock the mutex before releasing the memory and the debugfs
@@ -1071,7 +1074,9 @@ static struct kgsl_process_private *kgsl_process_private_open(
		kgsl_process_init_sysfs(device, private);
		kgsl_process_init_debugfs(private);

		spin_lock(&kgsl_driver.proclist_lock);
		list_add(&private->list, &kgsl_driver.process_list);
		spin_unlock(&kgsl_driver.proclist_lock);
	}

done:
@@ -4870,6 +4875,7 @@ static const struct file_operations kgsl_fops = {

struct kgsl_driver kgsl_driver  = {
	.process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex),
	.proclist_lock = __SPIN_LOCK_UNLOCKED(kgsl_driver.proclist_lock),
	.ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock),
	.devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock),
	/*
+2 −0
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ struct kgsl_context;
 * @pagetable_list: LIst of open pagetables
 * @ptlock: Lock for accessing the pagetable list
 * @process_mutex: Mutex for accessing the process list
 * @proclist_lock: Lock for accessing the process list
 * @devlock: Mutex protecting the device list
 * @stats: Struct containing atomic memory statistics
 * @full_cache_threshold: the threshold that triggers a full cache flush
@@ -131,6 +132,7 @@ struct kgsl_driver {
	struct list_head pagetable_list;
	spinlock_t ptlock;
	struct mutex process_mutex;
	spinlock_t proclist_lock;
	struct mutex devlock;
	struct {
		atomic_long_t vmalloc;
+44 −59
Original line number Diff line number Diff line
@@ -634,10 +634,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));

@@ -646,8 +644,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);
@@ -686,6 +683,29 @@ static void _check_if_freed(struct kgsl_iommu_context *ctx,
	}
}

static struct kgsl_process_private *kgsl_iommu_get_process(u64 ptbase)
{
	struct kgsl_process_private *p;
	struct kgsl_iommu_pt *iommu_pt;

	spin_lock(&kgsl_driver.proclist_lock);

	list_for_each_entry(p, &kgsl_driver.process_list, list) {
		iommu_pt = p->pagetable->priv;
		if (iommu_pt->ttbr0 == ptbase) {
			if (!kgsl_process_private_get(p))
				p = NULL;

			spin_unlock(&kgsl_driver.proclist_lock);
			return p;
		}
	}

	spin_unlock(&kgsl_driver.proclist_lock);

	return NULL;
}

static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	struct device *dev, unsigned long addr, int flags, void *token)
{
@@ -694,7 +714,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;
@@ -704,9 +724,9 @@ 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";
	char *comm = "unknown";
	struct kgsl_process_private *private;

	static DEFINE_RATELIMIT_STATE(_rs,
					DEFAULT_RATELIMIT_INTERVAL,
@@ -721,21 +741,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";
@@ -746,12 +751,17 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
		fault_type = "transaction stalled";

	if (context != NULL) {
		/* save pagefault timestamp for GFT */
		set_bit(KGSL_CONTEXT_PRIV_PAGEFAULT, &context->priv);
		pid = context->proc_priv->pid;
	ptbase = KGSL_IOMMU_GET_CTX_REG_Q(ctx, TTBR0);
	private = kgsl_iommu_get_process(ptbase);

	if (private) {
		pid = private->pid;
		comm = private->comm;
	}

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

	ctx->fault = 1;

	if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
@@ -766,9 +776,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;
	/*
@@ -777,43 +785,19 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
	 * search and delays the trace unnecessarily.
	 */
	trace_kgsl_mmu_pagefault(ctx->kgsldev, addr,
			ptname,
			context != NULL ? context->proc_priv->comm : "unknown",
			write ? "write" : "read");
			ptname, comm, write ? "write" : "read");

	if (test_bit(KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE,
		&adreno_dev->ft_pf_policy))
		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";

		dev_crit(ctx->kgsldev->dev,
			"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)
			ptname, comm);
		dev_crit(ctx->kgsldev->dev,
				"Pagetable address mismatch: HW address is 0x%llx but SW expected 0x%llx\n",
				ptbase, proc_ptbase);
		}

		dev_crit(ctx->kgsldev->dev,
			"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) {
@@ -833,7 +817,7 @@ static int kgsl_iommu_fault_handler(struct iommu_domain *domain,
			dev_err(ctx->kgsldev->dev,
				      "---- 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
@@ -876,7 +860,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;
}