Power management fix for v4.14-rc7
This fixes a device power management quality of service (PM QoS) framework implementation issue causing "no restriction" requests for device resume latency, including "no restriction" set by user space, to effectively override requests with specific device resume latency requirements. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJZ8bk1AAoJEILEb/54YlRx1BYP/1l7SG1e9fZZYWR38luZMWu4 fsfr5h6Iwk0zmuHL3gOiH7KNhkMzJyFf6VMCjv0+B1XPrzWVTIFCJnmyUb1vs7fP TkUs66q/O3sOlxUq/oqTZrj/qnoKNgaDeJgqtPPOgJdJXoZdpiOyXJZg22lZEwuR 07Vu49oCXgVf2b2Vr8e4L12onDytj51pzIlfh45mk2J/Qbc7tAZvgaKPIJ3HomoP 1sEBpWvN0PCLvGw877r8barWE1plWHupo4jKv9XyM/RY+avgT9Bz4KFNXGgaLlm7 V36ek0kZOTCKNPTs0wIEwT1dRRr01rb9YgYjvv40ncmJ9CCpWZKHsFiqUH+o84Ac gXQrvNrB0LdOYm0Ox7dgG/Tn6lMvPZgrJ64ukLpBX8Kv/josKaV275vBp+tneBTs GUqGdotc8SQIs8wPIiA1hYnAFSy6D9ZuuvIPPBqe2qyZHzRSbnacRKdCDEAW1Dql EEkC9NrqPsA1YxOIMbHrBnLSQTdXmJq3+aiG1AJ23ZM27gFbJ0qlSsuBChyNSfaa z6c2QQ3f2trcAsOX6TaKZzYSvI4H77CJkcIQh76aC88GJaxq+E5ZH3eTfyCuxc9H k4bgfWj5tsqWys0uZjFsxDPGuxX3oFq2nG31fVyqn9ZLY5soJ5v1yZLfNWfDatnG EoIzWIHTojWsV95M0TLQ =rWEH -----END PGP SIGNATURE----- Merge tag 'pm-4.14-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull power management fix from Rafael Wysocki: "This fixes a device power management quality of service (PM QoS) framework implementation issue causing 'no restriction' requests for device resume latency, including 'no restriction' set by user space, to effectively override requests with specific device resume latency requirements. It is late in the cycle, but the bug in question is in the 'user space can trigger unexpected behavior' category and the fix is stable-candidate, so here it goes" * tag 'pm-4.14-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: PM / QoS: Fix device resume latency PM QoS
This commit is contained in:
commit
5678255027
8 changed files with 63 additions and 35 deletions
|
@ -211,7 +211,9 @@ Description:
|
|||
device, after it has been suspended at run time, from a resume
|
||||
request to the moment the device will be ready to process I/O,
|
||||
in microseconds. If it is equal to 0, however, this means that
|
||||
the PM QoS resume latency may be arbitrary.
|
||||
the PM QoS resume latency may be arbitrary and the special value
|
||||
"n/a" means that user space cannot accept any resume latency at
|
||||
all for the given device.
|
||||
|
||||
Not all drivers support this attribute. If it isn't supported,
|
||||
it is not present.
|
||||
|
|
|
@ -377,7 +377,8 @@ int register_cpu(struct cpu *cpu, int num)
|
|||
|
||||
per_cpu(cpu_sys_devices, num) = &cpu->dev;
|
||||
register_cpu_under_node(num, cpu_to_node(num));
|
||||
dev_pm_qos_expose_latency_limit(&cpu->dev, 0);
|
||||
dev_pm_qos_expose_latency_limit(&cpu->dev,
|
||||
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -14,23 +14,20 @@
|
|||
static int dev_update_qos_constraint(struct device *dev, void *data)
|
||||
{
|
||||
s64 *constraint_ns_p = data;
|
||||
s32 constraint_ns = -1;
|
||||
s64 constraint_ns = -1;
|
||||
|
||||
if (dev->power.subsys_data && dev->power.subsys_data->domain_data)
|
||||
constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns;
|
||||
|
||||
if (constraint_ns < 0) {
|
||||
if (constraint_ns < 0)
|
||||
constraint_ns = dev_pm_qos_read_value(dev);
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
}
|
||||
if (constraint_ns == 0)
|
||||
|
||||
if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* constraint_ns cannot be negative here, because the device has been
|
||||
* suspended.
|
||||
*/
|
||||
if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
|
||||
if (constraint_ns < *constraint_ns_p || *constraint_ns_p < 0)
|
||||
*constraint_ns_p = constraint_ns;
|
||||
|
||||
return 0;
|
||||
|
@ -63,10 +60,14 @@ static bool default_suspend_ok(struct device *dev)
|
|||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
if (constraint_ns < 0)
|
||||
if (constraint_ns == 0)
|
||||
return false;
|
||||
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
|
||||
constraint_ns = -1;
|
||||
else
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
|
||||
/*
|
||||
* We can walk the children without any additional locking, because
|
||||
* they all have been suspended at this point and their
|
||||
|
@ -76,14 +77,19 @@ static bool default_suspend_ok(struct device *dev)
|
|||
device_for_each_child(dev, &constraint_ns,
|
||||
dev_update_qos_constraint);
|
||||
|
||||
if (constraint_ns > 0) {
|
||||
constraint_ns -= td->suspend_latency_ns +
|
||||
td->resume_latency_ns;
|
||||
if (constraint_ns == 0)
|
||||
return false;
|
||||
if (constraint_ns < 0) {
|
||||
/* The children have no constraints. */
|
||||
td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
|
||||
td->cached_suspend_ok = true;
|
||||
} else {
|
||||
constraint_ns -= td->suspend_latency_ns + td->resume_latency_ns;
|
||||
if (constraint_ns > 0) {
|
||||
td->effective_constraint_ns = constraint_ns;
|
||||
td->cached_suspend_ok = true;
|
||||
} else {
|
||||
td->effective_constraint_ns = 0;
|
||||
}
|
||||
}
|
||||
td->effective_constraint_ns = constraint_ns;
|
||||
td->cached_suspend_ok = constraint_ns >= 0;
|
||||
|
||||
/*
|
||||
* The children have been suspended already, so we don't need to take
|
||||
|
@ -145,13 +151,14 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
|
|||
td = &to_gpd_data(pdd)->td;
|
||||
constraint_ns = td->effective_constraint_ns;
|
||||
/* default_suspend_ok() need not be called before us. */
|
||||
if (constraint_ns < 0) {
|
||||
if (constraint_ns < 0)
|
||||
constraint_ns = dev_pm_qos_read_value(pdd->dev);
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
}
|
||||
if (constraint_ns == 0)
|
||||
|
||||
if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
|
||||
continue;
|
||||
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
|
||||
/*
|
||||
* constraint_ns cannot be negative here, because the device has
|
||||
* been suspended.
|
||||
|
|
|
@ -189,7 +189,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
|
|||
plist_head_init(&c->list);
|
||||
c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
|
||||
c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
|
||||
c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
|
||||
c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
|
||||
c->type = PM_QOS_MIN;
|
||||
c->notifiers = n;
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
|
|||
|| (dev->power.request_pending
|
||||
&& dev->power.request == RPM_REQ_RESUME))
|
||||
retval = -EAGAIN;
|
||||
else if (__dev_pm_qos_read_value(dev) < 0)
|
||||
else if (__dev_pm_qos_read_value(dev) == 0)
|
||||
retval = -EPERM;
|
||||
else if (dev->power.runtime_status == RPM_SUSPENDED)
|
||||
retval = 1;
|
||||
|
|
|
@ -218,7 +218,14 @@ static ssize_t pm_qos_resume_latency_show(struct device *dev,
|
|||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", dev_pm_qos_requested_resume_latency(dev));
|
||||
s32 value = dev_pm_qos_requested_resume_latency(dev);
|
||||
|
||||
if (value == 0)
|
||||
return sprintf(buf, "n/a\n");
|
||||
else if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
|
||||
value = 0;
|
||||
|
||||
return sprintf(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t pm_qos_resume_latency_store(struct device *dev,
|
||||
|
@ -228,11 +235,21 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev,
|
|||
s32 value;
|
||||
int ret;
|
||||
|
||||
if (kstrtos32(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
if (!kstrtos32(buf, 0, &value)) {
|
||||
/*
|
||||
* Prevent users from writing negative or "no constraint" values
|
||||
* directly.
|
||||
*/
|
||||
if (value < 0 || value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
|
||||
return -EINVAL;
|
||||
|
||||
if (value < 0)
|
||||
if (value == 0)
|
||||
value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
|
||||
} else if (!strcmp(buf, "n/a") || !strcmp(buf, "n/a\n")) {
|
||||
value = 0;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req,
|
||||
value);
|
||||
|
|
|
@ -298,8 +298,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
|
|||
data->needs_update = 0;
|
||||
}
|
||||
|
||||
/* resume_latency is 0 means no restriction */
|
||||
if (resume_latency && resume_latency < latency_req)
|
||||
if (resume_latency < latency_req &&
|
||||
resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
|
||||
latency_req = resume_latency;
|
||||
|
||||
/* Special case when user has set very strict latency requirement */
|
||||
|
|
|
@ -27,16 +27,17 @@ enum pm_qos_flags_status {
|
|||
PM_QOS_FLAGS_ALL,
|
||||
};
|
||||
|
||||
#define PM_QOS_DEFAULT_VALUE -1
|
||||
#define PM_QOS_DEFAULT_VALUE (-1)
|
||||
#define PM_QOS_LATENCY_ANY S32_MAX
|
||||
|
||||
#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
|
||||
#define PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE 0
|
||||
#define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE 0
|
||||
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY
|
||||
#define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0
|
||||
#define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
|
||||
#define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1))
|
||||
|
||||
#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
|
||||
#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1)
|
||||
|
|
Loading…
Reference in a new issue