From 2a5341be4935ffba010ee93cd2b28fba7a062a96 Mon Sep 17 00:00:00 2001 From: Johnny Liu Date: Thu, 26 Jun 2025 07:55:49 +0000 Subject: [PATCH] devfreq: nvhost_podgov: Fix NULL dereference issue When switching the governor to nvhost_pogdov or switching back to other governors, we will need to lock the devfreq lock to prevent triggering DVFS cycle from other paths. The nvhost_pod_target_freq callback will be called when triggering the DVFS cycle. However, the callback expects governor data is already allocated and initialized. We need to synchronize the operations when we switch the governor so that DVFS cycle can only be triggered when governor data is ready. Bug 5354161 Bug 5351714 Change-Id: Iaf8af8291ea09a7c2bfbdc5e1453bb976ee0987b Signed-off-by: Johnny Liu Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3392341 Reviewed-by: svcacv Reviewed-by: Bibek Basu GVS: buildbot_gerritrpt Reviewed-by: Rajesh Devaraj Reviewed-by: Rajkumar Kasirajan --- drivers/devfreq/governor_pod_scaling.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/devfreq/governor_pod_scaling.c b/drivers/devfreq/governor_pod_scaling.c index 0b542e70..9e611308 100644 --- a/drivers/devfreq/governor_pod_scaling.c +++ b/drivers/devfreq/governor_pod_scaling.c @@ -170,6 +170,17 @@ static int nvhost_pod_target_freq(struct devfreq *df, unsigned long *freq) unsigned int down_threshold; int index, err; + /* + * NOTE: + * Since "cancel_delayed_work_sync(&devfreq->work)" is not synchronized + * by the devfreq->lock mutex lock in the devfreq_monitor_stop function + * in the devfreq core, the condition check here is necessary due to + * out-of-order execution even though devfreq timer has stopped and the + * df->governor_data is already freed in the nvhost_pod_exit handler. + */ + if (!podgov) + return -ENOENT; + err = devfreq_update_stats(df); if (err) return err; @@ -273,10 +284,6 @@ static int nvhost_pod_init(struct devfreq *df) if (err) goto unregister_notifier; - if (!pm_runtime_suspended(df->dev.parent)) { - devfreq_monitor_start(df); - } - return 0; unregister_notifier: @@ -291,7 +298,6 @@ static void nvhost_pod_exit(struct devfreq *df) { struct podgov_data *podgov = df->governor_data; - devfreq_monitor_stop(df); sysfs_remove_group(&df->dev.kobj, &dev_attr_group); devfreq_unregister_notifier(df, &podgov->nb, DEVFREQ_TRANSITION_NOTIFIER); @@ -320,10 +326,16 @@ static int nvhost_pod_event_handler(struct devfreq *df, switch (event) { case DEVFREQ_GOV_START: + mutex_lock(&df->lock); ret = nvhost_pod_init(df); + mutex_unlock(&df->lock); + devfreq_monitor_start(df); break; case DEVFREQ_GOV_STOP: + devfreq_monitor_stop(df); + mutex_lock(&df->lock); nvhost_pod_exit(df); + mutex_unlock(&df->lock); break; case DEVFREQ_GOV_UPDATE_INTERVAL: devfreq_update_interval(df, (unsigned int *)data);