On 26.11.2019 23:01, Matthias Kaehlcke wrote:
On Tue, Nov 26, 2019 at 05:17:13PM +0200, Leonard Crestez wrote:
Support for adding per-device frequency limits was removed in commit 2aac8bdf7a0f ("PM: QoS: Drop frequency QoS types from device PM QoS") after cpufreq switched to use a new "freq_constraints" construct.
Restore support for per-device freq limits but base this upon freq_constraints. This is primarily meant to be used by the devfreq subsystem.
This removes the "static" marking on freq_qos_apply but does not export it for modules.
Signed-off-by: Leonard Crestez leonard.crestez@nxp.com
drivers/base/power/qos.c | 73 ++++++++++++++++++++++++++++++++++++---- include/linux/pm_qos.h | 12 +++++++ kernel/power/qos.c | 4 ++- 3 files changed, 82 insertions(+), 7 deletions(-)
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 350dcafd751f..8e93167f1783 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -113,14 +113,24 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) unsigned long flags; s32 ret; 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; }
spin_unlock_irqrestore(&dev->power.lock, flags); @@ -157,10 +167,14 @@ static int apply_constraint(struct dev_pm_qos_request *req, if (ret) { value = pm_qos_read_value(&qos->latency_tolerance); 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);
case DEV_PM_QOS_FLAGS: ret = pm_qos_update_flags(&qos->flags, &req->data.flr, action, value); break; default:break;
@@ -207,10 +221,12 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->target_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; c->default_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; 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); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); @@ -267,10 +283,24 @@ void dev_pm_qos_constraints_destroy(struct device *dev) plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 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));
- }
nit: these loops look a bit redundant, might be worth to encapsulate them in a macro.
There are only 2 identical loops though? Other requests to be cleared are enumerated in slightly different ways.
A pm_qos_for_each_request macro could be added but it would only be used in a handful of places.
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); memset(req, 0, sizeof(*req)); } @@ -312,15 +342,26 @@ static int __dev_pm_qos_add_request(struct device *dev, ret = -ENODEV; else if (!dev->power.qos) 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; }
/**
- dev_pm_qos_add_request - inserts new qos request into the list
@@ -380,10 +421,14 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, switch(req->type) { case DEV_PM_QOS_RESUME_LATENCY: 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;
case DEV_PM_QOS_FLAGS: curr_value = req->data.flr.flags; break; default: return -EINVAL;break;
@@ -505,10 +550,18 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, switch (type) { case DEV_PM_QOS_RESUME_LATENCY: 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);
default: WARN_ON(1); ret = -EINVAL; }break;
@@ -544,10 +597,18 @@ int dev_pm_qos_remove_notifier(struct device *dev, switch (type) { case DEV_PM_QOS_RESUME_LATENCY: 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);
default: WARN_ON(1); ret = -EINVAL; }break;
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 678fec6da5b9..19eafca5680e 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -32,10 +32,12 @@ enum pm_qos_flags_status { #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE PM_QOS_LATENCY_ANY #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 +#define PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE 0 +#define PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE FREQ_QOS_MAX_DEFAULT_VALUE #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) struct pm_qos_request { @@ -99,25 +101,29 @@ struct freq_qos_request { enum dev_pm_qos_req_type { DEV_PM_QOS_RESUME_LATENCY = 1, DEV_PM_QOS_LATENCY_TOLERANCE,
- DEV_PM_QOS_MIN_FREQUENCY,
- DEV_PM_QOS_MAX_FREQUENCY, DEV_PM_QOS_FLAGS, };
struct dev_pm_qos_request { enum dev_pm_qos_req_type type; union { struct plist_node pnode; struct pm_qos_flags_request flr;
} data; struct device *dev; };struct freq_qos_request freq;
struct dev_pm_qos { struct pm_qos_constraints resume_latency; struct pm_qos_constraints latency_tolerance;
- struct freq_constraints freq; struct pm_qos_flags flags; struct dev_pm_qos_request *resume_latency_req; struct dev_pm_qos_request *latency_tolerance_req; struct dev_pm_qos_request *flags_req; };
@@ -212,10 +218,14 @@ static inline s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) { switch (type) { case DEV_PM_QOS_RESUME_LATENCY: return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
- case DEV_PM_QOS_MIN_FREQUENCY:
return PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
- case DEV_PM_QOS_MAX_FREQUENCY:
default: WARN_ON(1); return 0; } }return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
@@ -291,10 +301,12 @@ s32 freq_qos_read_value(struct freq_constraints *qos, int freq_qos_add_request(struct freq_constraints *qos, struct freq_qos_request *req, enum freq_qos_req_type type, s32 value); int freq_qos_update_request(struct freq_qos_request *req, s32 new_value); int freq_qos_remove_request(struct freq_qos_request *req); +int freq_qos_apply(struct freq_qos_request *req,
enum pm_qos_req_action action, s32 value);
int freq_qos_add_notifier(struct freq_constraints *qos, enum freq_qos_req_type type, struct notifier_block *notifier); int freq_qos_remove_notifier(struct freq_constraints *qos, diff --git a/kernel/power/qos.c b/kernel/power/qos.c index a45cba7df0ae..83edf8698118 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -712,12 +712,14 @@ s32 freq_qos_read_value(struct freq_constraints *qos, /**
- freq_qos_apply - Add/modify/remove frequency QoS request.
- @req: Constraint request to apply.
- @action: Action to perform (add/update/remove).
- @value: Value to assign to the QoS request.
*/
- This is only meant to be called from inside pm_qos, not drivers.
-static int freq_qos_apply(struct freq_qos_request *req, +int freq_qos_apply(struct freq_qos_request *req, enum pm_qos_req_action action, s32 value) { int ret; switch(req->type) {
Reviewed-by: Matthias Kaehlcke mka@chromium.org Tested-by: Matthias Kaehlcke mka@chromium.org
Thanks for taking the time to look into this.