msm: kgsl: Use kthread instead of workqueue for event work

Currently a workqueue is being used to process the event work. In
certain scenarios like when most of CPU cores are busy, there can be a
significant delay between the actual timestamp retire event and when the
work is processed by the events workqueue as workqueues cannot have RT
priority. Hence use kthread instead of workqueue for event work.

Change-Id: Ib1ec7fa1ec3a133d03104c9a029dcc4c06180609
Signed-off-by: Puranam V G Tejaswi <quic_pvgtejas@quicinc.com>
Signed-off-by: Pankaj Gupta <quic_gpankaj@quicinc.com>
This commit is contained in:
Pankaj Gupta 2024-12-06 13:06:02 +05:30 • committed by Michael Bestas
commit e97ce5ceb5
No known key found for this signature in database
GPG key ID: CC95044519BE6669
5 changed files with 24 additions and 26 deletions

View file

@ -1746,7 +1746,7 @@ static int adreno_pm_suspend(struct device *dev)
if (status)
return status;
flush_workqueue(device->events_wq);
kthread_flush_worker(device->events_worker);
flush_workqueue(kgsl_driver.mem_workqueue);
return status;

View file

@ -686,11 +686,11 @@ int kgsl_context_init(struct kgsl_device_private *dev_priv,
if (id == -ENOSPC) {
/*
* Before declaring that there are no contexts left try
* flushing the event workqueue just in case there are
* flushing the event worker just in case there are
* detached contexts waiting to finish
*/
flush_workqueue(device->events_wq);
kthread_flush_worker(device->events_worker);
id = _kgsl_get_context_id(device);
}
@ -4526,15 +4526,16 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
device->pwrctrl.interrupt_num = status;
disable_irq(device->pwrctrl.interrupt_num);
device->events_wq = alloc_workqueue("kgsl-events",
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS | WQ_HIGHPRI, 0);
device->events_worker = kthread_create_worker(0, "kgsl-events");
if (!device->events_wq) {
dev_err(device->dev, "Failed to allocate events workqueue\n");
status = -ENOMEM;
if (IS_ERR(device->events_worker)) {
status = PTR_ERR(device->events_worker);
dev_err(device->dev, "Failed to create events worker ret=%d\n", status);
goto error_pwrctrl_close;
}
sched_set_fifo(device->events_worker->task);
/* This can return -EPROBE_DEFER */
status = kgsl_mmu_probe(device);
if (status != 0)
@ -4559,10 +4560,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
return 0;
error_pwrctrl_close:
if (device->events_wq) {
destroy_workqueue(device->events_wq);
device->events_wq = NULL;
}
if (!IS_ERR(device->events_worker))
kthread_destroy_worker(device->events_worker);
kgsl_pwrctrl_close(device);
error:
@ -4572,10 +4571,7 @@ error:
void kgsl_device_platform_remove(struct kgsl_device *device)
{
if (device->events_wq) {
destroy_workqueue(device->events_wq);
device->events_wq = NULL;
}
kthread_destroy_worker(device->events_worker);
kgsl_device_snapshot_close(device);

View file

@ -295,7 +295,7 @@ typedef void (*kgsl_event_func)(struct kgsl_device *, struct kgsl_event_group *,
* @priv: Private data passed to the callback function
* @node: List node for the kgsl_event_group list
* @created: Jiffies when the event was created
* @work: Work struct for dispatching the callback
* @work: kthread_work struct for dispatching the callback
* @result: KGSL event result type to pass to the callback
* group: The event group this event belongs to
*/
@ -307,7 +307,7 @@ struct kgsl_event {
void *priv;
struct list_head node;
unsigned int created;
struct work_struct work;
struct kthread_work work;
int result;
struct kgsl_event_group *group;
};

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __KGSL_DEVICE_H
#define __KGSL_DEVICE_H
@ -293,7 +294,7 @@ struct kgsl_device {
struct kgsl_pwrscale pwrscale;
int reset_counter; /* Track how many GPU core resets have occurred */
struct workqueue_struct *events_wq;
struct kthread_worker *events_worker;
/* Number of active contexts seen globally for this device */
int active_context_count;

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2011-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/debugfs.h>
@ -21,17 +22,17 @@ static inline void signal_event(struct kgsl_device *device,
{
list_del(&event->node);
event->result = result;
queue_work(device->events_wq, &event->work);
kthread_queue_work(device->events_worker, &event->work);
}
/**
* _kgsl_event_worker() - Work handler for processing GPU event callbacks
* @work: Pointer to the work_struct for the event
* @work: Pointer to the kthread_work for the event
*
* Each event callback has its own work struct and is run on a event specific
* workqeuue. This is the worker that queues up the event callback function.
* Each event callback has its own kthread_work struct and is run on a event specific
* worker thread. This is the worker that queues up the event callback function.
*/
static void _kgsl_event_worker(struct work_struct *work)
static void _kgsl_event_worker(struct kthread_work *work)
{
struct kgsl_event *event = container_of(work, struct kgsl_event, work);
int id = KGSL_CONTEXT_ID(event->context);
@ -268,7 +269,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group,
event->created = jiffies;
event->group = group;
INIT_WORK(&event->work, _kgsl_event_worker);
kthread_init_work(&event->work, _kgsl_event_worker);
trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func);
@ -283,7 +284,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group,
if (timestamp_cmp(retired, timestamp) >= 0) {
event->result = KGSL_EVENT_RETIRED;
queue_work(device->events_wq, &event->work);
kthread_queue_work(device->events_worker, &event->work);
spin_unlock(&group->lock);
return 0;
}