Skip to content

Commit

Permalink
cpufreq: Add QoS requests for userspace constraints
Browse files Browse the repository at this point in the history
This implements QoS requests to manage userspace configuration of min
and max frequency.

Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Tested-by: syzbot <syzbot+de771ae9390dffed7266@syzkaller.appspotmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Viresh Kumar authored and Rafael J. Wysocki committed Jul 8, 2019
1 parent c57b25b commit 18c4992
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 49 deletions.
98 changes: 55 additions & 43 deletions drivers/cpufreq/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -2342,32 +2369,19 @@ 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",
new_policy->cpu, new_policy->min, new_policy->max);

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);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 2 additions & 6 deletions include/linux/cpufreq.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@ struct cpufreq_cpuinfo {
unsigned int transition_latency;
};

struct cpufreq_user_policy {
unsigned int min; /* in kHz */
unsigned int max; /* in kHz */
};

struct cpufreq_policy {
/* CPUs sharing clock, require sw coordination */
cpumask_var_t cpus; /* Online CPUs only */
Expand Down Expand Up @@ -84,7 +79,8 @@ struct cpufreq_policy {
struct work_struct update; /* if update_policy() needs to be
* called, but you're in IRQ context */

struct cpufreq_user_policy user_policy;
struct dev_pm_qos_request *min_freq_req;
struct dev_pm_qos_request *max_freq_req;
struct cpufreq_frequency_table *freq_table;
enum cpufreq_table_sorting freq_table_sorted;

Expand Down

0 comments on commit 18c4992

Please sign in to comment.