1
0
Fork 0

Power management updates for 5.5-rc2

- Add PM QoS support, based on the frequency QoS introduced during
    the 5.4 cycle, to devfreq (Leonard Crestez).
 
  - Fix some assorted devfreq issues (Leonard Crestez).
 
  - Fix an unintentional cpuidle behavior change (introduced during
    the 5.4 cycle) related to the active polling time limit (Marcelo
    Tosatti).
 
  - Fix a recently introduced cpuidle helper function and do a minor
    cleanup in the cpuidle core (Rafael Wysocki).
 
  - Avoid adding devices with special power management requirements,
    like fans, to the generic ACPI PM domain (Rafael Wysocki).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAl3zXUcSHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxZBsP/0WiPxXXBiWucxDbbPJy/MeMueQQg+I7
 X8aogw1+sPZ2kE8lwrAKlLhsFIKzXSFfSArImuJ4/V2fPMbv8mchmbbd2sOwGiwM
 fYCRWKoJsWMh2y8BVwc5oKuBBh+hwvjNo/dJEA+urXLm3SWNWZninfVgC/vKxS0s
 XLLxpGF/J4xdICUMV5Bka3p+wJpEUXXu+O0J60f8yvJsksJrfGRtrSO4ykyFr33v
 H0pk5Por+3JRMEzW0Tg9ZgMOoCGmk+P9dMm806fSnpJzhwXay7+PQdI6knxXT4Ek
 FHy7RFYUv1AGHJahWtdCVs0tfUbPljl3SwrOgSCO04dUchjtK7wrSy5d1S1CloNw
 Sef2lxBik5+ZxxqGHhle10tZBjosT7KKgKJk14zVJc9hvP1lHiszbMnTnCK0o5w8
 t8jifxcl5gzBWodcpXnBc7WUiiHimP94N6YW1iMRpwEmgEJWpWbWwnjAZ4iFSnkF
 /6LyXipjLd3SVRhVv8raAmbE4KaukOqQg8J6fASrVQrxwJm6ImvIHNK+TZuIM5GX
 YQB9ZReFMLdRxO7bIjkiTkcey9344RJAnFLpRPbWy8J85FNNL6/4gybt71c66pY2
 9wx4bXzu20liR9Lg1JkTQO3aSwD7/FHnSud/a1XT45p1jrkRwc/JDObWxkQxE01N
 d8o8SEf9PAoL
 =mU27
 -----END PGP SIGNATURE-----

Merge tag 'pm-5.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "These add PM QoS support to devfreq and fix a few issues in that
  subsystem, fix two cpuidle issues and do one minor cleanup in there,
  and address an ACPI power management problem related to devices with
  special power management requirements, like fans.

  Specifics:

   - Add PM QoS support, based on the frequency QoS introduced during
     the 5.4 cycle, to devfreq (Leonard Crestez).

   - Fix some assorted devfreq issues (Leonard Crestez).

   - Fix an unintentional cpuidle behavior change (introduced during the
     5.4 cycle) related to the active polling time limit (Marcelo
     Tosatti).

   - Fix a recently introduced cpuidle helper function and do a minor
     cleanup in the cpuidle core (Rafael Wysocki).

   - Avoid adding devices with special power management requirements,
     like fans, to the generic ACPI PM domain (Rafael Wysocki)"

* tag 'pm-5.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  cpuidle: Drop unnecessary type cast in cpuidle_poll_time()
  cpuidle: Fix cpuidle_driver_state_disabled()
  ACPI: PM: Avoid attaching ACPI PM domain to certain devices
  cpuidle: use first valid target residency as poll time
  PM / devfreq: Use PM QoS for sysfs min/max_freq
  PM / devfreq: Add PM QoS support
  PM / devfreq: Don't fail devfreq_dev_release if not in list
  PM / devfreq: Introduce get_freq_range helper
  PM / devfreq: Set scaling_max_freq to max on OPP notifier error
  PM / devfreq: Fix devfreq_notifier_call returning errno
alistair/sunxi64-5.5-dsi
Linus Torvalds 2019-12-13 14:43:26 -08:00
commit 6bd2c87aaf
5 changed files with 236 additions and 78 deletions

View File

