diff options
Diffstat (limited to 'drivers/gpu/msm/kgsl_cmdbatch.c')
| -rw-r--r-- | drivers/gpu/msm/kgsl_cmdbatch.c | 63 |
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 |
