Re: [PATCH V3 3/5] PM / QoS: Add support for MIN/MAX frequency constraints
From: Ulf Hansson
Date: Mon Jun 17 2019 - 05:29:05 EST
On Mon, 10 Jun 2019 at 12:52, Viresh Kumar <viresh.kumar@xxxxxxxxxx> wrote:
>
> This patch introduces the min-frequency and max-frequency device
> constraints, which will be used by the cpufreq core to begin with.
>
> Signed-off-by: Viresh Kumar <viresh.kumar@xxxxxxxxxx>
Reviewed-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
Kind regards
Uffe
> ---
> drivers/base/power/qos.c | 103 +++++++++++++++++++++++++++++++++------
> include/linux/pm_qos.h | 18 +++++++
> 2 files changed, 107 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
> index 7bb88174371f..e977aef437a5 100644
> --- a/drivers/base/power/qos.c
> +++ b/drivers/base/power/qos.c
> @@ -151,6 +151,14 @@ 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:
> + ret = pm_qos_update_target(&qos->min_frequency,
> + &req->data.pnode, action, value);
> + break;
> + case DEV_PM_QOS_MAX_FREQUENCY:
> + ret = pm_qos_update_target(&qos->max_frequency,
> + &req->data.pnode, action, value);
> + break;
> case DEV_PM_QOS_FLAGS:
> ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
> action, value);
> @@ -179,12 +187,11 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
> if (!qos)
> return -ENOMEM;
>
> - n = kzalloc(sizeof(*n), GFP_KERNEL);
> + n = kzalloc(3 * sizeof(*n), GFP_KERNEL);
> if (!n) {
> kfree(qos);
> return -ENOMEM;
> }
> - BLOCKING_INIT_NOTIFIER_HEAD(n);
>
> c = &qos->resume_latency;
> plist_head_init(&c->list);
> @@ -193,6 +200,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
> c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
> c->type = PM_QOS_MIN;
> c->notifiers = n;
> + BLOCKING_INIT_NOTIFIER_HEAD(n);
>
> c = &qos->latency_tolerance;
> plist_head_init(&c->list);
> @@ -201,6 +209,24 @@ 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;
>
> + c = &qos->min_frequency;
> + plist_head_init(&c->list);
> + c->target_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
> + c->default_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
> + c->no_constraint_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
> + c->type = PM_QOS_MAX;
> + c->notifiers = ++n;
> + BLOCKING_INIT_NOTIFIER_HEAD(n);
> +
> + c = &qos->max_frequency;
> + plist_head_init(&c->list);
> + c->target_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
> + c->default_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
> + c->no_constraint_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
> + c->type = PM_QOS_MIN;
> + c->notifiers = ++n;
> + BLOCKING_INIT_NOTIFIER_HEAD(n);
> +
> INIT_LIST_HEAD(&qos->flags.list);
>
> spin_lock_irq(&dev->power.lock);
> @@ -254,11 +280,25 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
> apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> memset(req, 0, sizeof(*req));
> }
> +
> c = &qos->latency_tolerance;
> 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->min_frequency;
> + plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
> + apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE);
> + memset(req, 0, sizeof(*req));
> + }
> +
> + c = &qos->max_frequency;
> + plist_for_each_entry_safe(req, tmp, &c->list, data.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);
> @@ -370,6 +410,8 @@ 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:
> + case DEV_PM_QOS_MIN_FREQUENCY:
> + case DEV_PM_QOS_MAX_FREQUENCY:
> curr_value = req->data.pnode.prio;
> break;
> case DEV_PM_QOS_FLAGS:
> @@ -482,9 +524,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
> {
> int ret = 0;
>
> - if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
> - return -EINVAL;
> -
> mutex_lock(&dev_pm_qos_mtx);
>
> if (IS_ERR(dev->power.qos))
> @@ -492,10 +531,28 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
> else if (!dev->power.qos)
> ret = dev_pm_qos_constraints_allocate(dev);
>
> - if (!ret)
> + if (ret)
> + goto unlock;
> +
> + 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 = blocking_notifier_chain_register(dev->power.qos->min_frequency.notifiers,
> + notifier);
> + break;
> + case DEV_PM_QOS_MAX_FREQUENCY:
> + ret = blocking_notifier_chain_register(dev->power.qos->max_frequency.notifiers,
> + notifier);
> + break;
> + default:
> + WARN_ON(1);
> + ret = -EINVAL;
> + }
>
> +unlock:
> mutex_unlock(&dev_pm_qos_mtx);
> return ret;
> }
> @@ -516,20 +573,35 @@ int dev_pm_qos_remove_notifier(struct device *dev,
> struct notifier_block *notifier,
> enum dev_pm_qos_req_type type)
> {
> - int retval = 0;
> -
> - if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
> - return -EINVAL;
> + int ret = 0;
>
> mutex_lock(&dev_pm_qos_mtx);
>
> /* Silently return if the constraints object is not present. */
> - if (!IS_ERR_OR_NULL(dev->power.qos))
> - retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
> - notifier);
> + if (IS_ERR_OR_NULL(dev->power.qos))
> + goto unlock;
>
> + 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 = blocking_notifier_chain_unregister(dev->power.qos->min_frequency.notifiers,
> + notifier);
> + break;
> + case DEV_PM_QOS_MAX_FREQUENCY:
> + ret = blocking_notifier_chain_unregister(dev->power.qos->max_frequency.notifiers,
> + notifier);
> + break;
> + default:
> + WARN_ON(1);
> + ret = -EINVAL;
> + }
> +
> +unlock:
> mutex_unlock(&dev_pm_qos_mtx);
> - return retval;
> + return ret;
> }
> EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
>
> @@ -589,6 +661,9 @@ static void __dev_pm_qos_drop_user_request(struct device *dev,
> req = dev->power.qos->flags_req;
> dev->power.qos->flags_req = NULL;
> break;
> + default:
> + WARN_ON(1);
> + return;
> }
> __dev_pm_qos_remove_request(req);
> kfree(req);
> diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
> index 55814d48c39c..3f994283a5ae 100644
> --- a/include/linux/pm_qos.h
> +++ b/include/linux/pm_qos.h
> @@ -40,6 +40,8 @@ enum pm_qos_flags_status {
> #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 (-1)
> #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
>
> #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
> @@ -58,6 +60,8 @@ struct pm_qos_flags_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,
> };
>
> @@ -99,10 +103,14 @@ struct pm_qos_flags {
> struct dev_pm_qos {
> struct pm_qos_constraints resume_latency;
> struct pm_qos_constraints latency_tolerance;
> + struct pm_qos_constraints min_frequency;
> + struct pm_qos_constraints max_frequency;
> 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;
> + struct dev_pm_qos_request *min_frequency_req;
> + struct dev_pm_qos_request *max_frequency_req;
> };
>
> /* Action requested to pm_qos_update_target */
> @@ -185,6 +193,12 @@ static inline s32 dev_pm_qos_raw_read_value(struct device *dev,
> case DEV_PM_QOS_RESUME_LATENCY:
> return IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT
> : pm_qos_read_value(&qos->resume_latency);
> + case DEV_PM_QOS_MIN_FREQUENCY:
> + return IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE
> + : pm_qos_read_value(&qos->min_frequency);
> + case DEV_PM_QOS_MAX_FREQUENCY:
> + return IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE
> + : pm_qos_read_value(&qos->max_frequency);
> default:
> WARN_ON(1);
> return 0;
> @@ -256,6 +270,10 @@ static inline s32 dev_pm_qos_raw_read_value(struct device *dev,
> 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;
> --
> 2.21.0.rc0.269.g1a574e7a288b
>