]> git.itanic.dy.fi Git - linux-stable/commitdiff
cpufreq: amd-pstate: Add ->fast_switch() callback
authorGautham R. Shenoy <gautham.shenoy@amd.com>
Wed, 17 May 2023 16:28:15 +0000 (16:28 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 5 Jun 2023 07:29:46 +0000 (09:29 +0200)
commit 4badf2eb1e986bdbf34dd2f5d4c979553a86fe54 upstream.

Schedutil normally calls the adjust_perf callback for drivers with
adjust_perf callback available and fast_switch_possible flag set.
However, when frequency invariance is disabled and schedutil tries to
invoke fast_switch. So, there is a chance of kernel crash if this
function pointer is not set. To protect against this scenario add
fast_switch callback to amd_pstate driver.

Fixes: 1d215f0319c2 ("cpufreq: amd-pstate: Add fast switch function for AMD P-State")
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
Signed-off-by: Wyes Karny <wyes.karny@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/cpufreq/amd-pstate.c

index 2375c950b249ad801cbdab7c52cfed967c8023b0..a7eb25066c27485d089572b176ffb97a5e293604 100644 (file)
@@ -422,9 +422,8 @@ static int amd_pstate_verify(struct cpufreq_policy_data *policy)
        return 0;
 }
 
-static int amd_pstate_target(struct cpufreq_policy *policy,
-                            unsigned int target_freq,
-                            unsigned int relation)
+static int amd_pstate_update_freq(struct cpufreq_policy *policy,
+                                 unsigned int target_freq, bool fast_switch)
 {
        struct cpufreq_freqs freqs;
        struct amd_cpudata *cpudata = policy->driver_data;
@@ -443,14 +442,36 @@ static int amd_pstate_target(struct cpufreq_policy *policy,
        des_perf = DIV_ROUND_CLOSEST(target_freq * cap_perf,
                                     cpudata->max_freq);
 
-       cpufreq_freq_transition_begin(policy, &freqs);
-       amd_pstate_update(cpudata, min_perf, des_perf,
-                         max_perf, false);
-       cpufreq_freq_transition_end(policy, &freqs, false);
+       WARN_ON(fast_switch && !policy->fast_switch_enabled);
+       /*
+        * If fast_switch is desired, then there aren't any registered
+        * transition notifiers. See comment for
+        * cpufreq_enable_fast_switch().
+        */
+       if (!fast_switch)
+               cpufreq_freq_transition_begin(policy, &freqs);
+
+       amd_pstate_update(cpudata, min_perf, des_perf, max_perf, fast_switch);
+
+       if (!fast_switch)
+               cpufreq_freq_transition_end(policy, &freqs, false);
 
        return 0;
 }
 
+static int amd_pstate_target(struct cpufreq_policy *policy,
+                            unsigned int target_freq,
+                            unsigned int relation)
+{
+       return amd_pstate_update_freq(policy, target_freq, false);
+}
+
+static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy,
+                                 unsigned int target_freq)
+{
+       return amd_pstate_update_freq(policy, target_freq, true);
+}
+
 static void amd_pstate_adjust_perf(unsigned int cpu,
                                   unsigned long _min_perf,
                                   unsigned long target_perf,
@@ -698,6 +719,7 @@ static int amd_pstate_cpu_exit(struct cpufreq_policy *policy)
 
        freq_qos_remove_request(&cpudata->req[1]);
        freq_qos_remove_request(&cpudata->req[0]);
+       policy->fast_switch_possible = false;
        kfree(cpudata);
 
        return 0;
@@ -1230,6 +1252,7 @@ static struct cpufreq_driver amd_pstate_driver = {
        .flags          = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS,
        .verify         = amd_pstate_verify,
        .target         = amd_pstate_target,
+       .fast_switch    = amd_pstate_fast_switch,
        .init           = amd_pstate_cpu_init,
        .exit           = amd_pstate_cpu_exit,
        .suspend        = amd_pstate_cpu_suspend,