Message ID | 44cfaddb5be6403f687a0e56e57f17925b9a521b.1574781196.git.leonard.crestez@nxp.com (mailing list archive) |
---|---|
State | Accepted |
Delegated to: | Rafael Wysocki |
Headers | show |
Series | PM / QoS: Restore DEV_PM_QOS_MIN/MAX_FREQUENCY | expand |
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); > + break; > case DEV_PM_QOS_FLAGS: > ret = pm_qos_update_flags(&qos->flags, &req->data.flr, > action, value); > break; > default: > @@ -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. > 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; > + break; > case DEV_PM_QOS_FLAGS: > curr_value = req->data.flr.flags; > break; > default: > return -EINVAL; > @@ -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); > + break; > default: > WARN_ON(1); > ret = -EINVAL; > } > > @@ -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); > + break; > default: > WARN_ON(1); > ret = -EINVAL; > } > > 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; > + struct freq_qos_request freq; > } data; > struct device *dev; > }; > > 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: > + return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; > default: > WARN_ON(1); > return 0; > } > } > @@ -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>
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); >> + break; >> case DEV_PM_QOS_FLAGS: >> ret = pm_qos_update_flags(&qos->flags, &req->data.flr, >> action, value); >> break; >> default: >> @@ -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; >> + break; >> case DEV_PM_QOS_FLAGS: >> curr_value = req->data.flr.flags; >> break; >> default: >> return -EINVAL; >> @@ -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); >> + break; >> default: >> WARN_ON(1); >> ret = -EINVAL; >> } >> >> @@ -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); >> + break; >> default: >> WARN_ON(1); >> ret = -EINVAL; >> } >> >> 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; >> + struct freq_qos_request freq; >> } data; >> struct device *dev; >> }; >> >> 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: >> + return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; >> default: >> WARN_ON(1); >> return 0; >> } >> } >> @@ -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.
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); + break; case DEV_PM_QOS_FLAGS: ret = pm_qos_update_flags(&qos->flags, &req->data.flr, action, value); break; default: @@ -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)); + } + 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; + break; case DEV_PM_QOS_FLAGS: curr_value = req->data.flr.flags; break; default: return -EINVAL; @@ -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); + break; default: WARN_ON(1); ret = -EINVAL; } @@ -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); + break; default: WARN_ON(1); ret = -EINVAL; } 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; + struct freq_qos_request freq; } data; struct device *dev; }; 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: + return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; default: WARN_ON(1); return 0; } } @@ -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) {
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(-)