devfreq: Fix possible race in tz_start() handler

The TZ governor start can be invoked through the sysfs governor store()
operation. This opens up a possible race when user space tries to set
the same governor operation twice  leading to calling the TZ governors
tz_handler() in succession. While the first one goes through
successfully and queues the work for starting the bandwidth governor,
before the work gets to run, the second invocation of the tz_handler
reinitializes the work. This causes the internal state of the work item
to be reset causing a crash in the kernel workqueue handler.

This patch tries to address this problem by not allowing the
tz_handler() to run the next time, if the first invocation was already
successful and potentially in progress.

Change-Id: Id2d6b9f680c873937a64eeb483ce8359306cd7b0
Signed-off-by: Sharat Masetty <smasetty@codeaurora.org>
This commit is contained in:
Sharat Masetty
2020-03-31 19:21:29 +05:30
parent 3fda7c70d5
commit a2d4f1e7f9

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/errno.h>
#include <linux/module.h>
@@ -470,11 +470,14 @@ static int tz_start(struct devfreq *devfreq)
unsigned int tz_pwrlevels[MSM_ADRENO_MAX_PWRLEVELS + 1];
int i, out, ret;
unsigned int version;
struct msm_adreno_extended_profile *gpu_profile;
struct msm_adreno_extended_profile *gpu_profile = container_of(
(devfreq->profile),
struct msm_adreno_extended_profile,
profile);
if (partner_gpu_profile)
return -EEXIST;
gpu_profile = container_of(devfreq->profile,
struct msm_adreno_extended_profile,
profile);
/*
* Assuming that we have only one instance of the adreno device
@@ -495,6 +498,7 @@ static int tz_start(struct devfreq *devfreq)
tz_pwrlevels[0] = i;
} else {
pr_err(TAG "tz_pwrlevels[] is too short\n");
partner_gpu_profile = NULL;
return -EINVAL;
}
@@ -511,6 +515,7 @@ static int tz_start(struct devfreq *devfreq)
sizeof(version));
if (ret != 0 || version > MAX_TZ_VERSION) {
pr_err(TAG "tz_init failed\n");
partner_gpu_profile = NULL;
return ret;
}
@@ -606,7 +611,7 @@ static int tz_handler(struct devfreq *devfreq, unsigned int event, void *data)
break;
}
if (partner_gpu_profile && partner_gpu_profile->bus_devfreq)
if (!result && partner_gpu_profile && partner_gpu_profile->bus_devfreq)
switch (event) {
case DEVFREQ_GOV_START:
queue_work(workqueue,