aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/msm/kgsl_cmdbatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/msm/kgsl_cmdbatch.c')
-rw-r--r--drivers/gpu/msm/kgsl_cmdbatch.c63
1 files changed, 36 insertions, 27 deletions
diff --git a/drivers/gpu/msm/kgsl_cmdbatch.c b/drivers/gpu/msm/kgsl_cmdbatch.c
index 7dfd691d84c..065686a2a49 100644
--- a/drivers/gpu/msm/kgsl_cmdbatch.c
+++ b/drivers/gpu/msm/kgsl_cmdbatch.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, 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
@@ -107,6 +107,7 @@ static void _kgsl_cmdbatch_timer(unsigned long data)
struct kgsl_device *device;
struct kgsl_cmdbatch *cmdbatch = (struct kgsl_cmdbatch *) data;
struct kgsl_cmdbatch_sync_event *event;
+ unsigned long flags;
if (cmdbatch == NULL || cmdbatch->context == NULL)
return;
@@ -121,14 +122,14 @@ static void _kgsl_cmdbatch_timer(unsigned long data)
kgsl_context_dump(cmdbatch->context);
clear_bit(CMDBATCH_FLAG_FENCE_LOG, &cmdbatch->priv);
- spin_lock(&cmdbatch->lock);
+ spin_lock_irqsave(&cmdbatch->lock, flags);
/* Print all the fences */
list_for_each_entry(event, &cmdbatch->synclist, node) {
if (KGSL_CMD_SYNCPOINT_TYPE_FENCE == event->type &&
event->handle && event->handle->fence)
kgsl_sync_fence_log(event->handle->fence);
}
- spin_unlock(&cmdbatch->lock);
+ spin_unlock_irqrestore(&cmdbatch->lock, flags);
dev_err(device->dev, "--gpu syncpoint deadlock print end--\n");
}
@@ -184,15 +185,15 @@ static void kgsl_cmdbatch_sync_expire(struct kgsl_device *device,
struct kgsl_cmdbatch_sync_event *event)
{
struct kgsl_cmdbatch_sync_event *e, *tmp;
+ unsigned long flags;
int sched = 0;
int removed = 0;
/*
- * We may have cmdbatch timer running, which also uses same lock,
- * take a lock with software interrupt disabled (bh) to avoid
- * spin lock recursion.
+ * cmdbatch timer or event callback might run at this time in interrupt
+ * context and uses same lock. So use irq-save version of spin lock.
*/
- spin_lock_bh(&event->cmdbatch->lock);
+ spin_lock_irqsave(&event->cmdbatch->lock, flags);
/*
* sync events that are contained by a cmdbatch which has been
@@ -207,8 +208,9 @@ static void kgsl_cmdbatch_sync_expire(struct kgsl_device *device,
}
}
+ event->handle = NULL;
sched = list_empty(&event->cmdbatch->synclist) ? 1 : 0;
- spin_unlock_bh(&event->cmdbatch->lock);
+ spin_unlock_irqrestore(&event->cmdbatch->lock, flags);
/* If the list is empty delete the canary timer */
if (sched)
@@ -269,19 +271,22 @@ void kgsl_cmdbatch_destroy(struct kgsl_cmdbatch *cmdbatch)
struct kgsl_cmdbatch_sync_event *event, *tmpsync;
LIST_HEAD(cancel_synclist);
int sched = 0;
-
+ unsigned long flags;
if (IS_ERR_OR_NULL(cmdbatch))
return;
/* Zap the canary timer */
del_timer_sync(&cmdbatch->timer);
- /* non-bh because we just destroyed timer */
- spin_lock(&cmdbatch->lock);
+ /*
+ * callback might run in interrupt context
+ * so need to use irqsave version of spinlocks.
+ */
+ spin_lock_irqsave(&cmdbatch->lock, flags);
/* Empty the synclist before canceling events */
list_splice_init(&cmdbatch->synclist, &cancel_synclist);
- spin_unlock(&cmdbatch->lock);
+ spin_unlock_irqrestore(&cmdbatch->lock, flags);
/*
* Finish canceling events outside the cmdbatch spinlock and
@@ -303,8 +308,15 @@ void kgsl_cmdbatch_destroy(struct kgsl_cmdbatch *cmdbatch)
kgsl_cmdbatch_sync_func, event);
} else if (event->type == KGSL_CMD_SYNCPOINT_TYPE_FENCE) {
/* Put events that are successfully canceled */
- if (kgsl_sync_fence_async_cancel(event->handle))
+ spin_lock_irqsave(&cmdbatch->lock, flags);
+
+ if (kgsl_sync_fence_async_cancel(event->handle)) {
+ event->handle = NULL;
+ spin_unlock_irqrestore(&cmdbatch->lock, flags);
kgsl_cmdbatch_sync_event_put(event);
+ } else {
+ spin_unlock_irqrestore(&cmdbatch->lock, flags);
+ }
}
/* Put events that have been removed from the synclist */
@@ -365,7 +377,7 @@ static int kgsl_cmdbatch_add_sync_fence(struct kgsl_device *device,
{
struct kgsl_cmd_syncpoint_fence *sync = priv;
struct kgsl_cmdbatch_sync_event *event;
-
+ unsigned long flags;
event = kzalloc(sizeof(*event), GFP_KERNEL);
if (event == NULL)
return -ENOMEM;
@@ -392,11 +404,6 @@ static int kgsl_cmdbatch_add_sync_fence(struct kgsl_device *device,
kref_get(&event->refcount);
- /* non-bh because, we haven't started cmdbatch timer yet */
- spin_lock(&cmdbatch->lock);
- list_add(&event->node, &cmdbatch->synclist);
- spin_unlock(&cmdbatch->lock);
-
/*
* Increment the reference count for the async callback.
* Decrement when the callback is successfully canceled, when
@@ -404,6 +411,10 @@ static int kgsl_cmdbatch_add_sync_fence(struct kgsl_device *device,
*/
kref_get(&event->refcount);
+
+ spin_lock_irqsave(&cmdbatch->lock, flags);
+ list_add(&event->node, &cmdbatch->synclist);
+
event->handle = kgsl_sync_fence_async_wait(sync->fd,
kgsl_cmdbatch_sync_fence_func, event);
@@ -411,17 +422,14 @@ static int kgsl_cmdbatch_add_sync_fence(struct kgsl_device *device,
int ret = PTR_ERR(event->handle);
event->handle = NULL;
-
- /* Failed to add the event to the async callback */
- kgsl_cmdbatch_sync_event_put(event);
-
/* Remove event from the synclist */
- spin_lock(&cmdbatch->lock);
list_del(&event->node);
- spin_unlock(&cmdbatch->lock);
+ spin_unlock_irqrestore(&cmdbatch->lock, flags);
+ /* Put for event removal from the synclist */
kgsl_cmdbatch_sync_event_put(event);
-
- /* Event no longer needed by this function */
+ /* Unable to add event to the async callback so a put */
+ kgsl_cmdbatch_sync_event_put(event);
+ /* Put since event no longer needed by this function */
kgsl_cmdbatch_sync_event_put(event);
/*
@@ -435,6 +443,7 @@ static int kgsl_cmdbatch_add_sync_fence(struct kgsl_device *device,
}
trace_syncpoint_fence(cmdbatch, event->handle->name);
+ spin_unlock_irqrestore(&cmdbatch->lock, flags);
/*
* Event was successfully added to the synclist, the async