Merge "msm: kgsl: Add a timer to detect possible syncpoint deadlocks"
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 588c243..1cc9663 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -155,6 +155,7 @@
struct adreno_context *drawctxt)
{
struct kgsl_cmdbatch *cmdbatch = NULL;
+ int pending;
mutex_lock(&drawctxt->mutex);
if (drawctxt->cmdqueue_head != drawctxt->cmdqueue_tail) {
@@ -164,7 +165,31 @@
* Don't dequeue a cmdbatch that is still waiting for other
* events
*/
- if (kgsl_cmdbatch_sync_pending(cmdbatch)) {
+
+ spin_lock(&cmdbatch->lock);
+ pending = list_empty(&cmdbatch->synclist) ? 0 : 1;
+
+ /*
+ * If changes are pending and the canary timer hasn't been
+ * started yet, start it
+ */
+ if (pending) {
+ /*
+ * If syncpoints are pending start the canary timer if
+ * it hasn't already been started
+ */
+ if (!timer_pending(&cmdbatch->timer))
+ mod_timer(&cmdbatch->timer, jiffies + (5 * HZ));
+ } else {
+ /*
+ * Otherwise, delete the timer to make sure it is good
+ * and dead before queuing the buffer
+ */
+ del_timer_sync(&cmdbatch->timer);
+ }
+ spin_unlock(&cmdbatch->lock);
+
+ if (pending) {
cmdbatch = ERR_PTR(-EAGAIN);
goto done;
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 5d78879..d1241e0 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1484,6 +1484,46 @@
struct kref refcount;
};
+static void _kgsl_cmdbatch_timer(unsigned long data)
+{
+ struct kgsl_cmdbatch *cmdbatch = (struct kgsl_cmdbatch *) data;
+ struct kgsl_cmdbatch_sync_event *event;
+
+ if (cmdbatch == NULL || cmdbatch->context == NULL)
+ return;
+
+ pr_err("kgsl: possible gpu syncpoint deadlock for context %d timestamp %d\n",
+ cmdbatch->context->id, cmdbatch->timestamp);
+ pr_err(" Active sync points:\n");
+
+ spin_lock(&cmdbatch->lock);
+
+ /* Print all the pending sync objects */
+ list_for_each_entry(event, &cmdbatch->synclist, node) {
+
+ switch (event->type) {
+ case KGSL_CMD_SYNCPOINT_TYPE_TIMESTAMP: {
+ unsigned int retired;
+
+ retired = kgsl_readtimestamp(event->device,
+ event->context, KGSL_TIMESTAMP_RETIRED);
+
+ pr_err(" [timestamp] context %d timestamp %d (retired %d)\n",
+ event->context->id, event->timestamp,
+ retired);
+ break;
+ }
+ case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
+ pr_err(" fence: [%p] %s\n", event->handle,
+ (event->handle && event->handle->fence)
+ ? event->handle->fence->name : "NULL");
+ break;
+ }
+ }
+
+ spin_unlock(&cmdbatch->lock);
+}
+
/**
* kgsl_cmdbatch_sync_event_destroy() - Destroy a sync event object
* @kref: Pointer to the kref structure for this object
@@ -1556,6 +1596,14 @@
}
sched = list_empty(&event->cmdbatch->synclist) ? 1 : 0;
+
+ /*
+ * If the list is empty delete the canary timer while
+ * still holding the lock
+ */
+ if (sched)
+ del_timer_sync(&event->cmdbatch->timer);
+
spin_unlock(&event->cmdbatch->lock);
/*
@@ -1600,10 +1648,12 @@
struct kgsl_cmdbatch_sync_event *event, *tmp;
LIST_HEAD(cancel_synclist);
- /*
- * Empty the synclist before canceling events
- */
spin_lock(&cmdbatch->lock);
+
+ /* Zap the canary timer */
+ del_timer_sync(&cmdbatch->timer);
+
+ /* Empty the synclist before canceling events */
list_splice_init(&cmdbatch->synclist, &cancel_synclist);
spin_unlock(&cmdbatch->lock);
@@ -1782,6 +1832,7 @@
event->cmdbatch = cmdbatch;
event->context = context;
event->timestamp = sync->timestamp;
+ event->device = device;
/*
* Two krefs are required to support events. The first kref is for
@@ -1917,6 +1968,10 @@
cmdbatch->context = context;
cmdbatch->flags = flags & ~KGSL_CONTEXT_SUBMIT_IB_LIST;
+ /* Add a timer to help debug sync deadlocks */
+ setup_timer(&cmdbatch->timer, _kgsl_cmdbatch_timer,
+ (unsigned long) cmdbatch);
+
return cmdbatch;
}
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 1e6fbc9..7f936ed 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -173,10 +173,9 @@
* @ibcount: Number of IBs in the command list
* @ibdesc: Pointer to the list of IBs
* @expires: Point in time when the cmdbatch is considered to be hung
- * @invalid: non-zero if the dispatcher determines the command and the owning
- * context should be invalidated
* @refcount: kref structure to maintain the reference count
* @synclist: List of context/timestamp tuples to wait for before issuing
+ * @timer: a timer used to track possible sync timeouts for this cmdbatch
*
* This struture defines an atomic batch of command buffers issued from
* userspace.
@@ -193,9 +192,9 @@
uint32_t ibcount;
struct kgsl_ibdesc *ibdesc;
unsigned long expires;
- int invalid;
struct kref refcount;
struct list_head synclist;
+ struct timer_list timer;
};
/**
@@ -728,27 +727,6 @@
}
/**
- * kgsl_cmdbatch_sync_pending() - return true if the cmdbatch is waiting
- * @cmdbatch: Pointer to the command batch object to check
- *
- * Return non-zero if the specified command batch is still waiting for sync
- * point dependencies to be satisfied
- */
-static inline int kgsl_cmdbatch_sync_pending(struct kgsl_cmdbatch *cmdbatch)
-{
- int ret;
-
- if (cmdbatch == NULL)
- return 0;
-
- spin_lock(&cmdbatch->lock);
- ret = list_empty(&cmdbatch->synclist) ? 0 : 1;
- spin_unlock(&cmdbatch->lock);
-
- return ret;
-}
-
-/**
* kgsl_sysfs_store() - parse a string from a sysfs store function
* @buf: Incoming string to parse
* @ptr: Pointer to an unsigned int to store the value
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index dc3ad21..0e694a7 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2014, 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
@@ -118,6 +118,7 @@
struct sync_pt *pt;
struct sync_fence *fence = NULL;
int ret = -EINVAL;
+ char fence_name[sizeof(fence->name)] = {};
if (len != sizeof(priv))
return -EINVAL;
@@ -140,8 +141,13 @@
ret = -ENOMEM;
goto fail_pt;
}
+ snprintf(fence_name, sizeof(fence_name),
+ "%s-pid-%d-ctx-%d-ts-%d",
+ device->name, current->group_leader->pid,
+ context_id, timestamp);
- fence = sync_fence_create("kgsl-fence", pt);
+
+ fence = sync_fence_create(fence_name, pt);
if (fence == NULL) {
/* only destroy pt when not added to fence */
kgsl_sync_pt_destroy(pt);