@ -1314,9 +1314,19 @@ static void acpi_dev_pm_detach(struct device *dev, bool power_off)
*/
int acpi_dev_pm_attach(struct device *dev, bool power_on)
{
/*
* Skip devices whose ACPI companions match the device IDs below,
* because they require special power management handling incompatible
* with the generic ACPI PM domain.
*/
static const struct acpi_device_id special_pm_ids[] = {
{"PNP0C0B", }, /* Generic ACPI fan */
{"INT3404", }, /* Fan */
{}
};
struct acpi_device *adev = ACPI_COMPANION(dev);
if (!adev)
if (!adev || !acpi_match_device_ids(adev, special_pm_ids))
return 0;
/*

View File

@ -381,7 +381,8 @@ u64 cpuidle_poll_time(struct cpuidle_driver *drv,
if (dev->states_usage[i].disable)
continue;
limit_ns = (u64)drv->states[i].target_residency_ns;
limit_ns = drv->states[i].target_residency_ns;
break;
}
dev->poll_limit_ns = limit_ns;

View File

@ -403,6 +403,13 @@ void cpuidle_driver_state_disabled(struct cpuidle_driver *drv, int idx,
mutex_lock(&cpuidle_lock);
spin_lock(&cpuidle_driver_lock);
if (!drv->cpumask) {
drv->states[idx].flags |= CPUIDLE_FLAG_UNUSABLE;
goto unlock;
}
for_each_cpu(cpu, drv->cpumask) {
struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
@ -415,5 +422,8 @@ void cpuidle_driver_state_disabled(struct cpuidle_driver *drv, int idx,
dev->states_usage[idx].disable &= ~CPUIDLE_STATE_DISABLED_BY_DRIVER;
}
unlock:
spin_unlock(&cpuidle_driver_lock);
mutex_unlock(&cpuidle_lock);
}

View File

@ -24,11 +24,14 @@
#include <linux/printk.h>
#include <linux/hrtimer.h>
#include <linux/of.h>
#include <linux/pm_qos.h>
#include "governor.h"
#define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h>
#define HZ_PER_KHZ 1000
static struct class *devfreq_class;
/*
@ -98,6 +101,54 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq)
return max_freq;
}
/**
* get_freq_range() - Get the current freq range
* @devfreq: the devfreq instance
* @min_freq: the min frequency
* @max_freq: the max frequency
*
* This takes into consideration all constraints.
*/
static void get_freq_range(struct devfreq *devfreq,
unsigned long *min_freq,
unsigned long *max_freq)
{
unsigned long *freq_table = devfreq->profile->freq_table;
s32 qos_min_freq, qos_max_freq;
lockdep_assert_held(&devfreq->lock);
/*
* Initialize minimum/maximum frequency from freq table.
* The devfreq drivers can initialize this in either ascending or
* descending order and devfreq core supports both.
*/
if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) {
*min_freq = freq_table[0];
*max_freq = freq_table[devfreq->profile->max_state - 1];
} else {
*min_freq = freq_table[devfreq->profile->max_state - 1];
*max_freq = freq_table[0];
}
/* Apply constraints from PM QoS */
qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent,
DEV_PM_QOS_MIN_FREQUENCY);
qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent,
DEV_PM_QOS_MAX_FREQUENCY);
*min_freq = max(*min_freq, (unsigned long)HZ_PER_KHZ * qos_min_freq);
if (qos_max_freq != PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE)
*max_freq = min(*max_freq,
(unsigned long)HZ_PER_KHZ * qos_max_freq);
/* Apply constraints from OPP interface */
*min_freq = max(*min_freq, devfreq->scaling_min_freq);
*max_freq = min(*max_freq, devfreq->scaling_max_freq);
if (*min_freq > *max_freq)
*min_freq = *max_freq;
}
/**
* devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance
@ -351,16 +402,7 @@ int update_devfreq(struct devfreq *devfreq)
err = devfreq->governor->get_target_freq(devfreq, &freq);
if (err)
return err;
/*
* Adjust the frequency with user freq, QoS and available freq.
*
* List from the highest priority
* max_freq
* min_freq
*/
max_freq = min(devfreq->scaling_max_freq, devfreq->max_freq);
min_freq = max(devfreq->scaling_min_freq, devfreq->min_freq);
get_freq_range(devfreq, &min_freq, &max_freq);
if (freq < min_freq) {
freq = min_freq;
@ -568,26 +610,69 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
void *devp)
{
struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
int ret;
int err = -EINVAL;
mutex_lock(&devfreq->lock);
devfreq->scaling_min_freq = find_available_min_freq(devfreq);
if (!devfreq->scaling_min_freq) {
mutex_unlock(&devfreq->lock);
return -EINVAL;
}
if (!devfreq->scaling_min_freq)
goto out;
devfreq->scaling_max_freq = find_available_max_freq(devfreq);
if (!devfreq->scaling_max_freq) {
mutex_unlock(&devfreq->lock);
return -EINVAL;
devfreq->scaling_max_freq = ULONG_MAX;
goto out;
}
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
err = update_devfreq(devfreq);
return ret;
out:
mutex_unlock(&devfreq->lock);
if (err)
dev_err(devfreq->dev.parent,
"failed to update frequency from OPP notifier (%d)\n",
err);
return NOTIFY_OK;
}
/**
* qos_notifier_call() - Common handler for QoS constraints.
* @devfreq: the devfreq instance.
*/
static int qos_notifier_call(struct devfreq *devfreq)
{
int err;
mutex_lock(&devfreq->lock);
err = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
if (err)
dev_err(devfreq->dev.parent,
"failed to update frequency from PM QoS (%d)\n",
err);
return NOTIFY_OK;
}
/**
* qos_min_notifier_call() - Callback for QoS min_freq changes.
* @nb: Should be devfreq->nb_min
*/
static int qos_min_notifier_call(struct notifier_block *nb,
unsigned long val, void *ptr)
{
return qos_notifier_call(container_of(nb, struct devfreq, nb_min));
}
/**
* qos_max_notifier_call() - Callback for QoS max_freq changes.
* @nb: Should be devfreq->nb_max
*/
static int qos_max_notifier_call(struct notifier_block *nb,
unsigned long val, void *ptr)
{
return qos_notifier_call(container_of(nb, struct devfreq, nb_max));
}
/**
@ -599,16 +684,36 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
static void devfreq_dev_release(struct device *dev)
{
struct devfreq *devfreq = to_devfreq(dev);
int err;
mutex_lock(&devfreq_list_lock);
if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
mutex_unlock(&devfreq_list_lock);
dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n");
return;
}
list_del(&devfreq->node);
mutex_unlock(&devfreq_list_lock);
err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max,
DEV_PM_QOS_MAX_FREQUENCY);
if (err && err != -ENOENT)
dev_warn(dev->parent,
"Failed to remove max_freq notifier: %d\n", err);
err = dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
if (err && err != -ENOENT)
dev_warn(dev->parent,
"Failed to remove min_freq notifier: %d\n", err);
if (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) {
err = dev_pm_qos_remove_request(&devfreq->user_max_freq_req);
if (err)
dev_warn(dev->parent,
"Failed to remove max_freq request: %d\n", err);
}
if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) {
err = dev_pm_qos_remove_request(&devfreq->user_min_freq_req);
if (err)
dev_warn(dev->parent,
"Failed to remove min_freq request: %d\n", err);
}
if (devfreq->profile->exit)
devfreq->profile->exit(devfreq->dev.parent);
@ -660,6 +765,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->dev.parent = dev;
devfreq->dev.class = devfreq_class;
devfreq->dev.release = devfreq_dev_release;
INIT_LIST_HEAD(&devfreq->node);
devfreq->profile = profile;
strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
devfreq->previous_freq = profile->initial_freq;
@ -681,7 +787,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
err = -EINVAL;
goto err_dev;
}
devfreq->min_freq = devfreq->scaling_min_freq;
devfreq->scaling_max_freq = find_available_max_freq(devfreq);
if (!devfreq->scaling_max_freq) {
@ -689,7 +794,6 @@ struct devfreq *devfreq_add_device(struct device *dev,
err = -EINVAL;
goto err_dev;
}
devfreq->max_freq = devfreq->scaling_max_freq;
devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev);
atomic_set(&devfreq->suspend_count, 0);
@ -730,6 +834,28 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_unlock(&devfreq->lock);
err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req,
DEV_PM_QOS_MIN_FREQUENCY, 0);
if (err < 0)
goto err_devfreq;
err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req,
DEV_PM_QOS_MAX_FREQUENCY,
PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
if (err < 0)
goto err_devfreq;
devfreq->nb_min.notifier_call = qos_min_notifier_call;
err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
if (err)
goto err_devfreq;
devfreq->nb_max.notifier_call = qos_max_notifier_call;
err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max,
DEV_PM_QOS_MAX_FREQUENCY);
if (err)
goto err_devfreq;
mutex_lock(&devfreq_list_lock);
governor = try_then_request_governor(devfreq->governor_name);
@ -1303,41 +1429,37 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
unsigned long value;
int ret;
/*
* Protect against theoretical sysfs writes between
* device_add and dev_pm_qos_add_request
*/
if (!dev_pm_qos_request_active(&df->user_min_freq_req))
return -EAGAIN;
ret = sscanf(buf, "%lu", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->lock);
/* Round down to kHz for PM QoS */
ret = dev_pm_qos_update_request(&df->user_min_freq_req,
value / HZ_PER_KHZ);
if (ret < 0)
return ret;
if (value) {
if (value > df->max_freq) {
ret = -EINVAL;
goto unlock;
}
} else {
unsigned long *freq_table = df->profile->freq_table;
/* Get minimum frequency according to sorting order */
if (freq_table[0] < freq_table[df->profile->max_state - 1])
value = freq_table[0];
else
value = freq_table[df->profile->max_state - 1];
}
df->min_freq = value;
update_devfreq(df);
ret = count;
unlock:
mutex_unlock(&df->lock);
return ret;
return count;
}
static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq *df = to_devfreq(dev);
unsigned long min_freq, max_freq;
return sprintf(buf, "%lu\n", max(df->scaling_min_freq, df->min_freq));
mutex_lock(&df->lock);
get_freq_range(df, &min_freq, &max_freq);
mutex_unlock(&df->lock);
return sprintf(buf, "%lu\n", min_freq);
}
static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
@ -1347,33 +1469,37 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
unsigned long value;
int ret;
/*
* Protect against theoretical sysfs writes between
* device_add and dev_pm_qos_add_request
*/
if (!dev_pm_qos_request_active(&df->user_max_freq_req))
return -EINVAL;
ret = sscanf(buf, "%lu", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->lock);
/*
* PM QoS frequencies are in kHz so we need to convert. Convert by
* rounding upwards so that the acceptable interval never shrinks.
*
* For example if the user writes "666666666" to sysfs this value will
* be converted to 666667 kHz and back to 666667000 Hz before an OPP
* lookup, this ensures that an OPP of 666666666Hz is still accepted.
*
* A value of zero means "no limit".
*/
if (value)
value = DIV_ROUND_UP(value, HZ_PER_KHZ);
else
value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
if (value) {
if (value < df->min_freq) {
ret = -EINVAL;
goto unlock;
}
} else {
unsigned long *freq_table = df->profile->freq_table;
ret = dev_pm_qos_update_request(&df->user_max_freq_req, value);
if (ret < 0)
return ret;
/* Get maximum frequency according to sorting order */
if (freq_table[0] < freq_table[df->profile->max_state - 1])
value = freq_table[df->profile->max_state - 1];
else
value = freq_table[0];
}
df->max_freq = value;
update_devfreq(df);
ret = count;
unlock:
mutex_unlock(&df->lock);
return ret;
return count;
}
static DEVICE_ATTR_RW(min_freq);
@ -1381,8 +1507,13 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq *df = to_devfreq(dev);
unsigned long min_freq, max_freq;
return sprintf(buf, "%lu\n", min(df->scaling_max_freq, df->max_freq));
mutex_lock(&df->lock);
get_freq_range(df, &min_freq, &max_freq);
mutex_unlock(&df->lock);
return sprintf(buf, "%lu\n", max_freq);
}
static DEVICE_ATTR_RW(max_freq);

