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@xxxxxxx> > --- > 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@xxxxxxxxxxxx> Tested-by: Matthias Kaehlcke <mka@xxxxxxxxxxxx>