From 18c49926c4bf4915e5194d1de3299c0537229f9f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Jul 2019 16:21:24 +0530 Subject: cpufreq: Add QoS requests for userspace constraints This implements QoS requests to manage userspace configuration of min and max frequency. Reviewed-by: Matthias Kaehlcke Reviewed-by: Ulf Hansson Signed-off-by: Viresh Kumar Tested-by: syzbot Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 98 ++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 43 deletions(-) (limited to 'drivers/cpufreq') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 79bac52919a59..99aa7d20b4583 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -718,23 +718,15 @@ static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf) static ssize_t store_##file_name \ (struct cpufreq_policy *policy, const char *buf, size_t count) \ { \ - int ret, temp; \ - struct cpufreq_policy new_policy; \ + unsigned long val; \ + int ret; \ \ - memcpy(&new_policy, policy, sizeof(*policy)); \ - new_policy.min = policy->user_policy.min; \ - new_policy.max = policy->user_policy.max; \ - \ - ret = sscanf(buf, "%u", &new_policy.object); \ + ret = sscanf(buf, "%lu", &val); \ if (ret != 1) \ return -EINVAL; \ \ - temp = new_policy.object; \ - ret = cpufreq_set_policy(policy, &new_policy); \ - if (!ret) \ - policy->user_policy.object = temp; \ - \ - return ret ? ret : count; \ + ret = dev_pm_qos_update_request(policy->object##_freq_req, val);\ + return ret >= 0 ? count : ret; \ } store_one(scaling_min_freq, min); @@ -1124,8 +1116,6 @@ void refresh_frequency_limits(struct cpufreq_policy *policy) new_policy = *policy; pr_debug("updating policy for CPU %u\n", policy->cpu); - new_policy.min = policy->user_policy.min; - new_policy.max = policy->user_policy.max; cpufreq_set_policy(policy, &new_policy); } } @@ -1281,6 +1271,9 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy) DEV_PM_QOS_MAX_FREQUENCY); dev_pm_qos_remove_notifier(dev, &policy->nb_min, DEV_PM_QOS_MIN_FREQUENCY); + dev_pm_qos_remove_request(policy->max_freq_req); + dev_pm_qos_remove_request(policy->min_freq_req); + kfree(policy->min_freq_req); cpufreq_policy_put_kobj(policy); free_cpumask_var(policy->real_cpus); @@ -1359,16 +1352,50 @@ static int cpufreq_online(unsigned int cpu) cpumask_and(policy->cpus, policy->cpus, cpu_online_mask); if (new_policy) { - policy->user_policy.min = policy->min; - policy->user_policy.max = policy->max; + struct device *dev = get_cpu_device(cpu); for_each_cpu(j, policy->related_cpus) { per_cpu(cpufreq_cpu_data, j) = policy; add_cpu_dev_symlink(policy, j); } - } else { - policy->min = policy->user_policy.min; - policy->max = policy->user_policy.max; + + policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req), + GFP_KERNEL); + if (!policy->min_freq_req) + goto out_destroy_policy; + + ret = dev_pm_qos_add_request(dev, policy->min_freq_req, + DEV_PM_QOS_MIN_FREQUENCY, + policy->min); + if (ret < 0) { + /* + * So we don't call dev_pm_qos_remove_request() for an + * uninitialized request. + */ + kfree(policy->min_freq_req); + policy->min_freq_req = NULL; + + dev_err(dev, "Failed to add min-freq constraint (%d)\n", + ret); + goto out_destroy_policy; + } + + /* + * This must be initialized right here to avoid calling + * dev_pm_qos_remove_request() on uninitialized request in case + * of errors. + */ + policy->max_freq_req = policy->min_freq_req + 1; + + ret = dev_pm_qos_add_request(dev, policy->max_freq_req, + DEV_PM_QOS_MAX_FREQUENCY, + policy->max); + if (ret < 0) { + policy->max_freq_req = NULL; + dev_err(dev, "Failed to add max-freq constraint (%d)\n", + ret); + goto out_destroy_policy; + } } if (cpufreq_driver->get && has_target()) { @@ -2342,7 +2369,6 @@ int cpufreq_set_policy(struct cpufreq_policy *policy, { struct cpufreq_governor *old_gov; struct device *cpu_dev = get_cpu_device(policy->cpu); - unsigned long min, max; int ret; pr_debug("setting new policy for CPU %u: %u - %u kHz\n", @@ -2350,24 +2376,12 @@ int cpufreq_set_policy(struct cpufreq_policy *policy, memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo)); - /* - * This check works well when we store new min/max freq attributes, - * because new_policy is a copy of policy with one field updated. - */ - if (new_policy->min > new_policy->max) - return -EINVAL; - /* * PM QoS framework collects all the requests from users and provide us * the final aggregated value here. */ - min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY); - max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY); - - if (min > new_policy->min) - new_policy->min = min; - if (max < new_policy->max) - new_policy->max = max; + new_policy->min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY); + new_policy->max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY); /* verify the cpu speed can be set within this limit */ ret = cpufreq_driver->verify(new_policy); @@ -2456,10 +2470,9 @@ int cpufreq_set_policy(struct cpufreq_policy *policy, * @cpu: CPU to re-evaluate the policy for. * * Update the current frequency for the cpufreq policy of @cpu and use - * cpufreq_set_policy() to re-apply the min and max limits saved in the - * user_policy sub-structure of that policy, which triggers the evaluation - * of policy notifiers and the cpufreq driver's ->verify() callback for the - * policy in question, among other things. + * cpufreq_set_policy() to re-apply the min and max limits, which triggers the + * evaluation of policy notifiers and the cpufreq driver's ->verify() callback + * for the policy in question, among other things. */ void cpufreq_update_policy(unsigned int cpu) { @@ -2519,10 +2532,9 @@ static int cpufreq_boost_set_sw(int state) break; } - down_write(&policy->rwsem); - policy->user_policy.max = policy->max; - cpufreq_governor_limits(policy); - up_write(&policy->rwsem); + ret = dev_pm_qos_update_request(policy->max_freq_req, policy->max); + if (ret) + break; } return ret; -- cgit v1.2.3