msm: kgsl: Keep the context alive until its fences signal

The context is currently kept alive until all its fences are
released. This means that we do not destroy a context until all
the fence fds associated with it are closed by userspace, which may
be a long time. Instead, remove the timeline's refcount on its
context when it is detached. This permits the context to be freed
once all its fences signal.

Change-Id: Ifc5dd55980358ddbb4d3f6220d8b5d9f725aed1b
Signed-off-by: Lynus Vaz <lvaz@codeaurora.org>
Signed-off-by: Hareesh Gundu <hareeshg@codeaurora.org>
This commit is contained in:
Lynus Vaz 2021-06-10 13:11:35 -07:00 committed by Hareesh Gundu
parent bca4e894c5
commit 5eafefa5f1
3 changed files with 51 additions and 28 deletions

View File

@ -758,8 +758,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);
}
@ -778,6 +777,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) {
@ -801,7 +802,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);

View File

@ -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 <linux/file.h>
@ -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, &timestamp_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, &timestamp_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, &timestamp_retired);
kgsl_readtimestamp(ktimeline->device, context,
KGSL_TIMESTAMP_QUEUED, &timestamp_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 = {

View File

@ -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)
{
}