Merge tag 'pm-5.5-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull additional power management updates from Rafael Wysocki: "These fix an ACPI EC driver bug exposed by the recent rework of the suspend-to-idle code flow, reintroduce frequency constraints into device PM QoS (in preparation for adding QoS support to devfreq), drop a redundant field from struct cpuidle_state and clean up Kconfig in some places. Specifics: - Avoid a race condition in the ACPI EC driver that may cause systems to be unable to leave suspend-to-idle (Rafael Wysocki) - Drop the "disabled" field, which is redundant, from struct cpuidle_state (Rafael Wysocki) - Reintroduce device PM QoS frequency constraints (temporarily introduced and than dropped during the 5.4 cycle) in preparation for adding QoS support to devfreq (Leonard Crestez) - Clean up indentation (in multiple places) and the cpuidle drivers help text in Kconfig (Krzysztof Kozlowski, Randy Dunlap)" * tag 'pm-5.5-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: ACPI: PM: s2idle: Rework ACPI events synchronization ACPI: EC: Rework flushing of pending work PM / devfreq: Add missing locking while setting suspend_freq PM / QoS: Restore DEV_PM_QOS_MIN/MAX_FREQUENCY PM / QoS: Reorder pm_qos/freq_qos/dev_pm_qos structs PM / QoS: Initial kunit test PM / QoS: Redefine FREQ_QOS_MAX_DEFAULT_VALUE to S32_MAX power: avs: Fix Kconfig indentation cpufreq: Fix Kconfig indentation cpuidle: minor Kconfig help text fixes cpuidle: Drop disabled field from struct cpuidle_state cpuidle: Fix Kconfig indentation
This commit is contained in:
@@ -148,6 +148,10 @@ config DEBUG_TEST_DRIVER_REMOVE
|
||||
unusable. You should say N here unless you are explicitly looking to
|
||||
test this functionality.
|
||||
|
||||
config PM_QOS_KUNIT_TEST
|
||||
bool "KUnit Test for PM QoS features"
|
||||
depends on KUNIT
|
||||
|
||||
config HMEM_REPORTING
|
||||
bool
|
||||
default n
|
||||
|
@@ -4,5 +4,6 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o wakeup_stats.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
||||
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
|
||||
obj-$(CONFIG_PM_QOS_KUNIT_TEST) += qos-test.o
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
|
117
drivers/base/power/qos-test.c
Normal file
117
drivers/base/power/qos-test.c
Normal file
@@ -0,0 +1,117 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
/* Basic test for aggregating two "min" requests */
|
||||
static void freq_qos_test_min(struct kunit *test)
|
||||
{
|
||||
struct freq_constraints qos;
|
||||
struct freq_qos_request req1, req2;
|
||||
int ret;
|
||||
|
||||
freq_constraints_init(&qos);
|
||||
memset(&req1, 0, sizeof(req1));
|
||||
memset(&req2, 0, sizeof(req2));
|
||||
|
||||
ret = freq_qos_add_request(&qos, &req1, FREQ_QOS_MIN, 1000);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
ret = freq_qos_add_request(&qos, &req2, FREQ_QOS_MIN, 2000);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 2000);
|
||||
|
||||
ret = freq_qos_remove_request(&req2);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 1000);
|
||||
|
||||
ret = freq_qos_remove_request(&req1);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN),
|
||||
FREQ_QOS_MIN_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
/* Test that requests for MAX_DEFAULT_VALUE have no effect */
|
||||
static void freq_qos_test_maxdef(struct kunit *test)
|
||||
{
|
||||
struct freq_constraints qos;
|
||||
struct freq_qos_request req1, req2;
|
||||
int ret;
|
||||
|
||||
freq_constraints_init(&qos);
|
||||
memset(&req1, 0, sizeof(req1));
|
||||
memset(&req2, 0, sizeof(req2));
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX),
|
||||
FREQ_QOS_MAX_DEFAULT_VALUE);
|
||||
|
||||
ret = freq_qos_add_request(&qos, &req1, FREQ_QOS_MAX,
|
||||
FREQ_QOS_MAX_DEFAULT_VALUE);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
ret = freq_qos_add_request(&qos, &req2, FREQ_QOS_MAX,
|
||||
FREQ_QOS_MAX_DEFAULT_VALUE);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
/* Add max 1000 */
|
||||
ret = freq_qos_update_request(&req1, 1000);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), 1000);
|
||||
|
||||
/* Add max 2000, no impact */
|
||||
ret = freq_qos_update_request(&req2, 2000);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), 1000);
|
||||
|
||||
/* Remove max 1000, new max 2000 */
|
||||
ret = freq_qos_remove_request(&req1);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), 2000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a freq_qos_request can be added again after removal
|
||||
*
|
||||
* This issue was solved by commit 05ff1ba412fd ("PM: QoS: Invalidate frequency
|
||||
* QoS requests after removal")
|
||||
*/
|
||||
static void freq_qos_test_readd(struct kunit *test)
|
||||
{
|
||||
struct freq_constraints qos;
|
||||
struct freq_qos_request req;
|
||||
int ret;
|
||||
|
||||
freq_constraints_init(&qos);
|
||||
memset(&req, 0, sizeof(req));
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN),
|
||||
FREQ_QOS_MIN_DEFAULT_VALUE);
|
||||
|
||||
/* Add */
|
||||
ret = freq_qos_add_request(&qos, &req, FREQ_QOS_MIN, 1000);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 1000);
|
||||
|
||||
/* Remove */
|
||||
ret = freq_qos_remove_request(&req);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN),
|
||||
FREQ_QOS_MIN_DEFAULT_VALUE);
|
||||
|
||||
/* Add again */
|
||||
ret = freq_qos_add_request(&qos, &req, FREQ_QOS_MIN, 2000);
|
||||
KUNIT_EXPECT_EQ(test, ret, 1);
|
||||
KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 2000);
|
||||
}
|
||||
|
||||
static struct kunit_case pm_qos_test_cases[] = {
|
||||
KUNIT_CASE(freq_qos_test_min),
|
||||
KUNIT_CASE(freq_qos_test_maxdef),
|
||||
KUNIT_CASE(freq_qos_test_readd),
|
||||
{},
|
||||
};
|
||||
|
||||
static struct kunit_suite pm_qos_test_module = {
|
||||
.name = "qos-kunit-test",
|
||||
.test_cases = pm_qos_test_cases,
|
||||
};
|
||||
kunit_test_suite(pm_qos_test_module);
|
@@ -115,10 +115,20 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type)
|
||||
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
|
||||
if (type == DEV_PM_QOS_RESUME_LATENCY) {
|
||||
switch (type) {
|
||||
case DEV_PM_QOS_RESUME_LATENCY:
|
||||
ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT
|
||||
: pm_qos_read_value(&qos->resume_latency);
|
||||
} else {
|
||||
break;
|
||||
case DEV_PM_QOS_MIN_FREQUENCY:
|
||||
ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE
|
||||
: freq_qos_read_value(&qos->freq, FREQ_QOS_MIN);
|
||||
break;
|
||||
case DEV_PM_QOS_MAX_FREQUENCY:
|
||||
ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE
|
||||
: freq_qos_read_value(&qos->freq, FREQ_QOS_MAX);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = 0;
|
||||
}
|
||||
@@ -159,6 +169,10 @@ static int apply_constraint(struct dev_pm_qos_request *req,
|
||||
req->dev->power.set_latency_tolerance(req->dev, value);
|
||||
}
|
||||
break;
|
||||
case DEV_PM_QOS_MIN_FREQUENCY:
|
||||
case DEV_PM_QOS_MAX_FREQUENCY:
|
||||
ret = freq_qos_apply(&req->data.freq, action, value);
|
||||
break;
|
||||
case DEV_PM_QOS_FLAGS:
|
||||
ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
|
||||
action, value);
|
||||
@@ -209,6 +223,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
|
||||
c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
|
||||
c->type = PM_QOS_MIN;
|
||||
|
||||
freq_constraints_init(&qos->freq);
|
||||
|
||||
INIT_LIST_HEAD(&qos->flags.list);
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
@@ -269,6 +285,20 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
|
||||
c = &qos->freq.min_freq;
|
||||
plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) {
|
||||
apply_constraint(req, PM_QOS_REMOVE_REQ,
|
||||
PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
|
||||
c = &qos->freq.max_freq;
|
||||
plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) {
|
||||
apply_constraint(req, PM_QOS_REMOVE_REQ,
|
||||
PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
|
||||
f = &qos->flags;
|
||||
list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) {
|
||||
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
|
||||
@@ -314,11 +344,22 @@ static int __dev_pm_qos_add_request(struct device *dev,
|
||||
ret = dev_pm_qos_constraints_allocate(dev);
|
||||
|
||||
trace_dev_pm_qos_add_request(dev_name(dev), type, value);
|
||||
if (!ret) {
|
||||
req->dev = dev;
|
||||
req->type = type;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
req->dev = dev;
|
||||
req->type = type;
|
||||
if (req->type == DEV_PM_QOS_MIN_FREQUENCY)
|
||||
ret = freq_qos_add_request(&dev->power.qos->freq,
|
||||
&req->data.freq,
|
||||
FREQ_QOS_MIN, value);
|
||||
else if (req->type == DEV_PM_QOS_MAX_FREQUENCY)
|
||||
ret = freq_qos_add_request(&dev->power.qos->freq,
|
||||
&req->data.freq,
|
||||
FREQ_QOS_MAX, value);
|
||||
else
|
||||
ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -382,6 +423,10 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req,
|
||||
case DEV_PM_QOS_LATENCY_TOLERANCE:
|
||||
curr_value = req->data.pnode.prio;
|
||||
break;
|
||||
case DEV_PM_QOS_MIN_FREQUENCY:
|
||||
case DEV_PM_QOS_MAX_FREQUENCY:
|
||||
curr_value = req->data.freq.pnode.prio;
|
||||
break;
|
||||
case DEV_PM_QOS_FLAGS:
|
||||
curr_value = req->data.flr.flags;
|
||||
break;
|
||||
@@ -507,6 +552,14 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
|
||||
ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers,
|
||||
notifier);
|
||||
break;
|
||||
case DEV_PM_QOS_MIN_FREQUENCY:
|
||||
ret = freq_qos_add_notifier(&dev->power.qos->freq,
|
||||
FREQ_QOS_MIN, notifier);
|
||||
break;
|
||||
case DEV_PM_QOS_MAX_FREQUENCY:
|
||||
ret = freq_qos_add_notifier(&dev->power.qos->freq,
|
||||
FREQ_QOS_MAX, notifier);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
@@ -546,6 +599,14 @@ int dev_pm_qos_remove_notifier(struct device *dev,
|
||||
ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
|
||||
notifier);
|
||||
break;
|
||||
case DEV_PM_QOS_MIN_FREQUENCY:
|
||||
ret = freq_qos_remove_notifier(&dev->power.qos->freq,
|
||||
FREQ_QOS_MIN, notifier);
|
||||
break;
|
||||
case DEV_PM_QOS_MAX_FREQUENCY:
|
||||
ret = freq_qos_remove_notifier(&dev->power.qos->freq,
|
||||
FREQ_QOS_MAX, notifier);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
|
Reference in New Issue
Block a user