diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 565ddfca6d6e..907cbbfd4bff 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -761,8 +761,7 @@ void kgsl_context_detach(struct kgsl_context *context) /* Remove the event group from the list */ kgsl_del_event_group(device, &context->events); - kgsl_sync_timeline_put(context->ktimeline); - + kgsl_sync_timeline_detach(context->ktimeline); kgsl_context_put(context); } @@ -781,6 +780,8 @@ kgsl_context_destroy(struct kref *kref) */ BUG_ON(!kgsl_context_detached(context)); + kgsl_sync_timeline_put(context->ktimeline); + write_lock(&device->context_lock); if (context->id != KGSL_CONTEXT_INVALID) { @@ -804,7 +805,6 @@ kgsl_context_destroy(struct kref *kref) context->id = KGSL_CONTEXT_INVALID; } write_unlock(&device->context_lock); - kgsl_sync_timeline_destroy(context); kgsl_process_private_put(context->proc_priv); device->ftbl->drawctxt_destroy(context); diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c index 00f0d5a62816..1103b512487d 100644 --- a/drivers/gpu/msm/kgsl_sync.c +++ b/drivers/gpu/msm/kgsl_sync.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2019, 2021, The Linux Foundation. All rights reserved. */ #include @@ -249,27 +249,41 @@ static void kgsl_sync_timeline_value_str(struct dma_fence *fence, { struct kgsl_sync_fence *kfence = (struct kgsl_sync_fence *)fence; struct kgsl_sync_timeline *ktimeline = kfence->parent; + struct kgsl_context *context = NULL; + unsigned long flags; + int ret = 0; - unsigned int timestamp_retired = 0; - unsigned int timestamp_queued = 0; + unsigned int timestamp_retired; + unsigned int timestamp_queued; if (!kref_get_unless_zero(&ktimeline->kref)) return; + if (!ktimeline->device) + goto put_timeline; - /* - * ktimeline->device might be NULL here but kgsl_readtimestamp() - * will handle that correctly - */ - kgsl_readtimestamp(ktimeline->device, ktimeline->context, - KGSL_TIMESTAMP_RETIRED, ×tamp_retired); + spin_lock_irqsave(&ktimeline->lock, flags); + ret = _kgsl_context_get(ktimeline->context); + context = ret ? ktimeline->context : NULL; + spin_unlock_irqrestore(&ktimeline->lock, flags); - kgsl_readtimestamp(ktimeline->device, ktimeline->context, - KGSL_TIMESTAMP_QUEUED, ×tamp_queued); + /* Get the last signaled timestamp if the context is not valid */ + timestamp_queued = ktimeline->last_timestamp; + timestamp_retired = timestamp_queued; + if (context) { + kgsl_readtimestamp(ktimeline->device, context, + KGSL_TIMESTAMP_RETIRED, ×tamp_retired); + + kgsl_readtimestamp(ktimeline->device, context, + KGSL_TIMESTAMP_QUEUED, ×tamp_queued); + + kgsl_context_put(context); + } snprintf(str, size, "%u queued:%u retired:%u", ktimeline->last_timestamp, timestamp_queued, timestamp_retired); +put_timeline: kgsl_sync_timeline_put(ktimeline); } @@ -298,7 +312,7 @@ int kgsl_sync_timeline_create(struct kgsl_context *context) { struct kgsl_sync_timeline *ktimeline; - /* Put context when timeline is released */ + /* Put context at detach time */ if (!_kgsl_context_get(context)) return -ENOENT; @@ -320,6 +334,11 @@ int kgsl_sync_timeline_create(struct kgsl_context *context) INIT_LIST_HEAD(&ktimeline->child_list_head); spin_lock_init(&ktimeline->lock); ktimeline->device = context->device; + + /* + * The context pointer is valid till detach time, where we put the + * refcount on the context + */ ktimeline->context = context; context->ktimeline = ktimeline; @@ -352,27 +371,30 @@ static void kgsl_sync_timeline_signal(struct kgsl_sync_timeline *ktimeline, kgsl_sync_timeline_put(ktimeline); } -void kgsl_sync_timeline_destroy(struct kgsl_context *context) +void kgsl_sync_timeline_detach(struct kgsl_sync_timeline *ktimeline) { - kfree(context->ktimeline); + unsigned long flags; + struct kgsl_context *context = ktimeline->context; + + /* Set context pointer to NULL and drop our refcount on the context */ + spin_lock_irqsave(&ktimeline->lock, flags); + ktimeline->context = NULL; + spin_unlock_irqrestore(&ktimeline->lock, flags); + kgsl_context_put(context); } -static void kgsl_sync_timeline_release(struct kref *kref) +static void kgsl_sync_timeline_destroy(struct kref *kref) { struct kgsl_sync_timeline *ktimeline = container_of(kref, struct kgsl_sync_timeline, kref); - /* - * Only put the context refcount here. The context destroy function - * will call kgsl_sync_timeline_destroy() to kfree it - */ - kgsl_context_put(ktimeline->context); + kfree(ktimeline); } void kgsl_sync_timeline_put(struct kgsl_sync_timeline *ktimeline) { if (ktimeline) - kref_put(&ktimeline->kref, kgsl_sync_timeline_release); + kref_put(&ktimeline->kref, kgsl_sync_timeline_destroy); } static const struct dma_fence_ops kgsl_sync_fence_ops = { diff --git a/drivers/gpu/msm/kgsl_sync.h b/drivers/gpu/msm/kgsl_sync.h index 989658d58eca..b8655ddc9ca2 100644 --- a/drivers/gpu/msm/kgsl_sync.h +++ b/drivers/gpu/msm/kgsl_sync.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2014,2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014,2018-2019, 2021, The Linux Foundation. All rights reserved. */ #ifndef __KGSL_SYNC_H #define __KGSL_SYNC_H @@ -9,7 +9,8 @@ /** * struct kgsl_sync_timeline - A sync timeline associated with a kgsl context - * @kref: Refcount to keep the struct alive until all its fences are released + * @kref: Refcount to keep the struct alive until all its fences are signaled, + and as long as the context exists * @name: String to describe this timeline * @fence_context: Used by the fence driver to identify fences belonging to * this context @@ -80,7 +81,7 @@ int kgsl_add_fence_event(struct kgsl_device *device, int kgsl_sync_timeline_create(struct kgsl_context *context); -void kgsl_sync_timeline_destroy(struct kgsl_context *context); +void kgsl_sync_timeline_detach(struct kgsl_sync_timeline *ktimeline); void kgsl_sync_timeline_put(struct kgsl_sync_timeline *ktimeline); @@ -118,7 +119,7 @@ static inline int kgsl_sync_timeline_create(struct kgsl_context *context) return 0; } -static inline void kgsl_sync_timeline_destroy(struct kgsl_context *context) +static inline void kgsl_sync_timeline_detach(struct kgsl_sync_timeline *ktimeline) { }