View File

@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/pm_opp.h>
#include <linux/pm_qos.h>
#define DEVFREQ_NAME_LEN 16
@ -123,8 +124,8 @@ struct devfreq_dev_profile {
* @previous_freq: previously configured frequency value.
* @data: Private data of the governor. The devfreq framework does not
* touch this.
* @min_freq: Limit minimum frequency requested by user (0: none)
* @max_freq: Limit maximum frequency requested by user (0: none)
* @user_min_freq_req: PM QoS minimum frequency request from user (via sysfs)
* @user_max_freq_req: PM QoS maximum frequency request from user (via sysfs)
* @scaling_min_freq: Limit minimum frequency requested by OPP interface
* @scaling_max_freq: Limit maximum frequency requested by OPP interface
* @stop_polling: devfreq polling status of a device.
@ -136,6 +137,8 @@ struct devfreq_dev_profile {
* @time_in_state: Statistics of devfreq states
* @last_stat_updated: The last time stat updated
* @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier
* @nb_min: Notifier block for DEV_PM_QOS_MIN_FREQUENCY
* @nb_max: Notifier block for DEV_PM_QOS_MAX_FREQUENCY
*
* This structure stores the devfreq information for a give device.
*
@ -161,8 +164,8 @@ struct devfreq {
void *data; /* private data for governors */
unsigned long min_freq;
unsigned long max_freq;
struct dev_pm_qos_request user_min_freq_req;
struct dev_pm_qos_request user_max_freq_req;
unsigned long scaling_min_freq;
unsigned long scaling_max_freq;
bool stop_polling;
@ -178,6 +181,9 @@ struct devfreq {
unsigned long last_stat_updated;
struct srcu_notifier_head transition_notifier_list;
struct notifier_block nb_min;
struct notifier_block nb_max;
};
struct devfreq_freqs {