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 <johnliu@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3392341
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Rajesh Devaraj <rdevaraj@nvidia.com>
Reviewed-by: Rajkumar Kasirajan <rkasirajan@nvidia.com>
This commit is contained in:
Johnny Liu
2025-06-26 07:55:49 +00:00
committed by Jon Hunter
parent 15c7804551
commit 2a5341be49

View File

@@ -170,6 +170,17 @@ static int nvhost_pod_target_freq(struct devfreq *df, unsigned long *freq)
unsigned int down_threshold; unsigned int down_threshold;
int index, err; 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); err = devfreq_update_stats(df);
if (err) if (err)
return err; return err;
@@ -273,10 +284,6 @@ static int nvhost_pod_init(struct devfreq *df)
if (err) if (err)
goto unregister_notifier; goto unregister_notifier;
if (!pm_runtime_suspended(df->dev.parent)) {
devfreq_monitor_start(df);
}
return 0; return 0;
unregister_notifier: unregister_notifier:
@@ -291,7 +298,6 @@ static void nvhost_pod_exit(struct devfreq *df)
{ {
struct podgov_data *podgov = df->governor_data; struct podgov_data *podgov = df->governor_data;
devfreq_monitor_stop(df);
sysfs_remove_group(&df->dev.kobj, &dev_attr_group); sysfs_remove_group(&df->dev.kobj, &dev_attr_group);
devfreq_unregister_notifier(df, &podgov->nb, DEVFREQ_TRANSITION_NOTIFIER); devfreq_unregister_notifier(df, &podgov->nb, DEVFREQ_TRANSITION_NOTIFIER);
@@ -320,10 +326,16 @@ static int nvhost_pod_event_handler(struct devfreq *df,
switch (event) { switch (event) {
case DEVFREQ_GOV_START: case DEVFREQ_GOV_START:
mutex_lock(&df->lock);
ret = nvhost_pod_init(df); ret = nvhost_pod_init(df);
mutex_unlock(&df->lock);
devfreq_monitor_start(df);
break; break;
case DEVFREQ_GOV_STOP: case DEVFREQ_GOV_STOP:
devfreq_monitor_stop(df);
mutex_lock(&df->lock);
nvhost_pod_exit(df); nvhost_pod_exit(df);
mutex_unlock(&df->lock);
break; break;
case DEVFREQ_GOV_UPDATE_INTERVAL: case DEVFREQ_GOV_UPDATE_INTERVAL:
devfreq_update_interval(df, (unsigned int *)data); devfreq_update_interval(df, (unsigned int *)data);